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=python test-function apis/xbuckets/composition.yaml

This command generates an embedded Python function called test-function and creates a new file in your project under functions/test-function/main.py. 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, you need Python and the Python Visual Studio Code extension. Refer to the Visual Studio Code Extensions documentation to learn how to install them.

Open the main.py function file in Visual Studio Code.

from crossplane.function import resource
from crossplane.function.proto.v1 import run_function_pb2 as fnv1

from .model.io.upbound.aws.s3.bucket import v1beta1

def compose(req: fnv1.RunFunctionRequest, rsp: fnv1.RunFunctionResponse):
    # Specify an S3 Bucket, using a generated model.
    bucket = v1beta1.Bucket(
        apiVersion="s3.aws.upbound.io/v1beta1",
        kind="Bucket",
        spec=bucketv1beta1.Spec(
            forProvider=bucketv1beta1.ForProvider(
                # Derive the bucket's region from the XR's region.
                region=req.observed.composite.resource["spec"]["region"],
            ),
        ),
    )

    # Update the function's desired composed resources to include the bucket.
    resource.update(rsp.desired.resources["bucket"], bucket)

Use import statements to load Crossplane’s Python SDK and Upbound’s generated models into your function.

Define your function’s logic in the compose function. Crossplane calls this function. It passes it a RunFunctionRequest and a RunFunctionResponse.

Specify your desired composed resources by passing them to resource.update. You can pass resource.update a model object, or a Python dictionary.

You can also use resource.update to update the desired XR:

from crossplane.function import resource
from crossplane.function.proto.v1 import run_function_pb2 as fnv1

def compose(req: fnv1.RunFunctionRequest, rsp: fnv1.RunFunctionResponse):
    # Update the function's desired XR's status using a dictionary.
    resource.update(rsp.desired.composite, {"status": {"widgets": 42}})

With the Visual Studio Code Python extension you get autocompletion, linting, type errors, and more.

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

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