Crossplane Composition Functions in CUE let you define complex cloud infrastructure declaratively, but the real magic is how CUE’s constraint-based language lets you build self-healing, self-validating infrastructure blueprints.
Let’s see this in action. Imagine we want to provision a PostgreSQL instance on AWS RDS using Crossplane. We’ll define a CompositeResourceDefinition (XRD) for our desired PostgreSQL, and then a Composition that uses a CUE function to translate this into actual AWS RDS resources.
First, our XRD:
apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
name: xpostgresqlinstances.database.example.com
spec:
group: database.example.com
names:
kind: XPostgreSQLInstance
plural: xpostgresqlinstances
versions:
- name: v1alpha1
served: true
referenceable: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
parameters:
type: object
properties:
storageGB:
type: integer
default: 20
class:
type: string
default: "db.t3.micro"
dbName:
type: string
required:
- parameters
This defines a XPostgreSQLInstance custom resource that users can create, specifying storageGB, class, and dbName.
Now, the Composition. This is where CUE shines. We’ll create a Composition that references a CUE package.
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: postgresql-rds.database.example.com
labels:
provider: aws
spec:
compositeTypeRef:
apiVersion: database.example.com/v1alpha1
kind: XPostgreSQLInstance
pull:
# This is a placeholder for a real OCI image containing our CUE function
image: xpkg.upbound.io/crossplane/function-cue:v0.1.0
patchSets:
- name: common-labels
patches:
- fromFieldPath: metadata.labels
toFieldPath: metadata.labels
The pull.image field points to a Docker image containing our CUE composition logic. Crossplane will pull this image and run it.
Inside that CUE image, we’d have a cue.mod/module.cue file like this:
package main
import (
"crossplane.io/crossplane/apis/apiextensions/v1"
"crossplane.io/crossplane/apis/apiextensions/v1beta1"
rds "github.com/crossplane/provider-aws/apis/rds/v1beta1"
"github.com/crossplane/provider-aws/apis/ec2/v1beta1"
"github.com/crossplane/crossplane-runtime/apis/common/v1"
)
// This defines the desired state for our PostgreSQL instance.
// It maps directly to the XPostgreSQLInstance XRD.
#XPostgreSQLInstance: {
apiVersion: "database.example.com/v1alpha1"
kind: "XPostgreSQLInstance"
spec: {
parameters: {
storageGB: int
class: string
dbName: string
}
}
}
// This is the core of the composition. It takes the desired state
// and translates it into actual Crossplane resources.
#PostgreSQLRDS: {
// This is the RDSInstance resource we want to create.
rds.DBInstance: {
metadata: {
name: "#XPostgreSQLInstance.spec.parameters.dbName"
// We can inject labels from the composite resource.
labels: "common-labels"
}
spec: {
forProvider: {
region: "us-east-1" // Hardcoded for simplicity, could be a parameter
dbInstanceClass: "#XPostgreSQLInstance.spec.parameters.class"
allocatedStorage: "#XPostgreSQLInstance.spec.parameters.storageGB"
engine: "postgres"
engineVersion: "13.7" // Specific version
dbSubnetGroupName: "my-db-subnet-group" // Assumed to exist
vpcSecurityGroupIDS: ["sg-0123456789abcdef0"] // Assumed to exist
skipFinalSnapshot: true
}
writeConnectionSecretToRef: {
name: "#XPostgreSQLInstance.spec.parameters.dbName" + "-connection"
namespace: "default" // Or derived from the composite
}
}
}
// We also need a DBSubnetGroup if it doesn't exist.
// In a real-world scenario, you'd likely manage this separately or
// have a more complex logic to ensure its existence.
// For this example, we'll assume it exists and just reference it.
// If you wanted to manage it, you'd add something like:
/*
rds.DBSubnetGroup: {
metadata: {
name: "my-db-subnet-group"
}
spec: {
forProvider: {
region: "us-east-1"
subnetIDs: ["subnet-0123456789abcdef0", "subnet-fedcba9876543210"]
}
}
}
*/
}
// This defines how the CUE function maps the composite resource to managed resources.
// The 'output' field specifies the list of managed resources to be created or updated.
#Output: {
output: {
apiVersion: "apiextensions.crossplane.io/v1"
kind: "Composition"
resources: [
#PostgreSQLRDS.rds.DBInstance,
// If we were managing the DBSubnetGroup:
// #PostgreSQLRDS.rds.DBSubnetGroup,
]
}
}
When a user creates an XPostgreSQLInstance like this:
apiVersion: database.example.com/v1alpha1
kind: XPostgreSQLInstance
metadata:
name: my-pg-instance
spec:
parameters:
storageGB: 50
class: "db.t3.medium"
dbName: "myappdb"
Crossplane’s CUE function will execute. It takes the XPostgreSQLInstance as input, uses the CUE logic to generate the rds.DBInstance and potentially other AWS resources, and returns them. Crossplane then reconciles these generated resources against AWS.
The most surprising thing about CUE for infrastructure is that it’s not just a templating language; it’s a constraint language. You define what must be true, and CUE figures out how to satisfy those constraints, automatically filling in defaults and validating against schemas. This means your infrastructure definitions are not only declarative but also inherently validated and self-correcting within the bounds of your CUE constraints.
The #XPostgreSQLInstance.spec.parameters.dbName syntax in CUE is a powerful way to directly reference values from the input composite resource. CUE’s "dot notation" with # allows you to traverse and extract specific fields from your input schemas, making the mapping between your abstract composite resource and concrete managed resources incredibly direct and type-safe.
This allows you to build complex, reusable infrastructure components where the CUE function acts as a sophisticated translator, ensuring that the generated resources always conform to your defined policies and best practices. The patchSets in the Composition can also be referenced within CUE, providing a way to inject common configurations like labels or annotations without repeating them in every CUE definition.
The next frontier is managing dependencies and ensuring state consistency across multiple managed resources within a single composition, especially when those resources might be managed by different providers.