Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
187 changes: 182 additions & 5 deletions content/master/composition/compositions.md
Original file line number Diff line number Diff line change
Expand Up @@ -606,15 +606,15 @@ Most composition functions read the observed state of the composite resource,
and use it to add composed resources to the desired state. This tells Crossplane
which composed resources it should create or update.

If the function needs __extra resources__ to determine the desired state it can
request any cluster-scoped resource Crossplane already has access to, either by
If the function needs __required resources__ to determine the desired state it can
request any cluster-scoped or namespaced resource Crossplane already has access to, either by
name or labels through the returned RunFunctionResponse. Crossplane then calls
the function again including the requested __extra resources__ and the
the function again including the requested __required resources__ and the
__context__ returned by the Function itself alongside the same __input__,
__observed__ and __desired state__ of the previous RunFunctionRequest. Functions
can iteratively request __extra resources__ if needed, but to avoid endlessly
can iteratively request __required resources__ if needed, but to avoid endlessly
looping Crossplane limits the number of iterations to 5. Crossplane considers
the function satisfied as soon as the __extra resources__ requests become
the function satisfied as soon as the __required resources__ requests become
stable, so the Function returns the same exact request two times in a row.
Crossplane errors if stability isn't reached after 5 iterations.

Expand Down Expand Up @@ -767,6 +767,183 @@ Crossplane doesn't validate function input. It's a good idea for a function to
validate its own input.
{{</hint>}}

### Required resources

{{<hint "note">}}
Crossplane v1 called this feature "extra resources." The v2 API
uses the name "required resources" and adds support for bootstrap requirements.
{{</hint>}}

Functions can request access to existing Kubernetes resources to help determine
the desired state. Functions use this capability to read configuration from
ConfigMaps, select the status of other resources, or make decisions based on
existing cluster state.

Functions can receive required resources in two ways:

#### Bootstrap requirements

You can provide required resources in the Composition pipeline step. This
approach performs better than requesting resources during function runtime:

```yaml
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: app-with-config
spec:
compositeTypeRef:
apiVersion: example.crossplane.io/v1
kind: App
mode: Pipeline
pipeline:
- step: create-deployment-from-config
functionRef:
name: crossplane-contrib-function-python
requirements:
requiredResources:
- requirementName: app-config
apiVersion: v1
kind: ConfigMap
name: app-configuration
namespace: default
input:
apiVersion: python.fn.crossplane.io/v1beta1
kind: Script
script: |
from crossplane.function import request

def compose(req, rsp):
observed_xr = req.observed.composite.resource

# Access the required ConfigMap using the helper function
config_map = request.get_required_resource(req, "app-config")

if not config_map:
# Fallback image if ConfigMap not found
image = "nginx:latest"
else:
# Read image from ConfigMap data
image = config_map.get("data", {}).get("image", "nginx:latest")

# Create deployment with the configured image
rsp.desired.resources["deployment"].resource.update({
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {
"labels": {"example.crossplane.io/app": observed_xr["metadata"]["name"]},
},
"spec": {
"replicas": 2,
"selector": {"matchLabels": {"example.crossplane.io/app": observed_xr["metadata"]["name"]}},
"template": {
"metadata": {
"labels": {"example.crossplane.io/app": observed_xr["metadata"]["name"]},
},
"spec": {
"containers": [{
"name": "app",
"image": image,
"ports": [{"containerPort": 80}]
}],
},
},
},
})
```

#### Dynamic resource requests

Functions can also request resources during runtime through the
RunFunctionResponse. Crossplane calls the function again with the requested
resources:

```yaml
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: app-dynamic-config
spec:
compositeTypeRef:
apiVersion: example.crossplane.io/v1
kind: App
mode: Pipeline
pipeline:
- step: create-deployment-from-dynamic-config
functionRef:
name: crossplane-contrib-function-python
input:
apiVersion: python.fn.crossplane.io/v1beta1
kind: Script
script: |
from crossplane.function import request, response

def compose(req, rsp):
observed_xr = req.observed.composite.resource

# Always request the ConfigMap to ensure stable requirements
config_name = observed_xr["spec"].get("configName", "default-config")
namespace = observed_xr["metadata"].get("namespace", "default")

response.require_resources(
rsp,
name="dynamic-config",
api_version="v1",
kind="ConfigMap",
match_name=config_name,
namespace=namespace
)

# Check if we have the required ConfigMap
config_map = request.get_required_resource(req, "dynamic-config")

if not config_map:
# ConfigMap not found yet - Crossplane will call us again
return

# ConfigMap found - use the image data to create deployment
image = config_map.get("data", {}).get("image", "nginx:latest")

rsp.desired.resources["deployment"].resource.update({
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {
"labels": {"example.crossplane.io/app": observed_xr["metadata"]["name"]},
},
"spec": {
"replicas": 2,
"selector": {"matchLabels": {"example.crossplane.io/app": observed_xr["metadata"]["name"]}},
"template": {
"metadata": {
"labels": {"example.crossplane.io/app": observed_xr["metadata"]["name"]},
},
"spec": {
"containers": [{
"name": "app",
"image": image,
"ports": [{"containerPort": 80}]
}],
},
},
},
})
```

{{<hint "tip">}}
Use bootstrap requirements when possible for better performance. Dynamic requests
require more function calls and work best when the
required resources depend on the observed state or earlier function results.
{{</hint>}}

Functions can request resources by:
- **Name**: `name: "my-configmap"` for a specific resource
- **Labels**: `matchLabels: {"env": "prod"}` for multiple resources
- **Namespace**: Include `namespace: "production"` for namespaced resources

Crossplane limits dynamic resource requests to 5 iterations to prevent infinite
loops. The function signals completion by returning the same resource requirements
two iterations in a row.

### Function pipeline context

Sometimes two functions in a pipeline want to share information with each other
Expand Down
Loading