This document is for an unreleased version of Crossplane.

This document applies to the Crossplane master branch and not to the latest release v1.20.

This guide discusses the use of “Composition Revisions” to make and roll back changes to a Crossplane Composition. It assumes familiarity with Crossplane and Compositions.

A Composition configures how Crossplane should reconcile a Composite Resource (XR). Put otherwise, when you create an XR the selected Composition determines what resources Crossplane creates in response. For example, you define a PlatformDB XR, which represents your organisation’s common database configuration of an Azure MySQL Server and some firewall rules. The Composition contains the ‘base’ configuration for the MySQL server and the firewall rules that the PlatformDB configuration extends.

A Composition associates with multiple XRs that use it. You might define a Composition named big-platform-db that’s used by ten different PlatformDB XRs. Often, in the interest of self-service, a different team manages the Composition than the actual PlatformDB XRs. For example a platform team member may write and maintain the Composition, while individual app teams create PlatformDB XRs that use said Composition.

Each Composition is mutable - you can update it as your organisation’s needs change. Updating a Composition without Composition Revisions can be a risky process. Crossplane constantly uses the Composition to ensure that your actual infrastructure - your MySQL Servers and firewall rules - match your desired state. If you have 10 PlatformDB XRs all using the big-platform-db Composition, all 10 of those XRs are instantly updated following any updates you make to the big-platform-db Composition.

Composition Revisions allow XRs to opt out of automatic updates. Instead you can update your XRs to use the latest Composition settings at your own pace. This enables you to canary changes to your infrastructure, or to roll back some XRs to previous Composition settings without rolling back all XRs.

Using composition revisions

When you enable Composition Revisions three things happen:

  1. Crossplane creates a CompositionRevision for each Composition update.
  2. Composite Resources gain a spec.crossplane.compositionRevisionRef field that specifies which CompositionRevision they use.
  3. Composite Resources gain a spec.crossplane.compositionUpdatePolicy field that specifies how Crossplane should update them to new Composition Revisions.

Each time you edit a Composition Crossplane automatically creates a CompositionRevision that represents that ‘revision’ of the Composition - that unique state. Crossplane allocates each revision an increasing revision number. This gives CompositionRevision consumers an idea about which revision is ’newest’.

You can discover which revisions exist using kubectl:

1# Find all revisions of the Composition named 'example'
2kubectl get compositionrevision -l crossplane.io/composition-name=example

This should produce output something like:

1NAME              REVISION   AGE
2example-18pdgs2   1          4m36s
3example-2bgdr31   2          73s
4example-xjrdmzz   3          61s

A Composition is a mutable resource that you can update as your needs change over time. Each CompositionRevision is an immutable snapshot of those needs at a particular time.

Crossplane behaves the same way by default whether you enable Composition Revisions or not. When you enable Composition Revisions all XRs default to the Automatic compositionUpdatePolicy. XRs support two update policies:

  • Automatic: Automatically use the latest CompositionRevision. (Default)
  • Manual: Require manual intervention to change CompositionRevision.

The below XR uses the Manual policy. When you use this policy the XR selects the latest CompositionRevision when it’s first created, but must manually update it when you wish it to use another CompositionRevision.

 1apiVersion: example.org/v1alpha1
 2kind: PlatformDB
 3metadata:
 4  name: example
 5spec:
 6  storageGB: 20
 7  crossplane:
 8    # The Manual policy specifies that you don't want this XR to update to the
 9    # latest CompositionRevision automatically.
10    compositionUpdatePolicy: Manual
11    compositionRef:
12      name: example

Crossplane sets an XR’s compositionRevisionRef automatically at creation time regardless of your chosen compositionUpdatePolicy. If you choose the Manual policy you must edit the compositionRevisionRef field when you want your XR to use a different CompositionRevision.

 1apiVersion: example.org/v1alpha1
 2kind: PlatformDB
 3metadata:
 4  name: example
 5spec:
 6  storageGB: 20
 7  crossplane:
 8    compositionUpdatePolicy: Manual
 9    compositionRef:
10      name: example
11    # Update the referenced CompositionRevision if and when you are ready.
12    compositionRevisionRef:
13      name: example-18pdg

Complete example

This tutorial discusses how CompositionRevisions work and how they manage Composite Resource (XR) updates. This starts with a Composition and CompositeResourceDefinition (XRD) that defines a MyVPC resource and continues with creating multiple XRs to observe different upgrade paths. Crossplane assigns different CompositionRevisions to composite resources each time you update the composition.

Preparation

Deploy composition and XRD examples

Apply the example Composition.

 1apiVersion: apiextensions.crossplane.io/v1
 2kind: Composition
 3metadata:
 4  labels:
 5    channel: dev
 6  name: myvpcs.aws.example.upbound.io
 7spec:
 8  compositeTypeRef:
 9    apiVersion: aws.example.upbound.io/v1alpha1
