Playing with Crossplane, for real

  • having to manage a state file which holds the result of the last apply
  • globally not possible to re-use code or build simple abstraction to create a resource in different clouds, like a K8s cluster in AWS and Google.

Then enters Crossplane !

TL ; DR

  • I’m no Crossplane expert and only spent few days playing with it for a Proof Of Concept. Please, help me better understand/use it if you think i’m wrong
  • OSS Crossplane Providers are really limited
  • Jet Providers are on par with Terraform providers, but lack docs and may be buggy and are not well supported or updated by the community
  • Still unsure how to replicate 100% of what you can do with TF

Crossplane

https://crossplane.github.io/docs/v1.9/concepts/composition.html#overview
https://crossplane.github.io/docs/v1.9/concepts/composition.html#how-it-works
  1. Infra: Deploy a Provider, like GKE, AWS, Helm, K8s, even a Terraform provider
  2. The provider created a set of CRDs corresponding to each Cloud resource it manages (yellow boxes)
  3. Infra: Create a CompositeResourceDefinition (XRD) which creates an interface with a limited set of parameters to tweak
  4. Crossplane will create and maintain two new CRDs based on the XRD: a Claim and a CompositeResource (XR) (green boxes). Crossplane will start watching and reconciling CR based on those CRDs
  5. Infra: Create a Composition, which will reference a source XRD and a list of Resources to created (from the CRDs created by the Provider). It’s a sort of templating resources with values from the interface (the XRD)
  6. Dev: Claim a resource (purple box) -> a Claim is actually a CustomResource of a type maintained by Crossplane
  7. Crossplane will create a CompositeResource (XR) based on the Claim
  8. Crossplane will create CustomResources (CR) which are instances of the Provider’s resources, based on the content of the CompositeResource (XR) (red boxes)
  9. Provider will reconcile the resources he manages, and call GCP API (in case of the GCP provider) to create the resources declared in the CR

Question 1: Providers

Smart ?

Question 2: Docs ?

  • Should I create Claims or XR ?
  • what is this compositionRef about ?
  • where should I set writeConnectionSecretToRef ? in the Claim ? The XR ? the resource in the Composition ?
  • What are the secret values that the provider is returning ?

Jet Providers, straight from Terraform

XRD

apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
name: xjetpostgresqls.database.wk
spec:
group: database.wk
names:
kind: XJetPostgreSQL
plural: xjetpostgresqls
claimNames:
kind: JetPostgreSQL
plural: jetpostgresqls
versions:
- name: v1alpha1
served: true
referenceable: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
parameters:
type: object
properties:
storageGB:
type: integer
description: size of the Database in GB - integer
dbName:
type: string
description: name of the new DB inside the DB instance - string
instanceSize:
type: string
description: instance size - string
enum:
- small
- medium
- large
required:
- storageGB
- dbName
- instanceSize
required:
- parameters
properties:
dbs:
type: array
items:
type: object
properties:
name:
type: string
description: name of the new DB inside the DB instance - string
users:
type: array
items:
properties:
name:
type: string

Compositions

apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: jetpostgresql.gcp.database.wk
labels:
provider: gcp
crossplane.io/xrd: xjetpostgresql.database.wk
spec:
# should I set this here ? Please help
# writeConnectionSecretsToNamespace: crossplane
compositeTypeRef:
apiVersion: database.wk/v1alpha1
kind: XJetPostgreSQL
resources:
- name: cloudsqlinstance
base:
apiVersion: sql.gcp.jet.crossplane.io/v1alpha2
kind: DatabaseInstance
metadata:
annotations:
crossplane.io/external-name: "crossplanesqlinstance"
spec:
providerConfigRef:
name: crossplane-provider-jet-gcp
deletionPolicy: Delete
forProvider:
databaseVersion: POSTGRES_14
region: us-central1
deletionProtection: false
settings:
- tier: db-custom-1-3840
diskType: PD_SSD
diskSize: 20
ipConfiguration:
- ipv4Enabled: true
authorizedNetworks:
- value: "0.0.0.0/0"
userLabels:
creator: crossplane
owner: prune
writeConnectionSecretToRef:
namespace: crossplane
name: cloudsqlinstance
patches:
# set diskSize based on the Claim
- fromFieldPath: "spec.parameters.storageGB"
toFieldPath: "spec.forProvider.settings[0].diskSize"
# set the secret name to the claim name
- fromFieldPath: "metadata.labels[crossplane.io/claim-name]"
toFieldPath: "spec.writeConnectionSecretToRef.name"
transforms:
- type: string
string:
fmt: "%s-pginstance"
# change secret namespace to the one of the claim
- fromFieldPath: "metadata.labels[crossplane.io/claim-namespace]"
toFieldPath: "spec.writeConnectionSecretToRef.namespace"
# set label app = name of the original claim
- fromFieldPath: "metadata.labels[crossplane.io/claim-name]"
toFieldPath: "metadata.labels[crossplane.io/app]"
# set the name of the external resource to be the name of the claim
- fromFieldPath: "metadata.labels[crossplane.io/claim-name]"
toFieldPath: "metadata.annotations[crossplane.io/external-name]"
# set instance size to the one defined in the claim
- fromFieldPath: "spec.parameters.instanceSize"
toFieldPath: "spec.forProvider.settings[0].tier"
transforms:
- type: map
map:
small: db-custom-1-3840
medium: db-custom-2-7680
large: db-custom-4-15360
policy:
fromFieldPath: Required
- name: cloudsqldb
base:
apiVersion: sql.gcp.jet.crossplane.io/v1alpha2
kind: Database
metadata:
annotations:
crossplane.io/external-name: "crossplanesqldb"
spec:
providerConfigRef:
name: crossplane-provider-jet-gcp
deletionPolicy: Delete
forProvider:
instanceSelector:
MatchControllerRef: true
writeConnectionSecretToRef:
namespace: crossplane
name: cloudsqldb
patches:
# set the secret name to the claim name
- fromFieldPath: "metadata.labels[crossplane.io/claim-name]"
toFieldPath: "spec.writeConnectionSecretToRef.name"
transforms:
- type: string
string:
fmt: "%s-pgdb"
# change secret namespace to the one of the claim
- fromFieldPath: "metadata.labels[crossplane.io/claim-namespace]"
toFieldPath: "spec.writeConnectionSecretToRef.namespace"
# set the name of the DB resource to be the name defined in the claim
- fromFieldPath: "spec.parameters.dbName"
toFieldPath: "metadata.annotations[crossplane.io/external-name]"
# set app Label
- fromFieldPath: "metadata.labels[crossplane.io/claim-name]"
toFieldPath: "metadata.labels[crossplane.io/app]"
- name: cloudsqldbuser
base:
apiVersion: sql.gcp.jet.crossplane.io/v1alpha2
kind: User
metadata:
annotations:
# set the name of the DB User, this is hardcoded for demo but should come from the CRD
crossplane.io/external-name: "existing-sa-for-db@my-project.iam"
spec:
providerConfigRef:
name: crossplane-provider-jet-gcp
deletionPolicy: Delete
forProvider:
instanceSelector:
MatchControllerRef: true
type: CLOUD_IAM_SERVICE_ACCOUNT
writeConnectionSecretToRef:
namespace: crossplane
name: cloudsqluser
patches:
# set the secret name to the claim name
- fromFieldPath: "metadata.labels[crossplane.io/claim-name]"
toFieldPath: "spec.writeConnectionSecretToRef.name"
transforms:
- type: string
string:
fmt: "%s-pguser"
# change secret namespace to the one of the claim
- fromFieldPath: "metadata.labels[crossplane.io/claim-namespace]"
toFieldPath: "spec.writeConnectionSecretToRef.namespace"
# set the name of the DB User, this is hardcoded for demo but should come from the Claim CRD
# - fromFieldPath: "spec.parameters.dbName"
# toFieldPath: "metadata.annotations[crossplane.io/external-name]"
# set app Label
- fromFieldPath: "metadata.labels[crossplane.io/claim-name]"
toFieldPath: "metadata.labels[crossplane.io/app]"

Claims

apiVersion: database.wk/v1alpha1
kind: JetPostgreSQL
metadata:
namespace: test-namespace
name: jet-db-claim
spec:
parameters:
storageGB: 25
dbName: xrdb
instanceSize: small # small, medium, large
writeConnectionSecretToRef:
name: jet-db-claim-details
# set the secret name to reference the claim name
- fromFieldPath: "metadata.labels[crossplane.io/claim-name]"
toFieldPath: "spec.writeConnectionSecretToRef.name"
transforms:
- type: string
string:
fmt: "%s-pginstance"
# change secret namespace to the one of the claim
- fromFieldPath: "metadata.labels[crossplane.io/claim-namespace]"
toFieldPath: "spec.writeConnectionSecretToRef.namespace"

Question 3: Provider’s execution

Question 4: Support

Conclusion

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store