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:
- Crossplane creates a
CompositionRevision
for eachComposition
update. - Composite Resources gain a
spec.crossplane.compositionRevisionRef
field that specifies whichCompositionRevision
they use. - 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. EachCompositionRevision
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 latestCompositionRevision
. (Default)Manual
: Require manual intervention to changeCompositionRevision
.
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
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]
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]
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
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]
vpc-dev
matches the updated label applied to Revision revision:3
.
vpc-staging
matches the label applied to Revision revision:2
.