10    kind: MyVPC
11  mode: Pipeline
12  pipeline:
13  - step: patch-and-transform
14    functionRef:
15      name: function-patch-and-transform
16    input:
17      apiVersion: pt.fn.crossplane.io/v1beta1
18      kind: Resources
19      resources:
20      - name: my-vpc
21        base:
22          apiVersion: ec2.aws.m.upbound.io/v1beta1
23          kind: VPC
24          spec:
25            forProvider:
26              region: us-west-1
27              cidrBlock: 192.168.0.0/16
28              enableDnsSupport: true
29              enableDnsHostnames: true

Apply the example XRD.

 1apiVersion: apiextensions.crossplane.io/v1
 2kind: CompositeResourceDefinition
 3metadata:
 4  name: myvpcs.aws.example.upbound.io
 5spec:
 6  group: aws.example.upbound.io
 7  names:
 8    kind: MyVPC
 9    plural: myvpcs
10  versions:
11  - name: v1alpha1
12    served: true 
13    referenceable: true 
14    schema:
15      openAPIV3Schema:
16        type: object 
17        properties:
18          spec:
19            type: object 
20            properties:
21              id:
22                type: string 
23                description: ID of this VPC that other objects will use to refer to it. 
24            required:
25            - id

Verify that Crossplane created the Composition revision

1kubectl get compositionrevisions -o="custom-columns=NAME:.metadata.name,REVISION:.spec.revision,CHANNEL:.metadata.labels.channel"

Expected Output:

1NAME                                    REVISION   CHANNEL
2myvpcs.aws.example.upbound.io-ad265bc   1          dev
Note
The label dev is automatically created from the Composition.

Create composite resources

This tutorial has four composite resources to cover different update policies and composition selection options. The default behavior is updating XRs to the latest revision of the Composition. You can change this by setting compositionUpdatePolicy: Manual in the XR. It’s also possible to select the latest revision with a specific label with compositionRevisionSelector.matchLabels together with compositionUpdatePolicy: Automatic.

Default update policy

Create an XR without a compositionUpdatePolicy defined. The update policy is Automatic by default:

1apiVersion: aws.example.upbound.io/v1alpha1
2kind: MyVPC
3metadata:
4  namespace: default
5  name: vpc-auto
6spec:
7  id: vpc-auto

Expected Output:

1myvpc.aws.example.upbound.io/vpc-auto created

Manual update policy

Create a Composite Resource with compositionUpdatePolicy: Manual and compositionRevisionRef.

 1apiVersion: aws.example.upbound.io/v1alpha1
 2kind: MyVPC
 3metadata:
 4  namespace: default
 5  name: vpc-man
 6spec:
 7  id: vpc-man
 8  crossplane:
 9    compositionUpdatePolicy: Manual
10    compositionRevisionRef:
11      name: myvpcs.aws.example.upbound.io-ad265bc

Expected Output:

1myvpc.aws.example.upbound.io/vpc-man created

Using a selector

Create an XR with a compositionRevisionSelector of channel: dev:

 1apiVersion: aws.example.upbound.io/v1alpha1
 2kind:  MyVPC
 3metadata:
 4  namespace: default
 5  name: vpc-dev
 6spec:
 7  id: vpc-dev
 8  crossplane:
 9    compositionRevisionSelector:
10      matchLabels:
11        channel: dev

Expected Output:

1myvpc.aws.example.upbound.io/vpc-dev created

Create an XR with a compositionRevisionSelector of channel: staging:

 1apiVersion: aws.example.upbound.io/v1alpha1
 2kind: MyVPC
 3metadata:
 4  namespace: default
 5  name: vpc-staging
 6spec:
 7  id: vpc-staging
 8  crossplane:
 9    compositionRevisionSelector:
10      matchLabels:
11        channel: staging

Expected Output:

1myvpc.aws.example.upbound.io/vpc-staging created

Verify the Composite Resource with the label channel: staging doesn’t have a REVISION.
All other XRs have a REVISION matching the created Composition Revision.

1kubectl get composite -o="custom-columns=NAME:.metadata.name,SYNCED:.status.conditions[0].status,REVISION:.spec.crossplane.compositionRevisionRef.name,POLICY:.spec.crossplane.compositionUpdatePolicy,MATCHLABEL:.spec.crossplane.compositionRevisionSelector.matchLabels"

Expected Output:

1NAME          SYNCED   REVISION                                POLICY      MATCHLABEL
2vpc-auto      True     myvpcs.aws.example.upbound.io-ad265bc   Automatic   <none>
3vpc-dev       True     myvpcs.aws.example.upbound.io-ad265bc   Automatic   map[channel:dev]
4vpc-man       True     myvpcs.aws.example.upbound.io-ad265bc   Manual      <none>
5vpc-staging   False    <none>                                  Automatic   map[channel:staging]
Note
The vpc-staging XR label doesn’t match any existing Composition Revisions.

Create new composition revisions

