GitHub - crossplane-contrib/function-go-templating: A Go templating composition function (original) (raw)

CI GitHub release (latest SemVer)

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-ready

Using this function

This 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 templates field, which may contain a slice of strings, or by using the --- document separator in the template field.

Use the FileSystem source to specify a directory of templates. TheFileSystem 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:

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 Optionsproperty. The default options are "missingkey=default". This can be overridden by the --default-options CLI flag or the FUNCTION_GO_TEMPLATING_DEFAULT_OPTIONSenvironment variable. Setting the default-options to "missingkey=error" will cause the template engine to return an error when a missing key is detected, instead of setting the value to "".

Connection Details

v1 Composite Resources (Legacy)

For legacy v1 XRs only, you can return desired composite resource connection details by including a template that produces the specialCompositeConnectionDetails resource:

apiVersion: meta.gotemplating.fn.crossplane.io/v1alpha1 kind: CompositeConnectionDetails data: connection-secret-key: connection-secret-value

Note: 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 connectionDetails field. However, if you want to include a connection secret value from somewhere else, you will need to use the b64enc Sprig function:

apiVersion: meta.gotemplating.fn.crossplane.io/v1alpha1 kind: CompositeConnectionDetails data: server-endpoint: {{ (index $.observed.resources "my-server").resource.status.atProvider.endpoint | b64enc }}

v2 Composite Resources

For v2 composite resources, the CompositeConnectionDetails resource is not supported. Instead, you should compose an explicit Kubernetes Secret resource that aggregates connection details from the other composed resources.

apiVersion: v1 kind: Secret metadata: annotations: {{ setResourceNameAnnotation "connection-secret" }} {{ if eq $.observed.resources nil }} data: {} {{ else }} data: server-endpoint: {{ (index $.observed.resources "my-server").resource.status.atProvider.endpoint | b64enc }} {{ end }}

For a detailed walkthrough and full example, please see theConnection Details Compositions guide in the Crossplane docs.

Readiness

To mark a desired composed resource or composite resource as ready or not ready, use the

gotemplating.fn.crossplane.io/ready annotation:

Composed resource

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: {}

Composite resource

apiVersion: example.crossplane.io/v1beta1 kind: XR metadata: annotations: gotemplating.fn.crossplane.io/ready: "True" status: {}

See the example directory for examples that you can run locally using the Crossplane CLI:

$ crossplane beta render xr.yaml composition.yaml functions.yaml

See the composition functions documentation to learn more about crossplane beta render.

ExtraResources

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,i, 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.

Writing to the Context

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:

To update Context data, match an existing key. For example, function-environment-configsstores 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: field

For more information, see the example in context.

Updating status or creating composed resources with the composite resource's type

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-status

On 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 recursion

Warning

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.

Setting Conditions on the Claim and Composite

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:

Additional Functions

The 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.
getComposedConnectionDetails Retrieves connection details of an observed composed resource.
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.

Developing this function

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