This composition function allows you to compose Crossplane resources using Go templates. If you've written a Helm chart before, using this function will be a familiar experience.
Here's an example:
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: example
spec:
compositeTypeRef:
apiVersion: example.crossplane.io/v1beta1
kind: XR
mode: Pipeline
pipeline:
- step: create-a-bucket
functionRef:
name: function-go-templating
input:
apiVersion: gotemplating.fn.crossplane.io/v1beta1
kind: GoTemplate
source: Inline
inline:
options:
- missingkey=error
template: |
apiVersion: s3.aws.upbound.io/v1beta1
kind: Bucket
metadata:
annotations:
gotemplating.fn.crossplane.io/composition-resource-name: bucket
spec:
forProvider:
region: {{ .observed.composite.resource.spec.region }}
- step: automatically-detect-ready-composed-resources
functionRef:
name: function-auto-readyThis function can load templates from three sources: Inline, FileSystem and Environment.
Use the Inline source to specify a simple template inline in your Composition.
Multiple YAML manifests can be specified using the --- document separator.
Use the FileSystem source to specify a directory of templates. The
FileSystem source treats all files under the specified directory as templates.
Use the Environment source to specify a key in the context environment that contains the templates.
This allows templates to be dynamically loaded from sources such as EnvironmentConfigs.
The templates are passed a RunFunctionRequest as data. This means that
you can access the composite resource, any composed resources, and the function
pipeline context using notation like:
{{ .observed.composite.resource.metadata.name }}{{ .desired.composite.resource.status.widgets }}{{ (index .desired.composed "resource-name").resource.spec.widgets }}{{ index .context "apiextensions.crossplane.io/environment" }}{{ index .extraResources "some-bucket-by-name" }}
This function supports all of Go's built-in template functions. The
above examples use the index function to access keys like resource-name that
contain periods, hyphens and other special characters. Like Helm, this function
also supports Sprig template functions as well as additional functions.
Template options can be provided using the Options
property.
To return desired composite resource connection details, include a template that
produces the special CompositeConnectionDetails resource:
apiVersion: meta.gotemplating.fn.crossplane.io/v1alpha1
kind: CompositeConnectionDetails
data:
connection-secret-key: connection-secret-valueNote: The value of the connection secret value must be base64 encoded. This is already the case if you are referencing a key from a mananged resource's
connectionDetailsfield. However, if you want to include a connection secret value from somewhere else, you will need to use theb64encSprig function:
apiVersion: meta.gotemplating.fn.crossplane.io/v1alpha1
kind: CompositeConnectionDetails
data:
server-endpoint: {{ (index $.observed.resources "my-server").resource.status.atProvider.endpoint | b64enc }}To mark a desired composed resource as ready, use the
gotemplating.fn.crossplane.io/ready annotation:
apiVersion: s3.aws.upbound.io/v1beta1
kind: Bucket
metadata:
annotations:
gotemplating.fn.crossplane.io/composition-resource-name: bucket
gotemplating.fn.crossplane.io/ready: "True"
spec: {}See the example directory for examples that you can run locally using the Crossplane CLI:
$ crossplane beta render xr.yaml composition.yaml functions.yamlSee the composition functions documentation to learn more
about crossplane beta render.
By defining one or more special ExtraResources, you can ask Crossplane to
retrieve additional resources from the local cluster and make them available to
your templates. See the docs for more information.
apiVersion: meta.gotemplating.fn.crossplane.io/v1alpha1
kind: ExtraResources
requirements:
some-foo-by-name:
# Resources can be requested either by name
apiVersion: example.com/v1beta1
kind: Foo
matchName: "some-extra-foo"
some-foo-by-labels:
# Or by label.
apiVersion: example.com/v1beta1
kind: Foo
matchLabels:
app: my-app
some-bar-by-a-computed-label:
# But you can also generate them dynamically using the template, for example:
apiVersion: example.com/v1beta1
kind: Bar
matchLabels:
foo: {{ .observed.composite.resource.name }}This will result in Crossplane retrieving the requested resources and making
them available to your templates under the extraResources key, with the
following format:
{
"extraResources": {
"some-foo-by-name": [
// ... the requested bucket if found, empty otherwise ...
],
"some-foo-by-labels": [
// ... the requested buckets if found, empty otherwise ...
],
// ... any other requested extra resources ...
}
}So, you can access the retrieved resources in your templates like this, for example:
{{- $someExtraResources := index .extraResources "some-extra-resources-key" }}
{{- range $i, $extraResource := $someExtraResources.items }}
#
# Do something for each retrieved extraResource
#
{{- end }}Additionally, the function will store retreived extraResources in the function
context under the apiextensions.crossplane.io/extra-resources key, so that
extra resources are passed down the pipeline.
{
"context": {
"apiextensions.crossplane.io/extra-resources": {
"some-foo-by-name": [
// ... the requested bucket if found, empty otherwise ...
],
"some-foo-by-labels": [
// ... the requested buckets if found, empty otherwise ...
],
// ... any other requested extra resources ...
}
}
}If a function previously set extra resources under the key in the context, the function will merge the new extra resources in, so both can be obtained in the next pipeline step.
This function can write to the Composition Context. Subsequent pipeline steps will be able to access the data.
---
apiVersion: meta.gotemplating.fn.crossplane.io/v1alpha1
kind: Context
data:
region: {{ $spec.region }}
id: field
array:
- "1"
- "2"To update Context data, match an existing key. For example, function-environment-configs
stores data under the key apiextensions.crossplane.io/environment.
In this case, Environment fields update and nestedEnvUpdate.hello would be updated with new values.
---
apiVersion: meta.gotemplating.fn.crossplane.io/v1alpha1
kind: Context
data:
"apiextensions.crossplane.io/environment":
kind: Environment
apiVersion: internal.crossplane.io/v1alpha1
update: environment
nestedEnvUpdate:
hello: world
otherContextData:
test: fieldFor more information, see the example in context.
This function applies special logic if a resource with the composite resource's type is found in the template.
If the resource name is not set (the gotemplating.fn.crossplane.io/composition-resource-name meta annotation is not present), then the function does not create composed resources with the composite resource's type. In this case only the composite resource's status is updated.
For example, the following composition does not create composed resources. Rather, it updates the composite resource's status to include dummy: cool-status.
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: example-update-status
spec:
compositeTypeRef:
apiVersion: example.crossplane.io/v1beta1
kind: XR
mode: Pipeline
pipeline:
- step: render-templates
functionRef:
name: function-go-templating
input:
apiVersion: gotemplating.fn.crossplane.io/v1beta1
kind: GoTemplate
source: Inline
inline:
template: |
apiVersion: example.crossplane.io/v1beta1
kind: XR
status:
dummy: cool-statusOn the other hand, if the resource name is set (using the gotemplating.fn.crossplane.io/composition-resource-name meta annotation), then the function creates composed resources with the composite resource's type.
For example, the following composition will create a composed resource:
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: example-allow-recursion
spec:
compositeTypeRef:
apiVersion: example.crossplane.io/v1beta1
kind: XR
mode: Pipeline
pipeline:
- step: render-templates
functionRef:
name: function-go-templating
input:
apiVersion: gotemplating.fn.crossplane.io/v1beta1
kind: GoTemplate
source: Inline
inline:
template: |
apiVersion: example.crossplane.io/v1beta1
kind: XR
metadata:
annotations:
{{ setResourceNameAnnotation "recursive-xr" }}
spec:
compositionRef:
name: example-other # make sure to avoid infinite recursionWarning
This can lead to infinite recursion. Make sure to terminate the recursion by specifying a different compositionRef at some point.
For more information, see the example in recursive.
Starting with Crossplane 1.17, Composition authors can set custom Conditions on the Composite and the Claim.
Add a ClaimConditions to your template to set Conditions:
apiVersion: meta.gotemplating.fn.crossplane.io/v1alpha1
kind: ClaimConditions
conditions:
# Guide to ClaimConditions fields:
# Type of the condition, e.g. DatabaseReady.
# 'Healthy', 'Ready' and 'Synced' are reserved for use by Crossplane and this function will raise an error if used
# - type:
# Status of the condition. String of "True"/"False"/"Unknown"
# status:
# Machine-readable PascalCase reason, for example "ErrorProvisioning"
# reason:
# Optional Target. Publish Condition only to the Composite, or the Composite and the Claim (CompositeAndClaim).
# Defaults to Composite
# target:
# Optional message:
# message:
- type: TestCondition
status: "False"
reason: InstallFail
message: "failed to install"
target: CompositeAndClaim
- type: ConditionTrue
status: "True"
reason: TrueCondition
message: we are true
target: Composite
- type: DatabaseReady
status: "True"
reason: Ready
message: Database is ready
target: CompositeAndClaimThe following custom template functions are available in addition to Go's built-in and Sprig functions:
| Name | Description |
|---|---|
randomChoice |
Randomly selects one of a given set of strings. |
toYaml |
Marshals any object into a YAML string. |
fromYaml |
Unmarshals a YAML string into an object. |
getResourceCondition |
Retrieves conditions of resources. |
getComposedResource |
Retrieves observed composed resources. |
getCompositeResource |
Retrieves the observed composite resource. |
getExtraResources |
Retrieves extra resources. |
getExtraResourcesFromContext |
Retrieves extra resources from the environment context. |
setResourceNameAnnotation |
Returns the special resource-name annotation with the given name. |
include |
Outputs a template as a string. |
See the linked examples for usage details.
This function uses Go, Docker, and the Crossplane CLI to build functions.
# Run code generation - see input/generate.go
$ go generate ./...
# Run tests - see fn_test.go
$ go test ./...
# Build the function's runtime image - see Dockerfile
$ docker build . --tag=runtime
# Build a function package - see package/crossplane.yaml
$ crossplane xpkg build -f package --embed-runtime-image=runtime