Crossplane creates a new CompositionRevision when you create or update a Composition. Label and annotation changes also trigger a new CompositionRevision.

Update the composition label

Update the Composition label to channel: staging:

1kubectl label composition myvpcs.aws.example.upbound.io channel=staging --overwrite

Expected Output:

1composition.apiextensions.crossplane.io/myvpcs.aws.example.upbound.io labeled

Verify that Crossplane creates a new Composition revision:

1kubectl get compositionrevisions -o="custom-columns=NAME:.metadata.name,REVISION:.spec.revision,CHANNEL:.metadata.labels.channel"

Expected Output:

1NAME                                    REVISION   CHANNEL
2myvpcs.aws.example.upbound.io-727b3c8   2          staging
3myvpcs.aws.example.upbound.io-ad265bc   1          dev

Verify that Crossplane assigns the Composite Resources vpc-auto and vpc-staging to Composite revision:2.
XRs vpc-man and vpc-dev are still assigned to the original revision:1:

1kubectl get composite -o="custom-columns=NAME:.metadata.name,SYNCED:.status.conditions[0].status,REVISION:.spec.crossplane.compositionRevisionRef.name,POLICY:.spec.crossplane.compositionUpdatePolicy,MATCHLABEL:.spec.crossplane.compositionRevisionSelector.matchLabels"

Expected Output:

1NAME          SYNCED   REVISION                                POLICY      MATCHLABEL
2vpc-auto      True     myvpcs.aws.example.upbound.io-727b3c8   Automatic   <none>
3vpc-dev       True     myvpcs.aws.example.upbound.io-ad265bc   Automatic   map[channel:dev]
4vpc-man       True     myvpcs.aws.example.upbound.io-ad265bc   Manual      <none>
5vpc-staging   True     myvpcs.aws.example.upbound.io-727b3c8   Automatic   map[channel:staging]
Note
vpc-auto always use the latest Revision.
vpc-staging now matches the label applied to Revision revision:2.

Update composition spec and label

Update the Composition to disable DNS support in the VPC and change the label from staging back to dev.

Apply the following changes to update the Composition spec and label:

 1apiVersion: apiextensions.crossplane.io/v1
 2kind: Composition
 3metadata:
 4  labels:
 5    channel: dev
 6  name: myvpcs.aws.example.upbound.io
 7spec:
 8  compositeTypeRef:
 9    apiVersion: aws.example.upbound.io/v1alpha1
10    kind: MyVPC
11  mode: Pipeline
12  pipeline:
13  - step: patch-and-transform
14    functionRef:
15      name: function-patch-and-transform
16    input:
17      apiVersion: pt.fn.crossplane.io/v1beta1
18      kind: Resources
19      resources:
20      - name: my-vpc
21        base:
22          apiVersion: ec2.aws.m.upbound.io/v1beta1
23          kind: VPC
24          spec:
25            forProvider:
26              region: us-west-1
27              cidrBlock: 192.168.0.0/16
28              enableDnsSupport: false
29              enableDnsHostnames: true

Expected Output:

1composition.apiextensions.crossplane.io/myvpcs.aws.example.upbound.io configured

Verify that Crossplane creates a new Composition revision:

1kubectl get compositionrevisions -o="custom-columns=NAME:.metadata.name,REVISION:.spec.revision,CHANNEL:.metadata.labels.channel"

Expected Output:

1NAME                                    REVISION   CHANNEL
2myvpcs.aws.example.upbound.io-727b3c8   2          staging
3myvpcs.aws.example.upbound.io-ad265bc   1          dev
4myvpcs.aws.example.upbound.io-f81c553   3          dev
Note
Changing the label and the spec values simultaneously is critical for deploying new changes to the dev channel.

Verify Crossplane assigns the Composite Resources vpc-auto and vpc-dev to Composite revision:3.
Crossplane assigns vpc-staging to revision:2, and still assigns vpc-man to the original revision:1:

1kubectl get composite -o="custom-columns=NAME:.metadata.name,SYNCED:.status.conditions[0].status,REVISION:.spec.crossplane.compositionRevisionRef.name,POLICY:.spec.crossplane.compositionUpdatePolicy,MATCHLABEL:.spec.crossplane.compositionRevisionSelector.matchLabels"

Expected Output:

1NAME          SYNCED   REVISION                                POLICY      MATCHLABEL
2vpc-auto      True     myvpcs.aws.example.upbound.io-f81c553   Automatic   <none>
3vpc-dev       True     myvpcs.aws.example.upbound.io-f81c553   Automatic   map[channel:dev]
4vpc-man       True     myvpcs.aws.example.upbound.io-ad265bc   Manual      <none>
5vpc-staging   True     myvpcs.aws.example.upbound.io-727b3c8   Automatic   map[channel:staging]
Note
vpc-dev matches the updated label applied to Revision revision:3. vpc-staging matches the label applied to Revision revision:2.