Compositions
Compositions are a template for creating multiple managed resources as a single object.
A Composition composes individual managed resources together into a larger, reusable, solution.
An example Composition may combine a virtual machine, storage resources and networking policies. A Composition template links all these individual resources together.
Here’s an example Composition. When you create an
composite resource
(XR) that uses this Composition, Crossplane uses the template to create the
Amazon S3
managed
resource.
1apiVersion: apiextensions.crossplane.io/v1
2kind: Composition
3metadata:
4 name: example
5spec:
6 compositeTypeRef:
7 apiVersion: custom-api.example.org/v1alpha1
8 kind: AcmeBucket
9 mode: Pipeline
10 pipeline:
11 - step: patch-and-transform
12 functionRef:
13 name: function-patch-and-transform
14 input:
15 apiVersion: pt.fn.crossplane.io/v1beta1
16 kind: Resources
17 resources:
18 - name: storage-bucket
19 base:
20 apiVersion: s3.aws.upbound.io/v1beta1
21 kind: Bucket
22 spec:
23 forProvider:
24 region: "us-east-2"
Crossplane has four core components that users commonly mix up:
- Compositions - This page. A template to define how to create resources.
- Composite Resource Definition
(
XRD
) - A custom API specification. - Composite Resource (
XR
) - Created by using the custom API defined in a Composite Resource Definition. XRs use the Composition template to create new managed resources. - Claims (
XRC
) - Like a Composite Resource, but with namespace scoping.
Create a Composition
Creating a Composition consists of:
- Using composition functions to define the resources to create.
- Enabling composite resources to use the Composition template.
A Composition is a pipeline of composition functions.
Composition functions (or just functions, for short) are Crossplane extensions that template Crossplane resources. Crossplane calls the composition functions to determine what resources it should create when you create a composite resource (XR).
The Crossplane community has built lots of functions that let you template Crossplane resources using CUE, KCL, Helm-like Go templates or legacy Crossplane Patch and Transforms.
You can also write your own function using Go or Python.
Crossplane has two modes of composition:
mode: Pipeline
mode: Resources
Use the Pipeline
mode to use composition functions.
The Resources
mode is deprecated, and you shouldn’t use it. Crossplane
supports Compositions that use the Resources
mode for backward compatibility,
but the feature is no longer maintained. Crossplane doesn’t accept new
Resources
features, and only accepts security bug fixes.
See the CLI documentation
to learn how to use the crossplane beta convert
command to convert a legacy
Resources
Composition to the Pipeline
mode.
Install a composition function
Installing a Function creates a function pod. Crossplane sends requests to this pod to ask it what resources to create when you create a composite resource.
Install a Function with a Crossplane
object setting the
value to the
location of the function package.
For example, to install Function Patch and Transform,
1apiVersion: pkg.crossplane.io/v1
2kind: Function
3metadata:
4 name: function-patch-and-transform
5spec:
6 package: xpkg.crossplane.io/crossplane-contrib/function-patch-and-transform:v0.8.2
By default, the Function pod installs in the same namespace as Crossplane
(crossplane-system
).
Verify a composition function
View the status of a Function with kubectl get functions
During the install a Function reports INSTALLED
as True
and HEALTHY
as
Unknown
.
1kubectl get functions
2NAME INSTALLED HEALTHY PACKAGE AGE
3function-patch-and-transform True Unknown xpkg.crossplane.io/crossplane-contrib/function-patch-and-transform:v0.8.2 10s
After the Function install completes and it’s ready for use the HEALTHY
status
reports True
.
Use a function in a composition
Crossplane calls a Function to determine what resources it should create when you create a composite resource. The Function also tells Crossplane what to do with these resources when you update or delete a composite resource.
When Crossplane calls a Function it sends it the current state of the composite resource. It also sends it the current state of any managed resources the composite resource owns.
Crossplane knows what Function to call when a composite resource changes by looking at the Composition the composite resource uses.
To use composition functions set the Composition
to
.
Define a
of
. Each
calls a Function.
Each
uses a
to reference the
of the Function to call.
Compositions using
can’t specify resource templates with a resources
field.
Use function “Patch and Transform” to create resource templates.
Some Functions also allow you to specify an
.
The function defines the
of input.
This example uses
Function Patch and Transform.
Function Patch and Transform implements Crossplane resource
templates.
The input kind is
,
and it accepts
as input.
1apiVersion: apiextensions.crossplane.io/v1
2kind: Composition
3# Removed for Brevity
4spec:
5 # Removed for Brevity
6 mode: Pipeline
7 pipeline:
8 - step: patch-and-transform
9 functionRef:
10 name: function-patch-and-transform
11 input:
12 apiVersion: pt.fn.crossplane.io/v1beta1
13 kind: Resources
14 resources:
15 - name: storage-bucket
16 base:
17 apiVersion: s3.aws.upbound.io/v1beta1
18 kind: Bucket
19 spec:
20 forProvider:
21 region: "us-east-2"
Use a pipeline of functions in a composition
Crossplane can ask more than one Function what to do when a composite resource changes. When a Composition has a pipeline of two or more steps, Crossplane calls them all. It calls them in the order they appear in the pipeline.
Crossplane passes each Function in the pipeline the result of the previous
Function. This enables powerful combinations of Functions. In this example,
Crossplane calls
to
create an S3 bucket. Crossplane then passes the bucket to
, which marks the
composite resource as ready when the bucket becomes ready.
1apiVersion: apiextensions.crossplane.io/v1
2kind: Composition
3# Removed for Brevity
4spec:
5 # Removed for Brevity
6 mode: Pipeline
7 pipeline:
8 - step: cue-export-resources
9 functionRef:
10 name: function-cue
11 input:
12 apiVersion: cue.fn.crossplane.io/v1beta1
13 kind: CUEInput
14 name: storage-bucket
15 export:
16 target: Resources
17 value: |
18 apiVersion: "s3.aws.upbound.io/v1beta1"
19 kind: "Bucket"
20 spec: forProvider: region: "us-east-2"
21 - step: automatically-detect-readiness
22 functionRef:
23 name: function-auto-ready
Enable composite resources
A Composition is only a template defining how to create managed resources. A Composition limits which Composite Resources can use this template.
A Composition’s
defines which Composite Resource type can use this Composition.
Inside a Composition’s
define the Composite Resource
and
that the Composition allows to use this template.
1apiVersion: apiextensions.crossplane.io/v1
2kind: Composition
3metadata:
4 name: dynamodb-with-bucket
5spec:
6 compositeTypeRef:
7 apiVersion: custom-api.example.org/v1alpha1
8 kind: database
9 # Removed for brevity
Store connection details
Some managed resources generate unique details like usernames, passwords, IP addresses, ports or other connection details.
When resources inside a Composition create connection details Crossplane creates a Kubernetes secret object for each managed resource generating connection details.
This section discusses creating Kubernetes secrets.
Crossplane also supports using external secret stores like
HashiCorp Vault.
Read the external secrets store guide for more information on using Crossplane with an external secret store.
Composite resource combined secret
Crossplane can combine all the secrets generated by the resources inside a Composition into a single Kubernetes secret and optionally copy the secret object for claims.
Set the value of writeConnectionSecretsToNamespace
to the namespace where
Crossplane should store the combined secret object.
1apiVersion: apiextensions.crossplane.io/v1
2kind: Composition
3# Removed for Brevity
4spec:
5 writeConnectionSecretsToNamespace: my-namespace
6 resources:
7 # Removed for brevity
Composed resource secrets
Inside the spec
of each resource producing connection details, define the
writeConnectionSecretToRef
, with a namespace
and name
of the secret object
for the resource.
If a writeConnectionSecretToRef
isn’t defined, Crossplane doesn’t write any
keys to the secret.
1apiVersion: apiextensions.crossplane.io/v1
2kind: Composition
3spec:
4 writeConnectionSecretsToNamespace: other-namespace
5 mode: Pipeline
6 pipeline:
7 - step: patch-and-transform
8 functionRef:
9 name: function-patch-and-transform
10 input:
11 apiVersion: pt.fn.crossplane.io/v1beta1
12 kind: Resources
13 resources:
14 - name: key
15 base:
16 apiVersion: iam.aws.upbound.io/v1beta1
17 kind: AccessKey
18 spec:
19 forProvider:
20 # Removed for brevity
21 writeConnectionSecretToRef:
22 namespace: docs
23 name: key1
Crossplane saves a secret with the name
in the namespace
provided.
External secret stores
Crossplane External Secret Stores write secrets and connection details to external secret stores like HashiCorp Vault.
External Secret Stores are an alpha feature.
They’re not recommended for production use. Crossplane disables External Secret Stores by default.
Use publishConnectionDetailsWithStoreConfigRef
in place of
writeConnectionSecretsToNamespace
to define the StoreConfig
to save
connection details to.
For example, using a StoreConfig
with the name
“vault,” use
publishConnectionDetailsWithStoreConfigRef.name
matching the
StoreConfig.name
, in this example, “vault.”
1apiVersion: gcp.crossplane.io/v1alpha1
2kind: StoreConfig
3metadata:
4 name: vault
5# Removed for brevity.
6---
7apiVersion: apiextensions.crossplane.io/v1
8kind: Composition
9# Removed for Brevity
10spec:
11 publishConnectionDetailsWithStoreConfigRef:
12 name: vault
13 # Removed for brevity
For more details read the External Secret Stores integration guide.
Test a composition
You can preview the output of any composition using the Crossplane CLI. You don’t need a Crossplane control plane to do this. The Crossplane CLI uses Docker Engine to run functions.
crossplane render
command only supports composition functions. It doesn’t
support mode: Resources
Compositions.crossplane render
requires Docker.Provide a composite resource, composition and composition functions to render the output locally.
1crossplane render xr.yaml composition.yaml functions.yaml
crossplane render
prints resources as YAML to stdout. It prints the
composite resource first, followed by the resources the composition functions
created.
1---
2apiVersion: example.crossplane.io/v1
3kind: XBucket
4metadata:
5 name: example-render
6---
7apiVersion: s3.aws.upbound.io/v1beta1
8kind: Bucket
9metadata:
10 annotations:
11 crossplane.io/composition-resource-name: storage-bucket
12 generateName: example-render-
13 labels:
14 crossplane.io/composite: example-render
15 ownerReferences:
16 - apiVersion: example.crossplane.io/v1
17 blockOwnerDeletion: true
18 controller: true
19 kind: XBucket
20 name: example-render
21 uid: ""
22spec:
23 forProvider:
24 region: us-east-2
You can recreate the output below by running crossplane render
with
these files.
The xr.yaml
file contains the composite resource to render:
1apiVersion: example.crossplane.io/v1
2kind: XBucket
3metadata:
4 name: example-render
5spec:
6 bucketRegion: us-east-2
The composition.yaml
file contains the Composition to use to render the
composite resource:
1apiVersion: apiextensions.crossplane.io/v1
2kind: Composition
3metadata:
4 name: example-render
5spec:
6 compositeTypeRef:
7 apiVersion: example.crossplane.io/v1
8 kind: XBucket
9 mode: Pipeline
10 pipeline:
11 - step: patch-and-transform
12 functionRef:
13 name: function-patch-and-transform
14 input:
15 apiVersion: pt.fn.crossplane.io/v1beta1
16 kind: Resources
17 resources:
18 - name: storage-bucket
19 base:
20 apiVersion: s3.aws.upbound.io/v1beta1
21 kind: Bucket
22 patches:
23 - type: FromCompositeFieldPath
24 fromFieldPath: spec.bucketRegion
25 toFieldPath: spec.forProvider.region
The functions.yaml
file contains the Functions the Composition references in
its pipeline steps:
The Crossplane CLI uses Docker Engine to run functions. You can change how the
Crossplane CLI runs a function by adding an annotation in functions.yaml
. Add
the render.crossplane.io/runtime
annotation to a Function to change how it’s
run.
crossplane render
supports two render.crossplane.io/runtime
values:
Docker
(the default) connects to Docker Engine. It uses Docker to pull and run a function runtime.Development
connects to a function runtime you have run manually.
When you use the
runtime the Crossplane CLI ignores the Function’s
. Instead it expects you to make sure the function
is listening on localhost port 9443. The function must be listening without gRPC
transport security. Most function SDKs let you run a function with the
--insecure
flag to disable transport security. For example you can run a Go
function locally using go run . --insecure
.
1apiVersion: pkg.crossplane.io/v1
2kind: Function
3metadata:
4 name: function-patch-and-transform
5 annotations:
6 render.crossplane.io/runtime: Development
7spec:
8 package: xpkg.crossplane.io/crossplane-contrib/function-patch-and-transform:v0.8.2
Development
runtime when you
write a composition function to test your
function end-to-end.crossplane render
also supports the following Function annotations. These
annotations affect how it runs Functions:
render.crossplane.io/runtime-docker-cleanup
- When using theDocker
runtime this annotation specifies whether the CLI should stop the function container after it calls the function. It supports the valuesStop
, to stop the container, andOrphan
, to leave it running.render.crossplane.io/runtime-docker-pull-policy
- When using theDocker
runtime this annotation specifies when the CLI should pull the Function’s package. It supports the valuesAlways
,Never
, andIfNotPresent
.render.crossplane.io/runtime-development-target
- When using theDevelopment
runtime this annotation tells the CLI to connect to a Function running at the specified target. It uses gRPC target syntax.
Verify a Composition
View all available Compositions with kubectl get composition
.
1kubectl get composition
2NAME XR-KIND XR-APIVERSION AGE
3xapps.aws.platformref.upbound.io XApp aws.platformref.upbound.io/v1alpha1 123m
4xclusters.aws.platformref.upbound.io XCluster aws.platformref.upbound.io/v1alpha1 123m
5xeks.aws.platformref.upbound.io XEKS aws.platformref.upbound.io/v1alpha1 123m
6xnetworks.aws.platformref.upbound.io XNetwork aws.platformref.upbound.io/v1alpha1 123m
7xservices.aws.platformref.upbound.io XServices aws.platformref.upbound.io/v1alpha1 123m
8xsqlinstances.aws.platformref.upbound.io XSQLInstance aws.platformref.upbound.io/v1alpha1 123m
The XR-KIND
lists the Composite Resource kind
that’s allowed to use the
Composition template.
The XR-APIVERSION
lists the Composite Resource API versions allowed to use the
Composition template.
The output of kubectl get composition
is different than kubectl get composite
.
kubectl get composition
lists all available Compositions.
kubectl get composite
lists all created Composite Resources and their related
Composition.
Composition validation
When creating a Composition, Crossplane automatically validates its integrity, checking that the Composition is well formed, for example:
If using mode: Resources
:
- The
resources
field isn’t empty. - All resources either use a
name
or don’t. Compositions can’t use both named and unnamed resources. - No duplicate resource names.
- Patch sets must have names.
- Patches that require a
fromFieldPath
value provide it. - Patches that require a
toFieldPath
value provide it. - Patches that require a
combine
field provide it. - Readiness checks using
matchString
aren’t empty. - Readiness checks using
matchInteger
isn’t0
. - Readiness checks requiring a
fieldPath
value provide it.
If using mode: Pipeline
(Composition Functions):
- The
pipeline
field isn’t empty. - No duplicate step names.
Composition schema aware validation
Crossplane also performs schema aware
validation of Compositions. Schema validation checks that patches
,
readinessChecks
and connectionDetails
are valid according to the resource
schemas. For example, checking that the source and destination fields of a patch
are valid according to the source and destination resource schema.
Composition schema aware validation is a beta feature. Crossplane enables beta features by default.
Disable schema aware validation by setting the
--enable-composition-webhook-schema-validation=false
flag on the Crossplane
pod.
The Crossplane Pods page has more information on enabling Crossplane flags.
Schema aware validation modes
Crossplane always rejects Compositions in case of integrity errors.
Set the schema aware validation mode to configure how Crossplane handles both missing resource schemas and schema aware validation errors.
The following modes are available:
Mode | Missing Schema | Schema Aware Error | Integrity Error |
---|---|---|---|
warn | Warning | Warning | Error |
loose | Warning | Error | Error |
strict | Error | Error | Error |
Change the validation mode for a Composition with the
annotation.
If not specified, the default mode is warn
.
For example, to enable loose
mode checking set the annotation value to
.
1apiVersion: apiextensions.crossplane.io/v1
2kind: Composition
3metadata:
4 annotations:
5 crossplane.io/composition-schema-aware-validation-mode: loose
6 # Removed for brevity
7spec:
8 # Removed for brevity
Validation modes also apply to Compositions defined by Configuration packages.
Depending on the mode configured in the Composition, schema aware validation issues may result in warnings or the rejection of the Composition.
View the Crossplane logs for validation warnings.
Crossplane sets a Configuration as unhealthy if there are validation errors.
View the Configuration details with kubectl describe configuration
to see the
specific errors.
Write a composition function
Composition functions let you replace complicated Compositions with code written in your programming language of choice. Crossplane has tools, software development kits (SDKs) and templates to help you write a function.
Here’s an example of a tiny, hello world function. This example is written in Go.
1func (f *Function) RunFunction(_ context.Context, req *fnv1.RunFunctionRequest) (*fnv1.RunFunctionResponse, error) {
2 rsp := response.To(req, response.DefaultTTL)
3 response.Normal(rsp, "Hello world!")
4 return rsp, nil
5}
Crossplane has language specific guides to writing a composition function. Refer to the guide for your preferred language to learn how to write a composition function.
When you’re writing a composition function it’s useful to know how composition functions work. Read the next section to learn how composition functions work.
How composition functions work
Each composition function is actually a gRPC server. gRPC is a high performance, open source remote procedure call (RPC) framework. When you install a function Crossplane deploys the function as a gRPC server. Crossplane encrypts and authenticates all gRPC communication.
You don’t have to be a gRPC expert to write a function. Crossplane’s function SDKs setup gRPC for you. It’s useful to understand how Crossplane calls your function though, and how your function should respond.
When you create, update, or delete a composite resource that uses composition functions Crossplane calls each function in the order they appear in the Composition’s pipeline. Crossplane calls each function by sending it a gRPC RunFunctionRequest. The function must respond with a gRPC RunFunctionResponse.
When Crossplane calls a function the first time it includes four important things in the RunFunctionRequest.
- The observed state of the composite resource, and any composed resources.
- The desired state of the composite resource, and any composed resources.
- The function’s input.
- The function pipeline’s context.
A function’s main job is to update the desired state and return it to Crossplane. It does this by returning a RunFunctionResponse.
Most composition functions read the observed state of the composite resource, and use it to add composed resources to the desired state. This tells Crossplane which composed resources it should create or update.
If the function needs extra resources to determine the desired state it can request any cluster-scoped resource Crossplane already has access to, either by by name or labels through the returned RunFunctionResponse. Crossplane then calls the function again including the requested extra resources and the context returned by the Function itself alongside the same input, observed and desired state of the previous RunFunctionRequest. Functions can iteratively request extra resources if needed, but to avoid endlessly looping Crossplane limits the number of iterations to 5. Crossplane considers the function satisfied as soon as the extra resources requests become stable, so the Function returns the same exact request two times in a row. Crossplane errors if stability isn’t reached after 5 iterations.
A composed resource is a resource created by a composite resource. Composed resources are usually Crossplane managed resources (MRs), but they can be any kind of Crossplane resource. For example a composite resource could also create a ProviderConfig, or another kind of composite resource.
Observed state
When you create a composite resource like this one, Crossplane observes it and sends it to the composition function as part of the observed state.
1apiVersion: example.crossplane.io/v1
2kind: XBucket
3metadata:
4 name: example-render
5spec:
6 bucketRegion: us-east-2
If any composed resources already exist, Crossplane observes them and sends them to your function as part of the observed state.
Crossplane also observes the connection details of your composite resource and any composed resources. It sends them to your function as part of the observed state.
Crossplane observes the composite resource and any composed resources once, right before it starts calling the functions in the pipeline. This means that Crossplane sends every function in the pipeline the same observed state.
Desired state
Desired state is the set of the changes the function pipeline wants to make to the composite resource and any composed resources. When a function adds composed resources to the desired state Crossplane creates them.
A function can change:
- The
status
of the composite resource. - The
metadata
andspec
of any composed resource.
A function can also change the connection details and readiness of the composite resource. A function indicates that the composite resource is ready by telling Crossplane whether its composed resources are ready. When the function pipeline tells Crossplane that all composed resources are ready, Crossplane marks the composite resource as ready.
A function can’t change:
- The
metadata
orspec
of the composite resource. - The
status
of any composed resource. - The connection details of any composed resource.
A pipeline of functions accumulates desired state. This means that each function builds upon the desired state of previous functions in the pipeline. Crossplane sends a function the desired state accumulated by all previous functions in the pipeline. The function adds to or updates the desired state and then passes it on. When the last function in the pipeline has run, Crossplane applies the desired state it returns.
A function must copy all desired state from its RunFunctionRequest to its RunFunctionResponse. If a function adds a resource to its desired state the next function must copy it to its desired state. If it doesn’t, Crossplane doesn’t apply the resource. If the resource exists, Crossplane deletes it.
A function can intentionally choose not to copy parts of the desired state. For example a function may choose not to copy a desired resource to prevent that resource from existing.
Most function SDKs handle copying desired state automatically.
A function should only add the fields it cares about to the desired state. It should add these fields every time Crossplane calls it. If a function adds a field to the desired state once, but doesn’t add it the next time it’s called, Crossplane deletes the field. The same is true for composed resources. If a function adds a composed resource to the desired state, but doesn’t add it the next time it’s called, Crossplane deletes the composed resource.
For example, if all a function wants is to make sure an S3 bucket in region
us-east-2
exists, it should add this resource to its desired composed
resources.
Even if the Bucket already exists and has other spec
fields, or a status
,
name
, labels
, etc the function should omit them. The function should only
include the fields it has an opinion about. Crossplane takes care of applying
the fields the function cares about, merging them with the existing Bucket.
Function input
If a Composition includes
Crossplane sends it to the function. Input is a useful way to provide extra
configuration to a function. Supporting input is optional. Not all functions
support input.
1apiVersion: apiextensions.crossplane.io/v1
2kind: Composition
3metadata:
4 name: example-render
5spec:
6 compositeTypeRef:
7 apiVersion: example.crossplane.io/v1
8 kind: XBucket
9 mode: Pipeline
10 pipeline:
11 - step: patch-and-transform
12 functionRef:
13 name: function-patch-and-transform
14 input:
15 apiVersion: pt.fn.crossplane.io/v1beta1
16 kind: Resources
17 resources:
18 - name: storage-bucket
19 base:
20 apiVersion: s3.aws.upbound.io/v1beta1
21 kind: Bucket
22 patches:
23 - type: FromCompositeFieldPath
24 fromFieldPath: spec.bucketRegion
25 toFieldPath: spec.forProvider.region
Function pipeline context
Sometimes two functions in a pipeline want to share information with each other that isn’t desired state. Functions can use context for this. Any function can write to the pipeline context. Crossplane passes the context to all following functions. When Crossplane has called all functions it discards the pipeline context.
Crossplane can write context too. If you enable the alpha
composition environment feature Crossplane
writes the environment to the top-level context field
apiextensions.crossplane.io/environment
.