Skip to main content

Composition Revisions

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:

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

This should produce output something like:

NAME              REVISION   AGE
example-18pdgs2 1 4m36s
example-2bgdr31 2 73s
example-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.

apiVersion: example.org/v1alpha1
kind: PlatformDB
metadata:
name: example
spec:
storageGB: 20
crossplane:
# The Manual policy specifies that you don't want this XR to update to the
# latest CompositionRevision automatically.
compositionUpdatePolicy: Manual
compositionRef:
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.

apiVersion: example.org/v1alpha1
kind: PlatformDB
metadata:
name: example
spec:
storageGB: 20
crossplane:
compositionUpdatePolicy: Manual
compositionRef:
name: example
# Update the referenced CompositionRevision if and when you are ready.
compositionRevisionRef:
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.

apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
labels:
channel: dev
name: myvpcs.aws.example.upbound.io
spec:
compositeTypeRef:
apiVersion: aws.example.upbound.io/v1alpha1
kind: MyVPC
mode: Pipeline
pipeline:
- step: patch-and-transform
functionRef:
name: function-patch-and-transform
input:
apiVersion: pt.fn.crossplane.io/v1beta1
kind: Resources
resources:
- name: my-vpc
base:
apiVersion: ec2.aws.m.upbound.io/v1beta1
kind: VPC
spec:
forProvider:
region: us-west-1
cidrBlock: 192.168.0.0/16
enableDnsSupport: true
enableDnsHostnames: true

Apply the example XRD.

apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
name: myvpcs.aws.example.upbound.io
spec:
group: aws.example.upbound.io
names:
kind: MyVPC
plural: myvpcs
versions:
- name: v1alpha1
served: true
referenceable: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
id:
type: string
description: ID of this VPC that other objects will use to refer to it.
required:
- id

Verify that Crossplane created the Composition revision

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

Expected Output:

NAME                                    REVISION   CHANNEL
myvpcs.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:

apiVersion: aws.example.upbound.io/v1alpha1
kind: MyVPC
metadata:
namespace: default
name: vpc-auto
spec:
id: vpc-auto

Expected Output:

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

Manual update policy

Create a Composite Resource with compositionUpdatePolicy: Manual and compositionRevisionRef.

apiVersion: aws.example.upbound.io/v1alpha1
kind: MyVPC
metadata:
namespace: default
name: vpc-man
spec:
id: vpc-man
crossplane:
compositionUpdatePolicy: Manual
compositionRevisionRef:
name: myvpcs.aws.example.upbound.io-ad265bc

Expected Output:

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

Using a selector

Create an XR with a compositionRevisionSelector of channel: dev:

apiVersion: aws.example.upbound.io/v1alpha1
kind: MyVPC
metadata:
namespace: default
name: vpc-dev
spec:
id: vpc-dev
crossplane:
compositionRevisionSelector:
matchLabels:
channel: dev

Expected Output:

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

Create an XR with a compositionRevisionSelector of channel: staging:

apiVersion: aws.example.upbound.io/v1alpha1
kind: MyVPC
metadata:
namespace: default
name: vpc-staging
spec:
id: vpc-staging
crossplane:
compositionRevisionSelector:
matchLabels:
channel: staging

Expected Output:

myvpc.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.

kubectl 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:

NAME          SYNCED   REVISION                                POLICY      MATCHLABEL
vpc-auto True myvpcs.aws.example.upbound.io-ad265bc Automatic <none>
vpc-dev True myvpcs.aws.example.upbound.io-ad265bc Automatic map[channel:dev]
vpc-man True myvpcs.aws.example.upbound.io-ad265bc Manual <none>
vpc-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:

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

Expected Output:

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

Verify that Crossplane creates a new Composition revision:

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

Expected Output:

NAME                                    REVISION   CHANNEL
myvpcs.aws.example.upbound.io-727b3c8 2 staging
myvpcs.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:

kubectl 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:

NAME          SYNCED   REVISION                                POLICY      MATCHLABEL
vpc-auto True myvpcs.aws.example.upbound.io-727b3c8 Automatic <none>
vpc-dev True myvpcs.aws.example.upbound.io-ad265bc Automatic map[channel:dev]
vpc-man True myvpcs.aws.example.upbound.io-ad265bc Manual <none>
vpc-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:

apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
labels:
channel: dev
name: myvpcs.aws.example.upbound.io
spec:
compositeTypeRef:
apiVersion: aws.example.upbound.io/v1alpha1
kind: MyVPC
mode: Pipeline
pipeline:
- step: patch-and-transform
functionRef:
name: function-patch-and-transform
input:
apiVersion: pt.fn.crossplane.io/v1beta1
kind: Resources
resources:
- name: my-vpc
base:
apiVersion: ec2.aws.m.upbound.io/v1beta1
kind: VPC
spec:
forProvider:
region: us-west-1
cidrBlock: 192.168.0.0/16
enableDnsSupport: false
enableDnsHostnames: true

Expected Output:

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

Verify that Crossplane creates a new Composition revision:

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

Expected Output:

NAME                                    REVISION   CHANNEL
myvpcs.aws.example.upbound.io-727b3c8 2 staging
myvpcs.aws.example.upbound.io-ad265bc 1 dev
myvpcs.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:

kubectl 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:

NAME          SYNCED   REVISION                                POLICY      MATCHLABEL
vpc-auto True myvpcs.aws.example.upbound.io-f81c553 Automatic <none>
vpc-dev True myvpcs.aws.example.upbound.io-f81c553 Automatic map[channel:dev]
vpc-man True myvpcs.aws.example.upbound.io-ad265bc Manual <none>
vpc-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.