diff --git a/content/master/concepts/environment-configs.md b/content/master/concepts/environment-configs.md index 7369218e..ff7a7991 100644 --- a/content/master/concepts/environment-configs.md +++ b/content/master/concepts/environment-configs.md @@ -11,12 +11,12 @@ TODO: Add Policies --> -A Crossplane EnvironmentConfig is a cluster scoped +A Crossplane EnvironmentConfig is a cluster-scoped, strongly-typed, [ConfigMap](https://kubernetes.io/docs/concepts/configuration/configmap/)-like resource used by Compositions. Compositions can use the environment to store information from individual resources or to apply patches. -Crossplane supports multiple EnvironmentConfigs, each acting as a unique +Crossplane supports multiple `EnvironmentConfigs`, each acting as a unique data store. When Crossplane creates a composite resource, Crossplane merges all the @@ -32,39 +32,6 @@ A composite resource can't read data in another composite resource's environment. {{< /hint >}} -## Enable EnvironmentConfigs -EnvironmentConfigs are an alpha feature. Alpha features aren't enabled by -default. - -Enable EnvironmentConfig support by -[changing the Crossplane pod setting]({{}}) -and enabling -{{}}--enable-environment-configs{{}} -argument. - -```yaml {label="deployment",copy-lines="12"} -$ kubectl edit deployment crossplane --namespace crossplane-system -apiVersion: apps/v1 -kind: Deployment -spec: -# Removed for brevity - template: - spec: - containers: - - args: - - core - - start - - --enable-environment-configs -``` - -{{}} - -The [Crossplane install guide]({{}}) -describes enabling feature flags like -{{}}--enable-environment-configs{{}} -with Helm. -{{< /hint >}} - ## Create an EnvironmentConfig @@ -95,30 +62,47 @@ data: - item2 ``` +## Access EnvironmentConfigs + +`EnvironmentConfigs` can be accessed by [Composition Functions] supporting +[extra-resources], e.g. [function-environment-configs] or +[function-go-templating]. + +## Migration from Alpha Composition Environment + +Crossplane (`<=v1.17`) natively supported selecting `EnvironmentConfigs`, +merging them into an `in-memory environment` and patching between that, +composed and composite resources. From `v1.18`, this native capability has been +removed, in favor of [Composition Functions]. + +Users that enabled Alpha Composition Environments +(`--enable-environment-configs`) and leveraged the native functionality +(`spec.environment.patches`, `spec.environment.environmentConfigs` and +`*Environment` patches), will have to migrate to Composition Functions to +continue doing so. + +Automated migration to `Pipeline` mode is available through `crossplane beta +convert pipeline-composition`, which will move a composition using `Resource` +mode, to [function-patch-and-transform] and, if needed, +[function-environment-configs]. + +See [function-environment-configs]' docs for more details about manual +migration. + -## Select an EnvironmentConfig +## Select an EnvironmentConfig using function-environment-configs -Select the EnvironmentConfigs to use -inside a Composition's -{{}}environment{{}} field. +Select the EnvironmentConfigs to use through [function-environment-configs]'s `Input`. -The {{}}environmentConfigs{{}} field is a -list of environments this Composition can use. +The `environmentConfigs` field is a list of `EnvironmentConfigs` we want +retrieved, merged and passed to the next step in the pipeline through the +[Context] at a well-known key, `apiextensions.crossplane.io/environment`. -Select an environment by -{{}}Reference{{}} or -by -{{}}Selector{{}}. +Select an environment by `Reference` or by `Selector`: -A -{{}}Reference{{}} -selects an environment by -{{}}name{{}}. -The -{{}}Selector{{}} selects an environment -based on the -{{}}Labels{{}} applied to the environment. +* A `Reference` selects an `EnvironmentConfig` by name. +* The `Selector` selects an `EnvironmentConfig` by labels. ```yaml {label="comp",copy-lines="none"} apiVersion: apiextensions.crossplane.io/v1 @@ -126,42 +110,41 @@ kind: Composition metadata: name: example-composition spec: - environment: - environmentConfigs: - - type: Reference - ref: - name: example-environment - - type: Selector - selector: - matchLabels: - # Removed for brevity + 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-environment + - type: Selector + selector: + matchLabels: + # Removed for brevity + # the environment will be passed to the next function in the pipeline + # as part of the context + +# Next step consuming the merged environment removed for brevity... ``` -If a Composition uses multiple -{{}}environmentConfigs{{}} -Crossplane merges them together in the order they're listed. - -{{}} -If multiple -{{}}environmentConfigs{{}} -use the same key, the Composition uses the value of the last environment listed. -{{}} +If a Composition uses multiple `EnvironmentConfigs`, +[function-environment-configs] merges them together in the order they're +listed. ### Select by name -Select an environment by name with -{{}}type: Reference{{}}. +Select an `EnvironmentConfig` by name with `type: Reference`. -Define the -{{}}ref{{}} object and the -{{}}name{{}} matching the exact name of -the environment. +Define `ref.name` to match the exact name of the environment. -For example, select the -{{}}environmentConfig{{}} -named -{{}}example-environment{{}} +For example, select the `EnvironmentConfig` named `example-environment`: ```yaml {label="byName",copy-lines="all"} apiVersion: apiextensions.crossplane.io/v1 @@ -169,38 +152,33 @@ kind: Composition metadata: name: example-composition spec: - environment: - environmentConfigs: - - type: Reference - ref: - name: example-environment + 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-environment ``` ### Select by label -Select an environment by labels with a -{{}}type: Selector{{}}. - -Define the {{}}selector{{}} object. +Select an `EnvironmentConfig` by labels with a `type: Selector`. -The -{{}}matchLabels{{}} object contains a -list of labels to match on. - -Selecting a label requires matching both the label -{{}}key{{}} -and the value of key. +Define `selector.matchLabels` to a list of selectors either of type `Value`, or `FromCompositeFieldPath`. When matching the label's value, provide an exact value with a -{{}}type: Value{{}} and provide the value -to match in the -{{}}value{{}} field. +`type: Value and provide the value to match in the `value` field. -Crossplane can also match a label's value based on an input in the composite -resource. Use -{{}}type: FromCompositeFieldPath{{}} -and provide the field to match in the -{{}}valueFromFieldPath{{}} field. +[function-environment-configs] can also match a label's value based on an input +in the composite resource. Use `type: FromCompositeFieldPath` and provide the +field to match in the `valueFromFieldPath` field. ```yaml {label="byLabel",copy-lines="all"} apiVersion: apiextensions.crossplane.io/v1 @@ -208,42 +186,39 @@ kind: Composition metadata: name: example-composition spec: - environment: - environmentConfigs: - - type: Selector - selector: - matchLabels: - - key: my-label-key - type: Value - value: my-label-value - - key: my-label-key - type: FromCompositeFieldPath - valueFromFieldPath: spec.parameters.deploy + mode: Pipeline + pipeline: + - step: environmentConfigs + functionRef: + name: function-environment-configs + input: + apiVersion: environmentconfigs.fn.crossplane.io/v1beta1 + kind: Input + spec: + environmentConfigs: + - type: Selector + selector: + matchLabels: + - key: my-label-key + type: Value + value: my-label-value + - key: my-label-key + type: FromCompositeFieldPath + valueFromFieldPath: spec.parameters.deploy # Removed for brevity ``` #### Manage selector results Selecting environments by labels may return more than one environment. -The Composition sorts all the results by the name of the environments and -only uses the first environment in the sorted list. - -Set the {{}}mode{{}} as -{{}}mode: Multiple{{}} to return -all matched environments. Use -{{}}mode: Single{{}} to -return a single environment. - -{{}} -Sorting and the selection -{{}}mode{{}} -only applies to a single -{{}}type: Selector{{}}. - -This doesn't change how Compositions merge multiple -{{}}environmentConfigs{{}}. -{{< /hint >}} +[function-environment-configs], by default, sorts all the results by name and +only uses the first environment in the sorted list. + +Set the `selector.mode` to `Multiple` to return all matched EnvironmentConfigs. +Use `mode: Single` to return a single environment, and error out if more than +one match is found. +Sorting and the selection mode only applies to a single `Selector`. ```yaml {label="selectResults"} apiVersion: apiextensions.crossplane.io/v1 @@ -251,42 +226,45 @@ kind: Composition metadata: name: example-composition spec: - environment: - environmentConfigs: - - type: Selector - selector: - mode: Multiple - matchLabels: - - key: my-label-key - type: Value - value: my-label-value - - key: my-label-key - type: FromCompositeFieldPath - valueFromFieldPath: spec.parameters.deploy - - type: Selector - selector: - mode: Single - matchLabels: - - key: my-other-label-key - type: Value - value: my-other-label-value - - key: my-other-label-key - type: FromCompositeFieldPath - valueFromFieldPath: spec.parameters.deploy + mode: Pipeline + pipeline: + - step: environmentConfigs + functionRef: + name: function-environment-configs + input: + apiVersion: environmentconfigs.fn.crossplane.io/v1beta1 + kind: Input + spec: + environmentConfigs: + - type: Selector + selector: + mode: Multiple + matchLabels: + - key: my-label-key + type: Value + value: my-label-value + - key: my-label-key + type: FromCompositeFieldPath + valueFromFieldPath: spec.parameters.deploy + - type: Selector + selector: + mode: Single + matchLabels: + - key: my-other-label-key + type: Value + value: my-other-label-value + - key: my-other-label-key + type: FromCompositeFieldPath + valueFromFieldPath: spec.parameters.deploy ``` -When using -{{}}mode: Multiple{{}} limit the -number of returned environments with -{{}}maxMatch{{}} and define the -maximum number of environments returned. +When using `mode: Multiple` limit the number of returned `EnvironmentConfigs` +with `maxMatch` and define the maximum number to select. -Use `minMatch` and define the minimum -number of environments returned. +Use `minMatch` and define the minimum number of environments returned. -The Composition sorts the returned environments alphabetically by name. Sort the -environments on a different field with -{{}}sortByFieldPath{{}} and define +The Function sorts the returned environments alphabetically by name by defaul. +Sort the environments on a different field with `sortByFieldPath` and define the field to sort by. @@ -296,36 +274,38 @@ kind: Composition metadata: name: example-composition spec: - environment: - environmentConfigs: - - type: Selector - selector: - mode: Multiple - maxMatch: 4 - sortByFieldPath: metadata.annotations[sort.by/weight] - matchLabels: - - key: my-label-key - type: Value - value: my-label-value - - key: my-label-key - type: FromCompositeFieldPath - valueFromFieldPath: spec.parameters.deploy + mode: Pipeline + pipeline: + - step: environmentConfigs + functionRef: + name: function-environment-configs + input: + apiVersion: environmentconfigs.fn.crossplane.io/v1beta1 + kind: Input + spec: + environmentConfigs: + - type: Selector + selector: + mode: Multiple + maxMatch: 4 + sortByFieldPath: metadata.annotations[sort.by/weight] + matchLabels: + - key: my-label-key + type: Value + value: my-label-value + - key: my-label-key + type: FromCompositeFieldPath + valueFromFieldPath: spec.parameters.deploy ``` -The environments selected by -{{}}matchLabels{{}} are then merged -into any other environments listed in the -{{}}environmentConfigs{{}}. +The EnvironmentConfigs selected by `matchLabels` are then merged with all the +other ones specified. #### Optional selector labels -By default, Crossplane issues an error if a -{{}}valueFromFieldPath{{}} +By default, Crossplane issues an error if the specified `valueFromFieldPath` field doesn't exist in the composite resource. -Add -{{}}fromFieldPathPolicy{{}} -as {{}}Optional{{}} -to ignore a field if it doesn't exist. +Set `fromFieldPathPolicy` to `Optional` to ignore a field if it doesn't exist. ```yaml {label="byLabelOptional",copy-lines="all"} apiVersion: apiextensions.crossplane.io/v1 @@ -333,34 +313,40 @@ kind: Composition metadata: name: example-composition spec: - environment: - environmentConfigs: - - type: Selector - selector: - matchLabels: - - key: my-first-label-key - type: Value - value: my-first-label-value - - key: my-second-label-key - type: FromCompositeFieldPath - valueFromFieldPath: spec.parameters.deploy - fromFieldPathPolicy: Optional + mode: Pipeline + pipeline: + - step: environmentConfigs + functionRef: + name: function-environment-configs + input: + apiVersion: environmentconfigs.fn.crossplane.io/v1beta1 + kind: Input + spec: + environmentConfigs: + - type: Selector + selector: + mode: Multiple + maxMatch: 4 + sortByFieldPath: metadata.annotations[sort.by/weight] + matchLabels: + - key: my-label-key + type: Value + value: my-label-value + - key: my-label-key + type: FromCompositeFieldPath + valueFromFieldPath: spec.parameters.deploy + fromFieldPathPolicy: Optional # Removed for brevity ``` -Set a default value for an optional label by setting the default -{{}}value{{}} for the -{{}}key{{}} first, then -define the -{{}}Optional{{}} label. +Set a default value for an optional label by setting the default `value` for +the `key` first using a `Value` selector, then define the `Optional` +`FromCompositeFieldPath` one. -For example, this Composition defines -{{}}value: my-default-value{{}} -for the key {{}}my-second-label-key{{}}. -If the label -{{}}my-second-label-key{{}} -exists, Crossplane uses the value from the label instead. +For example, this Composition defines `value: my-default-value` for the key +`my-second-label-key`. If the Composite resource defines +`spec.parameters.deploy`, [function-environment-configs] will use that instead. ```yaml {label="byLabelOptionalDefault",copy-lines="all"} apiVersion: apiextensions.crossplane.io/v1 @@ -368,47 +354,141 @@ kind: Composition metadata: name: example-composition spec: - environment: - environmentConfigs: - - type: Selector - selector: - matchLabels: - - key: my-first-label-key - type: Value - value: my-label-value - - key: my-second-label-key - type: Value - value: my-default-value - - key: my-second-label-key - type: FromCompositeFieldPath - valueFromFieldPath: spec.parameters.deploy - fromFieldPathPolicy: Optional + mode: Pipeline + pipeline: + - step: environmentConfigs + functionRef: + name: function-environment-configs + input: + apiVersion: environmentconfigs.fn.crossplane.io/v1beta1 + kind: Input + spec: + environmentConfigs: + - type: Selector + selector: + matchLabels: + - key: my-first-label-key + type: Value + value: my-label-value + - key: my-second-label-key + type: Value + value: my-default-value + - key: my-second-label-key + type: FromCompositeFieldPath + valueFromFieldPath: spec.parameters.deploy + fromFieldPathPolicy: Optional # Removed for brevity ``` {{}} -Crossplane applies values in order. The value of the last key defined always takes precedence. +[function-environment-configs] applies values in order. The value of the last +key defined always takes precedence. Defining the default value _after_ the label always overwrites the label value. {{< /hint >}} -## Use EnvironmentConfigs in a Composition +## Patching with EnvironmentConfigs using [function-patch-and-transform] -When Crossplane creates or updates a composite resource, it merges all the -specified EnvironmentConfigs into an in-memory environment. +`EnvironmentConfigs` selected as explained above, are then merged in an +`in-memory environment` by [function-environment-configs] and passed to the +next function in the pipeline at a well-known key, +`apiextensions.crossplane.io/environment`. -Crossplane sends the merged, in-memory environment to the composition function -pipeline using the -[pipeline context]({{}}). -It writes the environment to the `apiextensions.crossplane.io/environment` -context key. - -Some composition functions can read the environment from the pipeline context -and use it to compose resources. +[function-patch-and-transform] can be used to read or write data between the in-memory environment and +composite resource or individual composed resources. {{}} The Patch and Transform function can use the environment to patch composed resources. Read about EnvironmentConfig patch types in the [Patch and Transform function documentation]({{}}). {{< /hint >}} + +### Patch between Composite resource and environment + +To patch between Composite resource and environment define patches at +`spec.environment.patches` in [function-patch-and-transform]'s `Resources` +input. + +Use the `ToCompositeFieldPath` patch type to copy data from the in-memory +environment to the Composite resource. Use the `FromCompositeFieldPath` to +copy data from the Composite resource to the in-memory environment. + +```yaml {label="xrpatch",copy-lines="none"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +metadata: + name: example-composition +spec: + mode: Pipeline + pipeline: + # Removed for Brevity + - step: patch-and-transform + functionRef: + name: function-patch-and-transform + input: + apiVersion: pt.fn.crossplane.io/v1beta1 + kind: Resources + environment: + patches: + - type: ToCompositeFieldPath + fromFieldPath: tags + toFieldPath: metadata.labels[envTag] + - type: FromCompositeFieldPath + fromFieldPath: metadata.name + toFieldPath: newEnvironmentKey +# Removed for Brevity +``` + +Individual resources can use any data written to the in-memory environment. + +`CombineFromComposite` and `CombineToComposite` can be used to combine multiple +values and write the result either to the in-memory environment or the +Composite resource, respectively. + +### Patch an individual resource + +To patch between individual resources and the in-memory environment, inside the +patches of the resource, use `ToEnvironmentFieldPath` to copy data from the +resource to the in-memory environment. Use `FromEnvironmentFieldPath` to copy +data to the resource from the in-memory environment. + +```yaml {label="envpatch",copy-lines="none"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +metadata: + name: example-composition +spec: + mode: Pipeline + pipeline: + # Removed for Brevity + - step: patch-and-transform + functionRef: + name: function-patch-and-transform + input: + apiVersion: pt.fn.crossplane.io/v1beta1 + kind: Resources + # Removed for Brevity + resources: + - name: vpc + base: + apiVersion: ec2.aws.upbound.io/v1beta1 + kind: VPC + spec: + forProvider: + cidrBlock: 172.16.0.0/16 + patches: + - type: ToEnvironmentFieldPath + fromFieldPath: status.atProvider.id + toFieldPath: vpcId + - type: FromEnvironmentFieldPath + fromFieldPath: tags + toFieldPath: spec.forProvider.tags +``` + +The [Patch and Transform]({{}}) documentation has +more information on patching individual resources. + +[extra-resources]: {{}} +[function-environment-configs]: https://github.com/crossplane-contrib/function-environment-configs +[function-patch-and-transform]: {{}} \ No newline at end of file