Skip to content

crossplane-contrib/function-environment-configs

Repository files navigation

function-environment-configs

CI GitHub release (latest SemVer)

Warning

This function relies on functionalities only available in Crossplane 1.15 and later. It will not work with earlier versions.

Important

This function is meant to replace native Composition Environment (--enable-environment-configs), see below for more details.

This composition function allows you to request EnvironmentConfigs, merge them in the requested order and return inject the computed environment into the Context at a well-known key, apiextensions.crossplane.io/environment, so that other functions such as function-patch-and-transform can access it.

Using this function

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

$ crossplane beta render \
  --extra-resources example/environmentConfigs.yaml \
  --include-context \
  example/xr.yaml example/composition.yaml example/functions.yaml

To validate the rendered output, you can use the crossplane beta validate command:

$ crossplane beta render \
  --extra-resources example/environmentConfigs.yaml \
  --include-full-xr \
  example/xr.yaml example/composition.yaml example/functions.yaml | crossplane beta validate example -

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

Migration from native Composition Environment

Crossplane 1.18 dropped native Composition Environment, meaning spec.environment and *Environment patches were removed, while EnvironmentConfig as a resource was promoted to Beta.

crossplane beta convert pipeline-composition has been updated to automatically migrate Compositions using those fields to this function.

A manual migration can be performed moving the following fields from a Composition spec to this function's Input:

  • spec.environment.environmentConfigs -> spec.environmentConfigs
  • spec.environment.defaultData -> spec.defaultData
  • spec.environment.policy.resolution -> spec.policy.resolution

spec.environment.policy.resolve is not configurable at the moment, defaulting to policy Always.

spec.environment.patches and resources' *Environment patches will have to be moved to function-patch-and-transform's input.

The diagram below shows what part of the usual Composition is replaced by this function and how it fits with other functions:

diagram.png

Migration Example

Given an example Resource-mode Composition:

Warning

The example below is using the old native Composition Environment, which was dropped in Crossplane 1.18.

Old Resource-mode Composition

apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
   name: foo
spec:
   compositeTypeRef:
      apiVersion: example.crossplane.io/v1
      kind: XR
   mode: Resources
   environment:
      environmentConfigs:
      - type: Reference
        ref:
           name: example-config
      patches:
      # So you can then use it in all your patches:
      # - Env -> XR
      - type: ToCompositeFieldPath
        fromFieldPath: "someFieldInTheEnvironment"
        toFieldPath: "status.someFieldFromTheEnvironment"
      # - XR -> Env
      - type: FromCompositeFieldPath
        fromFieldPath: "spec.someFieldInTheXR"
        toFieldPath: "someFieldFromTheXR"
   resources:
   - name: bucket
     base:
       apiVersion: s3.aws.upbound.io/v1beta1
       kind: Bucket
       spec:
         forProvider:
           region: us-east-2
     patches:
       # - Env -> Resource
       - type: FromEnvironmentFieldPath
         fromFieldPath: "someFieldInTheEnvironment"
         toFieldPath: "spec.forProvider.someFieldFromTheEnvironment"
       # - Resource -> Env
       - type: ToEnvironmentFieldPath
         fromFieldPath: "status.someOtherFieldInTheResource"
         toFieldPath: "someOtherFieldInTheEnvironment"

The above can be converted to use this function and function-patch-and-transform as follows:

apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
   name: foo
spec:
  compositeTypeRef:
    apiVersion: example.crossplane.io/v1
    kind: XR
  mode: Pipeline
  pipeline:
  - step: environmentConfigs
    functionRef:
      name: function-environment-configs
    input:
      apiVersion: environmentconfigs.fn.crossplane.io/v1beta1
      kind: Input
      spec:
        environmentConfigs:
        - type: Reference
          ref:
            name: example-config
    # the environment is be passed to the next function in the pipeline
    # as part of the context
  - step: patch-and-transform
    # function-patch-and-transform knows it has to look for the environment in the
    # context at "apiextensions.crossplane.io/environment"
    functionRef:
      name: function-patch-and-transform
    input:
      apiVersion: pt.fn.crossplane.io/v1beta1
      kind: Resources
      environment:
        patches:
        # So you can then use it in all your patches:
        # - Env -> XR
        - type: ToCompositeFieldPath
          fromFieldPath: "someFieldInTheEnvironment"
          toFieldPath: "status.someFieldFromTheEnvironment"
        # - XR -> Env
        - type: FromCompositeFieldPath
          fromFieldPath: "spec.someFieldInTheXR"
          toFieldPath: "someFieldFromTheXR"
        resources:
        - name: bucket
          base:
            apiVersion: s3.aws.upbound.io/v1beta1
            kind: Bucket
            spec:
              forProvider:
                region: us-east-2
          patches:
          # - Env -> Resource
          - type: FromEnvironmentFieldPath
            fromFieldPath: "someFieldInTheEnvironment"
            toFieldPath: "spec.forProvider.someFieldFromTheEnvironment"
          # - Resource -> Env
          - type: ToEnvironmentFieldPath
            fromFieldPath: "status.someOtherFieldInTheResource"
            toFieldPath: "someOtherFieldInTheEnvironment"

Consuming environment from Context-aware functions

This function just merges selected EnvironmentConfigs into the Context at a well-known key, apiextensions.crossplane.io/environment, therefore any Context-aware function can access it.

For example, using function-go-templating:

apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
   name: foo
spec:
  compositeTypeRef:
    apiVersion: example.crossplane.io/v1
    kind: XR
  mode: Pipeline
  pipeline:
  - step: environmentConfigs
    functionRef:
      name: function-environment-configs
    input:
      apiVersion: environmentconfigs.fn.crossplane.io/v1beta1
      kind: Input
      spec:
        environmentConfigs:
        - type: Reference
          ref:
            name: example-config
    # the environment is be passed to the next function in the pipeline
    # as part of the context
  - step: go-templating
    functionRef:
      name: function-go-templating
    input:
      apiVersion: gotemplating.fn.crossplane.io/v1beta1
      kind: GoTemplate
      source: Inline
      inline:
        template: |
          ---
          apiVersion: example.crossplane.io/v1
          kind: XR
          status:
            fromEnv: {{ index .context "apiextensions.crossplane.io/environment" "complex" "c" "d" }}

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