mirror of https://github.com/crossplane/docs.git
Document using required (fka extra) resources in compositions
Right now AFAIK only function-python supports this. Signed-off-by: Nic Cope <nicc@rk0n.org>
This commit is contained in:
parent
0314e647e0
commit
1413873fa7
|
|
@ -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.
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue