Chainsaw lets you run your Crossplane Compositions end-to-end against a live Kubernetes cluster, ensuring they behave as expected before they ever hit production.
Let’s see it in action. Imagine you have a Composition that provisions an AWS RDS instance.
First, you’ll need a Composition and a CompositeResourceDefinition (XRD) defined.
# apx-rds-instance.yaml
apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
name: rdsinstances.aws.example.org
spec:
group: aws.example.org
names:
kind: RDSInstance
plural: rdsinstances
versions:
- name: v1alpha1
served: true
referenceable: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
parameters:
type: object
properties:
dbInstanceClass:
type: string
default: db.t3.micro
storageGB:
type: integer
default: 20
required:
- parameters
status:
type: object
properties:
instanceEndpoint:
type: string
---
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: rdsinstance-composition
labels:
provider: aws
resource: rdsinstance
spec:
compositeTypeRef:
apiVersion: aws.example.org/v1alpha1
kind: RDSInstance
resources:
- name: rds-instance
base:
apiVersion: database.aws.crossplane.io/v1beta1
kind: RDSInstance
spec:
forProvider:
region: us-east-1
skipFinalizer: false # Important for Chainsaw
writeConnectionSecretToRef:
namespace: crossplane-system
patches:
- fromFieldPath: spec.parameters.dbInstanceClass
toFieldPath: spec.parameters.dbInstanceClass
- fromFieldPath: spec.parameters.storageGB
toFieldPath: spec.parameters.storageGB
Now, let’s write a Chainsaw test for this. You’ll define a Test resource that specifies the CompositeResource you want to create and the Steps Chainsaw should execute.
# tests/rds-instance-test.yaml
apiVersion: chainsaw.kyverno.io/v1alpha1
kind: Test
metadata:
name: rds-instance-composition-test
spec:
steps:
- name: create-rds-instance
try:
- apply:
dir: ./
file: rds-instance-composite.yaml
- name: wait-for-rds-instance
try:
- assert:
dir: ./
file: rds-instance-composite-ready.yaml
- name: delete-rds-instance
try:
- delete:
dir: ./
file: rds-instance-composite.yaml
And the corresponding rds-instance-composite.yaml and rds-instance-composite-ready.yaml would look like this:
# rds-instance-composite.yaml
apiVersion: aws.example.org/v1alpha1
kind: RDSInstance
metadata:
name: my-rds-instance
spec:
parameters:
dbInstanceClass: db.t3.micro
storageGB: 30
# rds-instance-composite-ready.yaml
apiVersion: aws.example.org/v1alpha1
kind: RDSInstance
metadata:
name: my-rds-instance
spec:
parameters:
dbInstanceClass: db.t3.micro
storageGB: 30
status:
atProvider:
dbInstanceStatus: available # This is what we assert
instanceEndpoint: "my-rds-instance.xxxxxxxxxxxx.us-east-1.rds.amazonaws.com" # Example endpoint
When you run chainsaw test, it does a few things:
- Applies the
CompositeResource: It takes yourRDSInstancemanifest and applies it to your Kubernetes cluster. Crossplane then sees this and starts provisioning the underlying AWS RDS instance. - Asserts the state: Chainsaw waits for the
RDSInstanceto reach areadystate. Therds-instance-composite-ready.yamldefines the expected state, includingstatus.atProvider.dbInstanceStatus: available. Chainsaw polls the cluster until the actual resource matches this desired state or times out. - Deletes the
CompositeResource: Finally, it removes theRDSInstancemanifest. Crossplane, seeing the resource deleted, will then trigger the deletion of the actual AWS RDS instance.
The magic here is that Chainsaw is running against a real Crossplane installation managing real cloud resources. It’s not just linting your YAML; it’s verifying the entire lifecycle.
The most surprising thing about Chainsaw is that it doesn’t just test your Composition’s creation flow. By defining delete steps and asserting intermediate states, you can effectively test upgrades, rollbacks, and even specific failure scenarios by carefully crafting your try and catch blocks within steps.
This gives you confidence that your Compositions will actually work when deployed. The next thing you’ll likely want to explore is how to integrate Chainsaw into your CI/CD pipeline to automate these tests on every commit.