diff --git a/content/master/composition/compositions.md b/content/master/composition/compositions.md index ec67327b3..4ba1e4523 100644 --- a/content/master/composition/compositions.md +++ b/content/master/composition/compositions.md @@ -599,6 +599,13 @@ things in the RunFunctionRequest. 1. The function's __input__. 1. The function pipeline's __context__. +Crossplane also populates __meta.capabilities__ in the request with the set of +protocol features it supports (for example, required resources, credentials, +conditions, required schemas). Functions can check for a capability before +relying on the corresponding feature and fall back gracefully when running with +an older Crossplane that doesn't support it. See +[Capability advertisement](#capability-advertisement) below. + A function's main job is to update the __desired state__ and return it to Crossplane. It does this by returning a RunFunctionResponse. @@ -944,6 +951,103 @@ 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. +### Required schemas + +{{}} +Required schemas are supported in Crossplane v2.2 and later. Functions can check +for the `CAPABILITY_REQUIRED_SCHEMAS` capability before using this feature. +{{}} + +Composition functions sometimes need OpenAPI schemas for resource kinds—for +example to validate resources, generate resources with correct field types, or +build schema-aware tooling. Per the [function specification](https://github.com/crossplane/crossplane/blob/main/contributing/specifications/functions.md), +functions cannot assume network access, so they cannot fetch schemas from the +API server directly. + +Crossplane extends the same pattern used for [required resources](#required-resources) +to support schema requests: + +1. The function returns `requirements.schemas` in its RunFunctionResponse, + specifying the API version and kind of each schema it needs (for example, + `apps/v1`, `Deployment`). +2. Crossplane fetches the OpenAPI schema from the cluster and calls the function + again with `required_schemas` populated on the RunFunctionRequest. +3. If a schema is not found (for example, the GVK does not exist), Crossplane + sets that map entry to an empty `Schema` message so the function can + distinguish "Crossplane tried but found nothing" from "not processed yet." + +You can provide required schemas in the Composition pipeline step (bootstrap) +or request them dynamically in the function response. Bootstrap is more +efficient. + +**Bootstrap required schemas in the Composition:** + +```yaml +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +metadata: + name: example-with-schemas +spec: + compositeTypeRef: + apiVersion: example.crossplane.io/v1 + kind: App + mode: Pipeline + pipeline: + - step: validate-and-compose + functionRef: + name: my-function + requirements: + requiredSchemas: + - requirementName: deployment-schema + apiVersion: apps/v1 + kind: Deployment +``` + +The function receives the schema in `req.required_schemas["deployment-schema"]` +and can use it for validation or code generation. The map key is the +`requirementName` you specify in the Composition or in the function's +`requirements.schemas` response. + +### Capability advertisement + +Crossplane populates `RequestMeta.capabilities` with all protocol features it +supports when calling a function. When a function uses a feature (for example, +required schemas or conditions) with an older Crossplane that doesn't support +it, Crossplane silently ignores the unknown fields. The function has no way to +know whether its request was honored. + +Capability advertisement fixes this: Crossplane sends the list of supported +capabilities in every RunFunctionRequest. Functions should check for a +capability before relying on the corresponding feature and fall back when it +is absent. + +| Capability | Meaning | +|------------|--------| +| `CAPABILITY_CAPABILITIES` | Crossplane advertises capabilities; if another capability is absent, Crossplane doesn't support it. | +| `CAPABILITY_REQUIRED_RESOURCES` | Crossplane supports `requirements.resources` and populates `required_resources`. | +| `CAPABILITY_CREDENTIALS` | Crossplane supports credentials from the Composition. | +| `CAPABILITY_CONDITIONS` | Crossplane supports status conditions in the function response. | +| `CAPABILITY_REQUIRED_SCHEMAS` | Crossplane supports `requirements.schemas` and populates `required_schemas`. | + +Example (Python): check for a capability before using the feature: + +```python +from crossplane.function import request, response + +if request.has_capability(req, fnv1.CAPABILITY_REQUIRED_SCHEMAS): + response.require_schema(rsp, "xr", "example.org/v1", "MyXR") + schema = request.get_required_schema(req, "xr") + if schema: + # Use schema for validation or code generation + pass +else: + # Crossplane doesn't support required schemas; skip or use a fallback + pass +``` + +Function SDKs provide helpers such as `has_capability` and `get_required_schema`; +see your SDK documentation for the exact API. + ### Function pipeline context Sometimes two functions in a pipeline want to share information with each other diff --git a/content/v2.2/composition/compositions.md b/content/v2.2/composition/compositions.md index ec67327b3..4ba1e4523 100644 --- a/content/v2.2/composition/compositions.md +++ b/content/v2.2/composition/compositions.md @@ -599,6 +599,13 @@ things in the RunFunctionRequest. 1. The function's __input__. 1. The function pipeline's __context__. +Crossplane also populates __meta.capabilities__ in the request with the set of +protocol features it supports (for example, required resources, credentials, +conditions, required schemas). Functions can check for a capability before +relying on the corresponding feature and fall back gracefully when running with +an older Crossplane that doesn't support it. See +[Capability advertisement](#capability-advertisement) below. + A function's main job is to update the __desired state__ and return it to Crossplane. It does this by returning a RunFunctionResponse. @@ -944,6 +951,103 @@ 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. +### Required schemas + +{{}} +Required schemas are supported in Crossplane v2.2 and later. Functions can check +for the `CAPABILITY_REQUIRED_SCHEMAS` capability before using this feature. +{{}} + +Composition functions sometimes need OpenAPI schemas for resource kinds—for +example to validate resources, generate resources with correct field types, or +build schema-aware tooling. Per the [function specification](https://github.com/crossplane/crossplane/blob/main/contributing/specifications/functions.md), +functions cannot assume network access, so they cannot fetch schemas from the +API server directly. + +Crossplane extends the same pattern used for [required resources](#required-resources) +to support schema requests: + +1. The function returns `requirements.schemas` in its RunFunctionResponse, + specifying the API version and kind of each schema it needs (for example, + `apps/v1`, `Deployment`). +2. Crossplane fetches the OpenAPI schema from the cluster and calls the function + again with `required_schemas` populated on the RunFunctionRequest. +3. If a schema is not found (for example, the GVK does not exist), Crossplane + sets that map entry to an empty `Schema` message so the function can + distinguish "Crossplane tried but found nothing" from "not processed yet." + +You can provide required schemas in the Composition pipeline step (bootstrap) +or request them dynamically in the function response. Bootstrap is more +efficient. + +**Bootstrap required schemas in the Composition:** + +```yaml +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +metadata: + name: example-with-schemas +spec: + compositeTypeRef: + apiVersion: example.crossplane.io/v1 + kind: App + mode: Pipeline + pipeline: + - step: validate-and-compose + functionRef: + name: my-function + requirements: + requiredSchemas: + - requirementName: deployment-schema + apiVersion: apps/v1 + kind: Deployment +``` + +The function receives the schema in `req.required_schemas["deployment-schema"]` +and can use it for validation or code generation. The map key is the +`requirementName` you specify in the Composition or in the function's +`requirements.schemas` response. + +### Capability advertisement + +Crossplane populates `RequestMeta.capabilities` with all protocol features it +supports when calling a function. When a function uses a feature (for example, +required schemas or conditions) with an older Crossplane that doesn't support +it, Crossplane silently ignores the unknown fields. The function has no way to +know whether its request was honored. + +Capability advertisement fixes this: Crossplane sends the list of supported +capabilities in every RunFunctionRequest. Functions should check for a +capability before relying on the corresponding feature and fall back when it +is absent. + +| Capability | Meaning | +|------------|--------| +| `CAPABILITY_CAPABILITIES` | Crossplane advertises capabilities; if another capability is absent, Crossplane doesn't support it. | +| `CAPABILITY_REQUIRED_RESOURCES` | Crossplane supports `requirements.resources` and populates `required_resources`. | +| `CAPABILITY_CREDENTIALS` | Crossplane supports credentials from the Composition. | +| `CAPABILITY_CONDITIONS` | Crossplane supports status conditions in the function response. | +| `CAPABILITY_REQUIRED_SCHEMAS` | Crossplane supports `requirements.schemas` and populates `required_schemas`. | + +Example (Python): check for a capability before using the feature: + +```python +from crossplane.function import request, response + +if request.has_capability(req, fnv1.CAPABILITY_REQUIRED_SCHEMAS): + response.require_schema(rsp, "xr", "example.org/v1", "MyXR") + schema = request.get_required_schema(req, "xr") + if schema: + # Use schema for validation or code generation + pass +else: + # Crossplane doesn't support required schemas; skip or use a fallback + pass +``` + +Function SDKs provide helpers such as `has_capability` and `get_required_schema`; +see your SDK documentation for the exact API. + ### Function pipeline context Sometimes two functions in a pipeline want to share information with each other