TL;DR - Summary
- Developers consume the infrastructure provisioned for their claim using connection details that Crossplane publishes
- Platform Engineers must specify exactly what connection details to publish to the developer from all the resources they compose together
- This is highly customizable, but can be challenging to get correct because each layer in the composition has to define specific details
- This blog post walks through each step to get connection information successfully published for the developer
- A small working example can be found in this gist
- Further details can be found in the official Crossplane documentation in "Understanding Connection Details"
Compositions in Crossplane allows you to assemble specific cloud provider resources of your choosing and expose them as a higher level abstraction for your developers, giving them self-service capability to provision when they need it. The underlying composed resources often have connection details that you’ll want to propagate up to your developers after successful provisioning so they can find the resources and consume them.
A common source of confusion is how to specify exactly what details should be surfaced. It’s not uncommon for platform engineers to build an opinionated API that results in the correct set of resources being provisioned in the cloud, but missing vital connection information for the developers to discover and connect to these resources.
Today’s entry in the Crossplane FAQ series will demystify this process for you and help you confidently answer the challenge of “where are my claim’s connection details?”
Aligning the Moving Parts
Let’s start with a visualization to refresh our memories on the various components and layers involved in composition:
- Developer creates a claim (XRC) in their namespace to express their desire to provision infrastructure
- A composite resource (XR) is created in response by Crossplane and matched to a composition
- A number of child composed resources are created as defined by the XR’s composition
Each child composed resource (e.g. a database, message queue, or even another nested composite resource) in this graph can have its own individual connection details, all of which can be propagated back up to the developer’s claim. The platform engineer who authors the composite resource and its composition needs to define the specific connection details from each of the children that should be aggregated and published back to the developer for consumption, as demonstrated below:
Specifying the details of this “aggregation” can be challenging to get correct, so let’s dive into the details to make it easy!
Critical Elements of a Connection
To get all of these resources and connection details fully wired up, there are 4 steps to take, working from top to bottom of our composition layers:
- The claim must specify the secret where the aggregated connection details should be written
- This is the
spec.writeConnectionSecretToReffield in a claim
- If creating a composite resource directly (without a claim) then this same field must be set on your composite resource instead
- This is the
- The composite resource definition must state which connection details to aggregate from its children to publish to the claim
- This is the
spec.connectionSecretKeysfield in a
- This is the
- The composition must define where to write its aggregated connection details
- This is the
spec.writeConnectionSecretsToNamespacefield in the
- This is the
- Each child composed resource must define the connection details it publishes and where to write them
- These are the
base.spec.writeConnectionSecretToReffields of the composed resources
- These are the
With these high level steps defined, let’s look more deeply at code samples for each.
1. Claim Connection Details
When the developer creates their claim to kick off the infrastructure provisioning process, they specify the name of the Kubernetes secret that Crossplane writes its aggregated connection details to. This is the location the developer will be looking to find the details they need to connect to the resources after provisioning is successful.
All they have to do is specify a name for the
spec.writeConnectionSecretToRef field in their claim:
apiVersion: acme.com/v1 kind: AcmeDatabase metadata: name: acme-db-prod spec: storageGB: 15 writeConnectionSecretToRef: name: acme-db-prod-conn
2. Composite Resource Connection Details
The composite resource is a parent for a number of child composed resources. As each of those children can have their own connection details, the parent composite resource needs to decide which connection details from each child to aggregate and then publish for the claim.
This is done in the
CompositeResourceDefinition for the composite resource, in the
connectionSecretKeys field. The easiest path, if you're okay with everything being exposed to the developer, is to not specify any entries at all. In that case, all available connection details from all the child composed resources will be propagated.
apiVersion: apiextensions.crossplane.io/v1 kind: CompositeResourceDefinition metadata: name: xacmedatabases.acme.com spec: … connectionSecretKeys: - connectionName - publicIP - serverCACertificateCert - databaseID
If you choose to include specific entries entries in
connectionSecretKeys, each should map to a specific connection detail from one of the child composed resources.
3. Composite Resource Connection Details Location
The composite resource also needs to specify the namespace where all of the connection details should be written that it is aggregating together from its children. Take note that this actually happens in the composition that is selected by the composite resource, not on the composite resource itself.
apiVersion: apiextensions.crossplane.io/v1 kind: Composition metadata: name: xacmedatabases.acme.com spec: writeConnectionSecretsToNamespace: crossplane-system ...
4. Child Composed Resource Connection Details and Location
Finally, each child composed resource needs to define the connection details it will publish and where to publish them to. This is demonstrated below by 2 separate child composed resources that represent a database instance and a DB within that instance. Some irrelevant details have been omitted, but the important part is that each child resource has the following fields specified:
connectionDetailsfor which connection details to write
base.spec.writeConnectionSecretToReffor where to write them
resources: - name: DBInstance base: apiVersion: sql.gcp.upbound.io/v1beta1 kind: DatabaseInstance spec: writeConnectionSecretToRef: namespace: crossplane-system … connectionDetails: - fromConnectionSecretKey: connectionName - fromConnectionSecretKey: publicIP - fromConnectionSecretKey: serverCACertificateCert - name: DB base: apiVersion: sql.gcp.upbound.io/v1beta1 kind: Database spec: writeConnectionSecretToRef: namespace: crossplane-system … connectionDetails: - type: FromFieldPath name: databaseID fromFieldPath: status.atProvider.id
Running the Full Solution
Now that we’ve seen each individual step of publishing connection details to a claim, we can examine the full solution details in the following gist: https://gist.github.com/jbw976/f81016a081f3bea663c98a5af3bf9da6
Assuming the composite resource definition and its composition have been applied to the cluster, the developer can provision an
AcmeDatabase resource by applying the claim. It will take a few minutes for the database to be provisioned, but after everything settles down, we can see that the full connection details have been aggregated from the child composed resources and written to the namespace of the claim, exactly where the developer requested them to be written (in a secret called
❯ kubectl describe secret Name: acme-db-prod-conn Namespace: default Data ==== serverCACertificateCert: 1272 bytes connectionName: 55 bytes databaseID: 100 bytes publicIP: 12 bytes
The developer is now free to consume the underlying infrastructure resources using these published connection details aggregated from the composite resource and its children.
Answers for all your Questions
Another FAQ has been answered and we of course want to keep the momentum going!
What Crossplane questions do you have on your mind? For any questions or problems with Crossplane that you’d like to see featured in this new FAQ series, just post about it on the Crossplane Slack #documentation channel, and we’ll try to include them in an upcoming post.
Crossplane is a community driven project and we welcome you to join the community and contribute through a variety of opportunities, such as opening and commenting on issues, joining the community meetings, sharing your adoption story, and providing feedback on design docs and pull requests.
We love to hear from the community, as they are exactly what makes this project great. Whether you are a developer, user, or just interested in what we're up to, feel free to join us via one of the following methods: