Skip to main content

Crossplane Ep 10: Introduction to Composition Functions

Rachmat Hidayat
Author
Rachmat Hidayat
Learn & sharing insights on TypeScript, Go, Kubernetes, DevOps, DevSecOps, SRE, Platform Engineering, AI/ML Engineering, and MLOps.
crossplane - This article is part of a series.
Part 10: This Article
If you have written a Composition with more than 10 Patches, you know the pain. YAML is not a programming language. You cannot write a for loop in YAML. You cannot execute a Regex replacement in YAML. To solve this, Crossplane v1.14 introduced a revolutionary paradigm shift: Composition Functions.

1. The Limitations of Patch & Transform (P&T)
#

Throughout this series, we have used the built-in “Patch & Transform” (P&T) engine to map XR fields to MR fields.

      patches:
        - type: FromCompositeFieldPath
          fromFieldPath: spec.parameters.storageGB
          toFieldPath: spec.forProvider.allocatedStorage

The Breaking Point
#

Imagine this scenario: Your Application Developer requests an XNetwork, and they provide an array of 5 Availability Zones (AZs) in their Claim. You need Crossplane to dynamically create exactly 5 Subnet MRs, one for each AZ.

You cannot do this with P&T.

P&T does not support for loops. To support 5 Subnets, a Platform Engineer would literally have to copy and paste the Subnet YAML block 5 times in the Composition, writing a separate if condition for each one. If the developer asks for a 6th AZ, the deployment fails.

This limitation forced Platform Engineers to write massive, unmaintainable, 5,000-line YAML Compositions.


2. Enter Composition Functions
#

A Composition Function is a standalone executable (packaged as an OCI container image) that takes the Developer’s Claim as input, runs arbitrary programming logic, and outputs the final Managed Resources.

Because it is an executable program, you can write it in Go, Python, TypeScript, or Rust.

How the Pipeline Works
#

When a Developer submits a Claim, Crossplane no longer uses its internal P&T engine. Instead, it passes the JSON data through a Function Pipeline:

  1. Input: The XR (storageGB: 50, AZs: ["a", "b", "c"]).
  2. Function 1 (The Go Program): Crossplane sends the XR via a gRPC call to your custom Go container.
  3. Execution: Your Go program runs a standard for loop over the AZ array, dynamically generating 3 Subnet JSON objects in memory.
  4. Output: Your Go program returns the 3 Subnets back to Crossplane via gRPC.
  5. Reconciliation: Crossplane creates the 3 Subnet MRs in the Kubernetes API.

3. The New Composition Syntax
#

When you use Functions, your Composition YAML becomes incredibly small. You delete the resources and patches blocks entirely.

Here is what a modern Composition looks like:

apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  name: xnetworks.aws.acmecorp.com
spec:
  compositeTypeRef:
    apiVersion: network.acmecorp.com/v1alpha1
    kind: XNetwork

  # 1. We declare the Pipeline!
  mode: Pipeline
  pipeline:
    # 2. Call the official Patch & Transform function (for legacy support)
    - step: run-basic-patches
      functionRef:
        name: function-patch-and-transform
        
    # 3. Call OUR CUSTOM GO FUNCTION!
    - step: run-advanced-go-logic
      functionRef:
        name: function-acmecorp-network-builder

Instead of 5,000 lines of hardcoded AWS resources, the Composition is simply a list of function calls. The complex logic is hidden inside the function-acmecorp-network-builder container.


4. The Ecosystem of Reusable Functions
#

You don’t always have to write your own functions from scratch. The Crossplane community publishes highly reusable functions to the Upbound Marketplace.

function-patch-and-transform
#

This is the official Upbound function that recreates the legacy P&T engine. It allows you to migrate older Compositions to the Pipeline architecture without rewriting them in Go.

function-go-templating
#

If you love Helm, you will love this. This function allows you to write your infrastructure as raw Go Templates (the exact same syntax used in Helm charts). You can write {{ range .AZs }} inside your Composition to dynamically loop over arrays without writing a dedicated Go program!

function-auto-ready
#

A simple utility function. Normally, an XR only becomes READY: True when every single MR underneath it becomes ready. This function intercepts the pipeline and allows you to declare the XR ready immediately, or based on custom logic.


Conclusion & Next Steps
#

Composition Functions represent the maturity of Crossplane. By decoupling the logic execution from the core Kubernetes reconciliation loop, Crossplane has allowed Platform Engineering to graduate from YAML engineering to true Software Engineering.

This concludes the Intermediate Tier! You have mastered the theory of dynamic infrastructure, environment contexts, and the transition from P&T to Pipelines.

In Episode 11: Writing Composition Functions in Go, we enter the Advanced Tier. We will write our very own Custom Function from scratch in Go, compile it, and deploy it to our cluster to handle dynamic array looping.

crossplane - This article is part of a series.
Part 10: This Article