Authoring a Composition in your Control Plane Projects

After you author an XRD, up composition generate allows you to create a composition based on the parameters of your XRD.

Scaffold the composition from the XRD

In the root folder of your control plane project, run the up composition generate command.

up composition generate apis/xbuckets/definition.yaml

This generates a new composition for you in apis/xbuckets/composition.yaml. Open the file in your editor to review the minimal file created.

apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  creationTimestamp: null
  name: xbuckets.devexdemo.upbound.io
spec:
  compositeTypeRef:
    apiVersion: devexdemo.upbound.io/v1alpha1
    kind: XBucket
  mode: Pipeline
  pipeline:
  - functionRef:
      name: crossplane-contrib-function-auto-ready
    step: crossplane-contrib-function-auto-ready

Generate an embedded function

Functions allow you to define the logic of your composition. Composition functions build, package, and manage deployment logic as part of your configuration. You can write functions in familiar programming languages rather than using the built-in patch-and-transform YAML workflow.

To generate a function based on your composition, run the following command:

up function generate --language=kcl test-function apis/xbuckets/composition.yaml

This command generates an embedded KCL function called test-function and creates a new file in your project under functions/test-function/main.k. The up function generate command also creates schema models to help with your authoring experience.

The Upbound CLI automatically updates your apis/xbuckets/composition.yaml file with your new function.

Your composition now contains new function references in the pipeline section.

  pipeline:
  - functionRef:
      name: acmeco-devexdemotest-function
    step: test-function
  - functionRef:
      name: crossplane-contrib-function-auto-ready
    step: crossplane-contrib-function-auto-ready

Authoring the composition function

For this example, make sure you have KCL and the KCL language server installed:

curl -fsSL "https://kcl-lang.io/script/install-cli.sh" | /bin/bash
curl -fsSL "https://kcl-lang.io/script/install-kcl-lsp.sh" | /bin/bash

Next, install and enable the KCL VSCode Extension.

Open the main.k function file in VSCode. The schema scaffold here builds your composition logic and contains placeholders for your desired inputs.

import models.v1beta1 as v1beta1
import models.v1beta2 as v1beta2
import models.k8s.apimachinery.pkg.apis.meta.v1 as metav1

oxr = option("params").oxr # observed composite resource
_ocds = option("params").ocds # observed composed resources
_dxr = option("params").dxr # desired composite resource
dcds = option("params").dcds # desired composed resources

_metadata = lambda name: str -> any {
    { annotations = { "krm.kcl.dev/composition-resource-name" = name }}
}

_items = [

]
items = _items

First, the function uses an import statement to load KCL language server logic into your composition.

import models.v1beta1 as v1beta1
import models.v1beta2 as v1beta2
import models.k8s.apimachinery.pkg.apis.meta.v1 as metav1

Next, the variable statements capture your desired resources and observed resources.

oxr = option("params").oxr # observed composite resource
_ocds = option("params").ocds # observed composed resources
_dxr = option("params").dxr # desired composite resource
dcds = option("params").dcds # desired composed resources

The _items stanza is empty and expects a resource object. Your function uses this assignment to pass your desired infrastructure to the control plane for management.

_items = [
  v1beta1.Bucket{
    metadata.name = oxr.metadata.name
    spec.forProvider: {
      objectLockEnabled = True
      forceDestroy = False
    }
  }

  v1beta1.BucketVersioning {
    spec.forProvider: {
        bucketRef.name = oxr.metadata.name
    }
   }   if oxr.spec.versioning and oxr.status.conditions == 'True' else {}

   v1beta1.BucketServerSideEncryptionConfiguration {
    spec.forProvider = {
        bucketRef.name = oxr.metadata.name
        rule: [
          {
            applyServerSideEncryptionByDefault: [
                {
                    sseAlgorithm = "AES256"
                }
            ]
            bucketKeyEnabled = True
          }
        ]
    }
   } if oxr.spec.versioning and oxr.status.conditions == 'True' else {}
]

The statement in the _items variable is a fully functional KCL function. With the VSCode KCL extension and KCL language server, you get autocompletion, linting, type errors, and more.

In this example, the oxr assignment captures the composite resources and the function adds server side encryption to the buckets your deployment creates.

In the next guide, you’ll run and test your composition.

For more KCL best practices, please refer to the documentation.