diff --git a/.gitignore b/.gitignore index 5267d4e43..e7ee0ba0e 100644 --- a/.gitignore +++ b/.gitignore @@ -75,4 +75,8 @@ jekyll_files/* # Netlify local dev ###################### -.netlify/* \ No newline at end of file +.netlify/* + +# tmp folder generated by htmltest # +###################### +tmp/ \ No newline at end of file diff --git a/content/knowledge-base/guides/connection-details.md b/content/knowledge-base/guides/connection-details.md new file mode 100644 index 000000000..7577a1654 --- /dev/null +++ b/content/knowledge-base/guides/connection-details.md @@ -0,0 +1,607 @@ +--- +title: Understanding Connection Details +weight: 11 +description: "How to create and manage connection details across Crossplane managed resources, composite resources, Compositions and Claims" +--- + +Using connection details in Crossplane requires the following components: +* Defining the `writeConnectionSecretToRef.name` in a [Claim]({{}}). +* Defining the `writeConnectionSecretsToNamespace` value in the [Composition]({{}}). +* Define the `writeConnectionSecretToRef` name and namespace for each resource in the + [Composition]({{}}). +* Define the list of secret keys produced by each composed resource with `connectionDetails` in the + [Composition]({{}}). +* Optionally, define the `connectionSecretKeys` in a + [CompositeResourceDefinition]({{}}). + +{{}} +This guide discusses creating Kubernetes secrets. +Crossplane also supports using external secret stores like [HashiCorp Vault](https://www.vaultproject.io/). + +Read the [external secrets store guide]({{}}) for more information on using Crossplane +with an external secret store. +{{}} + +## Background +When a [Provider]({{}}) creates a managed +resource, the resource may generate resource-specific details. These details can include +usernames, passwords or connection details like an IP address. + +Crossplane refers to this information as the _connection details_ or +_connection secrets_. + +The Provider +defines what information to present as a _connection +detail_ from a managed resource. + + + +When a managed resource is part of a +[Composition]({{}}), the Composition, +[Composite Resource Definition]({{}}) +and optionally, the +[Claim]({{}}) define what details are visible +and where they're stored. + + +{{}} +All the following examples use the same set of Compositions, +CompositeResourceDefinitions and Claims. + +All examples rely on +[Upbound provider-aws-iam](https://marketplace.upbound.io/providers/upbound/provider-aws-iam/) +to create resources. + +{{}} +```yaml +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +metadata: + name: xsecrettest.example.org +spec: + writeConnectionSecretsToNamespace: other-namespace + compositeTypeRef: + apiVersion: example.org/v1alpha1 + kind: XSecretTest + resources: + - name: key + base: + apiVersion: iam.aws.upbound.io/v1beta1 + kind: AccessKey + spec: + forProvider: + userSelector: + matchControllerRef: true + writeConnectionSecretToRef: + namespace: docs + name: key1 + connectionDetails: + - fromConnectionSecretKey: username + - fromConnectionSecretKey: password + - fromConnectionSecretKey: attribute.secret + - fromConnectionSecretKey: attribute.ses_smtp_password_v4 + patches: + - fromFieldPath: "metadata.uid" + toFieldPath: "spec.writeConnectionSecretToRef.name" + transforms: + - type: string + string: + fmt: "%s-secret1" + - name: user + base: + apiVersion: iam.aws.upbound.io/v1beta1 + kind: User + spec: + forProvider: {} + - name: user2 + base: + apiVersion: iam.aws.upbound.io/v1beta1 + kind: User + metadata: + labels: + docs.crossplane.io: user + spec: + forProvider: {} + - name: key2 + base: + apiVersion: iam.aws.upbound.io/v1beta1 + kind: AccessKey + spec: + forProvider: + userSelector: + matchLabels: + docs.crossplane.io: user + writeConnectionSecretToRef: + namespace: docs + name: key2 + connectionDetails: + - name: key2-user + fromConnectionSecretKey: username + - name: key2-password + fromConnectionSecretKey: password + - name: key2-secret + fromConnectionSecretKey: attribute.secret + - name: key2-smtp + fromConnectionSecretKey: attribute.ses_smtp_password_v4 + patches: + - fromFieldPath: "metadata.uid" + toFieldPath: "spec.writeConnectionSecretToRef.name" + transforms: + - type: string + string: + fmt: "%s-secret2" +``` +{{}} + +{{}} + +```yaml +apiVersion: apiextensions.crossplane.io/v1 +kind: CompositeResourceDefinition +metadata: + name: xsecrettests.example.org +spec: + group: example.org + connectionSecretKeys: + - username + - password + - attribute.secret + - attribute.ses_smtp_password_v4 + - key2-user + - key2-pass + - key2-secret + - key2-smtp + names: + kind: XSecretTest + plural: xsecrettests + claimNames: + kind: SecretTest + plural: secrettests + versions: + - name: v1alpha1 + served: true + referenceable: true + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object +``` +{{}} + +{{}} +```yaml +apiVersion: example.org/v1alpha1 +kind: SecretTest +metadata: + name: test-secrets + namespace: default +spec: + writeConnectionSecretToRef: + name: my-access-key-secret +``` +{{}} +{{}} + +## Connection secrets in a managed resource + + + + +When a managed resource creates connection secrets, Crossplane can write the +secrets to a +[Kubernetes secret]({{}}) +or an +[external secret store]({{}}). + + + +Creating an individual managed resource shows the connection secrets the +resource creates. + +{{}} +Read the [managed resources]({{}}) +documentation for more information on configuring resources and storing +connection secrets for individual resources. +{{< /hint >}} + + +For example, create an +{{}}AccessKey{{}} resource and save the +connection secrets in a Kubernetes secret named +{{}}my-accesskey-secret{{}} +in the +{{}}default{{}} namespace. + +```yaml {label="mr"} +apiVersion: iam.aws.upbound.io/v1beta1 +kind: AccessKey +metadata: + name: test-accesskey +spec: + forProvider: + userSelector: + matchLabels: + docs.crossplane.io: user + writeConnectionSecretToRef: + namespace: default + name: my-accesskey-secret +``` + +View the Kubernetes secret to see the connection details from the managed +resource. +This includes an +{{}}attribute.secret{{}}, +{{}}attribute.ses_smtp_password_v4{{}}, +{{}}password{{}} and +{{}}username{{}} + +```yaml {label="mrSecret",copy-lines="1"} +kubectl describe secret my-accesskey-secret +Name: my-accesskey-secret +Namespace: default +Labels: +Annotations: + +Type: connection.crossplane.io/v1alpha1 + +Data +==== +attribute.secret: 40 bytes +attribute.ses_smtp_password_v4: 44 bytes +password: 40 bytes +username: 20 bytes +``` + +Compositions and CompositeResourceDefinitions require the exact names of the +secrets generated by a resource. + +## Connection secrets in Compositions + +Resources in a Composition that create connection details still create a +secret object containing their connection details. +Crossplane also generates +another secret object for each composite resource, +containing the secrets from all the defined resources. + +For example, a Composition defines two +{{}}AccessKey{{}} +objects. +Each {{}}AccessKey{{}} writes a +connection secrets to the {{}}name{{}} +inside the {{}}namespace{{}} defined by +the resource +{{}}writeConnectionSecretToRef{{}}. + +Crossplane also creates a secret object for the entire Composition +saved in the namespace defined by +{{}}writeConnectionSecretsToNamespace{{}} +with a Crossplane generated name. + +```yaml {label="comp1",copy-lines="none"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +spec: + writeConnectionSecretsToNamespace: other-namespace + resources: + - name: key1 + base: + apiVersion: iam.aws.upbound.io/v1beta1 + kind: AccessKey + spec: + forProvider: + # Removed for brevity + writeConnectionSecretToRef: + namespace: docs + name: key1-secret + - name: key2 + base: + apiVersion: iam.aws.upbound.io/v1beta1 + kind: AccessKey + spec: + forProvider: + # Removed for brevity + writeConnectionSecretToRef: + namespace: docs + name: key2-secret + # Removed for brevity +``` + +After applying a Claim, view the Kubernetes secrets to see three secret objects +created. + +The secret +{{}}key1-secret{{}} is from the resource +{{}}key1{{}}, +{{}}key2-secret{{}} is from the resource +{{}}key2{{}}. + +Crossplane creates another secret in the namespace +{{}}other-namespace{{}} with the +secrets from resource in the Composition. + + +```shell {label="compGetSec",copy-lines="1"} +kubectl get secrets -A +NAMESPACE NAME TYPE DATA AGE +docs key1-secret connection.crossplane.io/v1alpha1 4 4s +docs key2-secret connection.crossplane.io/v1alpha1 4 4s +other-namespace 70975471-c44f-4f6d-bde6-6bbdc9de1eb8 connection.crossplane.io/v1alpha1 0 6s +``` + +Although Crossplane creates a secret object, by default, Crossplane doesn't add +any data to the object. + +```yaml {copy-lines="none"} +kubectl describe secret 70975471-c44f-4f6d-bde6-6bbdc9de1eb8 -n other-namespace +Name: 70975471-c44f-4f6d-bde6-6bbdc9de1eb8 +Namespace: other-namespace + +Type: connection.crossplane.io/v1alpha1 + +Data +==== +``` + +The Composition must list the connection secrets to store for each resource. +Use the +{{}}connectionDetails{{}} object under +each resource and define the secret keys the resource creates. + + +{{}} +You can't change the +{{}}connectionDetails{{}} +of a Composition. +You must delete and +recreate the Composition to change the +{{}}connectionDetails{{}}. +{{}} + +```yaml {label="comp2",copy-lines="16-20"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +spec: + writeConnectionSecretsToNamespace: other-namespace + resources: + - name: key + base: + apiVersion: iam.aws.upbound.io/v1beta1 + kind: AccessKey + spec: + forProvider: + # Removed for brevity + writeConnectionSecretToRef: + namespace: docs + name: key1 + connectionDetails: + - fromConnectionSecretKey: username + - fromConnectionSecretKey: password + - fromConnectionSecretKey: attribute.secret + - fromConnectionSecretKey: attribute.ses_smtp_password_v4 + # Removed for brevity +``` + +After applying a Claim the composite resource secret object contains the list of +keys listed in the +{{}}connectionDetails{{}}. + +```shell {copy-lines="1"} +kubectl describe secret -n other-namespace +Name: b0dc71f8-2688-4ebc-818a-bbad6a2c4f9a +Namespace: other-namespace + +Type: connection.crossplane.io/v1alpha1 + +Data +==== +username: 20 bytes +attribute.secret: 40 bytes +attribute.ses_smtp_password_v4: 44 bytes +password: 40 bytes +``` + +{{}} +If a key isn't listed in the +{{}}connectionDetails{{}} +it isn't stored in the secret object. +{{< /hint >}} + +### Managing conflicting secret keys +If resources produce conflicting keys, create a unique name with a connection +details +{{}}name{{}}. + +```yaml {label="comp3",copy-lines="none"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +spec: + writeConnectionSecretsToNamespace: other-namespace + resources: + - name: key + base: + kind: AccessKey + spec: + # Removed for brevity + writeConnectionSecretToRef: + namespace: docs + name: key1 + connectionDetails: + - fromConnectionSecretKey: username + - name: key2 + base: + kind: AccessKey + spec: + # Removed for brevity + writeConnectionSecretToRef: + namespace: docs + name: key2 + connectionDetails: + - name: key2-user + fromConnectionSecretKey: username +``` + +The secret object contains both keys, +{{}}username{{}} +and +{{}}key2-user{{}} + +```shell {label="comp3Sec",copy-lines="1"} +kubectl describe secret -n other-namespace +Name: b0dc71f8-2688-4ebc-818a-bbad6a2c4f9a +Namespace: other-namespace + +Type: connection.crossplane.io/v1alpha1 + +Data +==== +username: 20 bytes +key2-user: 20 bytes +# Removed for brevity. +``` + +## Connection secrets in Composite Resource Definitions + +The CompositeResourceDefinition (`XRD`), can restrict which secrets keys are +put in the combined secret and provided to a Claim. + +By default an XRD writes all secret keys listed in the composed resource +`connectionDetails` to the combined secret object. + +Limit the keys passed to the combined secret object and Claims with a +{{}}connectionSecretKeys{{}} object. + +Inside the {{}}connectionSecretKeys{{}} list +the secret key names to create. Crossplane only adds the keys listed to the +combined secret. + +{{}} +You can't change the +{{}}connectionSecretKeys{{}} of an XRD. +You must delete and +recreate the XRD to change the +{{}}connectionSecretKeys{{}}. +{{}} + +For example, an XRD may restrict the secrets to only the +{{}}username{{}}, +{{}}password{{}} and custom named +{{}}key2-user{{}} keys. + +```yaml {label="xrd",copy-lines="4-12"} +kind: CompositeResourceDefinition +spec: + # Removed for brevity. + connectionSecretKeys: + - username + - password + - key2-user +``` + +The secret from an individual resource contains all the resources detailed in +the Composition's `connectionDetails`. + +```shell {label="xrdSec",copy-lines="1"} +kubectl describe secret key1 -n docs +Name: key1 +Namespace: docs + +Data +==== +password: 40 bytes +username: 20 bytes +attribute.secret: 40 bytes +attribute.ses_smtp_password_v4: 44 bytes +``` + +The Claim's secret only contains the +keys allowed by the XRD +{{}}connectionSecretKeys{{}} +fields. + +```shell {label="xrdSec2",copy-lines="2"} +kubectl describe secret my-access-key-secret +Name: my-access-key-secret + +Data +==== +key2-user: 20 bytes +password: 40 bytes +username: 20 bytes +``` + +## Secret objects +Compositions create a secret object for each resource and an extra secret +containing all the secrets from all resources. + +Crossplane saves the resource secret objects in the location defined by the +resource's +{{}}writeConnectionSecretToRef{{}}. + +Crossplane saves the combined secret with a Crossplane generated name in the +namespace defined in the Composition's +{{}}writeConnectionSecretsToNamespace{{}}. + +```yaml {label="comp4",copy-lines="none"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +spec: + writeConnectionSecretsToNamespace: other-namespace + resources: + - name: key + base: + kind: AccessKey + spec: + # Removed for brevity + writeConnectionSecretToRef: + namespace: docs + name: key1 + connectionDetails: + - fromConnectionSecretKey: username + - name: key2 + base: + kind: AccessKey + spec: + # Removed for brevity + writeConnectionSecretToRef: + namespace: docs + name: key2 + connectionDetails: + - name: key2-user + fromConnectionSecretKey: username +``` + +If a Claim uses a secret, it's stored in the same namespace as the Claim with +the name defined in the Claim's +{{}}writeConnectionSecretToRef{{}}. + +```yaml {label="claim3",copy-lines="none"} +apiVersion: example.org/v1alpha1 +kind: SecretTest +metadata: + name: test-secrets + namespace: default +spec: + writeConnectionSecretToRef: + name: my-access-key-secret +``` + +After applying the Claim Crossplane creates the following secrets: +* The Claim's secret, {{}}my-access-key-secret{{}} + in the Claim's {{}}namespace{{}}. +* The first resource's secret object, {{}}key1{{}}. +* The second resource's secret object, {{}}key2{{}}. +* The composite resource secret object in the + {{}}other-namespace{{}} defined by the + Composition's `writeConnectionSecretsToNamespace`. + + +```shell {label="allSec",copy-lines="none"} + kubectl get secret -A +NAMESPACE NAME TYPE DATA AGE +default my-access-key-secret connection.crossplane.io/v1alpha1 8 29m +docs key1 connection.crossplane.io/v1alpha1 4 31m +docs key2 connection.crossplane.io/v1alpha1 4 31m +other-namespace b0dc71f8-2688-4ebc-818a-bbad6a2c4f9a connection.crossplane.io/v1alpha1 8 31m +``` \ No newline at end of file diff --git a/content/knowledge-base/guides/multi-tenant.md b/content/knowledge-base/guides/multi-tenant.md index 8c3446a83..52950de05 100644 --- a/content/knowledge-base/guides/multi-tenant.md +++ b/content/knowledge-base/guides/multi-tenant.md @@ -304,7 +304,7 @@ dedicated control planes to many tenants within a single organization. [managed resources]: {{}} [RBAC]: https://kubernetes.io/docs/reference/access-authn-authz/rbac/ -[Composition]: {{}} +[Composition]: {{}} [CustomResourceDefinitions]: https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/ [Open Policy Agent]: https://www.openpolicyagent.org/ [Rego]: https://www.openpolicyagent.org/docs/latest/policy-language/ diff --git a/content/knowledge-base/guides/troubleshoot.md b/content/knowledge-base/guides/troubleshoot.md index 1940b1154..024f40908 100644 --- a/content/knowledge-base/guides/troubleshoot.md +++ b/content/knowledge-base/guides/troubleshoot.md @@ -12,7 +12,6 @@ indicator that the kubectl Crossplane you're using is outdated. In other words some Crossplane API has been graduated from alpha to beta or stable and the old plugin is not aware of this change. -You can follow the [install Crossplane CLI] instructions to upgrade the plugin. ## Resource Status and Conditions @@ -469,8 +468,46 @@ spec: version: ">=v0.18.2" ``` + +## Tips, Tricks, and Troubleshooting + +In this section we'll cover some common tips, tricks, and troubleshooting steps +for working with Composite Resources. If you're trying to track down why your +Composite Resources aren't working the [Troubleshooting][trouble-ref] page also +has some useful information. + +### Troubleshooting Claims and XRs + +Crossplane relies heavily on status conditions and events for troubleshooting. +You can see both using `kubectl describe` - for example: + +```console +# Describe the PostgreSQLInstance claim named my-db +kubectl describe postgresqlinstance.database.example.org my-db +``` + +Per Kubernetes convention, Crossplane keeps errors close to the place they +happen. This means that if your claim is not becoming ready due to an issue with +your `Composition` or with a composed resource you'll need to "follow the +references" to find out why. Your claim will only tell you that the XR is not +yet ready. + +To follow the references: + +1. Find your XR by running `kubectl describe` on your claim and looking for its + "Resource Ref" (aka `spec.resourceRef`). +1. Run `kubectl describe` on your XR. This is where you'll find out about issues + with the `Composition` you're using, if any. +1. If there are no issues but your XR doesn't seem to be becoming ready, take a + look for the "Resource Refs" (or `spec.resourceRefs`) to find your composed + resources. +1. Run `kubectl describe` on each referenced composed resource to determine + whether it is ready and what issues, if any, it is encountering. + + + + - +[semver spec]: https://github.com/Masterminds/semver#basic-comparisons + + diff --git a/content/master/concepts/_index.md b/content/master/concepts/_index.md index ee3072485..4c41bfe47 100644 --- a/content/master/concepts/_index.md +++ b/content/master/concepts/_index.md @@ -53,4 +53,4 @@ learn more about all of these concepts in the [composition documentation]. [providers documentation]: {{}} [Upbound Marketplace]: https://marketplace.upbound.io [managed resources documentation]: {{}} -[composition documentation]: {{}} +[composition documentation]: {{}} diff --git a/content/master/concepts/claims.md b/content/master/concepts/claims.md new file mode 100644 index 000000000..d26e1b67a --- /dev/null +++ b/content/master/concepts/claims.md @@ -0,0 +1,208 @@ +--- +title: Claims +weight: 60 +description: "Claims are a way to consume Crossplane resources with namespace scoping" +--- + +Claims represents a set of managed resources as a single +Kubernetes object, inside a namespace. + +Users create claims when they access the +custom API, defined in the CompositeResourceDefinition. + +{{< hint "tip" >}} + +Claims are like [composite resources]({{}}). The +difference between Claims and composite resources is Crossplane can create +Claims in a namespace, while composite resources are cluster scoped. +{{< /hint >}} + +{{}} +Crossplane has four core components that users commonly mix up: + +* [Compositions]({{}}) - A template to define how to create resources. +* [Composite Resource Definition]({{}}) + (`XRD`) - A custom API specification. +* [Composite Resources]({{}}) (`XR`) - Created by + using the custom API defined in a Composite Resource Definition. XRs use the + Composition template to create new managed resources. +* Claims (`XRC`) - This page. Like a Composite Resource, but + with namespace scoping. +{{}} + +## Creating a Claim + +Creating a Claim requires a +[Composition]({{}}) and a +[CompositeResourceDefinition]({{}}) +(`XRD`) already installed. + +{{}} +The XRD must +[enable Claims]({{}}). +{{< /hint >}} + +The Composition defines the set of resources to create. +The XRD defines the custom API users call to request the set of resources. + +![Diagram of the relationship of Crossplane components](/media/composition-how-it-works.svg) + +For example, +this {{}}CompositeResourceDefinition{{}} +creates a composite resource API endpoint +{{}}xmydatabases.example.org{{}} and +enables a Claim API endpoint +{{}}database.example.org{{}} + +```yaml {label="xrd1",copy-lines="none"} +apiVersion: apiextensions.crossplane.io/v1 +kind: CompositeResourceDefinition +metadata: + name: xmydatabases.example.org +spec: + group: example.org + names: + kind: XMyDatabase + plural: xmydatabases + claimNames: + kind: Database + plural: databases + # Removed for brevity +``` + +The Claim uses the XRD's +{{}}kind{{}} API endpoint to request +resources. + +The Claim's {{}}apiVersion{{}} matches +the XRD {{}}group{{}} and the +{{}}kind{{}} matches the XRD +{{}}claimNames.kind{{}} + +```yaml {label="claim1",copy-lines="none"} +apiVersion: example.org/v1alpha1 +kind: database +metadata: + name: my-claimed-database +spec: + # Removed for brevity +``` + +When a user creates a Claim in a namespace Crossplane also creates a composite +resource. + +Use {{}}kubectl describe{{}} on the +Claim to view the related composite resource. + +The {{}}Resource Ref{{}} is the +composite resource Crossplane created for this Claim. + +```shell {label="claimcomp",copy-lines="1"} +kubectl describe database.example.org/my-claimed-database +Name: my-claimed-database +API Version: example.org/v1alpha1 +Kind: database +Spec: + Resource Ref: + API Version: example.org/v1alpha1 + Kind: XMyDatabase + Name: my-claimed-database-rr4ll +# Removed for brevity. +``` + +Use {{}}kubectl describe{{}} on the +composite resource to view the +{{}}Claim Ref{{}} linking the +composite resource to the original Claim. + +```shell {label="getcomp",copy-lines="1"} +kubectl describe xmydatabase.example.org/my-claimed-database-rr4ll +Name: my-claimed-database-rr4ll +API Version: example.org/v1alpha1 +Kind: XMyDatabase +Spec: + Claim Ref: + API Version: example.org/v1alpha1 + Kind: database + Name: my-claimed-database + Namespace: default +``` + +{{}} +Crossplane supports directly creating composite resources. Claims allow +namespace scoping and isolation for users consuming the custom APIs. + +If you don't use namespaces in your Kubernetes deployment Claims aren't necessary. +{{< /hint >}} + +### Claiming existing composite resources + +By default, creating a Claim creates a new composite resource. Claims can also +link to existing composite resources. + +A use case for claiming existing composite resources may be slow to provision +resources. Composite resources can be pre-provisioned and a Claim can +use those resources without waiting for their creation. + +Set the Claim's {{}}resourceRef{{}} +and match the pre-existing composite resource +{{}}name{{}}. + +```yaml {label="resourceref",copy-lines="none"} +apiVersion: example.org/v1alpha1 +kind: database +metadata: + name: my-claimed-database +spec: + resourceRef: + apiVersion: example.org/v1alpha1 + kind: XMyDatabase + name: my-pre-created-xr +``` + +If a Claim specifies a +{{}}resourceRef{{}} that doesn't +exist, Crossplane doesn't create a composite resource. + +{{}} +All Claims have a +{{}}resourceRef{{}}. Manually +defining the +{{}}resourceRef{{}} +isn't required. Crossplane fills in the +{{}}resourceRef{{}} +with the information from the composite resource created for the Claim. +{{< /hint >}} + +## Claim connection secrets + +If a Claim expects connection secrets the Claim must define a +{{}}writeConnectionSecretToRef{{}} +object. + +The +{{}}writeConnectionSecretToRef{{}} +object defines the name of the Kubernetes secret object where Crossplane saves +the connection details. + +{{}} +The Crossplane creates the secret object in the same namespace as the Claim. +{{< /hint >}} + +For example, to a new secret object named +{{}}my-claim-secret{{}} use +{{}}writeConnectionSecretToRef{{}} with +the +{{}}name: my-claim-secret{{}}. +```yaml {label="claimSec"} +apiVersion: example.org/v1alpha1 +kind: database +metadata: + name: my-claimed-database +spec: + writeConnectionSecretToRef: + name: my-claim-secret +``` + +For more information on connection secrets read the [Connection Secrets +knowledge base article]({{}}). \ No newline at end of file diff --git a/content/master/concepts/composite-resource-definitions.md b/content/master/concepts/composite-resource-definitions.md new file mode 100644 index 000000000..9638865c2 --- /dev/null +++ b/content/master/concepts/composite-resource-definitions.md @@ -0,0 +1,836 @@ +--- +title: Composite Resource Definitions +weight: 40 +description: "Composite Resource Definitions or XRDs define custom API schemas" +--- + +Composite resource definitions (`XRDs`) define the schema for a custom API. +Users create composite resources (`XRs`) and Claims (`XCs`) using the API +schema defined by an `XRD`. + + +{{< hint "note" >}} + +Read the [composite resources]({{}}) page for more +information about composite resources. + +Read the [Claims]({{}}) page for more +information about Claims. +{{}} + + +{{}} +Crossplane has four core components that users commonly mix up: + +* [Compositions]({{}}) - A template to define how to create resources. +* Composite Resource Definition (`XRD`) - This page. A custom API specification. +* [Composite Resource]({{}}) (`XR`) - Created by + using the custom API defined in a Composite Resource Definition. XRs use the + Composition template to create new managed resources. +* [Claims]({{}}) (`XRC`) - Like a Composite Resource, but + with namespace scoping. +{{}} + +Crossplane XRDs are like +[Kubernetes custom resource definitions](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/). +XRDs require fewer fields and add options related to Crossplane, like Claims and +connection secrets. + +## Creating a CompositeResourceDefinition + +Creating a CompositeResourceDefinition consists of: +* [Defining a custom API group](#xrd-groups). +* [Defining a custom API name](#xrd-names). +* [Defining a custom API schema and version](#xrd-versions). + +Optionally, CompositeResourceDefinitions also support: +* [Offering a Claim](#enable-claims). +* [Defining connection secrets](#manage-connection-secrets). +* [Setting composite resource defaults](#set-composite-resource-defaults). + +Composite resource definitions (`XRDs`) create new API endpoints inside a +Kubernetes cluster. + +Creating a new API requires defining an API +{{}}group{{}}, +{{}}name{{}} and +{{}}version{{}}. + +```yaml {label="xrd1",copy-lines="none"} +apiVersion: apiextensions.crossplane.io/v1 +kind: CompositeResourceDefinition +metadata: + name: xmydatabases.example.org +spec: + group: example.org + names: + kind: XMyDatabase + plural: xmydatabases + versions: + - name: v1alpha1 + # Removed for brevity +``` + +After applying an XRD, Crossplane creates a new Kubernetes custom resource +definition matching the defined API. + +For example, the XRD +{{}}xmydatabases.example.org{{}} +creates a custom resource definition +{{}}xmydatabases.example.org{{}}. + +```shell {label="kubeapi",copy-lines="3"} +kubectl api-resources +NAME SHORTNAMES APIVERSION NAMESPACED KIND +xmydatabases.example.org v1alpha1 false xmydatabases +# Removed for brevity +``` + +{{}} +You can't change the XRD +{{}}group{{}} or +{{}}names{{}}. +You must delete and +recreate the XRD to change the +{{}}group{{}} or +{{}}names{{}}. +{{}} + +### XRD groups + +Groups define a collection of related API endpoints. The `group` can be any +value, but common convention is to map to a fully qualified domain name. + + +Many XRDs may use the same `group` to create a logical collection of APIs. + +For example a `database` group may have a `relational` and `nosql` kinds. + +{{}} +Group names are cluster scoped. Choose group names that don't conflict with +Providers. +Avoid Provider names in the group. +{{< /hint >}} + +### XRD names + +The `names` field defines how to refer to this specific XRD. +The required name fields are: + +* `kind` - the `kind` value to use when calling this API. The kind is + [UpperCamelCased](https://kubernetes.io/docs/contribute/style/style-guide/#use-upper-camel-case-for-api-objects). + Crossplane recommends starting XRD `kinds` with an `X` to show + it's a custom Crossplane API definition. +* `plural` - the plural name used for the API URL. The plural name must be + lowercase. + +{{}} +The XRD +{{}}metadata.name{{}} must be +{{}}plural{{}} name, `.` (dot character), +{{}}group{{}}. + +For example, {{}}xmydatabases.example.org{{}} matches the {{}}plural{{}} name +{{}}xmydatabases{{}}, `.` +{{}}group{{}} name, +{{}}example.org{{}}. + +```yaml {label="xrdName",copy-lines="none"} +apiVersion: apiextensions.crossplane.io/v1 +kind: CompositeResourceDefinition +metadata: + name: xmydatabases.example.org +spec: + group: example.org + names: + kind: XMyDatabase + plural: xmydatabases + # Removed for brevity +``` +{{}} + +### XRD versions + +The XRD `version` is like the +[API versioning used by Kubernetes](https://kubernetes.io/docs/reference/using-api/#api-versioning). +The version shows how mature or stable the API is and increments when changing, +adding or removing fields in the API. + +Crossplane doesn't require specific versions or a specific version naming +convention, but following +[Kubernetes API versioning guidelines](https://kubernetes.io/docs/reference/using-api/#api-versioning) +is strongly recommended. + +* `v1alpha1` - A new API that may change at any time. +* `v1beta1` - An existing API that's considered stable. Breaking changes are + strongly discouraged. +* `v1` - A stable API that doesn't have breaking changes. + +#### Define a schema + + + +The `schema` defines the names +of the parameters, the data types of the parameters and which parameters are +required or optional. + + + +{{}} +All `schemas` follow the Kubernetes custom resource definition +[Open APIv3 structural schema](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#specifying-a-structural-schema). +{{< /hint >}} + +Each +{{}}version{{}} of the API has a unique +{{}}schema{{}}. + +All XRD {{}}schemas{{}} validate against +the {{}}openAPIV3Schema{{}}. The schema +is an OpenAPI +{{}}object{{}} with the +{{}}properties{{}} of a +{{}}spec{{}} +{{}}object{{}}. + +Inside the {{}}spec.properties{{}} is the custom +API definition. + +In this example, the key {{}}region{{}} +is a {{}}string{{}}. + +```yaml {label="schema",copy-lines="none"} +apiVersion: apiextensions.crossplane.io/v1 +kind: CompositeResourceDefinition +metadata: + name: xdatabases.custom-api.example.org +spec: + group: custom-api.example.org + names: + kind: xDatabase + plural: xdatabases + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + properties: + region: + type: string + # Removed for brevity +``` + +A composite resource using this API references the +{{}}group/version{{}} and +{{}}kind{{}}. The +{{}}spec{{}} has the +{{}}region{{}} key with a string value. + +```yaml {label="xr"} +apiVersion: custom-api.example.org/v1alpha1 +kind: xDatabase +metadata: + name: my-composite-resource +spec: + region: "US" +``` + + +{{}} +The custom API defined inside the +{{}}spec.properties{{}} is an OpenAPIv3 +specification. The +[data models page](https://swagger.io/docs/specification/data-models/) of +the Swagger documentation provides a list of examples using data types and input +restrictions. + +The Kubernetes documentation lists +[the set of special restrictions](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#validation) +on what your OpenAPIv3 custom API can use. +{{< /hint >}} + +{{}} + +Changing or expanding the XRD schema requires restarting the [Crossplane +pod]({{}}) to take effect. +{{< /hint >}} + +##### Required fields + +By default all fields in a schema are optional. Define a parameter as required +with the +{{< hover label="required" line="25">}}required{{}} attribute. + +In this example the XRD requires +{{< hover label="required" line="19">}}region{{}} and +{{< hover label="required" line="21">}}size{{}} but +{{< hover label="required" line="23">}}name{{}} is optional. +```yaml {label="required",copy-lines="none"} +apiVersion: apiextensions.crossplane.io/v1 +kind: CompositeResourceDefinition +metadata: + name: xdatabases.custom-api.example.org +spec: + group: custom-api.example.org + names: + kind: xDatabase + plural: xdatabases + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + properties: + region: + type: string + size: + type: string + name: + type: string + required: + - region + - size + # Removed for brevity +``` + +According to the OpenAPIv3 specification, the `required` field is per-object. If +a schema contains multiple objects the schema may need multiple `required` +fields. + +This XRD defines two objects: + 1. the top-level {{}}spec{{}} object + 2. a second {{}}location{{}} object + +The {{}}spec{{}} object +{{}}requires{{}} the +{{}}size{{}} and +{{}}location{{}} but +{{}}name{{}} is optional. + +Inside the required {{}}location{{}} +object, +{{}}country{{}} is +{{}}required{{}} and +{{}}zone{{}} is optional. + +```yaml {copy-lines="none",label="required2"} +# Removed for brevity +- name: v1alpha1 + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + properties: + size: + type: string + name: + type: string + location: + type: object + properties: + country: + type: string + zone: + type: string + required: + - country + required: + - size + - location +``` + +The Swagger "[Describing +Parameters](https://swagger.io/docs/specification/describing-parameters/)" +documentation has more examples. + +##### Crossplane reserved fields + +Crossplane doesn't allow the following fields in a schema: +* `spec.resourceRef` +* `spec.resourceRefs` +* `spec.claimRef` +* `spec.writeConnectionSecretToRef` +* `status.conditions` +* `status.connectionDetails` + +Crossplane ignores any fields matching the reserved fields. + +#### Serve and reference a schema + +To use a schema it must be +{{}}served: true{{}} +and +{{}}referenceable: true{{}}. + +```yaml {label="served"} +apiVersion: apiextensions.crossplane.io/v1 +kind: CompositeResourceDefinition +metadata: + name: xdatabases.custom-api.example.org +spec: + group: custom-api.example.org + names: + kind: xDatabase + plural: xdatabases + versions: + - name: v1alpha1 + served: true + referenceable: true + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + properties: + region: + type: string +``` + +Composite resources can use any schema version set as +{{}}served: true{{}}. +Kubernetes rejects any composite resource using a schema version set as `served: +false`. + +{{< hint "tip" >}} +Setting a schema version as `served:false` causes errors for users using an older +schema. This can be an effective way to identify and upgrade users before +deleting the older schema version. +{{< /hint >}} + +The {{}}referenceable: true{{}} +field indicates which version of the schema Compositions use. Only one +version can be `referenceable`. + +{{< hint "note" >}} +Changing which version is `referenceable:true` requires [updating the +`compositeTypeRef.apiVersion`]({{}}) +of any Compositions referencing that XRD. +{{< /hint >}} + + +#### Multiple schema versions + +{{}} +Crossplane supports defining multiple `versions`, but the schema of each version +can't change any existing fields, also called "making a breaking change." + +Breaking schema changes between versions requires the use of [conversion +webhooks](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definition-versioning/#webhook-conversion). + +New versions may define new optional parameters, but new required fields are +a "breaking change." + +Crossplane XRDs use Kubernetes custom resource definitions for versioning. +Read the Kubernetes documentation on [versions in +CustomResourceDefinitions](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definition-versioning/) +for more background on versions and breaking changes. + +Crossplane recommends implementing breaking schema changes as brand new XRDs. +{{< /hint >}} + +For XRDs, to create a new version of an API add a new +{{}}name{{}} in the +{{}}versions{{}} +list. + +For example, this XRD version +{{}}v1alpha1{{}} only has the field +{{}}region{{}}. + +A second version, +{{}}v1{{}} expands the API to have both +{{}}region{{}} and +{{}}size{{}}. + +```yaml {label="ver",copy-lines="none"} +apiVersion: apiextensions.crossplane.io/v1 +kind: CompositeResourceDefinition +metadata: + name: xdatabases.custom-api.example.org +spec: + group: custom-api.example.org + names: + kind: xDatabase + plural: xdatabases + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + properties: + region: + type: string + - name: v1 + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + properties: + region: + type: string + size: + type: string +``` + +{{}} + +Changing or expanding the XRD schema requires restarting the [Crossplane +pod]({{}}) to take effect. +{{< /hint >}} + +### Enable Claims + +Optionally, XRDs can allow Claims to use the XRD API. + +{{}} + +Read the [Claims]({{}}) page for more +information about Claims. +{{}} + +XRDs offer Claims with a +{{}}claimNames{{}} object. + +The {{}}claimNames{{}} defines a +{{}}kind{{}} and +{{}}plural{{}} like the XRD +{{}}names{{}} object. +Also like XRD +{{}}names{{}}, use UpperCamelCase +for the +{{}}kind{{}} and lowercase for the +{{}}plural{{}}. + +The Claim +{{}}kind{{}} and +{{}}plural{{}} must be unique. They +can't match any other Claim or other XRD +{{}}kind{{}}. + +{{}} +Common Crossplane convention is to use +{{}}claimNames{{}} that match the XRD +{{}}names{{}}, but without the beginning +"x." +{{}} + +```yaml {label="claim",copy-lines="none"} +apiVersion: apiextensions.crossplane.io/v1 +kind: CompositeResourceDefinition +metadata: + name: xdatabases.custom-api.example.org +spec: + group: custom-api.example.org + names: + kind: xDatabase + plural: xdatabases + claimNames: + kind: Database + plural: databases + versions: + # Removed for brevity +``` + +{{}} +You can't change the +{{}}claimNames{{}} +after they're defined. You must delete and +recreate the XRD to change the +{{}}claimNames{{}}. +{{}} + +### Manage connection secrets + +When a composite resource creates managed resources, Crossplane provides any +[connection secrets]({{}}) +to the composite resource or Claim. This requires the creators of composite +resources and Claims to know the secrets provided by a managed resource. +In other cases, Crossplane administrators may not want to expose some or all the +generated connection secrets. + +XRDs can define a list of +{{}}connectionSecretKeys{{}} +to limit what's provided to a composite resource or Claim. + +Crossplane only provides the keys listed in the +{{}}connectionSecretKeys{{}} +to the composite resource or Claim using this XRD. Any other connection +secrets aren't passed to the composite resource or Claim. + +{{}} +The keys listed in the +{{}}connectionSecretKeys{{}} must match the +key names listed in the Composition's `connectionDetails`. + +An XRD ignores any keys listed that aren't created by a managed resource. + +For more information read the +[Composition documentation]({{}}). +{{< /hint >}} + + +For example, an XRD passes the keys +{{}}username{{}}, +{{}}password{{}} and +{{}}address{{}}. + +Composite resources or Claims save these in the secret defined by their +`writeConnectionSecretToRef` field. + +```yaml {label="key",copy-lines="none"} +apiVersion: apiextensions.crossplane.io/v1 +kind: CompositeResourceDefinition +metadata: + name: xdatabases.custom-api.example.org +spec: + group: custom-api.example.org + names: + kind: xDatabase + plural: xdatabases + connectionSecretKeys: + - username + - password + - address + versions: + # Removed for brevity +``` + +{{}} +You can't change the `connectionSecretKeys` of an XRD. You must delete and +recreate the XRD to change the `connectionSecretKeys`. +{{}} + +For more information on connection secrets read the [Connection Secrets +knowledge base article]({{}}). + +### Set composite resource defaults +XRDs can set default parameters for composite resources and Claims. + + +#### defaultCompositeDeletePolicy + +The `defaultCompositeDeletePolicy` defines the deletion policy for composite +resources and claims. + +Using a `defaultCompositeDeletePolicy: Background` policy deletes +the composite resource or Claim and relies on Kubernetes to delete the remaining +dependent objects, like managed resources or secrets. + +Using `defaultCompositeDeletePolicy: Foreground` causes Kubernetes to attach a +`foregroundDeletion` finalizer to the composite resource or Claim. Kubernetes +deletes all the dependent objects before deleting the composite resource or +Claim. + +The default value is `defaultCompositeDeletePolicy: Background`. + +Set +{{}}defaultCompositeDeletePolicy: Foreground{{}} +to change the XRD deletion policy. + +```yaml {label="delete",copy-lines="none"} +apiVersion: apiextensions.crossplane.io/v1 +kind: CompositeResourceDefinition +metadata: + name: xdatabases.custom-api.example.org +spec: + defaultCompositeDeletePolicy: Foreground + group: custom-api.example.org + names: + # Removed for brevity + versions: + # Removed for brevity +``` + + +#### defaultCompositionRef + +It's possible for multiple [Compositions]({{}}) to +reference the same XRD. If more than one Composition references the same XRD, +the composite resource or Claim must select which Composition to use. + +An XRD can define the default Composition to use with the +`defaultCompositionRef` value. + +Set a +{{}}defaultCompositionRef{{}} +to set the default Composition. + +```yaml {label="defaultComp",copy-lines="none"} +apiVersion: apiextensions.crossplane.io/v1 +kind: CompositeResourceDefinition +metadata: + name: xdatabases.custom-api.example.org +spec: + defaultCompositionRef: + name: myComposition + group: custom-api.example.org + names: + # Removed for brevity + versions: + # Removed for brevity +``` + + +#### defaultCompositionUpdatePolicy + + +Changes to a Composition generate a new Composition revision. By default all +composite resources and Claims use the updated Composition revision. + +Set the XRD `defaultCompositionUpdatePolicy` to `Manual` to prevent composite +resources and Claims from automatically using the new revision. + +The default value is `defaultCompositionUpdatePolicy: Automatic`. + +Set {{}}defaultCompositionUpdatePolicy: Manual{{}} +to set the default Composition update policy for composite resources and Claims +using this XRD. + +```yaml {label="compRev",copy-lines="none"} +apiVersion: apiextensions.crossplane.io/v1 +kind: CompositeResourceDefinition +metadata: + name: xdatabases.custom-api.example.org +spec: + defaultCompositionUpdatePolicy: Manual + group: custom-api.example.org + names: + # Removed for brevity + versions: + # Removed for brevity +``` + + +#### enforcedCompositionRef + +To require all composite resources or Claims to use a specific Composition use +the `enforcedCompositionRef` setting in the XRD. + +For example, to require all composite resources and Claims using this XRD to use +the Composition +{{}}myComposition{{}} +set +{{}}enforcedCompositionRef.name: myComposition{{}}. + +```yaml {label="defaultComp",copy-lines="none"} +apiVersion: apiextensions.crossplane.io/v1 +kind: CompositeResourceDefinition +metadata: + name: xdatabases.custom-api.example.org +spec: + enforcedCompositionRef: + name: myComposition + group: custom-api.example.org + names: + # Removed for brevity + versions: + # Removed for brevity +``` + +## Verify a CompositeResourceDefinition + +Verify an XRD with `kubectl get compositeresourcedefinition` or the short form, +{{}}kubectl get xrd{{}}. + +```yaml {label="getxrd",copy-lines="1"} +kubectl get xrd +NAME ESTABLISHED OFFERED AGE +xdatabases.custom-api.example.org True True 22m +``` + +The `ESTABLISHED` field indicates Crossplane installed the Kubernetes custom +resource definition for this XRD. + +The `OFFERED` field indicates this XRD offers a Claim and Crossplane installed +the Kubernetes custom resource definitions for the Claim. + +### XRD conditions +Crossplane uses a standard set of `Conditions` for XRDs. +View the conditions of a XRD under their `Status` with +`kubectl describe xrd`. + +```yaml {copy-lines="none"} +kubectl describe xrd +Name: xpostgresqlinstances.database.starter.org +API Version: apiextensions.crossplane.io/v1 +Kind: CompositeResourceDefinition +# Removed for brevity +Status: + Conditions: + Reason: WatchingCompositeResource + Status: True + Type: Established + Reason: WatchingCompositeResourceClaim + Status: True + Type: Offered +# Removed for brevity +``` + + +#### WatchingCompositeResource + +`Reason: WatchingCompositeResource` indicates Crossplane defined the new +Kubernetes custom resource definitions related to the composite resource and is +watching for the creation of new composite resources. + +```yaml +Type: Established +Status: True +Reason: WatchingCompositeResource +``` + + +#### TerminatingCompositeResource + +`Reason: TerminatingCompositeResource` indicates Crossplane is deleting the +custom resource definitions related to the composite resource and is +terminating the composite resource controller. + +```yaml +Type: Established +Status: False +Reason: TerminatingCompositeResource +``` + + +#### WatchingCompositeResourceClaim + +`Reason: WatchingCompositeResourceClaim` indicates Crossplane defined the new +Kubernetes custom resource definitions related to the offered Claims and is +watching for the creation of new Claims. + +```yaml +Type: Offered +Status: True +Reason: WatchingCompositeResourceClaim +``` + + +#### TerminatingCompositeResourceClaim + +`Reason: TerminatingCompositeResourceClaim` indicates Crossplane is deleting the +custom resource definitions related to the offered Claims and is +terminating the Claims controller. + +```yaml +Type: Offered +Status: False +Reason: TerminatingCompositeResourceClaim +``` \ No newline at end of file diff --git a/content/master/concepts/composite-resources.md b/content/master/concepts/composite-resources.md new file mode 100644 index 000000000..db4fa87b5 --- /dev/null +++ b/content/master/concepts/composite-resources.md @@ -0,0 +1,470 @@ +--- +title: Composite Resources +weight: 50 +description: "Composite resources, an XR or XRs, represent a collection of related cloud resources." +--- + +A composite resource represents a set of managed resources as a single +Kubernetes object. Crossplane creates composite resources when users access a +custom API, defined in the CompositeResourceDefinition. + +{{}} +Composite resources are a _composite_ of managed resources. +A _Composition_ defines how to _compose_ the managed resources together. +{{< /hint >}} + +{{}} +Crossplane has four core components that users commonly mix up: + +* [Compositions]({{}}) - A template to define how to create resources. +* [Composite Resource Definition]({{}}) + (`XRD`) - A custom API specification. +* Composite Resource (`XR`) - This page. Created by + using the custom API defined in a Composite Resource Definition. XRs use the + Composition template to create new managed resources. +* [Claims]({{}}) (`XRC`) - Like a Composite Resource, but + with namespace scoping. +{{}} + +## Creating composite resources + +Creating composite resources requires a +[Composition]({{}}) and a +[CompositeResourceDefinition]({{}}) +(`XRD`). +The Composition defines the set of resources to create. +The XRD defines the custom API users call to request the set of resources. + +![Diagram of the relationship of Crossplane components](/media/composition-how-it-works.svg) + +XRDs define the API used to create a composite resource. +For example, +this {{}}CompositeResourceDefinition{{}} +creates a custom API endpoint +{{}}xmydatabases.example.org{{}}. + +```yaml {label="xrd1",copy-lines="none"} +apiVersion: apiextensions.crossplane.io/v1 +kind: CompositeResourceDefinition +metadata: + name: xmydatabases.example.org +spec: + group: example.org + names: + kind: xMyDatabase + plural: xmydatabases + # Removed for brevity +``` + +When a user calls the custom API, +{{}}xmydatabases.example.org{{}}, +Crossplane chooses the Composition to use based on the Composition's +{{}}compositeTypeRef{{}} + +```yaml {label="typeref",copy-lines="none"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +metadata: + name: my-composition +spec: + compositeTypeRef: + apiVersion: example.org/v1alpha1 + kind: xMyDatabase + # Removed for brevity +``` + +The Composition +{{}}compositeTypeRef{{}} matches the +XRD {{}}group{{}} and +{{}}kind{{}}. + +Crossplane creates the resources defined in the matching Composition and +represents them as a single `composite` resource. + +```shell{copy-lines="1"} +kubectl get composite +NAME SYNCED READY COMPOSITION AGE +my-composite-resource True True my-composition 4s +``` + +### Naming external resources +By default, managed resources created by a composite resource have the name of +the composite resource, followed by a random suffix. + + +For example, a composite resource named "my-composite-resource" creates external +resources named "my-composite-resource-fqvkw." + + +Resource names can be deterministic by applying an +{{}}annotation{{}} to the composite +resource. + +```yaml {label="annotation",copy-lines="none"} +apiVersion: example.org/v1alpha1 +kind: xMyDatabase +metadata: + name: my-composite-resource + annotations: + crossplane.io/external-name: my-custom-name +# Removed for brevity +``` + +Inside the Composition, use a +{{}}patch{{}} +to apply the external-name to the resources. + +The {{}}fromFieldPath{{}} patch copies the +{{}}metadata.annotations{{}} field from +the composite resource to the +{{}}metadata.annotations{{}} inside the +managed resource. + +{{}} +If a managed resource has the `crossplane.io/external-name` annotation +Crossplane uses the annotation value to name the external resource. +{{}} + +```yaml {label="comp",copy-lines="none"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +metadata: + name: my-composition +spec: + resources: + - name: database + base: + # Removed for brevity + patches: + - fromFieldPath: metadata.annotations + toFieldPath: metadata.annotations +``` + +For more information on patching resources refer to the [Patch and +Transform]({{}}) documentation. + +### Composition selection + +Select a specific Composition for a composite resource to use with +{{}}compositionRef{{}} + +{{}} +The selected Composition must allow the composite resource to use it with a +`compositeTypeRef`. Read more about the `compositeTypeRef` field in the +[Enabling Composite Resources]({{}}) +section of the Composition documentation. +{{< /hint >}} + +```yaml {label="compref",copy-lines="none"} +apiVersion: example.org/v1alpha1 +kind: xMyDatabase +metadata: + name: my-composite-resource +spec: + compositionRef: + name: my-other-composition + # Removed for brevity +``` + +A composite resource can also select a Composition based on labels instead of +the exact name with a +{{}}compositionSelector{{}}. + +Inside the {{}}matchLabels{{}} section +provide one or more Composition labels to match. + +```yaml {label="complabel",copy-lines="none"} +apiVersion: example.org/v1alpha1 +kind: xMyDatabase +metadata: + name: my-composite-resource +spec: + compositionSelector: + matchLabels: + environment: production + # Removed for brevity +``` + +### Composition revision policy + +Crossplane tracks changes to Compositions as +[Composition revisions]({{}}) . + +A composite resource can use +a {{}}compositionUpdatePolicy{{}} to +manually or automatically reference newer Composition revisions. + +The default +{{}}compositionUpdatePolicy{{}} is +"Automatic." Composite resources automatically use the latest Composition +revision. + +Change the policy to +{{}}Manual{{}} to prevent composite +resources from automatically upgrading. + +```yaml {label="comprev",copy-lines="none"} +apiVersion: example.org/v1alpha1 +kind: xMyDatabase +metadata: + name: my-composite-resource +spec: + compositionUpdatePolicy: Manual + # Removed for brevity +``` + +### Composition revision selection + +Crossplane records changes to Compositions as +[Composition revisions]({{}}). +A composite resource can +select a specific Composition revision. + + +Use {{}}compositionRevisionRef{{}} to +select a specific Composition revision by name. + +For example, to select a specific Composition revision use the name of the +desired Composition revision. + +```yaml {label="comprevref",copy-lines="none"} +apiVersion: example.org/v1alpha1 +kind: xMyDatabase +metadata: + name: my-composite-resource +spec: + compositionRevisionRef: my-composition-b5aa1eb + # Removed for brevity +``` + +{{}} +Find the Composition revision name from +{{}}kubectl get compositionrevision{{}} + +```shell {label="getcomprev",copy-lines="1"} +kubectl get compositionrevision +NAME REVISION XR-KIND XR-APIVERSION AGE +my-composition-5c976ad 1 xmydatabases example.org/v1alpha1 65m +my-composition-b5aa1eb 2 xmydatabases example.org/v1alpha1 64m +``` +{{< /hint >}} + +A Composite resource can also select Composition revisions based on labels +instead of the exact name with a +{{}}compositionRevisionSelector{{}}. + +Inside the {{}}matchLabels{{}} +section provide one or more Composition revision labels to match. + + +```yaml {label="comprevsel",copy-lines="none"} +apiVersion: example.org/v1alpha1 +kind: xMyDatabase +metadata: + name: my-composite-resource +spec: + compositionRevisionSelector: + matchLabels: + channel: dev + # Removed for brevity +``` + +### Manage connection secrets + +When a composite resource creates resources, Crossplane provides any +[connection secrets]({{}}) +to the composite resource. + +{{}} + +A resource may only access connection secrets allowed by the XRD. By +default XRDs provide access to all connection secrets generated by managed +resources. +Read more about [managing connection +secrets]({{}}) +in the XRD documentation. +{{< /hint >}} + +Use +{{}}writeConnectionSecretToRef{{}} +to specify where the composite resource writes their connection secrets to. + +For example, this composite resource saves the connection secrets in a +Kubernetes secret object named +{{}}my-secret{{}} in the namespace +{{}}crossplane-system{{}}. + +```yaml {label="writesecret",copy-lines="none"} +apiVersion: example.org/v1alpha1 +kind: xMyDatabase +metadata: + name: my-composite-resource +spec: + writeConnectionSecretToRef: + name: my-secret + namespace: crossplane-system + # Removed for brevity +``` + +Composite resources can write connection secrets to an +[external secret store]({{}}), +like HashiCorp Vault. + +{{}} +External secret stores are an alpha feature. Alpha features aren't enabled by +default. +{{< /hint >}} + +Use the {{}}publishConnectionDetailsTo{{}} field to save connection +secrets to an external secrets store. + +```yaml {label="publishsecret",copy-lines="none"} +apiVersion: example.org/v1alpha1 +kind: xMyDatabase +metadata: + name: my-composite-resource +spec: + publishConnectionDetailsTo: + name: my-external-secret-store + # Removed for brevity +``` + +Read the [External Secrets Store]({{}}) documentation for more information on using +external secret stores. + +For more information on connection secrets read the [Connection Secrets +knowledge base article]({{}}). + +### Pausing composite resources + + +Crossplane supports pausing composite resources. A paused composite resource +doesn't check or make changes on its external resources. + + +To pause a composite resource apply the +{{}}crossplane.io/paused{{}} annotation. + +```yaml {label="pause",copy-lines="none"} +apiVersion: example.org/v1alpha1 +kind: xMyDatabase +metadata: + name: my-composite-resource + annotations: + crossplane.io/paused: "true" +spec: + # Removed for brevity +``` + +## Verify composite resources +Use +{{}}kubectl get composite{{}} +to view all the composite resources Crossplane created. + +```shell{copy-lines="1",label="getcomposite"} +kubectl get composite +NAME SYNCED READY COMPOSITION AGE +my-composite-resource True True my-composition 4s +``` + +Use `kubectl get` for the specific custom API endpoint to view +only those resources. + +```shell {copy-lines="1"} +kubectl get xMyDatabase.example.org +NAME SYNCED READY COMPOSITION AGE +my-composite-resource True True my-composition 12m +``` + +Use +{{}}kubectl describe composite{{}} +to view the linked +{{}}Composition Ref{{}}, +and unique managed resources created in the +{{}}Resource Refs{{}}. + + +```yaml {copy-lines="1",label="desccomposite"} +kubectl describe composite my-composite-resource +Name: my-composite-resource +API Version: example.org/v1alpha1 +Kind: xMyDatabase +Spec: + Composition Ref: + Name: my-composition + Composition Revision Ref: + Name: my-composition-cf2d3a7 + Composition Update Policy: Automatic + Resource Refs: + API Version: s3.aws.upbound.io/v1beta1 + Kind: Bucket + Name: my-composite-resource-fmrks + API Version: dynamodb.aws.upbound.io/v1beta1 + Kind: Table + Name: my-composite-resource-wnr9t +# Removed for brevity +``` + +### Composite resource conditions + +The conditions of composite resources match the conditions of their managed +resources. + +Read the +[conditions section]({{}}) of the +managed resources documentation for details. + +## Composite resource labels + +Crossplane adds labels to composite resources to show their relationship to +other Crossplane components. + +### Composite label +Crossplane adds the +{{}} crossplane.io/composite{{}} label +to all composite resources. The label matches the name of the composite. +Crossplane applies the composite label to any manged resource created by a +composite, creating a reference between the managed resource and owning +composite resource. + +```shell {label="claimname",copy-lines="1"} +kubectl describe xmydatabase.example.org/my-claimed-database-x9rx9 +Name: my-claimed-database2-x9rx9 +Namespace: +Labels: crossplane.io/composite=my-claimed-database-x9rx9 +``` + +### Claim name label +Crossplane adds the +{{}}crossplane.io/claim-name{{}} +label to composite resources created from a Claim. The label indicates the name +of the Claim linked to this composite resource. + +```shell {label="claimname",copy-lines="1"} +kubectl describe xmydatabase.example.org/my-claimed-database-x9rx9 +Name: my-claimed-database2-x9rx9 +Namespace: +Labels: crossplane.io/claim-name=my-claimed-database +``` + +Composite resources created directly, without using a Claim, don't have a +{{}}crossplane.io/claim-name{{}} +label. + +### Claim namespace label +Crossplane adds the +{{}}crossplane.io/claim-namespace{{}} +label to composite resources created from a Claim. The label indicates the +namespace of the Claim linked to this composite resource. + +```shell {label="claimname",copy-lines="1"} +kubectl describe xmydatabase.example.org/my-claimed-database-x9rx9 +Name: my-claimed-database2-x9rx9 +Namespace: +Labels: crossplane.io/claim-namespace=default +``` + +Composite resources created directly, without using a Claim, don't have a +{{}}crossplane.io/claim-namespace{{}} +label. \ No newline at end of file diff --git a/content/knowledge-base/guides/composition-functions.md b/content/master/concepts/composition-functions.md similarity index 98% rename from content/knowledge-base/guides/composition-functions.md rename to content/master/concepts/composition-functions.md index 880554fb8..6f0e0aee7 100644 --- a/content/knowledge-base/guides/composition-functions.md +++ b/content/master/concepts/composition-functions.md @@ -2,13 +2,26 @@ title: Composition Functions state: alpha alphaVersion: "1.11" +weight: 80 +description: "Composition Functions or XFNs allow for complex Composition patches" +aliases: + - /knowledge-base/guides/composition-functions --- + + Composition Functions allow you to supplement or replace your Compositions with advanced logic not implementable through available patching strategies. + + You can build a Function using general purpose programming -languages such as Go or Python, or relevant tools such as Helm, kustomize, or -CUE. Functions compliment contemporary "Patch and Transform" (P&T) style +languages such as Go or Python, or relevant tools such as Helm, +[Kustomize](https://kustomize.io/), or +[CUE](https://cuelang.org/). + +Functions compliment contemporary "Patch and Transform" (P&T) style Composition. It's possible to use only P&T, only Functions, or a mix of both in the same Composition. diff --git a/content/master/concepts/composition.md b/content/master/concepts/composition.md deleted file mode 100644 index e3c648442..000000000 --- a/content/master/concepts/composition.md +++ /dev/null @@ -1,1377 +0,0 @@ ---- -title: Composite Resources -weight: 103 ---- - -Crossplane Composite Resources are opinionated Kubernetes Custom Resources that -are _composed_ of [Managed Resources][managed-resources]. We often call them XRs -for short. - -![Diagram of claims, XRs, and Managed Resources][xrs-and-mrs] - -Composite Resources are designed to let you build your own platform with your -own opinionated concepts and APIs without needing to write a Kubernetes -controller from scratch. Instead, you define the schema of your XR and teach -Crossplane which Managed Resources it should compose (i.e. create) when someone -creates the XR you defined. - -If you're already familiar with Composite Resources and looking for a detailed -configuration reference or some tips, tricks, and troubleshooting information, -try the [Composition Reference][xr-ref]. - -Below is an example of a Composite Resource: - -```yaml -apiVersion: database.example.org/v1alpha1 -kind: XPostgreSQLInstance -metadata: - name: my-db -spec: - parameters: - storageGB: 20 - compositionRef: - name: production - writeConnectionSecretToRef: - namespace: crossplane-system - name: my-db-connection-details -``` - -You define your own XRs, so they can be of whatever API version and kind you -like, and contain whatever spec and status fields you need. - -## How It Works - -The first step towards using Composite Resources is configuring Crossplane so -that it knows what XRs you'd like to exist, and what to do when someone creates -one of those XRs. This is done using a `CompositeResourceDefinition` (XRD) -resource and one or more `Composition` resources. - -Once you've configured Crossplane with the details of your new XR you can either -create one directly, or use a _claim_. Typically only the folks responsible for -configuring Crossplane (often a platform or SRE team) have permission to create -XRs directly. Everyone else manages XRs via a lightweight proxy resource called -a Composite Resource Claim (or claim for short). More on that later. - -![Diagram combining all Composition concepts][how-it-works] - -> If you're coming from the Terraform world you can think of an XRD as similar -> to the `variable` blocks of a Terraform module, while the `Composition` is -> the rest of the module's HCL code that describes how to use those variables to -> create a bunch of resources. In this analogy the XR or claim is a little like -> a `tfvars` file providing inputs to the module. - -### Defining Composite Resources - -A `CompositeResourceDefinition` (or XRD) defines the type and schema of your XR. -It lets Crossplane know that you want a particular kind of XR to exist, and what -fields that XR should have. An XRD is a little like a `CustomResourceDefinition` -(CRD), but slightly more opinionated. Writing an XRD is mostly a matter of -specifying an OpenAPI ["structural schema"][crd-docs]. - -The XRD that defines the `XPostgreSQLInstance` XR above would look like this: - -```yaml -apiVersion: apiextensions.crossplane.io/v1 -kind: CompositeResourceDefinition -metadata: - name: xpostgresqlinstances.database.example.org -spec: - group: database.example.org - names: - kind: XPostgreSQLInstance - plural: xpostgresqlinstances - claimNames: - kind: PostgreSQLInstance - plural: postgresqlinstances - versions: - - name: v1alpha1 - served: true - referenceable: true - schema: - openAPIV3Schema: - type: object - properties: - spec: - type: object - properties: - parameters: - type: object - properties: - storageGB: - type: integer - required: - - storageGB - required: - - parameters -``` - -You might notice that the `XPostgreSQLInstance` example above has some fields -that don't appear in the XRD, like the `writeConnectionSecretToRef` and -`compositionRef` fields. This is because Crossplane automatically injects some -standard Crossplane Resource Model (XRM) fields into all XRs. - -### Configuring Composition - -A `Composition` lets Crossplane know what to do when someone creates a Composite -Resource. Each `Composition` creates a link between an XR and a set of one or -more Managed Resources - when the XR is created, updated, or deleted the set of -Managed Resources are created, updated or deleted accordingly. - -You can add multiple Compositions for each XRD, and choose which should be used -when XRs are created. This allows a Composition to act like a class of service - -for example you could configure one Composition for each environment you -support, such as production, staging, and development. - -A basic `Composition` for the above `XPostgreSQLInstance` might look like this: - -```yaml -apiVersion: apiextensions.crossplane.io/v1 -kind: Composition -metadata: - name: example - labels: - crossplane.io/xrd: xpostgresqlinstances.database.example.org - provider: gcp -spec: - writeConnectionSecretsToNamespace: crossplane-system - compositeTypeRef: - apiVersion: database.example.org/v1alpha1 - kind: XPostgreSQLInstance - resources: - - name: cloudsqlinstance - base: - apiVersion: database.gcp.crossplane.io/v1beta1 - kind: CloudSQLInstance - spec: - forProvider: - databaseVersion: POSTGRES_12 - region: us-central1 - settings: - tier: db-custom-1-3840 - dataDiskType: PD_SSD - ipConfiguration: - ipv4Enabled: true - authorizedNetworks: - - value: "0.0.0.0/0" - patches: - - type: FromCompositeFieldPath - fromFieldPath: spec.parameters.storageGB - toFieldPath: spec.forProvider.settings.dataDiskSizeGb -``` - -The above `Composition` tells Crossplane that when someone creates an -`XPostgreSQLInstance` XR Crossplane should create a `CloudSQLInstance` in -response. The `storageGB` field of the `XPostgreSQLInstance` should be used to -configure the `dataDiskSizeGb` field of the `CloudSQLInstance`. This is only a -small subset of the functionality a `Composition` enables - take a look at the -[reference page][xr-ref] to learn more. - -> We almost always talk about XRs composing Managed Resources, but actually an -> XR can also compose other XRs to allow nested layers of abstraction. XRs don't -> support composing arbitrary Kubernetes resources (e.g. Deployments, operators, -> etc) directly but you can do so using our [Kubernetes][provider-kubernetes] -> and [Helm][provider-helm] providers. - -### Claiming Composite Resources - -Crossplane uses Composite Resource Claims (or just claims, for short) to allow -application operators to provision and manage XRs. When we talk about using XRs -it's typically implied that the XR is being used via a claim. Claims are almost -identical to their corresponding XRs. It helps to think of a claim as an -application team’s interface to an XR. You could also think of claims as the -public (app team) facing part of the opinionated platform API, while XRs are the -private (platform team) facing part. - -A claim for the `XPostgreSQLInstance` XR above would look like this: - -```yaml -apiVersion: database.example.org/v1alpha1 -kind: PostgreSQLInstance -metadata: - namespace: default - name: my-db -spec: - parameters: - storageGB: 20 - compositionRef: - name: production - writeConnectionSecretToRef: - name: my-db-connection-details -``` - -There are three key differences between an XR and a claim: - -1. Claims are namespaced, while XRs (and Managed Resources) are cluster scoped. -1. Claims are of a different `kind` than the XR - by convention the XR's `kind` - without the proceeding `X`. For example a `PostgreSQLInstance` claims an - `XPostgreSQLInstance`. -1. An active claim contains a reference to its corresponding XR, while an XR - contains both a reference to the claim an array of references to the managed - resources it composes. - -Not all XRs offer a claim - doing so is optional. See the XRD section of the -[Composition reference][xr-ref] to learn how to offer a claim. - -![Diagram showing the relationship between claims and XRs][claims-and-xrs] - -Claims may seem a little superfluous at first, but they enable some handy -scenarios, including: - -- **Private XRs.** Sometimes a platform team might not want a type of XR to be - directly consumed by their application teams. For example because the XR - represents 'supporting' infrastructure - consider the above VPC `XNetwork` XR. App - teams might create `PostgreSQLInstance` claims that _reference_ (i.e. consume) - an `XNetwork`, but they shouldn't be _creating their own_. Similarly, some - kinds of XR might be intended only for 'nested' use - intended only to be - composed by other XRs. - -- **Global XRs**. Not all infrastructure is conceptually namespaced. Say your - organisation uses team scoped namespaces. A `PostgreSQLInstance` that belongs - to Team A should probably be part of the `team-a` namespace - you'd represent - this by creating a `PostgreSQLInstance` claim in that namespace. On the other - hand the `XNetwork` XR we mentioned previously could be referenced (i.e. used) - by XRs from many different namespaces - it doesn't exist to serve a particular - team. - -- **Pre-provisioned XRs**. Finally, separating claims from XRs allows a platform - team to pre-provision certain kinds of XR. Typically an XR is created - on-demand in response to the creation of a claim, but it's also possible for a - claim to instead request an existing XR. This can allow application teams to - instantly claim infrastructure like database instances that would otherwise - take minutes to provision on-demand. - - -This reference provides detailed examples of defining, configuring, and using -Composite Resources in Crossplane. You can also refer to Crossplane's [API -documentation][api-docs] for more details. If you're looking for a more general -overview of Composite Resources and Composition in Crossplane, try the -[Composite Resources][xr-concepts] page under Concepts. - -## Composite Resources and Claims - -The type and most of the schema of Composite Resources and claims are largely of -your own choosing, but there is some common 'machinery' injected into them. -Here's a hypothetical XR that doesn't have any user-defined fields and thus only -includes the automatically injected Crossplane machinery: - -```yaml -apiVersion: database.example.org/v1alpha1 -kind: XPostgreSQLInstance -metadata: - # This XR was created automatically by a claim, so its name is derived from - # the claim's name. - name: my-db-mfd1b - annotations: - # The external name annotation has special meaning in Crossplane. When a - # claim creates an XR its external name will automatically be propagated to - # the XR. Whether and how the external name is propagated to the resources - # the XR composes is up to its Composition. - crossplane.io/external-name: production-db-0 -spec: - # XRs have a reference to the claim that created them (or, if the XR was - # pre-provisioned, to the claim that later claimed them). - claimRef: - apiVersion: database.example.org/v1alpha1 - kind: PostgreSQLInstance - name: my-db - # The compositeDeletePolicy specifies the propagation policy that will be used by Crossplane - # when deleting the Composite Resource that is associated with the Claim. The default - # value is Background, which causes the Composite resource to be deleted using - # the kubernetes default propagation policy of Background, and all associated - # resources will be deleted simultaneously. The other value for this field is Foreground, - # which will cause the Composite resource to be deleted using Foreground Cascading Deletion. - # Kubernetes will add a foregroundDeletion finalizer to all of the resources in the - # dependency graph, and they will be deleted starting with the edge or leaf nodes and - # working back towards the root Composite. See https://kubernetes.io/docs/concepts/architecture/garbage-collection/#cascading-deletion - # for more information on cascading deletion. - compositeDeletePolicy: Background - # The compositionRef specifies which Composition this XR will use to compose - # resources when it is created, updated, or deleted. This can be omitted and - # will be set automatically if the XRD has a default or enforced composition - # reference, or if the below composition selector is set. - compositionRef: - name: production-us-east - # The compositionSelector allows you to match a Composition by labels rather - # than naming one explicitly. It is used to set the compositionRef if none is - # specified explicitly. - compositionSelector: - matchLabels: - environment: production - region: us-east - provider: gcp - # The environment is an in-memory object that can be patched from / to during - # rendering. - # The environment is composed by merging the 'data' of all EnvironmentConfigs - # referenced below. It is disposed after every reconcile. - # NOTE: EnvironmentConfigs are an alpha feature and need to be enabled with - # the '--enable-environment-configs' flag on startup. - environment: - # EnvironmentConfigs is a list of object references that is made up of - # name references and label selectors - environmentConfigs: - - type: Reference # this is the default - ref: - name: example-environment - - type: Selector - selector: - - key: stage - type: FromCompositeFieldPath # this is the default - valueFromFieldPath: spec.parameters.stage - - key: provider - type: Value - value: "gcp" - # The resourceRefs array contains references to all of the resources of which - # this XR is composed. Despite being in spec this field isn't intended to be - # configured by humans - Crossplane will take care of keeping it updated. - resourceRefs: - - apiVersion: database.gcp.crossplane.io/v1beta1 - kind: CloudSQLInstance - name: my-db-mfd1b-md9ab - # The writeConnectionSecretToRef field specifies a Kubernetes Secret that this - # XR should write its connection details (if any) to. - writeConnectionSecretToRef: - namespace: crossplane-system - name: my-db-connection-details -status: - # An XR's 'Ready' condition will become True when all of the resources it - # composes are deemed ready. Refer to the Composition 'readinessChecks' field - # for more information. - conditions: - - type: Ready - statue: "True" - reason: Available - lastTransitionTime: 2021-10-02T07:20:50.52Z - # The last time the XR published its connection details to a Secret. - connectionDetails: - lastPublishedTime: 2021-10-02T07:20:51.24Z -``` - -Similarly, here's an example of the claim that corresponds to the above XR: - -```yaml -apiVersion: database.example.org/v1alpha1 -kind: PostgreSQLInstance -metadata: - # Claims are namespaced, unlike XRs. - namespace: default - name: my-db - annotations: - # The external name annotation has special meaning in Crossplane. When a - # claim creates an XR its external name will automatically be propagated to - # the XR. Whether and how the external name is propagated to the resources - # the XR composes is up to its Composition. - crossplane.io/external-name: production-db-0 -spec: - # The resourceRef field references the XR this claim corresponds to. You can - # either set it to an existing (compatible) XR that you'd like to claim or - # (the more common approach) leave it blank and let Crossplane automatically - # create and reference an XR for you. - resourceRef: - apiVersion: database.example.org/v1alpha1 - kind: XPostgreSQLInstance - name: my-db-mfd1b - # A claim's compositionRef and compositionSelector work the same way as an XR. - compositionRef: - name: production-us-east - compositionSelector: - matchLabels: - environment: production - region: us-east - provider: gcp - # A claim's writeConnectionSecretToRef mostly works the same way as an XR's. - # The one difference is that the Secret is always written to the namespace of - # the claim. - writeConnectionSecretToRef: - name: my-db-connection-details -status: - # A claim's 'Ready' condition will become True when its XR's 'Ready' condition - # becomes True. - conditions: - - type: Ready - statue: "True" - reason: Available - lastTransitionTime: 2021-10-02T07:20:50.52Z - # The last time the claim published its connection details to a Secret. - connectionDetails: - lastPublishedTime: 2021-10-02T07:20:51.24Z -``` - -> If your XR or claim isn't working as you'd expect you can try running `kubectl -> describe` against it for details - pay particular attention to any events and -> status conditions. You may need to follow the references from claim to XR to -> composed resources to find out what's happening. - -## CompositeResourceDefinitions - -Below is an example `CompositeResourceDefinition` that includes all configurable -fields. - -```yaml -apiVersion: apiextensions.crossplane.io/v1 -kind: CompositeResourceDefinition -metadata: - # XRDs must be named '.', per the plural and group names below. - name: xpostgresqlinstances.example.org -spec: - # This XRD defines an XR in the 'example.org' API group. - group: example.org - # The kind of this XR will be 'XPostgreSQLInstance`. You may also optionally - # specify a singular name and a listKind. - names: - kind: XPostgreSQLInstance - plural: xpostgresqlinstances - # This type of XR offers a claim. Omit claimNames if you don't want to do so. - # The claimNames must be different from the names above - a common convention - # is that names are prefixed with 'X' while claim names are not. This lets app - # team members think of creating a claim as (e.g.) 'creating a - # PostgreSQLInstance'. - claimNames: - kind: PostgreSQLInstance - plural: postgresqlinstances - # Each type of XR can declare any keys they write to their connection secret - # which will act as a filter during aggregation of the connection secret from - # composed resources. It's recommended to provide the set of keys here so that - # consumers of claims and XRs can see what to expect in the connection secret. - # If no key is given, then all keys in the aggregated connection secret will - # be written to the connection secret of the XR. - connectionSecretKeys: - - hostname - # Each type of XR may specify a default Composite Delete Policy to be used - # when the Claim has no compositeDeletePolicy. The valid values are Background - # and Foreground, and the default is Background. See the description of the - # compositeDeletePolicy parameter for more information. - defaultCompositeDeletePolicy: Background - # Each type of XR may specify a default Composition to be used when none is - # specified (e.g. when the XR has no compositionRef or selector). A similar - # enforceCompositionRef field also exists to allow XRs to enforce a specific - # Composition that should always be used. - defaultCompositionRef: - name: example - # Each type of XR may specify a default Composition Update Policy to be used - # when the Claim has no compositionUpdatePolicy. The valid values are Automatic - # and Manual and the default is Automatic. - defaultCompositionUpdatePolicy: Automatic - # Each type of XR may be served at different versions - e.g. v1alpha1, v1beta1 - # and v1 - simultaneously. Currently Crossplane requires that all versions - # have an identical schema, so this is mostly useful to 'promote' a type of XR - # from alpha to beta to production ready. - versions: - - name: v1alpha1 - # Served specifies that XRs should be served at this version. It can be set - # to false to temporarily disable a version, for example to test whether - # doing so breaks anything before a version is removed wholesale. - served: true - # Referenceable denotes the version of a type of XR that Compositions may - # use. Only one version may be referenceable. - referenceable: true - # Schema is an OpenAPI schema just like the one used by Kubernetes CRDs. It - # determines what fields your XR and claim will have. Note that Crossplane - # will automatically extend with some additional Crossplane machinery. - schema: - openAPIV3Schema: - type: object - properties: - spec: - type: object - properties: - parameters: - type: object - properties: - storageGB: - type: integer - required: - - storageGB - required: - - parameters - status: - type: object - properties: - address: - description: Address of this MySQL server. - type: string -``` - -Take a look at the Kubernetes [CRD documentation][crd-docs] for a more detailed -guide to writing OpenAPI schemas. Note that the following fields are reserved -for Crossplane machinery, and will be ignored if your schema includes them: - -* `spec.resourceRef` -* `spec.resourceRefs` -* `spec.claimRef` -* `spec.writeConnectionSecretToRef` -* `status.conditions` -* `status.connectionDetails` - -> If your `CompositeResourceDefinition` isn't working as you'd expect you can -> try running `kubectl describe xrd` for details - pay particular attention to -> any events and status conditions. - -## Compositions - -You'll encounter a lot of 'field paths' when reading or writing a `Composition`. -Field paths reference a field within a Kubernetes object via a simple string -'path'. [API conventions][field-paths] describe the syntax as: - -> Standard JavaScript syntax for accessing that field, assuming the JSON object -> was transformed into a JavaScript object, without the leading dot, such as -> `metadata.name`. - - Valid field paths include: - -* `metadata.name` - The `name` field of the `metadata` object. -* `spec.containers[0].name` - The `name` field of the 0th `containers` element. -* `data[.config.yml]` - The `.config.yml` field of the `data` object. -* `apiVersion` - The `apiVersion` field of the root object. - - While the following are invalid: - -* `.metadata.name` - Leading period. -* `metadata..name` - Double period. -* `metadata.name.` - Trailing period. -* `spec.containers[]` - Empty brackets. -* `spec.containers.[0].name` - Period before open bracket. - -Below is a detailed example of a `Composition`. While detailed, this example -doesn't include every patch, transform, connection detail, and readiness check -type. Keep reading below to discover those. - -```yaml -apiVersion: apiextensions.crossplane.io/v1 -kind: Composition -metadata: - name: example - labels: - # An optional convention is to include a label of the XRD. This allows - # easy discovery of compatible Compositions. - crossplane.io/xrd: xpostgresqlinstances.database.example.org - # The following label marks this Composition for GCP. This label can - # be used in 'compositionSelector' in an XR or Claim. - provider: gcp -spec: - - # Each Composition must declare that it is compatible with a particular type - # of Composite Resource using its 'compositeTypeRef' field. The referenced - # version must be marked 'referenceable' in the XRD that defines the XR. - compositeTypeRef: - apiVersion: database.example.org/v1alpha1 - kind: XPostgreSQLInstance - - # When an XR is created in response to a claim Crossplane needs to know where - # it should create the XR's connection secret. This is configured using the - # 'writeConnectionSecretsToNamespace' field. - writeConnectionSecretsToNamespace: crossplane-system - - # Each Composition must specify at least one composed resource template. In - # this case the Composition tells Crossplane that it should create, update, or - # delete a CloudSQLInstance whenever someone creates, updates, or deletes an - # XPostgresSQLInstance. - resources: - - # It's good practice to provide a unique name for each entry. Note that - # this identifies the resources entry within the Composition - it's not - # the name the CloudSQLInstance. The 'name' field will be required in a - # future version of this API. - - name: cloudsqlinstance - - # The 'base' template for the CloudSQLInstance Crossplane will create. - # You can use the base template to specify fields that never change, or - # default values for fields that may optionally be patched over. Bases must - # be a valid Crossplane resource - a Managed Resource, Composite Resource, - # or a ProviderConfig. - base: - apiVersion: database.gcp.crossplane.io/v1beta1 - kind: CloudSQLInstance - spec: - forProvider: - databaseVersion: POSTGRES_12 - region: us-central1 - settings: - dataDiskType: PD_SSD - ipConfiguration: - ipv4Enabled: true - authorizedNetworks: - - value: "0.0.0.0/0" - - # Each resource can optionally specify a set of 'patches' that copy fields - # from (or to) the XR. - patches: - # FromCompositeFieldPath is the default when 'type' is omitted, but it's - # good practice to always include the type for readability. - - type: FromCompositeFieldPath - fromFieldPath: spec.parameters.size - toFieldPath: spec.forProvider.settings.tier - - # Each patch can optionally specify one or more 'transforms', which - # transform the 'from' field's value before applying it to the 'to' field. - # Transforms are applied in the order they are specified; each transform's - # output is passed to the following transform's input. - transforms: - - type: map - map: - medium: db-custom-1-3840 - - policy: - # By default a patch from a field path that does not exist is simply - # skipped until it does. Use the 'Required' policy to instead block and - # return an error when the field path does not exist. - fromFieldPath: Required - - # You can patch entire objects or arrays from one resource to another. - # By default the 'to' object or array will be overwritten, not merged. - # Use the 'mergeOptions' field to override this behaviour. Note that - # these fields accidentally leak Go terminology - 'slice' means 'array'. - # 'map' means 'map' in YAML or 'object' in JSON. - mergeOptions: - appendSlice: true - keepMapValues: true - - # You can include connection details to propagate from this CloudSQLInstance - # up to the XPostgreSQLInstance XR (and then on to the PostgreSQLInstance - # claim). Remember that your XRD must declare which connection secret keys - # it supports. - connectionDetails: - - name: hostname - fromConnectionSecretKey: hostname - - # By default an XR's 'Ready' status condition will become True when the - # 'Ready' status conditions of all of its composed resources become true. - # You can optionally specify custom readiness checks to override this. - readinessChecks: - - type: None - - - # If you find yourself repeating patches a lot you can group them as a named - # 'patch set' then use a PatchSet type patch to reference them. - patchSets: - - name: metadata - patches: - - type: FromCompositeFieldPath - # When both field paths are the same you can omit the 'toFieldPath' and it - # will default to the 'fromFieldPath'. - fromFieldPath: metadata.labels[some-important-label] -``` - -### Pause Annotation -There is an annotation named `crossplane.io/paused` that you can use on -Composite Resources and Composite Resource Claims to temporarily pause -reconciliations of their respective controllers on them. An example -for a Composite Resource Claim is as follows: -```yaml -apiVersion: test.com/v1alpha1 -kind: MyResource -metadata: - annotations: - crossplane.io/paused: "true" - namespace: upbound-system - name: my-resource -spec: - parameters: - tagValue: demo-test - compositionRef: - name: example -``` -where `MyResource` is a Composite Resource Claim kind. -When a Composite Resource or a Claim has the `crossplane.io/paused` annotation -with its value set to `true`, the Composite Resource controller or the Claim -controller pauses reconciliations on the resource until -the annotation is removed or its value set to something other than `true`. -Before temporarily pausing reconciliations, an event with the type `Synced`, -the status `False`, and the reason `ReconcilePaused` is emitted -on the resource. -Please also note that annotations on a Composite Resource Claim are propagated -to the associated Composite Resource but when the -`crossplane.io/paused: "true"` annotation is added to a Claim, because -reconciliations on the Claim are now paused, this newly added annotation -will not be propagated. However, whenever the annotation's value is set to a -non-`true` value, reconciliations on the Claim will now resume, and thus the -annotation will now be propagated to the associated Composite Resource -with a non-`true` value. An implication of the described behavior is that -pausing reconciliations on the Claim will not inherently pause reconciliations -on the associated Composite Resource. - - -### Patch Types - -You can use the following types of patch in a `Composition`: - -`FromCompositeFieldPath`. The default if the `type` is omitted. This type -patches from a field within the XR to a field within the composed resource. It's -commonly used to expose a composed resource spec field as an XR spec field. - -```yaml -# Patch from the XR's spec.parameters.size field to the composed resource's -# spec.forProvider.settings.tier field. -- type: FromCompositeFieldPath - fromFieldPath: spec.parameters.size - toFieldPath: spec.forProvider.settings.tier -``` - -`ToCompositeFieldPath`. The inverse of `FromCompositeFieldPath`. This type -patches from a field within the composed resource to a field within the XR. It's -commonly used to derive an XR status field from a composed resource status -field. - -```yaml -# Patch from the composed resource's status.atProvider.zone field to the XR's -# status.zone field. -- type: ToCompositeFieldPath - fromFieldPath: status.atProvider.zone - toFieldPath: status.zone -``` - -`FromCompositeFieldPath` and `ToCompositeFieldPath` patches can also take a wildcarded -field path in the `toFieldPath` parameter and patch each array element in the `toFieldPath` -with the singular value provided in the `fromFieldPath`. - -```yaml -# Patch from the XR's spec.parameters.allowedIPs to the CIDRBlock elements -# inside the array spec.forProvider.firewallRules on the composed resource. -resources: -- name: exampleFirewall - base: - apiVersion: firewall.example.crossplane.io/v1beta1 - kind: Firewall - spec: - forProvider: - firewallRules: - - Action: "Allow" - Destination: "example1" - CIDRBlock: "" - - Action: "Allow" - Destination: "example2" - CIDRBlock: "" -- type: FromCompositeFieldPath - fromFieldPath: spec.parameters.allowedIP - toFieldPath: spec.forProvider.firewallRules[*].CIDRBlock -``` - -`FromEnvironmentFieldPath`. This type patches from a field within the in-memory -environment to a field within the composed resource. It's commonly used to -expose a composed resource spec field as an XR spec field. -Note that EnvironmentConfigs are an alpha feature and need to be enabled with -the `--enable-environment-configs` flag on startup. - -```yaml -# Patch from the environment's tier.name field to the composed resource's -# spec.forProvider.settings.tier field. -- type: FromEnvironmentFieldPath - fromFieldPath: tier.name - toFieldPath: spec.forProvider.settings.tier -``` - -`ToEnvironmentFieldPath`. This type patches from a composed field to the -in-memory environment. Note that, unlike `ToCompositeFieldPath` patches, this -is executed before the composed resource is applied on the cluster which means -that the `status` is not available. -Note that EnvironmentConfigs are an alpha feature and need to be enabled with -the `--enable-environment-configs` flag on startup. - -```yaml -# Patch from the environment's tier.name field to the composed resource's -# spec.forProvider.settings.tier field. -- type: ToEnvironmentFieldPath - fromFieldPath: spec.forProvider.settings.tier - toFieldPath: tier.name -``` - -Note that the field to be patched requires some initial value to be set. - -`CombineFromComposite`. Combines multiple fields from the XR to produce one -composed resource field. - -```yaml -# Patch from the XR's spec.parameters.location field and the -# metadata.labels[crossplane.io/claim-name] label to the composed -# resource's spec.forProvider.administratorLogin field. -- type: CombineFromComposite - combine: - # The patch will only be applied when all variables have non-zero values. - variables: - - fromFieldPath: spec.parameters.location - - fromFieldPath: metadata.labels[crossplane.io/claim-name] - strategy: string - string: - fmt: "%s-%s" - toFieldPath: spec.forProvider.administratorLogin - # By default Crossplane will skip the patch until all of the variables to be - # combined have values. Set the fromFieldPath policy to 'Required' to instead - # abort composition and return an error if a variable has no value. - policy: - fromFieldPath: Required -``` - -`CombineFromEnvironment`. Combines multiple fields from the in-memory -environment to produce one composed resource field. -Note that EnvironmentConfigs are an alpha feature and need to be enabled with -the `--enable-environment-configs` flag on startup. - -```yaml -# Patch from the environments's location field and region to the composed -# resource's spec.forProvider.administratorLogin field. -- type: CombineFromEnvironment - combine: - # The patch will only be applied when all variables have non-zero values. - variables: - - fromFieldPath: location - - fromFieldPath: region - strategy: string - string: - fmt: "%s-%s" - toFieldPath: spec.forProvider.administratorLogin -``` - -At the time of writing only the `string` combine strategy is supported. It uses -[Go string formatting][pkg/fmt] to combine values, so if the XR's location was -`us-west` and its claim name was `db` the composed resource's administratorLogin -would be set to `us-west-db`. - -`CombineToComposite` is the inverse of `CombineFromComposite`. - -```yaml -# Patch from the composed resource's spec.parameters.administratorLogin and -# status.atProvider.fullyQualifiedDomainName fields back to the XR's -# status.adminDSN field. -- type: CombineToComposite - combine: - variables: - - fromFieldPath: spec.parameters.administratorLogin - - fromFieldPath: status.atProvider.fullyQualifiedDomainName - strategy: string - # Here, our administratorLogin parameter and fullyQualifiedDomainName - # status are formatted to a single output string representing a DSN. - string: - fmt: "mysql://%s@%s:3306/my-database-name" - toFieldPath: status.adminDSN -``` - -`CombineToEnvironment` is the inverse of `CombineFromEnvironment`. -Note that EnvironmentConfigs are an alpha feature and need to be enabled with -the `--enable-environment-configs` flag on startup. - -```yaml -# Patch from the composed resource's spec.parameters.administratorLogin and -# spec.forProvider.domainName fields back to the environment's adminDSN field. -- type: CombineToEnvironment - combine: - variables: - - fromFieldPath: spec.parameters.administratorLogin - - fromFieldPath: spec.forProvider.domainName - strategy: string - # Here, our administratorLogin parameter and fullyQualifiedDomainName - # status are formatted to a single output string representing a DSN. - string: - fmt: "mysql://%s@%s:3306/my-database-name" - toFieldPath: adminDSN -``` - -`PatchSet`. References a named set of patches defined in the `spec.patchSets` -array of a `Composition`. - -```yaml -# This is equivalent to specifying all of the patches included in the 'metadata' -# PatchSet. -- type: PatchSet - patchSetName: metadata -``` - -The `patchSets` array may not contain patches of `type: PatchSet`. The -`transforms` and `patchPolicy` fields are ignored by `type: PatchSet`. - -### Transform Types - -You can use the following types of transform on a value being patched: - -`map`. Transforms values using a map. - -```yaml -# If the value of the 'from' field is 'us-west', the value of the 'to' field -# will be set to 'West US'. -- type: map - map: - us-west: West US - us-east: East US - au-east: Australia East -``` - -`match`. A more complex version of `map` that can match different kinds of -patterns. It should be used if more advanced pattern matchings than a simple -string equality check are required. -The result of the first matching pattern is used as the output of this -transform. -If no pattern matches, you can either fallback to a given `fallbackValue` or -fallback to the input value by setting the `fallbackTo` field to `Input`. - -```yaml -# In the example below, if the value in the 'from' field is 'us-west', the -# value in the 'to' field will be set to 'West US'. -# If the value in the 'from' field is 'eu-west', the value in the 'to' field -# will be set to 'Unknown' because no pattern matches. -- type: match - match: - patterns: - - type: literal # Not needed. This is the default. - literal: us-west - result: West US - - type: regexp - regexp: '^af-.*' - result: Somewhere in Africa - fallbackTo: Value # Not needed. This is the default. - fallbackValue: Unknown - -# If fallbackTo is set to Input, the output will be the input value if no -# pattern matches. -# In the example below, if the value in the 'from' field is 'us-west', the -# value in the 'to' field will be set to 'West US'. -# If the value in the 'from' field is 'eu-west', the value in the 'to' field -# will be set to 'eu-west' because no pattern matches. -- type: match - match: - patterns: - - type: literal - literal: us-west - result: West US - - type: regexp - regexp: '^af-.*' - result: Somewhere in Africa - fallbackTo: Input -``` - -`math`. Transforms values using math. The input value must be an integer. -* math transform type `Multiply`, multiplies the input by the given value. -* math transform type `ClampMin`, sets a minimum value for the output. -* math transform type `ClampMax`, sets a maximum value for the output. - -```yaml -# If you omit the field type, by default type is set to `Multiply` -# If the value of the 'from' field is 2, the value of the 'to' field will be set -# to 4. -- type: math - math: - multiply: 2 - -# This is the same as above -# If the value of the 'from' field is 2, the value of the 'to' field will be set -# to 4. -- type: math - math: - type: Multiply - multiply: 2 - -# If the value of the 'from' field is 3, the value of the 'to' field will -# be set to 4. -- type: math - math: - type: ClampMin - clampMin: 4 - -# If the value of the 'from' field is 3, the value of the 'to' field will -# be set to 2. -- type: math - math: - type: ClampMax - clampMax: 2 -``` - -`string`. Transforms string values. -* string transform type `Format`, Currently only Go style fmt is supported. [Go style `fmt`][pkg/fmt] is supported. -* string transform type `Convert`, accepts one of `ToUpper`, `ToLower`, `ToBase64`, `FromBase64`, `ToJson`, `ToSha1`, `ToSha256`, `ToSha512`. -* string transform type `TrimPrefix`, accepts a string to be trimmed from the beginning of the input. -* string transform type `TrimSuffix`, accepts a string to be trimmed from the end of the input. -* string transform type `Regexp`, accepts a string for regexp to be applied to. - -```yaml -# If you omit the field type, by default type is set to `Format` -# If the value of the 'from' field is 'hello', the value of the 'to' field will -# be set to 'hello-world'. -- type: string - string: - fmt: "%s-world" - -# This is the same as above -# the value of the 'to' field will be set to 'hello-world'. -- type: string - string: - type: Format - fmt: "%s-world" - -# If the value of the 'from' field is 'hello', the value of the 'to' field will -# be set to 'HELLO'. -- type: string - string: - type: Convert - convert: ToUpper - -# If the value of the 'from' field is 'Hello', the value of the 'to' field will -# be set to 'hello'. -- type: string - string: - type: Convert - convert: ToLower - -# If the value of the 'from' field is 'Hello', the value of the 'to' field will -# be set to 'SGVsbG8='. -- type: string - string: - type: Convert - convert: ToBase64 - -# If the value of the 'from' field is 'SGVsbG8=', the value of the 'to' field will -# be set to 'Hello'. -- type: string - string: - type: Convert - convert: FromBase64 - -# If the value of the 'from' field is not nil, the value of the 'to' field will be -# set to raw JSON representation of the 'from' field. -- type: string - string: - type: Convert - convert: ToJson - -# The output will be the hash of the JSON representation of the 'from' field. -- type: string - string: - type: Convert - convert: ToSha1 # alternatives: 'ToSha256' or 'ToSha512' - -# If the value of the 'from' field is https://crossplane.io, the value of the 'to' field will -# be set to crossplane.io -- type: string - string: - type: TrimPrefix - trim: 'https://' - -# If the value of the 'from' field is my-string-test, the value of the 'to' field will -# be set to my-string -- type: string - string: - type: TrimSuffix - trim: '-test' - -# If the value of the 'from' field is 'arn:aws:iam::42:example, the value of the -# 'to' field will be set to "42". Note that the 'to' field is always a string. -- type: string - string: - type: Regexp - regexp: - match: 'arn:aws:iam::(\d+):.*' - group: 1 # Optional capture group. Omit to match the entire regexp. -``` - -`convert`. Transforms values of one type to another, for example from a string -to an integer. The following values are supported by the `from` and `to` fields: - -* `string` -* `bool` -* `int` -* `int64` -* `float64` - -The strings 1, t, T, TRUE, true, and True are considered 'true', while 0, f, F, -FALSE, false, and False are considered 'false'. The integer 1 and float 1.0 are -considered true, while all other values are considered false. Similarly, boolean -true converts to integer 1 and float 1.0, while false converts to 0 and 0.0. - -```yaml -# If the value to be converted is "1" (a string), the value of the 'toType' -# field will be set to 1 (an integer). -- type: convert - convert: - toType: int -``` - -Converting `string` to `float64` additionally supports parsing string in -[K8s quantity format](https://pkg.go.dev/k8s.io/apimachinery/pkg/api/resource#Quantity), -such as `1000m` or `500 Mi`: - -```yaml -- type: convert - convert: - toType: float64 - format: quantity -``` - -### Connection Details - -Connection details secret of XR is an aggregated sum of the connection details -of the composed resources. It's recommended that the author of XRD specify -exactly which keys will be allowed in the XR connection secret by listing them -in `spec.connectionSecretKeys` so that consumers of claims and XRs can see what -they can expect in the connection details secret. - -If `spec.connectionSecretKeys` is empty, then all keys of the aggregated connection -details secret will be propagated. - -You can derive the following types of connection details from a composed -resource to be aggregated: - -`FromConnectionSecretKey`. Derives an XR connection detail from a connection -secret key of a composed resource. - -```yaml -# Derive the XR's 'user' connection detail from the 'username' key of the -# composed resource's connection secret. -- type: FromConnectionSecretKey - name: user - fromConnectionSecretKey: username -``` - -`FromFieldPath`. Derives an XR connection detail from a field path within the -composed resource. - -```yaml -# Derive the XR's 'user' connection detail from the 'adminUser' status field of -# the composed resource. -- type: FromFieldPath - name: user - fromFieldPath: status.atProvider.adminUser -``` - -`FromValue`. Derives an XR connection detail from a fixed value. - -```yaml -# Always sets the XR's 'user' connection detail to 'admin'. -- type: FromValue - name: user - value: admin -``` - -### Readiness Checks - -Crossplane can use the following types of readiness check to determine whether a -composed resource is ready (and therefore whether the XR and claim should be -considered ready). Specify multiple readiness checks if multiple conditions must -be met for a composed resource to be considered ready. - -> Note that if you don't specify any readiness checks Crossplane will consider -> the composed resource to be ready when its 'Ready' status condition becomes -> 'True'. - -`MatchString`. Considers the composed resource to be ready when the value of a -field within that resource matches a specified string. - -```yaml -# The composed resource will be considered ready when the 'state' status field -# matches the string 'Online'. -- type: MatchString - fieldPath: status.atProvider.state - matchString: "Online" -``` - -`MatchInteger`. Considers the composed resource to be ready when the value of a -field within that resource matches a specified integer. - -```yaml -# The composed resource will be considered ready when the 'state' status field -# matches the integer 4. -- type: MatchInteger - fieldPath: status.atProvider.state - matchInteger: 4 -``` - -`NonEmpty`. Considers the composed resource to be ready when a field exists in -the composed resource. The name of this check can be a little confusing in that -a field that exists with a zero value (e.g. an empty string or zero integer) is -not considered to be 'empty', and thus will pass the readiness check. - -```yaml -# The composed resource will be considered ready if and when 'online' status -# field exists. -- type: NonEmpty - fieldPath: status.atProvider.online -``` - -`None`. Considers the composed resource to be ready as soon as it exists. - -### Composition validation - -Crossplane uses a `Validating Webhook` to inform users of any potential -errors in a `Composition`. By default webhooks only perform -`logical checks`. `logical checks` enforce requirements that -aren't explicitly defined in the schema but Crossplane assumes to hold at runtime. - -#### Experimental validation with schemas - -Enable experimental schema-aware validation in Crossplane -through the `--enable-composition-webhook-schema-validation` feature flag. This -enables Composition validation against available schemas in the cluster. -For example, ensuring that `fieldPaths` are valid and source and destination -types match taking into account provided transforms too. - -The `crossplane.io/composition-validation-mode` annotation on the Composition -allows setting one of two modes for schema validation: - -- `loose` (default): Validates Compositions against required schemas. If a - required schema is missing, schema validation stops, emits a warning and - falls back to `logical checks` only. -- `strict`: Validates Compositions against required schemas, and rejects them - when finding errors. Rejects any Compositions missing required schemas. - -See the [Composition Validating Webhook design document][validation-design-doc] -for more information about future development around schema-aware validation. - -#### Disabling webhooks - -Crossplane enables webhooks by default. Turn off webhooks by -`webhooks.enabled` to `false` in the provided Helm Chart. - -### Missing Functionality - -You might find while reading through this reference that Crossplane is missing -some functionality you need to compose resources. If that's the case, please -[raise an issue] with as much detail **about your use case** as possible. Please -understand that the Crossplane maintainers are growing the feature set of the -`Composition` type conservatively. We highly value the input of our users and -community, but we also feel it's critical to avoid bloat and complexity. We -therefore wish to carefully consider each new addition. We feel some features -may be better suited for a real, expressive programming language and intend to -build an alternative to the `Composition` type as it's documented here per -[this proposal][issue-2524]. - -## Tips, Tricks, and Troubleshooting - -In this section we'll cover some common tips, tricks, and troubleshooting steps -for working with Composite Resources. If you're trying to track down why your -Composite Resources aren't working the [Troubleshooting][trouble-ref] page also -has some useful information. - -### Troubleshooting Claims and XRs - -Crossplane relies heavily on status conditions and events for troubleshooting. -You can see both using `kubectl describe` - for example: - -```console -# Describe the PostgreSQLInstance claim named my-db -kubectl describe postgresqlinstance.database.example.org my-db -``` - -Per Kubernetes convention, Crossplane keeps errors close to the place they -happen. This means that if your claim is not becoming ready due to an issue with -your `Composition` or with a composed resource you'll need to "follow the -references" to find out why. Your claim will only tell you that the XR is not -yet ready. - -To follow the references: - -1. Find your XR by running `kubectl describe` on your claim and looking for its - "Resource Ref" (aka `spec.resourceRef`). -1. Run `kubectl describe` on your XR. This is where you'll find out about issues - with the `Composition` you're using, if any. -1. If there are no issues but your XR doesn't seem to be becoming ready, take a - look for the "Resource Refs" (or `spec.resourceRefs`) to find your composed - resources. -1. Run `kubectl describe` on each referenced composed resource to determine - whether it is ready and what issues, if any, it is encountering. - -### Composite Resource Connection Secrets - -Claim and Composite Resource connection secrets are often derived from the -connection secrets of the managed resources they compose. This is a common -source of confusion because several things need to align for it to work: - -1. The **claim** must specify the secret where the aggregated connection details - should be written - * This is the `spec.writeConnectionSecretToRef` field in a claim - * If creating a composite resource directly (without a claim) then this same - field must be set on your composite resource instead -1. The **composite resource definition** must state which connection details to - aggregate from its children to publish to the claim - * This is the `spec.connectionSecretKeys` field in a - `CompositeResourceDefinition` -1. The **composition** must define where to write its aggregated connection - details - * This is the `spec.writeConnectionSecretsToNamespace` field in the - `Composition` -1. Each child **composed resource** must define the connection details it - publishes and where to write them - * These are the `connectionDetails` and - `base.spec.writeConnectionSecretToRef` fields of the composed resources - -Finally, you can't currently edit a XRD's supported connection details. The -XRD's `spec.connectionSecretKeys` is effectively immutable. This may change in -future per [this issue][issue-2024] - -### Claiming an Existing Composite Resource - -Most people create Composite Resources using a claim, but you can actually claim -an existing Composite Resource as long as its a type of XR that offers a claim -and no one else has already claimed it. To do so: - -1. Set the `spec.resourceRef` of your claim to reference the existing XR. - ```yaml - apiVersion: database.example.org/v1alpha1 - kind: PostgreSQLInstance - metadata: - name: example - namespace: default - spec: - resourceRef: - apiVersion: database.example.org/v1alpha1 - kind: XPostgreSQLInstance - name: example-d4lmv - ``` -1. Make sure the rest of your claim's spec fields match the XR's. - -If your claim's spec fields don't match the XR's Crossplane will still claim it -but will then try to update the XR's spec fields to match the claim's. - -### Influencing External Names - -The `crossplane.io/external-name` annotation has special meaning to Crossplane -managed resources - it specifies the name (or identifier) of the resource in the -external system, for example the actual name of a `CloudSQLInstance` in the GCP -API. Some managed resources don't let you specify an external name - in those -cases Crossplane will set it for you to whatever the external system requires. - -If you add the `crossplane.io/external-name` annotation to a claim Crossplane -will automatically propagate it when it creates an XR. It's good practice to -have your `Composition` further propagate the annotation to one or more composed -resources, but it's not required. - -### Mixing and Matching Providers - -Crossplane has providers for many things in addition to the big clouds. Take a -look at the [Upbound Marketplace][upbound-marketplace] to find many of them. -Keep in mind that you can mix and match managed resources from different -providers within a `Composition` to create Composite Resources. For example you -might use provider-aws and provider-sql to create an XR that provisions an -`RDSInstance` then creates an SQL `Database` and `User`, or provider-gcp and -provider-helm to create a `GKECluster` and deploy a Helm Chart `Release` to it. - -Often when mixing and matching providers you'll need to compose a -`ProviderConfig` for one provider that loads credentials from the connection -secret of a managed resource from another provider. Sometimes you may need to -use an intermediary XR to mutate the connection details to suit your needs. -[This example][helm-and-gcp] from provider-helm demonstrates using a GKE cluster -connection secret as Helm `ProviderConfig` credentials. - -### Patching From One Composed Resource to Another - -It's not possible to patch _directly_ from one composed resource to another - -i.e. from one entry in the `spec.resources` array of a `Composition` to another. -It is however possible to achieve this by using the XR as an intermediary. To do -so: - -1. Use a `ToCompositeFieldPath` patch to patch from your source composed - resource to the XR. Typically you'll want to patch to a status field or an - annotation. -1. Use a `FromCompositeFieldPath` patch to patch from the 'intermediary' field - you patched to in step 1 to a field on the destination composed resource. - -[crd-docs]: https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/ -[raise an issue]: https://github.com/crossplane/crossplane/issues/new?assignees=&labels=enhancement&template=feature_request.md -[issue-2524]: https://github.com/crossplane/crossplane/issues/2524 -[field-paths]: https://github.com/kubernetes/community/blob/61f3d0/contributors/devel/sig-architecture/api-conventions.md#selecting-fields -[pkg/fmt]: https://pkg.go.dev/fmt -[upbound-marketplace]: https://marketplace.upbound.io -[helm-and-gcp]: https://github.com/crossplane-contrib/provider-helm/blob/2dcbdd0/examples/in-composition/composition.yaml -[issue-2024]: https://github.com/crossplane/crossplane/issues/2024 -[xrs-and-mrs]: /media/composition-xrs-and-mrs.svg -[how-it-works]: /media/composition-how-it-works.svg -[provider-kubernetes]: https://marketplace.upbound.io/providers/crossplane-contrib/provider-kubernetes -[provider-helm]: https://marketplace.upbound.io/providers/crossplane-contrib/provider-helm/ -[claims-and-xrs]: /media/composition-claims-and-xrs.svg -[xr-ref]: {{}} -[managed-resources]: {{}} -[validation-design-doc]: https://github.com/crossplane/crossplane/blob/master/design/design-doc-composition-validating-webhook.md diff --git a/content/master/concepts/compositions.md b/content/master/concepts/compositions.md new file mode 100644 index 000000000..f99fc6969 --- /dev/null +++ b/content/master/concepts/compositions.md @@ -0,0 +1,1226 @@ +--- +title: Compositions +weight: 30 +aliases: + - composition +description: "Compositions are a template for creating Crossplane resources" +--- + +Compositions are a template for creating multiple managed resources as a single +object. + +A Composition _composes_ individual managed resources together into a larger, +reusable, solution. + +An example Composition may combine a virtual machine, storage resources and +networking policies. A Composition template links all these individual +resources together. + +{{}} +Crossplane has four core components that users commonly mix up: + +* Compositions - This page. A template to define how to create resources. +* [Composite Resource Definition]({{}}) + (`XRD`) - A custom API specification. +* [Composite Resource]({{}}) (`XR`) - Created by + using the custom API defined in a Composite Resource Definition. XRs use the + Composition template to create new managed resources. +* [Claims]({{}}) (`XRC`) - Like a Composite Resource, but + with namespace scoping. +{{}} + +## Creating Compositions + +Creating Compositions consists of: +* [Resource templates](#resource-templates) defining the resources to create. +* [Enabling Composite Resources](#enabling-composite-resources) to use this + Composition template. + + +Optionally, Compositions also support: +* [Modifying and patching](#changing-resource-fields) resource settings. +* [Storing connection details](#storing-connection-details) and secrets + generated by the managed resources. +* Using [Composition functions](#composition-functions) to allow custom + programs to run alongside the Composition. +* Creating a + [custom check of when a resource is ready](#resource-readiness-checks) + to use. + + +### Resource templates +The +{{}}resources{{}} field of a +Composition's +{{}}spec{{}} +defines the set of things that a Composite Resource creates. + +{{}} +Read more about Composite Resources in the +[Composite Resources page]({{}}). +{{< /hint >}} + + +For example, a Composition can define a template to create a VM and an +associated storage bucket at the same time. + +The {{}}resources{{}} field lists the +individual resources with a +{{}}name{{}}. +This name identifies the +resource inside the Composition and isn't related to the external name used with +the Provider. + +#### Template a managed resource +The contents of the +{{}}base{{}} are identical to +creating a standalone [managed resource]({{}}). + +This example uses +[Upbound's Provider AWS](https://marketplace.upbound.io/providers/upbound/provider-aws/v0.35.0) +to define a +S3 storage {{}}Bucket{{}} and +EC2 compute {{}}Instance{{}}. + +After defining the +{{}}apiVersion{{}} and +{{}}kind{{}}, define the +{{}}spec.forProvider{{}} fields +defining the settings of the resource. + +```yaml {copy-lines="none",label="resources"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +spec: + resources: + - name: StorageBucket + base: + apiVersion: s3.aws.upbound.io/v1beta1 + kind: Bucket + spec: + forProvider: + region: "us-east-2" + - name: VM + base: + apiVersion: ec2.aws.upbound.io/v1beta1 + kind: Instance + spec: + forProvider: + ami: ami-0d9858aa3c6322f73 + instanceType: t2.micro + region: "us-east-2" +``` + +When a [Composite Resource]({{}}) uses this +Composition template, the Composite Resource creates two new managed resources +with all the provided +{{}}spec.forProvider{{}} settings. + +The {{}}spec{{}} +supports any settings used in a managed resource including +applying `annotations` and `labels` or using a specific +`providerConfigRef`. + +{{}} +Compositions allow applying a `metadata.name` to a resource's +{{}}spec{{}} but ignores it. The +`metadata.name` field doesn't influence the name +of the managed resource in Crossplane or the external resource inside the +Provider. + +Use the `crossplane.io/external-name` annotation on the resource to influence +the external resource name. +{{< /hint >}} + +#### Template a ProviderConfig + +Compositions can define a ProviderConfig like it defines managed resources. +Generating a ProviderConfig may be useful in providing unique credentials to +each deployment. + + +```yaml {copy-lines="none"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +spec: + resources: + - name: my-aws-provider-config + base: + apiVersion: aws.upbound.io/v1beta1 + kind: ProviderConfig + spec: + source: Secret + secretRef: + namespace: crossplane-system + name: aws-secret + key: creds +``` + + +#### Template another Composite Resource + +Compositions can use other Composite Resources to define more complicated +templates. + +A common use case is a Composition that uses other +Compositions. For example, creating a Composition to create a standard set of +networking resources that other Compositions reference. + +{{< hint "note" >}} +Both Compositions must have corresponding XRDs. +{{< /hint >}} + +This example networking Composition defines the set of resources required to +create a new AWS virtual network. This includes a +{{}}VPC{{}}, +{{}}InternetGateway{{}} and +{{}}Subnet{{}}. + +```yaml {copy-lines="none",label="xnetwork"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +spec: + resources: + - name: vpc-resource + base: + apiVersion: ec2.aws.upbound.io/v1beta1 + kind: VPC + # Removed for Brevity + - name: gateway-resource + base: + apiVersion: ec2.aws.upbound.io/v1beta1 + kind: InternetGateway + # Removed for Brevity + - name: subnet-resource + base: + apiVersion: ec2.aws.upbound.io/v1beta1 + kind: Subnet + # Removed for Brevity + compositeTypeRef: + apiVersion: aws.platformref.upbound.io/v1alpha1 + kind: XNetwork +``` + +The {{}}compositeTypeRef{{}} gives +this Composition an +{{}}apiVersion{{}} and +{{}}kind{{}} to reference in another +Composition. + +{{}} +The [Enabling a Composite Resource](#enabling-composite-resources) section +describes the +{{}}compositeTypeRef{{}} field. +{{< /hint >}} + +A second Composition, defining other resources, in this example, an AWS Elastic +Kubernetes Cluster, can reference the previous +{{}}XNetwork{{}} + +```yaml {copy-lines="none",label="xcluster"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +spec: + resources: + - name: nested-network-composition + base: + apiVersion: aws.platformref.upbound.io/v1alpha1 + kind: XNetwork + # Removed for brevity + - name: eks-cluster-resource + base: + apiVersion: eks.aws.upbound.io/v1beta1 + kind: Cluster + # Removed for brevity +``` + +When a Composite Resource creates all the managed resources from this +Composition, the resources defined by the +{{}}XNetwork{{}} get created along with +the EKS {{}}cluster{{}}. + +{{}} +This abbreviated example is from the Upbound [AWS Reference +Platform](https://github.com/upbound/platform-ref-aws). + +View the complete Compositions in the reference platform's +[package directory](https://github.com/upbound/platform-ref-aws/blob/main/package/cluster/composition.yaml). +{{}} + +#### Cross-resource references + +Inside a Composition some resources use identifiers or names of other resources. +For example, creating a new `network` and applying the network identifier to a +virtual machine. + +Resources inside a Composition can cross-reference other resources by matching +a label or a _controller reference_. + +{{}} +Providers allow matching of labels and controller references on a +per-resource basis. Check the documentation for the specific Provider resource +to see what's supported. + +Matching labels and controllers isn't supported across different Providers. +{{< /hint >}} + +##### Match resource labels + +To match a resource label, first apply a +{{}}label{{}} to the resource to +match and use +{{}}matchLabels{{}} +in the second resource. + +This example creates a AWS +{{}}Role{{}} and applies a +{{}}label{{}}. The second resource +is a {{}}RolePolicyAttachment{{}}, +which requires attaching to an existing `Role`. + +Using the resource's +{{}}roleSelector.matchLabels{{}} +ensures this +{{}}RolePolicyAttachment{{}} +references the matching +{{}}Role{{}}, even if the unique role +identifier isn't known. + +```yaml {label="matchlabel",copy-lines="none"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +spec: + resources: + - base: + apiVersion: iam.aws.upbound.io/v1beta1 + kind: Role + name: iamRole + metadata: + labels: + role: controlplane + - base: + apiVersion: iam.aws.upbound.io/v1beta1 + kind: RolePolicyAttachment + name: iamPolicy + spec: + forProvider: + roleSelector: + matchLabels: + role: controlplane + # Removed for brevity +``` + +##### Match a controller reference + +Matching a controller reference ensures that the matching resource is +in the same composite resource. + +Matching only a controller reference simplifies the matching process without +requiring labels or more information. + +For example, creating an AWS +{{}}InternetGateway{{}} requires a +{{}}VPC{{}}. + +The {{}}InternetGateway{{}} could +match a label, but every VPC created by this Composition share the same label. + +Using {{}}matchControllerRef{{}} +matches only the VPC created in the same composite resource that created the +{{}}InternetGateway{{}}. + +```yaml {label="controller1",copy-lines="none"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +spec: + resources: + - base: + apiVersion: ec2.aws.upbound.io/v1beta1 + kind: VPC + name: my-vpc + spec: + forProvider: + # Removed for brevity + - base: + apiVersion: ec2.aws.upbound.io/v1beta1 + kind: InternetGateway + name: my-gateway + spec: + forProvider: + vpcIdSelector: + matchControllerRef: true +# Removed for brevity +``` + + +Resources can match both labels and a controller reference to match a specific +resource in the larger composite resource. + +For example, this Composition creates two +{{}}VPC{{}} +resources, but the +{{}}InternetGateway{{}} +must match only one. + +Applying a {{}}label{{}} to the +second {{}}VPC{{}} allows the +{{}}InternetGateway{{}} to match +the label +{{}}type: internet{{}} and only +match objects in the same composite resource with +{{}}matchControllerRef{{}}. + +```yaml {label="controller2",copy-lines="none"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +spec: + resources: + - base: + apiVersion: ec2.aws.upbound.io/v1beta1 + kind: VPC + name: my-first-vpc + metadata: + labels: + type: backend + spec: + forProvider: + # Removed for brevity + - base: + apiVersion: ec2.aws.upbound.io/v1beta1 + kind: VPC + name: my-second-vpc + metadata: + labels: + type: internet + spec: + forProvider: + # Removed for brevity + - base: + apiVersion: ec2.aws.upbound.io/v1beta1 + kind: InternetGateway + name: my-gateway + spec: + forProvider: + vpcIdSelector: + matchControllerRef: true + matchLabels: + type: internet +# Removed for brevity +``` +### Enabling Composite Resources + +A Composition is only a template defining how to create managed +resources. A Composition limits which Composite Resources can use this +template. + +A Composition's {{}}compositeTypeRef{{}} +defines which Composite Resource type can use this Composition. + +{{}} +Read more about Composite Resources in the +[Composite Resources page]({{}}). +{{< /hint >}} + +Inside a Composition's +{{}}spec{{}} +define the Composite Resource +{{}}apiVersion{{}} and +{{}}kind{{}} +that the Composition allows to use this template. + +```yaml {label="typeref",copy-lines="none"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +metadata: + name: dynamodb-with-bucket +spec: + compositeTypeRef: + apiVersion: custom-api.example.org/v1alpha1 + kind: database + # Removed for brevity +``` + +### Changing resource fields + +Most Compositions require customizing the fields of the resources. This can +include applying unique passwords, modifying where to deploy resources, +or applying labels or annotations. + +The primary method to change resources is using a resource [patch and +transform]({{}}). Patch and transforms allow +matching specific input fields, modifying them and applying them to the managed +resource. + +{{}} +The details of creating patch and transforms and their options are in the +[Patch and Transform page]({{}}). + +This section describes applying patches and transforms to Compositions. +{{< /hint >}} + +Apply patches to individual `resources` with the +{{}}patches{{}} +field. + +For example, taking the +{{}}location{{}} provided in a Claim +and applying it to the +{{}}region{{}} value in the managed +resource. + +```yaml {copy-lines="none",label="patchClaim"} +apiVersion: example.org/v1alpha1 +kind: ExampleClaim +metadata: + name: my-example-claim +spec: + location: "eu-north-1" +``` + +The Composition patch uses the +{{}}fromFieldPath{{}} to match the +{{}}location{{}} +field in the Claim and the +{{}}toFieldPath{{}} to define which field +to change inside the Composition. + +```yaml {copy-lines="none",label="patch"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +# Removed for Brevity +spec: + resources: + - name: s3Bucket + base: + apiVersion: s3.aws.upbound.io/v1beta1 + kind: Bucket + spec: + forProvider: + region: "us-east-2" + patches: + - type: FromCompositeFieldPath + fromFieldPath: "spec.location" + toFieldPath: "spec.forProvider.region" +``` + +#### Patch sets + +Some Compositions have resources which need identical patches applied. Instead +of repeating the same `patches` field, resources can reference a single +`patchSet`. + +Define a {{}}patchSet{{}} with a +{{}}name{{}} +and +{{}}patch{{}} +operations. + +Then apply the +{{}}patchSet{{}} +to each resource with +{{}}type: patchSet{{< /hover >}}, referencing +the {{}}name{{< /hover >}} in the +{{}}patchSetName{{< /hover >}} field. + +```yaml {copy-lines="none",label="patchset"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +# Removed for Brevity +spec: + patchSets: + - name: reusable-patch + patches: + - type: FromCompositeFieldPath + fromFieldPath: "location" + toFieldPath: "spec.forProvider.region" + resources: + - name: first-resource + base: + # Removed for Brevity + patches: + - type: PatchSet + patchSetName: reusable-patch + - name: second-resource + base: + # Removed for Brevity + patches: + - type: PatchSet + patchSetName: reusable-patch +``` + +#### Patch with EnvironmentConfigs + +Crossplane uses EnvironmentConfigs as an in-memory data store. Compositions can +read and write from this data store as part of the patch process. + +{{}} +EnvironmentConfigs are an alpha feature. Alpha features aren't enabled by +default. +{{< /hint >}} + + EnvironmentConfigs can predefine data that Compositions can use + or a Composite Resource can write data to the EnvironmentConfig for other + resources to read. + + +{{< hint "note" >}} + +Read the [EnvironmentConfigs]({{}}) page for +more information on using EnvironmentConfigs. +{{< /hint >}} + +To apply a patch using EnvironmentConfigs, first define which EnvironmentConfig +to use with +{{}}environment.environmentConfigs{{}}. + + + + +Use either a +[reference]({{}}) +or a [selector]({{}}) to +identify the EnvironmentConfig to use. + + +```yaml {label="envselect",copy-lines="none"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +# Removed for Brevity +spec: + environment: + environmentConfigs: + - ref: + name: example-environment + resources: + # Removed for Brevity +``` + +Inside the {{}}patches{{}} of the +resource, use +{{}}ToEnvironmentFieldPath{{}} to copy +data from the resource to the EnvironmentConfig. +Use {{}}FromEnvironmentFieldPath{{}} +to copy data to the resource from the EnvironmentConfig. + +```yaml {label="envpatch",copy-lines="none"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +# Removed for Brevity +spec: + environment: + # Removed for Brevity + resources: + # Removed for Brevity + - 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 [EnvironmentConfigs]({{}}) page has +more information on EnvironmentConfigs options and usage. + +### Composition functions + +Composition functions (`XFNs`) are containers executing any code you define. +Composition functions can read and write to any resource in the Composition +they're attached to. This allows composition functions to perform complex +operations to patch fields, determine if a resource is ready for use or notify +an external system about the details of resource. + +{{}} +Composition functions are an alpha feature. Alpha features aren't enabled by +default. +{{< /hint >}} + +To attach a composition function to a Composition define a +{{}}function{{}} inside the Composition +{{}}spec{{}}. + +Provide a {{}}name{{}} for the function. +The {{}}type{{}} is always `Container`. +The {{}}container.image{{}} is the +location of the composition function container image. + +```yaml {label="xfn",copy-lines="none"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +# Removed for Brevity +spec: + resources: + # Removed for Brevity + functions: + - name: rds-instance-password + type: Container + container: + image: xpkg.upbound.io/provider-aws-xfns/random-rds-password:v0.1.0 +``` + +Read the [composition functions]({{}}) page for +details on building and using composition functions. + +### Storing connection details + +Some managed resources generate unique details like usernames, passwords, +IP addresses, ports or other connection details. + +When resources inside a Composition create connection details Crossplane creates +a Kubernetes secret object for each managed resource generating connection +details. + +{{}} +This section discusses creating Kubernetes secrets. +Crossplane also supports using external secret stores like [HashiCorp Vault](https://www.vaultproject.io/). + +Read the [external secrets store guide]({{}}) for more information on using Crossplane +with an external secret store. +{{}} + +#### Composite resource combined secret +Crossplane can combine all the secrets generated by the resources inside a +Composition into a single Kubernetes secret and optionally copy the +secret object for [Claims]({{}}). + +Set the value of +{{}}writeConnectionSecretsToNamespace{{}} +to the namespace where Crossplane should store the combined secret object. + +```yaml {copy-lines="none",label="writeConn"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +# Removed for Brevity +spec: + writeConnectionSecretsToNamespace: my-namespace + resources: + # Removed for brevity +``` + +#### Composed resource secrets +Inside the +{{}}spec{{}} of +each resource producing connection details, define the +{{}}writeConnectionSecretToRef{{}}, with +a +{{}}namespace{{}} and +{{}}name{{}} of the secret object for +the resource. + +If a +{{}}writeConnectionSecretToRef{{}} +isn't defined, Crossplane doesn't write any keys to the secret. + + +```yaml {label="writeConnRes"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +spec: + writeConnectionSecretsToNamespace: other-namespace + resources: + - name: key + base: + apiVersion: iam.aws.upbound.io/v1beta1 + kind: AccessKey + spec: + forProvider: + # Removed for brevity + writeConnectionSecretToRef: + namespace: docs + name: key1 +``` + +Crossplane saves a secret with the +{{}}name{{}} +in the +{{}}namespace{{}} provided. + +```shell {label="viewComposedSec"} +kubectl get secrets -n docs +NAME TYPE DATA AGE +key1 connection.crossplane.io/v1alpha1 4 4m30s +``` + +{{}} + +Crossplane recommends using a [Patch]({{}}) to +create a unique name for each secret. + +For example, a +{{}}patch{{}} +to add the unique identifier of the resource as the key +name. + +```yaml {label="tipPatch",copy-lines="14-20"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +spec: + # Removed for brevity + resources: + - name: key + base: + apiVersion: iam.aws.upbound.io/v1beta1 + kind: AccessKey + spec: + # Removed for brevity + writeConnectionSecretToRef: + namespace: docs + name: key1 + patches: + - fromFieldPath: "metadata.uid" + toFieldPath: "spec.writeConnectionSecretToRef.name" + transforms: + - type: string + string: + fmt: "%s-secret" +``` +{{< /hint >}} + +#### Define secret keys + + +A Composition must define the specific secret keys a resource creates with +the +{{}}connectionDetails{{}} object. + +{{}} +| Secret Type | Description | +| --- | --- | +| {{}}fromConnectionSecretKey{{}} | Create a secret key matching the key of a secret generated by the resource. | +| {{}}fromFieldPath{{}} | Create a secret key matching a field path of the resource. | +| {{}}value{{}} | Create a secret key with a pre-defined value. | +{{< /table >}} + +{{}} +The {{}}value{{}} type must use a +string value. + +The {{}}value{{}} isn't added to the +individual resource secret object. The +{{}}value{{}} only appears in the +combined composite resource secret. +{{< /hint >}} + +```yaml {label="conDeet",copy-lines="none"} +kind: Composition +spec: + writeConnectionSecretsToNamespace: other-namespace + resources: + - name: key + base: + # Removed for brevity + spec: + forProvider: + # Removed for brevity + writeConnectionSecretToRef: + namespace: docs + name: key1 + connectionDetails: + - name: myUsername + fromConnectionSecretKey: username + - name: myFieldSecret + fromFieldPath: spec.forProvider.user + - name: myStaticSecret + value: "docs.crossplane.io" +``` + +The +{{}}connectionDetails{{}} +in a resource can reference a secret from a resource with +{{}}fromConnectionSecretKey{{}}, +from another field in the resource with +{{}}fromFieldPath{{}} +or a statically defined value with +{{}}value{{}}. + +Crossplane sets the secret key to the +{{}}name{{}} value. + +Describe the secret to view the secret keys inside the secret object. + +{{}} +If more than one resource generates secrets with the same secret key name, +Crossplane only saves one value. + +Use a custom {{}}name{{}} to create +unique secret keys. +{{< /hint >}} + +{{}} +Crossplane only adds connection details listed in the +{{}}connectionDetails{{}} +to the combined secret object. +Any connection secrets in a managed resource, not defined in the +{{}}connectionDetails{{}} +aren't added to the combined secret object. +{{< /hint >}} + + +```shell {copy-lines="1"} +kubectl describe secret +Name: my-access-key-secret +Namespace: default +Labels: +Annotations: + +Type: connection.crossplane.io/v1alpha1 + +Data +==== +myUsername: 20 bytes +myFieldSecret: 24 bytes +myStaticSecret: 18 bytes +``` + +{{}} +The CompositeResourceDefinition can also limit which keys Crossplane stores from +the composite resources. +By default an XRD writes all secret keys listed in the composed resources +`connectionDetails` to the combined secret object. + +Read the +[CompositeResourceDefinition documentation]({{}}) +for more information on restricting secret keys. +{{< /hint >}} + +For more information on connection secrets read the [Connection Secrets +knowledge base article]({{}}). + +{{}} +You can't change the +{{}}connectionDetails{{}} +of a Composition. +You must delete and +recreate the Composition to change the +{{}}connectionDetails{{}} . +{{}} + + +#### Save connection details to an external secret store + +Crossplane +[External Secret Stores]({{}}) +write secrets and connection details to external secret stores like HashiCorp +Vault. + +{{}} +External Secret Stores are an alpha feature. + +They're not recommended for production use. Crossplane disables External Secret +Stores by default. +{{< /hint >}} + +Use +{{}}publishConnectionDetailsWithStoreConfigRef{{}} +in place of +`writeConnectionSecretsToNamespace` to define the +{{}}StoreConfig{{}} +to save connection details to. + +For example, using a +{{}}StoreConfig{{}} with the +{{}}name{{}} "vault," +use +{{}}publishConnectionDetailsWithStoreConfigRef.name{{}} +matching the {{}}StoreConfig.name{{}}, +in this example, "vault." + + +```yaml {label="gcp-storeconfig",copy-lines="none"} +apiVersion: gcp.crossplane.io/v1alpha1 +kind: StoreConfig +metadata: + name: vault +# Removed for brevity. +--- +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +# Removed for Brevity +spec: + publishConnectionDetailsWithStoreConfigRef: + name: vault + resources: + # Removed for brevity +``` + +For more details read the [External Secret Stores]({{}}) +integration guide. + +### Resource readiness checks + +By default Crossplane considers a Composite Resource or Claim as `READY` when +the status of all created resource are `Type: Ready` and `Status: True` + +Some resources, for example, a ProviderConfig, don't have a Kubernetes status +and are never considered `Ready`. + +Custom readiness checks allow Compositions to define what custom conditions +to meet for a resource to be `Ready`. + +{{< hint "tip" >}} +Use multiple readiness checks if a resource must meet multiple conditions for it +to be `Ready`. +{{< /hint >}} + + +Define a custom readiness check with the +{{}}readinessChecks{{}} field on a +resource. + + +Checks have a +{{}}type{{}} defining how to match the +resource and a {{}}fieldPath{{}} of +which field in the resource to compare. + +```yaml {label="check",copy-lines="none"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +# Removed for Brevity +spec: + resources: + # Removed for Brevity + - name: my-resource + base: + # Removed for brevity + readinessChecks: + - type: + fieldPath: +``` + +Compositions support matching resource fields by: + * [string match](#match-a-string) + * [integer match](#match-an-integer) + * [non-empty match](#match-that-a-field-exists) + * [always ready](#always-consider-a-resource-ready) + +#### Match a string + +{{}}MatchString{{}} considers the composed resource to be ready when the value of a +field within that resource matches a specified string. + +{{}} + +Crossplane only supports exact string matches. Substrings and regular +expressions aren't supported in a readiness check. + +{{}} + + +For example, matching the string +{{}}Online{{}} +in the resource's +{{}}status.atProvider.state{{}} +field. + +```yaml {label="matchstring",copy-lines="none"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +# Removed for Brevity +spec: + resources: + # Removed for Brevity + - name: my-resource + base: + # Removed for brevity + readinessChecks: + - type: MatchString + fieldPath: status.atProvider.state + matchString: "Online" +``` + +#### Match an integer + +{{}}MatchInteger{{}} considers the composed resource to be ready when the value of a +field within that resource matches a specified integer. + +{{}} + +Crossplane doesn't support matching `0`. + +{{}} + +For example, matching the number +{{}}4{{}} +in the resource's +{{}}status.atProvider.state{{}} +field. + +```yaml {label="matchint",copy-lines="none"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +# Removed for Brevity +spec: + resources: + # Removed for Brevity + - name: my-resource + base: + # Removed for brevity + readinessChecks: + - type: MatchInteger + fieldPath: status.atProvider.state + matchInteger: 4 +``` + +#### Match that a field exists +{{}}NonEmpty{{}} considers the +composed resource to be ready when a field exists with a value. + +{{}} + +Crossplane considers a value of `0` or an empty string as empty. +{{}} + +For example, to check that a resource's +{{}}status.atProvider.state{{}} +field isn't empty. + + +```yaml {label="NonEmpty",copy-lines="none"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +# Removed for Brevity +spec: + resources: + # Removed for Brevity + - name: my-resource + base: + # Removed for brevity + readinessChecks: + - type: NonEmpty + fieldPath: status.atProvider.state +``` + +{{}} +Checking {{}}NonEmpty{{}} doesn't +require a `match` field. +{{< /hint >}} + +#### Always consider a resource ready +{{}}None{{}} considers the +composed resource to be ready as soon as it's created. Crossplane doesn't wait +for any other conditions before declaring the resource ready. + +For example, consider +{{}}my-resource{{}} +ready as soon as it's created. + + +```yaml {label="none",copy-lines="none"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +# Removed for Brevity +spec: + resources: + # Removed for Brevity + - name: my-resource + base: + # Removed for brevity + readinessChecks: + - type: None +``` + +## Verify a Composition + +View all available Compositions with `kubectl get composition`. + +```shell {copy-lines="1"} +kubectl get composition +NAME XR-KIND XR-APIVERSION AGE +xapps.aws.platformref.upbound.io XApp aws.platformref.upbound.io/v1alpha1 123m +xclusters.aws.platformref.upbound.io XCluster aws.platformref.upbound.io/v1alpha1 123m +xeks.aws.platformref.upbound.io XEKS aws.platformref.upbound.io/v1alpha1 123m +xnetworks.aws.platformref.upbound.io XNetwork aws.platformref.upbound.io/v1alpha1 123m +xservices.aws.platformref.upbound.io XServices aws.platformref.upbound.io/v1alpha1 123m +xsqlinstances.aws.platformref.upbound.io XSQLInstance aws.platformref.upbound.io/v1alpha1 123m +``` + +The `XR-KIND` lists the Composite Resource `kind` that's allowed to use the +Composition template. +The `XR-APIVERSION` lists the Composite Resource API versions allowed to use the +Composition template. + +{{}} +The output of `kubectl get composition` is different than `kubectl get +composite`. + +`kubectl get composition` lists all available Compositions. + +`kubectl get composite` lists all created Composite Resources and their related +Composition. +{{< /hint >}} + +## Composition validation + +When creating a Composition Crossplane automatically validates specific +parameters in the Composition. + +* All resources either use a `name` or don't. Compositions can't use both named + and unnamed resources. +* No duplicate resource names. +* Patch sets must have names. +* Patches that require a `fromFieldPath` value provide it. +* Patches that require a `toFieldPath` value provide it. +* Patches that require a `combine` field provide it. +* Readiness checks using `matchString` aren't empty. +* Readiness checks using `matchInteger` isn't `0`. +* Readiness checks requiring a `fieldPath` value provide it. +* If using composition functions, all resources must have names. +* Composition function container field isn't empty. +* Composition function `type` is `container`. +* Composition function names are unique. + +### Resource schema validation + +Optionally, Crossplane can also validate the schema of the resources defined +inside a Composition. This verifies that the resource `apiVersion` and `kinds` +are valid. + + +Enable "schema validation" with the +`--enable-composition-webhook-schema-validation` flag on the Crossplane pod. +The [Crossplane Pods]({{}}) page has +more information on enabling Crossplane flags. + +{{}} + +Schema validation only checks the `apiVersion` and `kind` are valid. Schema +validation doesn't validate the fields of a specific resource. + +{{< /hint >}} + +The default validations are still checked with schema validation enabled. +#### Validation modes + +Schema validation supports two modes: +* `loose` (default) - Sends an warning for each schema error and installs the + Composition if the default validations pass. +* `strict` - Send an error for every schema validation error and rejects the + Composition. + +Change the validation mode for a Composition with the +{{}}crossplane.io/composition-validation-mode{{}} +annotation. + +For example, to enable `strict` mode checking: + +```yaml {copy-lines="none",label="mode"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +metadata: + annotations: + crossplane.io/composition-validation-mode: strict + # Removed for brevity +spec: + # Removed for brevity +``` diff --git a/content/master/concepts/environment-configs.md b/content/master/concepts/environment-configs.md new file mode 100644 index 000000000..74dc3f13f --- /dev/null +++ b/content/master/concepts/environment-configs.md @@ -0,0 +1,303 @@ +--- +title: Environment Configurations +weight: 90 +state: alpha +alphaVersion: "1.11" +description: "Environment Configurations or EnvironmentConfigs are an in-memory datastore used in patching Compositions" +--- + +A Crossplane EnvironmentConfig is an in-memory data store. Composition +patches can read from and write to an environment. + +Crossplane supports multiple EnvironmentConfigs, each acting as a unique +data store. + +## 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 + + +An {{}}EnvironmentConfig{{}} has a single +object field, +{{}}data{{}}. + +An EnvironmentConfig supports any data inside the +{{}}data{{}} field. + +Here an example +{{}}EnvironmentConfig{{}}. + +```yaml {label="env1"} +apiVersion: apiextensions.crossplane.io/v1alpha1 +kind: EnvironmentConfig +metadata: + name: example-environment +data: + locations: + us: us-east-2 + eu: eu-north-1 + key1: value1 + key2: value2 + key3: + - item1 + - item2 +``` + +## Patching with EnvironmentConfigs + +To patch data from or to an EnvironmentConfig, reference the EnvironmentConfig +inside a Composition's +{{}}environment{{}} field. + +The {{}}environmentConfigs{{}} field is a +list of environments this Composition can use. + +{{}} +Read about EnvironmentConfig patch types in the +[Patch and Transform]({{}}) documentation. +{{< /hint >}} + +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. + +```yaml {label="comp",copy-lines="none"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +metadata: + name: example-composition +spec: + environment: + environmentConfigs: + - type: Reference + ref: + name: example-environment + - type: Selector + selector: + matchLabels: + # 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. +{{}} + +### Select by name + +Select an environment by name with +{{}}type: Reference{{}}. + +Define the +{{}}ref{{}} object and the +{{}}name{{}} matching the exact name of +the environment. + + +For example, select the +{{}}environmentConfig{{}} +named +{{}}example-environment{{}} + +```yaml {label="byName",copy-lines="all"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +metadata: + name: example-composition +spec: + environment: + environmentConfigs: + - type: Reference + ref: + name: example-environment +``` + +### Select by label + +Select an environment by labels with a +{{}}type: Selector{{}}. + +Define the {{}}selector{{}} object. + +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. + +When matching the label's value, provide an exact value with a +{{}}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. + +```yaml {label="byLabel",copy-lines="all"} +apiVersion: apiextensions.crossplane.io/v1 +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 + resources: + # 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 >}} + + +```yaml {label="selectResults"} +apiVersion: apiextensions.crossplane.io/v1 +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 +``` + +When using +{{}}mode: Multiple{{}} limit the +number of returned environments with +{{}}maxMatch{{}} and define the +maximum 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 field to sort by. + + +```yaml {label="maxMatch"} +apiVersion: apiextensions.crossplane.io/v1 +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 +``` + +The environments selected by +{{}}matchLabels{{}} are then merged +into any other environments listed in the +{{}}environmentConfigs{{}}. + diff --git a/content/master/concepts/managed-resources.md b/content/master/concepts/managed-resources.md index 70dcc7ac9..d49fd9349 100644 --- a/content/master/concepts/managed-resources.md +++ b/content/master/concepts/managed-resources.md @@ -1,6 +1,7 @@ --- title: Managed Resources -weight: 102 +weight: 10 +description: "Managed resources are the Crossplane representation of external provider resources" --- A _managed resource_ (`MR`) represents an external service in a Provider. When @@ -21,7 +22,7 @@ Examples of managed resources include: {{< hint "tip" >}} You can create individual managed resources, but Crossplane recommends using -[Compositions]({{}}) and Claims to create +[Compositions]({{}}) and Claims to create managed resources. {{< /hint >}} @@ -166,7 +167,8 @@ spec: Matching by selector is the most flexible matching method. {{}} -The [Composition]({{}}) section covers the + +The [Compositions]({{}}) section covers the `matchControllerRef` selector. {{}} @@ -217,7 +219,7 @@ The managed resource `managementPolicy` option is an alpha feature. Enable the `managementPolicy` in a provider with `--enable-management-policies` in a -[ControllerConfig]({{}}). +[ControllerConfig]({{}}). {{< /hint >}} A `managementPolicy` determines if Crossplane can make changes to managed @@ -246,7 +248,7 @@ information on using the `managementPolicy` to import existing resources. The `providerConfigRef` on a managed resource tells the Provider which -[ProviderConfig]({{}}) to +[ProviderConfig]({{}}) to use when creating the managed resource. Use a ProviderConfig to define the authentication method to use when diff --git a/content/master/concepts/packages.md b/content/master/concepts/packages.md index f7fb1a28f..0e90ae880 100644 --- a/content/master/concepts/packages.md +++ b/content/master/concepts/packages.md @@ -1,7 +1,11 @@ --- title: Crossplane Packages weight: 104 +description: "Packages combine multiple Crossplane resources into a single, portable, OCI image." --- + Crossplane packages are opinionated [OCI images] that contain a stream of YAML that can be parsed by the Crossplane package manager. Crossplane packages come @@ -498,7 +502,7 @@ by [pre-pulling images] onto nodes in the cluster. [configuration-docs]: https://doc.crds.dev/github.com/crossplane/crossplane/meta.pkg.crossplane.io/Configuration/v1 [lock-api]: https://doc.crds.dev/github.com/crossplane/crossplane/pkg.crossplane.io/Lock/v1beta1 [specification]: https://github.com/Masterminds/semver#basic-comparisons -[composition]: {{}} +[composition]: {{}} [IAM Roles for Service Accounts]: https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html [controller-config-docs]: https://doc.crds.dev/github.com/crossplane/crossplane/pkg.crossplane.io/ControllerConfig/v1alpha1 [package format]: https://github.com/crossplane/crossplane/blob/1aa83092172bdf0d2ed64754d33517c612ff7368/design/one-pager-package-format-v2.md diff --git a/content/master/concepts/patch-and-transform.md b/content/master/concepts/patch-and-transform.md new file mode 100644 index 000000000..f8cb4767b --- /dev/null +++ b/content/master/concepts/patch-and-transform.md @@ -0,0 +1,1658 @@ +--- +title: Patch and Transforms +weight: 70 +description: "Crossplane Compositions use patches and transforms to modify inputs from claims and composite resources before creating managed resources" +--- + +Crossplane Compositions allow for "patch and transform" operations. With patches +a Composition can apply changes to the resources defined by the Composition. + +When users create Claims, Crossplane passes the settings in the Claim to +the associated composite resource. Patches can use these settings to change the +associated composite resource or managed resources. + +Examples of using patch and transforms include: + * changing the name of the external resource + * mapping generic terms like "east" or "west" to specific provider locations + * appending custom labels or strings to resource fields + + +{{}} + +Crossplane expects patch and transform operations to be simple changes. +Use [Composition Functions]({{}}) for more +complex or programmatic modifications. + +{{}} + + +A Composition [patch](#create-a-patch) is the action of changing a field. +A Composition [transform](#transform-a-patch) modifies the values before +applying the patch. + +## Create a patch + +Patches are part of an individual +{{}}resource{{}} inside a +{{}}Composition{{}}. + +The {{}}patches{{}} field takes a +list of patches to apply to the individual resource. + +Each patch has a {{}}type{{}}, which +defines what kind of patch action Crossplane applies. + +Patches reference fields inside a composite resource or Composition differently +depending on the patch type, but all patches reference a +{{}}fromFieldPath{{}} and +{{}}toFieldPath{{}}. + +The {{}}fromFieldPath{{}} defines +the patch's input values. +The {{}}toFieldPath{{}} defines the +data to change with a patch. + +Here is an example patch applied to a resource in a Composition. +```yaml {label="createComp",copy-lines="none"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +spec: + resources: + - name: my-composed-resource + base: + # Removed for brevity + patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.field1 + toFieldPath: metadata.labels["patchLabel"] +``` + +### Selecting fields + +Crossplane selects fields in a composite resource or managed +resource with +a subset of +[JSONPath selectors](https://kubernetes.io/docs/reference/kubectl/jsonpath/), +called "field selectors." + +Field selectors can select any field in a composite resource or managed resource +object, including the `metadata`, `spec` or `status` fields. + +Field selectors can be a string matching a field name or an array index, in +brackets. Field names may use a `.` character to select child elements. + + +{{< expand "Example field selectors" >}} +Here are some example selectors from a composite resource object. +{{
}} +| Selector | Selected element | +| --- | --- | +| `kind` | {{}}kind{{}} | +| `metadata.labels['crossplane.io/claim-name']` | {{}}my-example-claim{{}} | +| `spec.desiredRegion` | {{}}eu-north-1{{}} | +| `spec.resourceRefs[0].name` | {{}}my-example-claim-978mh-r6z64{{}} | +{{
}} + +```yaml {label="select",copy-lines="none"} +$ kubectl get composite -o yaml +apiVersion: example.org/v1alpha1 +kind: xExample +metadata: + # Removed for brevity + labels: + crossplane.io/claim-name: my-example-claim + crossplane.io/claim-namespace: default + crossplane.io/composite: my-example-claim-978mh +spec: + desiredRegion: eu-north-1 + field1: field1-text + resourceRefs: + - apiVersion: s3.aws.upbound.io/v1beta1 + kind: Bucket + name: my-example-claim-978mh-r6z64 + - apiVersion: s3.aws.upbound.io/v1beta1 + kind: Bucket + name: my-example-claim-978mh-cnlhj + - apiVersion: s3.aws.upbound.io/v1beta1 + kind: Bucket + name: my-example-claim-978mh-rv5nm + # Removed for brevity +``` +{{< /expand >}} + +## Reuse a patch + +A Composition can reuse a patch object on multiple resources with a +PatchSet. + +To create a PatchSet, define a +{{}}PatchSets{{}} object inside the +Composition's +{{}}spec{{}}. + +Each patch inside a PatchSet has a +{{}}name{{}} and a list of +{{}}patches{{}}. + +{{}} +For multiple PatchSets only use a single +{{}}PatchSets{{}} object. + +Identify each unique PatchSet with a unique +{{}}name{{}}. +{{}} + +Apply the PatchSet to a resource with a patch +{{}}type: PatchSet{{}}. +Set the +{{}}patchSetName{{}} to the +{{}}name{{}} of the PatchSet. + +```yaml {label="patchset"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +# Removed for brevity +spec: + patchSets: + - name: my-patchset + patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.desiredRegion + toFieldPath: spec.forProvider.region + resources: + - name: bucket1 + base: + # Removed for brevity + patches: + - type: PatchSet + patchSetName: my-patchset + - name: bucket2 + base: + # Removed for brevity + patches: + - type: PatchSet + patchSetName: my-patchset +``` + +{{}} +A PatchSet can't contain other PatchSets. + +Crossplane ignores any [transforms](#transform-a-patch) or +[policies](#patch-policies) in a PatchSet. +{{< /hint >}} + +## Patching between resources + +Compositions can't directly patch between resources in the same Composition. +For example, generating a network resource and patching the resource name to +a compute resource. + +{{}} +The [ToEnvironmentFieldPath](#toenvironmentfieldpath) patch can't read from a +`Status` field. +{{< /hint >}} + +A resource can patch to a user-defined +{{}}Status{{}} +field in the composite resource. + +A resource can then read from that +{{}}Status{{}} +field to patch a field. + +First, define a custom +{{}}Status{{}} +in the Composite Resource Definition and a custom field, for example +{{}}secondResource{{}} + +```yaml {label="xrdPatch",copy-lines="13-17"} +kind: CompositeResourceDefinition +# Removed for brevity. +spec: + # Removed for brevity. + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + type: object + properties: + spec: + # Removed for brevity. + status: + type: object + properties: + secondResource: + type: string +``` + +Inside the Composition the resource with the source data uses a +{{}}ToCompositeFieldPath{{}} +patch to write data to the +{{}}status.secondResource{{}} +field in the composite resource. + +The destination resource uses a +{{}}FromCompositeFieldPath{{}} +patch to read data from the composite resource +{{}}status.secondResource{{}} +field in the composite resource and write it to a label named +{{}}secondResource{{}} in the +managed resource. + +```yaml {label="patchBetween",copy-lines="9-11"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +# Removed for brevity + - name: bucket1 + base: + apiVersion: s3.aws.upbound.io/v1beta1 + kind: Bucket + # Removed for brevity + patches: + - type: ToCompositeFieldPath + fromFieldPath: metadata.name + toFieldPath: status.secondResource + - name: bucket2 + base: + apiVersion: s3.aws.upbound.io/v1beta1 + kind: Bucket + # Removed for brevity + patches: + - type: FromCompositeFieldPath + fromFieldPath: status.secondResource + toFieldPath: metadata.labels['secondResource'] +``` + +Describe the composite resource to view the +{{}}resources{{}} and the +{{}}status.secondResource{{}} +value. + +```yaml {label="descCompPatch",copy-lines="none"} +$ kubectl describe composite +Name: my-example-claim-jp7rx +Spec: + # Removed for brevity + Resource Refs: + Name: my-example-claim-jp7rx-gfg4m + # Removed for brevity + Name: my-example-claim-jp7rx-fttpj +Status: + # Removed for brevity + Second Resource: my-example-claim-jp7rx-gfg4m +``` + +Describe the destination managed resource to see the label +{{}}secondResource{{}}. + +```yaml {label="bucketlabel",copy-lines="none"} +$ kubectl describe bucket +kubectl describe bucket my-example-claim-jp7rx-fttpj +Name: my-example-claim-jp7rx-fttpj +Labels: crossplane.io/composite=my-example-claim-jp7rx + secondResource=my-example-claim-jp7rx-gfg4m +``` + +## Types of patches +Crossplane supports multiple patch types, each using a different source for data +and applying the patch to a different location. + +Summary of Crossplane patches +{{< table "table table-hover" >}} +| Patch Type | Data Source | Data Destination | +| --- | --- | --- | +| [FromCompositeFieldPath](#fromcompositefieldpath) | A field in the composite resource. | A field in the patched managed resource. | +| [ToCompositeFieldPath](#tocompositefieldpath) | A field in the patched managed resource. | A field in the composite resource. | +| [CombineFromComposite](#tocompositefieldpath) | Multiple fields in the composite resource. | A field in the patched managed resource. | +| [CombineToComposite](#tocompositefieldpath) | Multiple fields in the patched managed resource. | A field in the composite resource. | +| [FromEnvironmentFieldPath](#tocompositefieldpath) | Data in the in-memory EnvironmentConfig Environment | A field in the patched managed resource. | +| [ToEnvironmentFieldPath](#tocompositefieldpath) | A field in the patched managed resource. | The in-memory EnvironmentConfig Environment. | +| [CombineFromEnvironment](#tocompositefieldpath) | Multiple fields in the in-memory EnvironmentConfig Environment. | A field in the patched managed resource. | +| [CombineToEnvironment](#tocompositefieldpath) | Multiple fields in the patched managed resource. | A field in the in-memory EnvironmentConfig Environment. | +{{< /table >}} + +{{}} +All the following examples use the same set of Compositions, +CompositeResourceDefinitions, Claims and EnvironmentConfigs. +Only the applied patches change between +examples. + +All examples rely on Upbound +[provider-aws-s3](https://marketplace.upbound.io/providers/upbound/provider-aws-s3/) +to create resources. + +{{< expand "Reference Composition" >}} +```yaml {copy-lines="all"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +metadata: + name: example-composition +spec: + compositeTypeRef: + apiVersion: example.org/v1alpha1 + kind: xExample + environment: + environmentConfigs: + - ref: + name: example-environment + resources: + - name: bucket1 + base: + apiVersion: s3.aws.upbound.io/v1beta1 + kind: Bucket + spec: + forProvider: + region: us-east-2 + - name: bucket2 + base: + apiVersion: s3.aws.upbound.io/v1beta1 + kind: Bucket + spec: + forProvider: + region: us-east-2 +``` +{{< /expand >}} + +{{}} +```yaml {copy-lines="all"} +apiVersion: apiextensions.crossplane.io/v1 +kind: CompositeResourceDefinition +metadata: + name: xexamples.example.org +spec: + group: example.org + names: + kind: xExample + plural: xexamples + claimNames: + kind: ExampleClaim + plural: exampleclaims + versions: + - name: v1alpha1 + served: true + referenceable: true + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + properties: + field1: + type: string + field2: + type: string + field3: + type: string + desiredRegion: + type: string + boolField: + type: boolean + numberField: + type: integer + status: + type: object + properties: + url: + type: string +``` +{{< /expand >}} + + +{{< expand "Reference Claim" >}} +```yaml {copy-lines="all"} +apiVersion: example.org/v1alpha1 +kind: ExampleClaim +metadata: + name: my-example-claim +spec: + field1: "field1-text" + field2: "field2-text" + desiredRegion: "eu-north-1" + boolField: false + numberField: 10 +``` +{{< /expand >}} + +{{< expand "Reference EnvironmentConfig">}} +```yaml {copy-lines="all"} +apiVersion: apiextensions.crossplane.io/v1alpha1 +kind: EnvironmentConfig +metadata: + name: example-environment +data: + locations: + us: us-east-2 + eu: eu-north-1 + key1: value1 + key2: value2 + +``` +{{< /expand >}} +{{< /hint >}} + + +### FromCompositeFieldPath + + +The +{{}}FromCompositeFieldPath{{}} +patch takes a value in a composite resource and applies it to a field in the +managed resource. + +{{< hint "tip" >}} +Use the +{{}}FromCompositeFieldPath{{}} +patch to apply options from users in their Claims to settings in managed +resource `forProvider` settings. +{{< /hint >}} + +For example, to use the value +{{}}desiredRegion{{}} provided by +a user in a composite resource to a managed resource's +{{}}region{{}}. + +The {{}}fromFieldPath{{}} value +is a field in the composite resource. + +The {{}}toFieldPath{{}} value is +the field in the managed resource to change. + +```yaml {label="fromComposite",copy-lines="9-11"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +# Removed for brevity + - name: bucket1 + base: + apiVersion: s3.aws.upbound.io/v1beta1 + kind: Bucket + spec: + forProvider: + region: us-east-2 + patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.desiredRegion + toFieldPath: spec.forProvider.region +``` + +View the managed resource to see the updated +{{}}region{{}} + +```yaml {label="fromCompMR",copy-lines="1"} +$ kubectl describe bucket +Name: my-example-claim-qlr68-29nqf +# Removed for brevity +Spec: + For Provider: + Region: eu-north-1 +``` + + +### ToCompositeFieldPath + + +The +{{}}ToCompositeFieldPath{{}} writes +data from an individual managed resource to +the composite resource that created it. + +{{< hint "tip" >}} +Use {{}}ToCompositeFieldPath{{}} +patches to take data from one managed resource in a Composition and use it in a +second managed resource in the same Composition. +{{< /hint >}} + +For example, after Crossplane creates a new managed resource, take the value +{{}}hostedZoneID{{}} and apply it +as a +{{}}label{{}} in the composite +resource. + +```yaml {label="toComposite",copy-lines="9-11"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +# Removed for brevity + - name: bucket1 + base: + apiVersion: s3.aws.upbound.io/v1beta1 + kind: Bucket + spec: + forProvider: + region: us-east-2 + patches: + - type: ToCompositeFieldPath + fromFieldPath: status.atProvider.hostedZoneId + toFieldPath: metadata.labels['ZoneID'] +``` + +View the created managed resource to see the +{{}}Hosted Zone Id{{}} field. +```yaml {label="toCompMR",copy-lines="none"} +$ kubectl describe bucket +Name: my-example-claim-p5pxf-5vnp8 +# Removed for brevity +Status: + At Provider: + Hosted Zone Id: Z2O1EMRO9K5GLX + # Removed for brevity +``` + +Next view the composite resource and confirm the patch applied the +{{}}label{{}} +```yaml {label="toCompositeXR",copy-lines="none"} +$ kubectl describe composite +Name: my-example-claim-p5pxf +Labels: ZoneID=Z2O1EMRO9K5GLX +``` + +{{}} +Crossplane doesn't apply the patch to the composite resource until the next +reconcile loop, after creating the managed resource. This creates a delay +between a managed resource being Ready and applying the patch. +{{< /hint >}} + + + +### CombineFromComposite + + +The +{{}}CombineFromComposite{{}} +patch takes values from the composite resource, combines them and applies them +to the managed resource. + +{{< hint "tip" >}} +Use the +{{}}CombineFromComposite{{}} +patch to create complex strings, like security policies and apply them to +a managed resource. +{{< /hint >}} + +For example, use the Claim value +{{}}desiredRegion{{}} and +{{}}field2{{}} to generate the +managed resource's +{{}}name{{}} + +The +{{}}CombineFromComposite{{}} +patch only supports the +{{}}combine{{}} option. + +The {{}}variables{{}} are the +list of +{{}}fromFieldPath{{}} values +from the composite resource to combine. + +The only supported +{{}}strategy{{}} is +{{}}strategy: string{{}}. + +Optionally you can apply a +{{}}string.fmt{{}}, based on +[Go string formatting](https://pkg.go.dev/fmt) to specify how to combine the +strings. + +The {{}}toFieldPath{{}} is the +field in the managed resource to apply the new string to. + +```yaml {label="combineFromComp",copy-lines="11-20"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +# Removed for brevity + - name: bucket1 + base: + apiVersion: s3.aws.upbound.io/v1beta1 + kind: Bucket + spec: + forProvider: + region: us-east-2 + patches: + - type: CombineFromComposite + combine: + variables: + - fromFieldPath: spec.desiredRegion + - fromFieldPath: spec.field2 + strategy: string + string: + fmt: "my-resource-%s-%s" + toFieldPath: metadata.name +``` + +Describe the managed resource to see the applied patch. + +```yaml {label="describeCombineFromComp",copy-lines="none"} +$ kubectl describe bucket +Name: my-resource-eu-north-1-field2-text +``` + + +### CombineToComposite + + +The +{{}}CombineToComposite{{}} +patch takes values from the managed resource, combines them and applies them +to the composite resource. + +{{}} +Use {{}}CombineToComposite{{}} +patches to create a single field like a URL from multiple fields in a managed +resource. +{{< /hint >}} + +For example, use the managed resource +{{}}name{{}} and +{{}}region{{}} to generate a +custom +{{}}url{{}} +field. + +{{< hint "important" >}} +Writing custom fields in the Status field of a composite resource requires +defining the custom fields in the CompositeResourceDefinition first. + +{{< /hint >}} + +The +{{}}CombineToComposite{{}} +patch only supports the +{{}}combine{{}} option. + +The {{}}variables{{}} are the +list of +{{}}fromFieldPath{{}} +the managed resource to combine. + +The only supported +{{}}strategy{{}} is +{{}}strategy: string{{}}. + +Optionally you can apply a +{{}}string.fmt{{}}, based on +[Go string formatting](https://pkg.go.dev/fmt) to specify how to combine the +strings. + +The {{}}toFieldPath{{}} is the +field in the composite resource to apply the new string to. + +```yaml {label="combineToComposite",copy-lines="9-11"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +# Removed for brevity + - name: bucket1 + base: + apiVersion: s3.aws.upbound.io/v1beta1 + kind: Bucket + spec: + forProvider: + region: us-east-2 + patches: + - type: CombineToComposite + combine: + variables: + - fromFieldPath: metadata.name + - fromFieldPath: spec.forProvider.region + strategy: string + string: + fmt: "https://%s.%s.com" + toFieldPath: status.url +``` + +View the composite resource to verify the applied patch. + +```yaml {copy-lines="none"} +$ kubectl describe composite +Name: my-example-claim-bjdjw +API Version: example.org/v1alpha1 +Kind: xExample +# Removed for brevity +Status: + # Removed for brevity + URL: https://my-example-claim-bjdjw-r6ncd.us-east-2.com +``` + + +### FromEnvironmentFieldPath + + +{{}} +EnvironmentConfigs are an alpha feature. They aren't enabled by default. + +For more information about using an EnvironmentConfig, read the [Environment +Configs]({{}}) documentation. +{{< /hint >}} + +The +{{}}FromEnvironmentFieldPath{{}} +patch takes values from the in-memory EnvironmentConfig environment and applies +them to the managed resource. + +{{}} +Use +{{}}FromEnvironmentFieldPath{{}} +to apply custom managed resource settings based on the current environment. +{{< /hint >}} + +For example, use the environment's +{{}}locations.eu{{}} value and +apply it as the +{{}}region{{}}. + +```yaml {label="fromEnvField",copy-lines="9-11"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +# Removed for brevity + - name: bucket1 + base: + apiVersion: s3.aws.upbound.io/v1beta1 + kind: Bucket + spec: + forProvider: + region: us-east-2 + patches: + - type: FromEnvironmentFieldPath + fromFieldPath: locations.eu + toFieldPath: spec.forProvider.region +``` + +Verify managed resource to confirm the applied patch. + +```yaml {copy-lines="none"} +kubectl describe bucket +Name: my-example-claim-8vrvc-xx5sr +Labels: crossplane.io/claim-name=my-example-claim +# Removed for brevity +Spec: + For Provider: + Region: eu-north-1 + # Removed for brevity +``` + + +### ToEnvironmentFieldPath + + +{{}} +EnvironmentConfigs are an alpha feature. They aren't enabled by default. + +For more information about using an EnvironmentConfig, read the [Environment +Configs]({{}}) documentation. +{{< /hint >}} + +The +{{}}ToEnvironmentFieldPath{{}} +patch takes values the managed resource and applies them to the in-memory +EnvironmentConfig environment. + +{{}} +Use +{{}}ToEnvironmentFieldPath{{}} +write data to the environment that any FromEnvironmentFieldPath +patch can access. +{{< /hint >}} + +For example, use the desired +{{}}region{{}} value and +apply it as the environment's +{{}}key1{{}}. + +{{< hint "important" >}} +The environment's key must already exist. Patches can't create new environment +keys. +{{< /hint >}} + +```yaml {label="toEnvField",copy-lines="9-11"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +# Removed for brevity + - name: bucket1 + base: + apiVersion: s3.aws.upbound.io/v1beta1 + kind: Bucket + spec: + forProvider: + region: us-east-2 + patches: + - type: ToEnvironmentFieldPath + fromFieldPath: spec.forProvider.region + toFieldPath: key1 +``` + +Because the environment is in-memory, there is no command to confirm the patch +wrote the value to the environment. + +{{}} +The +{{}}ToEnvironmentFieldPath{{}} +patch happens **before** creating a resource. +The +{{}}fromFieldPath{{}} can't +read from the `atProvider` or `Status` fields. +{{< /hint >}} + + + +### CombineFromEnvironment + + +{{}} +EnvironmentConfigs are an alpha feature. They aren't enabled by default. + +For more information about using an EnvironmentConfig, read the [Environment +Configs]({{}}) documentation. +{{< /hint >}} + +The +{{}}CombineFromEnvironment{{}} +patch combines multiple values from the in-memory EnvironmentConfig environment and applies +them to the managed resource. + +{{}} +Use +{{}}CombineFromEnvironment{{}} +patch to create complex strings, like security policies and apply them to +a managed resource. +{{< /hint >}} + +For example, combine multiple fields in the environment to create a unique +{{}}annotation{{}} +. + +The +{{}}CombineFromEnvironment{{}} +patch only supports the +{{}}combine{{}} option. + +The only supported +{{}}strategy{{}} is +{{}}strategy: string{{}}. + +The {{}}variables{{}} are the +list of +{{}}fromFieldPath{{}} values +from the in-memory environment to combine. + +Optionally you can apply a +{{}}string.fmt{{}}, based on +[Go string formatting](https://pkg.go.dev/fmt) to specify how to combine the +strings. + +The {{}}toFieldPath{{}} is the +field in the managed resource to apply the new string to. + + +```yaml {label="combineFromEnv",copy-lines="11-20"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +# Removed for brevity + - name: bucket1 + base: + apiVersion: s3.aws.upbound.io/v1beta1 + kind: Bucket + spec: + forProvider: + region: us-east-2 + patches: + - type: CombineFromEnvironment + combine: + strategy: string + variables: + - fromFieldPath: key1 + - fromFieldPath: key2 + string: + fmt: "%s-%s" + toFieldPath: metadata.annotations[EnvironmentPatch] +``` + +Describe the managed resource to see new +{{}}annotation{{}}. + +```yaml {copy-lines="none",label="combineFromEnvDesc"} +$ kubectl describe bucket +Name: my-example-claim-zmxdg-grl6p +# Removed for brevity +Annotations: EnvironmentPatch: value1-value2 +# Removed for brevity +``` + + +### CombineToEnvironment + + +{{}} +EnvironmentConfigs are an alpha feature. They aren't enabled by default. + +For more information about using an EnvironmentConfig, read the [Environment +Configs]({{}}) documentation. +{{< /hint >}} + +The +{{}}CombineToEnvironment{{}} +patch combines multiple values from the managed resource and applies them to the in-memory EnvironmentConfig environment. + +{{}} +Use +{{}}CombineToEnvironment{{}} +patch to create complex strings, like security policies to use in other managed resources. +{{< /hint >}} + +For example, combine multiple fields in the managed resource to create a unique +string and store it in the environment's +{{}}key2{{}} value. + +The string combines the +managed resource +{{}}Kind{{}} and +{{}}region{{}}. + +The +{{}}CombineToEnvironment{{}} +patch only supports the +{{}}combine{{}} option. + +The only supported +{{}}strategy{{}} is +{{}}strategy: string{{}}. + +The {{}}variables{{}} are the +list of +{{}}fromFieldPath{{}} +values in the managed resource to combine. + +Optionally you can apply a +{{}}string.fmt{{}}, based on +[Go string formatting](https://pkg.go.dev/fmt) to specify how to combine the +strings. + +The {{}}toFieldPath{{}} is the +key in the environment to write the new string to. + +{{< hint "important" >}} +The environment's key must already exist. Patches can't create new environment +keys. +{{< /hint >}} + +```yaml {label="combineToEnv",copy-lines="none"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +# Removed for brevity + - name: bucket1 + base: + apiVersion: s3.aws.upbound.io/v1beta1 + kind: Bucket + spec: + forProvider: + region: us-east-2 + patches: + - type: CombineToEnvironment + combine: + strategy: string + variables: + - fromFieldPath: kind + - fromFieldPath: spec.forProvider.region + string: + fmt: "%s.%s" + toFieldPath: key2 +``` + +Because the environment is in-memory, there is no command to confirm the patch +wrote the value to the environment. + +## Transform a patch + +When applying a patch, Crossplane supports modifying the data before applying it +as a patch. Crossplane calls this a "transform" operation. + +Summary of Crossplane transforms. +{{< table "table table-hover" >}} +| Transform Type | Action | +| --- | --- | +| [convert](#convert-transforms) | Converts an input data type to a different type. Also called "casting." | +| [map](#map-transforms) | Selects a specific output based on a specific input. | +| [match](#match-transform) | Selects a specific output based on a string or regular expression. | +| [math](#math-transforms) | Applies a mathematical operation on the input. | +| [string](#string-transforms) | Change the input string using [Go string formatting](https://pkg.go.dev/fmt). | +{{< /table >}} + +Apply a transform directly to an individual patch with the +{{}}transforms{{}} field. + +A +{{}}transform{{}} +requires a +{{}}type{{}}, indicating the +transform action to take. + +The other transform field is the same as the +{{}}type{{}}, in this example, +{{}}map{{}}. + +The other fields depend on the patch type used. + +This example uses a +{{}}type: map{{}} transform, taking +the input +{{}}spec.desiredRegion{{}}, matching +it to either +{{}}us{{}} or +{{}}eu{{}} and returning the +corresponding AWS region for the +{{}}spec.forProvider.region{{}} +value. + +```yaml {label="transform1",copy-lines="none"} +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +# Removed for brevity + - name: bucket1 + base: + apiVersion: s3.aws.upbound.io/v1beta1 + kind: Bucket + spec: + forProvider: + region: us-east-2 + patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.desiredRegion + toFieldPath: spec.forProvider.region + transforms: + - type: map + map: + us: us-east-2 + eu: eu-north-1 +``` + +### Convert transforms + +The {{}}convert{{}} transform type +changes the input data type to a different data type. + +{{< hint "tip" >}} +Some provider APIs require a field to be a string. Use a +{{}}convert{{}} type to +change any boolean or integer fields to strings. +{{< /hint >}} + +A {{}}convert{{}} +transform requires a {{}}toType{{}}, +defining the output data type. + +```yaml {label="convert",copy-lines="none"} +patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.numberField + toFieldPath: metadata.label["numberToString"] + transforms: + - type: convert + convert: + toType: string +``` + +Supported `toType` values: +{{< table "table table-sm table-hover" >}} +| `toType` value | Description | +| -- | -- | +| `bool` | A boolean value of `true` or `false`. | +| `float64` | A 64-bit float value. | +| `int` | A 32-bit integer value. | +| `int64` | A 64-bit integer value. | +| `string` | A string value. | +{{< /table >}} + +#### Converting strings to booleans +When converting from a string to a `bool` Crossplane considers the string values +`1`, `t`, `T`, `TRUE`, `True` and `true` +equal to the boolean value `True`. + +The strings +`0`, `f`, `F`, `FALSE`, `False` and `false` +are equal to the boolean value `False`. + +#### Converting numbers to booleans +Crossplane considers the integer `1` and float `1.0` equal to the boolean +value `True`. +Any other integer or float value is `False`. + +#### Converting booleans to numbers +Crossplane converts the boolean value `True` to the integer `1` or float64 +`1.0`. + +The value `False` converts to the integer `0` or float64 `0.0` + +#### Converting strings to float64 +When converting from a `string` to a +{{}}float64{{}} Crossplane supports +an optional +{{}}format: quantity{{}} field. + +Using {{}}format: quantity{{}} translates +size suffixes like `M` for megabyte or `Mi` for megabit into the correct float64 +value. + +{{}} +Refer to the [Go language docs](https://pkg.go.dev/k8s.io/apimachinery/pkg/api/resource#Quantity) +for a full list of supported suffixes. +{{}} + +Add {{}}format: quantity{{}} to the +{{}}convert{{}} object to enable quantity +suffix support. + +```yaml {label="format",copy-lines="all"} +- type: convert + convert: + toType: float64 + format: quantity +``` + +### Map transforms +The {{}}map{{}} transform type +_maps_ an input value to an output value. + +{{< hint "tip" >}} +The {{}}map{{}} transform is useful for +translating generic region names like `US` or `EU` to provider specific region +names. +{{< /hint >}} + +The {{}}map{{}} transform compares the value +from the {{}}fromFieldPath{{}} to the +options listed in the {{}}map{{}}. + +If Crossplane finds the value, Crossplane puts +the mapped value in the {{}}toFieldPath{{}}. + +{{}} +Crossplane ignores the patch if the value isn't found. +{{< /hint >}} + +{{}}spec.field1{{}} is the string +{{}}"field1-text"{{}} then Crossplane uses +the string +{{}}firstField{{}} for the +{{}}annotation{{}}. + +If +{{}}spec.field1{{}} is the string +{{}}"field2-text"{{}} then Crossplane uses +the string +{{}}secondField{{}} for the +{{}}annotation{{}}. + +```yaml {label="map",copy-lines="none"} +patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.field1 + toFieldPath: metadata.annotations["myAnnotation"] + transforms: + - type: map + map: + "field1-text": "firstField" + "field2-text": "secondField" +``` +In this example, the value of +{{}}spec.field1{{}} is +{{}}field1-text{{}}. + +```yaml {label="comositeMap",copy-lines="none"} +$ kubectl describe composite +Name: my-example-claim-twx7n +Spec: + # Removed for brevity + field1: field1-text +``` + +The annotation applied to the managed resource is +{{}}firstField{{}}. + +```yaml {label="mrMap",copy-lines="none"} +$ kubectl describe bucket +Name: my-example-claim-twx7n-ndb2f +Annotations: crossplane.io/composition-resource-name: bucket1 + myLabel: firstField +# Removed for brevity. +``` + +### Match transform +The {{}}match{{}} transform is like the +`map` transform. + +The {{}}match{{}} +transform adds support for regular expressions along with exact +strings and can provide default values if there isn't a match. + +A {{}}match{{}} object requires a +{{}}patterns{{}} object. + +The {{}}patterns{{}} is a list of one or +more patterns to attempt to match the input value against. + +```yaml {label="match",copy-lines="1-8"} +patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.field1 + toFieldPath: metadata.annotations["myAnnotation"] + transforms: + - type: match + match: + patterns: + - type: literal + # Removed for brevity + - type: regexp + # Removed for brevity +``` + +Match {{}}patterns{{}} can be either +{{}}type: literal{{}} to match an +exact string or +{{}}type: regexp{{}} to match a +regular expression. + +{{}} +Crossplane stops processing matches after the first pattern match. +{{< /hint >}} + +#### Match an exact string +Use a {{}}pattern{{}} with +{{}}type: literal{{}} to match an +exact string. + +On a successful match Crossplane provides the +{{}}result:{{}} to +the patch {{}}toFieldPath{{}}. + +```yaml {label="matchLiteral"} +patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.field1 + toFieldPath: metadata.annotations["myAnnotation"] + transforms: + - type: match + match: + patterns: + - type: literal + literal: "field1-text" + result: "matchedLiteral" +``` + +#### Match a regular expression +Use a {{}}pattern{{}} with +{{}}type: regexp{{}} to match a regular +expression. +Define a +{{}}regexp{{}} key with the value of the +regular expression to match. + +On a successful match Crossplane provides the +{{}}result:{{}} to +the patch {{}}toFieldPath{{}}. + +```yaml {label="matchRegex"} +patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.field1 + toFieldPath: metadata.annotations["myAnnotation"] + transforms: + - type: match + match: + patterns: + - type: regexp + regexp: '^field1.*' + result: "foundField1" +``` + +#### Using default values + +Optionally you can provide a default value to use if there is no matching +pattern. + +The default value can either be the original input value or a defined default +value. + +Use +{{}}fallbackTo: Value{{}} to +provide a default value if a match isn't found. + +For example if the string +{{}}unknownString{{}} isn't +matched, Crossplane provides the +{{}}Value{{}} +{{}}StringNotFound{{}} to the +{{}}toFieldPath{{}} + + +```yaml {label="defaultValue"} +patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.field1 + toFieldPath: metadata.annotations["myAnnotation"] + transforms: + - type: match + match: + patterns: + - type: literal + literal: "UnknownString" + result: "foundField1" + fallbackTo: Value + fallbackValue: "StringNotFound" +``` + +To use the original input as the fallback value use +{{}}fallbackTo: Input{{}}. + +Crossplane uses the original +{{}}fromFieldPath{{}} input for the +{{}}toFieldPath{{}} value. +```yaml {label="defaultInput"} +patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.field1 + toFieldPath: metadata.annotations["myAnnotation"] + transforms: + - type: match + match: + patterns: + - type: literal + literal: "UnknownString" + result: "foundField1" + fallbackTo: Input +``` + +### Math transforms + +Use the {{}}math{{}} transform to multiply +an input or apply a minimum or maximum value. + +{{}} +A {{}}math{{}} transform only supports +integer inputs. +{{< /hint >}} + +```yaml {label="math",copy-lines="1-7"} +patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.numberField + toFieldPath: metadata.annotations["mathAnnotation"] + transforms: + - type: math + math: + ... +``` + + +#### clampMin + + +The {{}}type: clampMin{{}} uses a defined +minimum value if an input is larger than the +{{}}type: clampMin{{}} value. + +For example, this +{{}}type: clampMin{{}} requires an +input to be greater than +{{}}20{{}}. + +If an input is lower than +{{}}20{{}}, Crossplane uses the +{{}}clampMin{{}} value for the +{{}}toFieldPath{{}}. + +```yaml {label="clampMin"} +patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.numberField + toFieldPath: metadata.annotations["mathAnnotation"] + transforms: + - type: math + math: + type: clampMin + clampMin: 20 +``` + + +#### clampMax + + +The {{}}type: clampMax{{}} uses a defined +minimum value if an input is larger than the +{{}}type: clampMax{{}} value. + +For example, this +{{}}type: clampMax{{}} requires an +input to be less than +{{}}5{{}}. + +If an input is higher than +{{}}5{{}}, Crossplane uses the +{{}}clampMax{{}} value for the +{{}}toFieldPath{{}}. + +```yaml {label="clampMax"} +patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.numberField + toFieldPath: metadata.annotations["mathAnnotation"] + transforms: + - type: math + math: + type: clampMax + clampMax: 5 +``` + + +#### Multiply + + +The {{}}type: multiply{{}} multiplies +the input by the {{}}multiply{{}} +value. + +For example, this +{{}}type: multiply{{}} multiplies the +value from the {{}}fromFieldPath{{}} +value by {{}}2{{}} + + +```yaml {label="multiply"} +patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.numberField + toFieldPath: metadata.annotations["mathAnnotation"] + transforms: + - type: math + math: + type: multiply + multiply: 2 +``` + +{{}} +The {{}}multiply{{}} value only +supports integers. +{{< /hint >}} + +### String transforms + +The {{}}string{{}} transform applies +string formatting or manipulation to string inputs. + +```yaml {label="string"} +patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.field1 + toFieldPath: metadata.annotations["stringAnnotation"] + transforms: + - type: string + string: + type: ... +``` + +String transforms support the following +{{}}types{{}} + +* [Convert](#string-convert) +* [Format](#string-format) +* [Regexp](#regular-expression-type) +* [TrimPrefix](#trim-prefix) +* [TrimSuffix](#trim-suffix) + +#### String convert + +The {{}}type: convert{{}} +converts the input based on one of the following conversion types: +* `ToUpper` - Change the string to all upper case letters. +* `ToLower` - Change the string to all lower case letters. +* `ToBase64` - Create a new base64 string from the input. +* `FromBase64` - Create a new text string from a base64 input. +* `ToJson` - Convert the input string to valid JSON. +* `ToSha1` - Create a SHA-1 hash of the input string. +* `ToSha256` - Create a SHA-256 hash of the input string. +* `ToSha512` - Create a SHA-512 hash of the input string. + +```yaml {label="stringConvert"} +patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.field1 + toFieldPath: metadata.annotations["FIELD1-TEXT"] + transforms: + - type: string + string: + type: Convert + convert: "ToUpper" +``` + +#### String format +The {{}}type: format{{}} +applies [Go string formatting](https://pkg.go.dev/fmt) to the input. + +```yaml {label="typeFormat"} +patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.field1 + toFieldPath: metadata.annotations["stringAnnotation"] + transforms: + - type: string + string: + type: Format + format: + fmt: "the-field-%s" +``` + +#### Regular expression type +The {{}}type: Regexp{{}} extracts +the part of the input matching a regular expression. + +Optionally use a +{{}}group{{}} to match a regular +expression capture group. +By default Crossplane matches the entire regular expression. + +```yaml {label="typeRegex"} +patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.desiredRegion + toFieldPath: metadata.annotations["euRegion"] + transforms: + - type: string + string: + type: Regexp + regexp: + match: '^eu-(.*)-' + group: 1 +``` + +#### Trim prefix + +The {{}}type: TrimPrefix{{}} removes +the matching string and all preceding characters. + +```yaml {label="typeRegex"} +patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.desiredRegion + toFieldPath: metadata.annotations["north-1"] + transforms: + - type: string + string: + type: TrimPrefix + trim: `eu- +``` + +#### Trim suffix + +The {{}}type: TrimSuffix{{}} removes +the matching string and all proceeding characters. + +```yaml {label="typeRegex"} +patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.desiredRegion + toFieldPath: metadata.annotations["eu"] + transforms: + - type: string + string: + type: TrimSuffix + trim: `-north-1' +``` + +## Patch policies + +Crossplane supports two types of patch policies: +* `fromFieldPath` +* `mergeOptions` + + +### fromFieldPath policy + + +Using a `fromFieldPath: Required` policy on a patch requires the +`fromFieldPath` to exist in the composite resource. + +{{}} +If a resource patch isn't working applying the `fromFieldPath: Required` policy +may produce an error in the composite resource to help troubleshoot. +{{< /hint >}} + +By default, Crossplane applies the policy `fromFieldPath: Optional`. With +`fromFieldPath: Optional` Crossplane +ignores a patch if the `fromFieldPath` doesn't exist. + +With +{{}}fromFieldPath: Required{{}} +the composite resource produces an error if the +{{}}fromFieldPath{{}} doesn't exist. + +```yaml {label="required"} +patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.desiredRegion + toFieldPath: metadata.annotations["eu"] + policy: + fromFieldPath: Required +``` + +### Merge options + +By default when applying a patch the destination data is overridden. Use +{{}}mergeOptions{{}} to allow patches to +merge arrays and objects without overwriting them. + +With an array input, use +{{}}appendSlice: true{{}} to append the +array data to the end of the existing array. + +With an object, use +{{}}keepMapValues: true{{}} to leave +existing object keys in tact. The patch updates any matching keys between the +input and destination data. + +```yaml {label="merge"} +patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.desiredRegion + toFieldPath: metadata.annotations["eu"] + policy: + mergeOptions: + appendSlice: true + keepMapValues: true +``` diff --git a/content/master/concepts/providers.md b/content/master/concepts/providers.md index ea8f01944..df8654c15 100644 --- a/content/master/concepts/providers.md +++ b/content/master/concepts/providers.md @@ -1,6 +1,7 @@ --- title: Providers -weight: 101 +weight: 5 +description: "Providers connect Crossplane to external APIs" --- Providers enable Crossplane to provision infrastructure on an diff --git a/content/master/concepts/terminology.md b/content/master/concepts/terminology.md deleted file mode 100644 index 7b98f6410..000000000 --- a/content/master/concepts/terminology.md +++ /dev/null @@ -1,211 +0,0 @@ ---- -title: Terminology -weight: 110 ---- -## A Note on Style - -Each type of Kubernetes resource has a ‘Pascal case’ name - i.e. a title case -name with no spaces between each word. Examples include ‘DaemonSet’ and -‘PersistentVolumeClaim’. Often these names are written using fixed width fonts -to draw attention to the fact that they’re a concrete type of resource within -the API - e.g. `PersistentVolumeClaim`. - -Crossplane follows this convention. We often use names like RDSInstance or -CompositeResourceDefinition when discussing Crossplane types. Crossplane also -has “classes of types” - i.e. concepts that aren’t a distinct type of API -resource, but rather describe a group of conceptually similar types. For example -there is no ManagedResource type in Crossplane - instead types like RDSInstance -and GKECluster are said to be “a managed resource”. - -Use your discretion as to whether you use pascal case when writing about a -distinct type - e.g. “RDS Instance” and “RDSInstance” are both fine. The pascal -case form makes more sense in contexts like documentation where you’re referring -to Crossplane’s RDSInstance managed resource rather than the general concept of -“an RDS instance”. Avoid using Pascal case when talking about classes of types - -i.e. always write “managed resource”, not “ManagedResource”. Each of the below -terms clarify whether they correspond to a single type, or a class of types. - -### Why 'X'? - -You may notice that Crossplane uses “X” as shorthand for “Crossplane” and/or -“Composite”. This is because some of our concepts - specifically Composite -Resources (XRs) and Composite Resource Definitions (XRDs) are modelled on -similar Kubernetes concepts - Custom Resources (CRs) and Custom Resource -Definitions (CRDs). We chose to abbreviate to (e.g.) XRD instead of CRD to avoid -confusion. - -## Crossplane Terms - -The below terms are commonly used in the Crossplane ecosystem. - -### Composition - -The term Composition has two related but distinct meanings. - -“Composition” refers broadly to the feature of Crossplane that allows teams to -define their own opinionated platform APIs. - -“A Composition” or `Composition` (fixed width) refers to the key Crossplane API -type that configures how Crossplane should compose resources into a higher level -“composite resource”. A Composition tells Crossplane “when someone creates -composite resource X, you should respond by creating resources Y and Z”. - -The latter use of Composition represents a distinct Crossplane API type so -Pascal case and fixed width fonts are appropriate. We also tend to capitalise -the former use, representing the feature in general, but fixed width fonts are -not appropriate in that context. - -> Folks accustomed to Terraform might think of a Composition as a Terraform -> module; the HCL code that describes how to take input variables and use them -> to create resources in some cloud API. Folks accustomed to Helm might think of -> a Composition as a Helm chart’s templates; the moustache templated YAML files -> that describe how to take Helm chart values and render Kubernetes resources. - -### Composite Resource - -A “Composite Resource” or “XR” is an API type defined using Crossplane. A -composite resource’s API type is arbitrary - dictated by the concept the author -wishes to expose as an API, for example an “AcmeCoDB”. A common convention is -for types to start with "X" - e.g. "XAcmeCoDB". - -We talk about Crossplane being a tool teams can use to define their own -opinionated platform APIs. Those APIs are made up of composite resources; when -you are interacting with an API that your platform team has defined, you’re -interacting with composite resources. - -A composite resource can be thought of as the interface to a Composition. It -provides the inputs a Composition uses to compose resources into a higher level -concept. In fact, the composite resource _is_ the high level concept. - -The term “Composite Resource” refers to a class of types, so avoid using Pascal -case - “Composite Resource” not CompositeResource. Use pascal case when -referring to a distinct type of composite resource - e.g. a XAcmeCoDB. - -> Folks accustomed to Terraform might think of a composite resource as a -> `tfvars` file that supplies values for the variables a Terraform module uses -> to create resources in some cloud API. Folks accustomed to Helm might think of -> a composite resource as the `values.yaml` file that supplies inputs to a Helm -> chart’s templates. - -### Composite Resource Claim - -A “Composite Resource Claim”, “XRC”, or just “a claim” is also an API type -defined using Crossplane. Each type of claim corresponds to a type of composite -resource, and the pair have nearly identical schemas. Like composite resources, -the type of a claim is arbitrary. - -We talk about Crossplane being a tool platform teams can use to offer -opinionated platform APIs to the application teams they support. The platform -team offers those APIs using claims. It helps to think of the claim as an -application team’s interface to a composite resource. You could also think of -claims as the public (app team) facing part of the opinionated platform API, -while composite resources are the private (platform team) facing part. - -A common convention is for a claim to be of the same type as its corresponding -composite resource, but without the "X" prefix. So an "AcmeCoDB" would be a type -of claim, and a "XAcmeCoDB" would be the corresponding type of composite -resource. This allows claim consumers to be relatively ignorant of Crossplane -and composition, and to instead simply think about managing “an AcmeCo DB” while -the platform team worries about the implementation details. - -The term “Composite Resource Claim” refers to a class of types, so avoid using -Pascal case - “Composite Resource Claim” not CompositeResourceClaim. Use Pascal -case when referring to a distinct type of composite resource claim - e.g. an -AcmeCoDB. - -> Claims map to the same concepts as described above under the composite -> resource heading; i.e. `tfvars` files and Helm `values.yaml` files. Imagine -> that some `tfvars` files and some `values.yaml` files were only accessible to -> the platform team while others were offered to application teams; that’s the -> difference between a composite resource and a claim. - -### Composite Resource Definition - -A “Composite Resource Definition” or “XRD” is the API type used to define new -types of composite resources and claims. Types of composite resources and types -of claims exist because they were defined into existence by an XRD. The XRD -configures Crossplane with support for the composite resources and claims that -make up a platform API. - -XRDs are often conflated with composite resources (XRs) - try to avoid this. -When someone uses the platform API to create infrastructure they’re not creating -XRDs but rather creating composite resources (XRs). It may help to think of a -composite resource as a database entry, while an XRD is a database schema. For -those familiar with Kubernetes, the relationship is very similar to that between -a Custom Resource Definition (CRD) and a Custom Resource (CR). - -A `CompositeResourceDefinition` is a distinct Crossplane API type, so Pascal -case and fixed width fonts are appropriate. - -> There isn’t a direct analog to XRDs in the Helm ecosystem, but they’re a -> little bit like the variable blocks in a Terraform module that define which -> variables exist, whether those variables are strings or integers, whether -> they’re required or optional, etc. - -### Managed Resource - -Managed resources are granular, high fidelity Crossplane representations of a -resource in an external system - i.e. resources that are managed by Crossplane. -Managed resources are what Crossplane enables platform teams to compose into -higher level composite resources, forming an opinionated platform API. They're -the building blocks of Crossplane. - -You’ll often hear three related terms used in the Crossplane ecosystem; composed -resource, managed resource, and external resource. While there are some subtle -contextual differences, these all broadly refer to the same thing. Take an -RDSInstance for example; it is a managed resource. A distinct resource within -the Crossplane API that represents an AWS RDS instance. When we make a -distinction between the managed resource and an external resource we’re simply -making the distinction between Crossplane’s representation of the thing (the -`RDSInstance` in the Kubernetes API), and the actual thing in whatever external -system Crossplane is orchestrating (the RDS instance in AWS's API). When we -mention composed resources, we mean a managed resource of which a composite -resource is composed. - -Managed resources are a class of resource, so avoid using Pascal case - “managed -resource” not “ManagedResource”. - -> Managed resources are similar to Terraform resource blocks, or a distinct -> Kubernetes resource within a Helm chart. - -### Package - -Packages extend Crossplane, either with support for new kinds of composite -resources and claims, or support for new kinds of managed resources. There are -two types of Crossplane package; configurations and providers. - -A package is not a distinct type in the Crossplane API, but rather a class of -types. Therefore Pascal case is not appropriate. - -### Configuration - -A configuration extends Crossplane by installing conceptually related groups of -XRDs and Compositions, as well as dependencies like providers or further -configurations. Put otherwise, it configures the opinionated platform API -that Crossplane exposes. - -A `Configuration` is a distinct type in the Crossplane API, therefore Pascal -case and fixed width fonts are appropriate. - -### Provider - -A provider extends Crossplane by installing controllers for new kinds of managed -resources. Providers typically group conceptually related managed resources; for -example the AWS provider installs support for AWS managed resources like -RDSInstance and S3Bucket. - -A `Provider` is a distinct type in the Crossplane API, therefore Pascal case and -fixed width fonts are appropriate. Note that each Provider package has its own -configuration type, called a `ProviderConfig`. Don’t confuse the two; the former -installs the provider while the latter specifies configuration that is relevant -to all of its managed resources. - -> Providers are directly analogous to Terraform providers. - -### Crossplane Resource Model - -The Crossplane Resource Model or XRM is neither a distinct Crossplane API type, -or a class of types. Rather it represents the fact that Crossplane has a -consistent, opinionated API. The strict definition of the XRM is currently -somewhat vague, but it could broadly be interpreted as a catchall term referring -to all of the concepts mentioned on this page. diff --git a/themes/geekboot/layouts/shortcodes/hover.html b/themes/geekboot/layouts/shortcodes/hover.html index 458034683..d95218d37 100644 --- a/themes/geekboot/layouts/shortcodes/hover.html +++ b/themes/geekboot/layouts/shortcodes/hover.html @@ -5,33 +5,4 @@ {{- if not (.Get "line") -}} {{- errorf "\n\nNo \"line\" number element for {{< hover >}} tag on %s \n\n\n" .Position -}} {{- end -}} -{{ .Inner }} -{{/* -How to Use - -Create a normal code fence with triple ticks. -```shell - - -add a goldmark attribute to set a label for JS to key on. -```shell {label="zzxxzz"} - -Put the code nomally. -```shell {label="zzxxzz"} -cat <}}kind: Provider{{< /hover >}} the command. - -Provide the label of the code block and the line number to highlight. -*/}} \ No newline at end of file +{{ .Inner }} \ No newline at end of file diff --git a/utils/vale/.vale.ini b/utils/vale/.vale.ini index 2f24eb7c9..be5097120 100644 --- a/utils/vale/.vale.ini +++ b/utils/vale/.vale.ini @@ -38,6 +38,8 @@ Google.Latin = NO Microsoft.We = NO Microsoft.FirstPerson = NO Microsoft.Foreign = NO +Microsoft.Quotes = NO +Microsoft.GeneralURL = NO # ignore indivudal tokens TokenIgnores = ({{< img) diff --git a/utils/vale/styles/Crossplane/spelling-exceptions.txt b/utils/vale/styles/Crossplane/spelling-exceptions.txt index c7909e5b5..98217a1ad 100644 --- a/utils/vale/styles/Crossplane/spelling-exceptions.txt +++ b/utils/vale/styles/Crossplane/spelling-exceptions.txt @@ -1,9 +1,16 @@ +API's APIs +bool +boolean +booleans Bootstrap +clampMax +clampMin conformant CRDs Crossplane Crossplane's +CUE Dataflow Datastore editCode @@ -15,8 +22,10 @@ GCP's Geekdocs Grammarly HashiCorp +JSONPath kubeconfig kubectl +Kustomize minikube namespace namespaced @@ -32,6 +41,8 @@ stdin stdout Subnet subnet +subnets +Substrings syscall tolerations untrusted @@ -41,6 +52,8 @@ Upjet Velero VSCode Webpack +XCluster +XNetwork xpkg XRD XRD's diff --git a/utils/vale/styles/Google/Headings.yml b/utils/vale/styles/Google/Headings.yml index f23a6d473..d7589f33f 100644 --- a/utils/vale/styles/Google/Headings.yml +++ b/utils/vale/styles/Google/Headings.yml @@ -10,18 +10,23 @@ exceptions: - Azure - BigQuery - Claim + - Claims - CLI - ClusterRoles - Code + - Composite Resource - Composite Resource Definitions - Composite Resources - CompositeResourceDefinition + - compositeTypeRef - Composition + - Compositions - Cosmos - Crossplane - Docker - DynamoDB - Emmet + - EnvironmentConfigs - gRPC - Hashicorpterms - Helm diff --git a/utils/vale/styles/Google/WordList.yml b/utils/vale/styles/Google/WordList.yml index 659287c36..be3e11469 100644 --- a/utils/vale/styles/Google/WordList.yml +++ b/utils/vale/styles/Google/WordList.yml @@ -20,12 +20,12 @@ swap: 'Google (?:I\-O|IO)': Google I/O 'tap (?:&|and) hold': touch & hold 'un(?:check|select)': clear + (?i)a\.k\.a|aka: or|also known as above: preceding account name: username action bar: app bar admin: administrator Ajax: AJAX - (?i)a\.k\.a|aka: or|also known as Android device: Android-powered device android: Android API explorer: APIs Explorer @@ -40,6 +40,7 @@ swap: check box: checkbox check: select click on: click|click in + composite functions: composition functions Container Engine: Kubernetes Engine content type: media type curated roles: predefined roles @@ -75,4 +76,4 @@ swap: touch: tap url: URL vs\.: versus - World Wide Web: web + World Wide Web: web \ No newline at end of file diff --git a/utils/vale/styles/Microsoft/HeadingAcronyms.yml b/utils/vale/styles/Microsoft/HeadingAcronyms.yml index 2e614e3e3..55864b69e 100644 --- a/utils/vale/styles/Microsoft/HeadingAcronyms.yml +++ b/utils/vale/styles/Microsoft/HeadingAcronyms.yml @@ -13,3 +13,5 @@ exceptions: - RBAC - CSS - SCSS + - XRD + - XRDs diff --git a/utils/vale/styles/gitlab/Uppercase.yml b/utils/vale/styles/gitlab/Uppercase.yml index 7516c6820..0f11a8527 100644 --- a/utils/vale/styles/gitlab/Uppercase.yml +++ b/utils/vale/styles/gitlab/Uppercase.yml @@ -17,7 +17,6 @@ exceptions: - ACL - AJAX - AKS - - ESS - ANSI - API - APM @@ -41,6 +40,7 @@ exceptions: - CSRF - CSS - CSV + - CUE - CVE - CVS - DAG @@ -57,6 +57,7 @@ exceptions: - EKS - ELB - EOL + - ESS - EWM - EXIF - FAQ @@ -216,6 +217,7 @@ exceptions: - WEBP - WIP - WSL + - XEKS - XML - XPKG - XRD diff --git a/utils/vale/styles/write-good/TooWordy.yml b/utils/vale/styles/write-good/TooWordy.yml index 99c8b97d3..3e90eafc3 100644 --- a/utils/vale/styles/write-good/TooWordy.yml +++ b/utils/vale/styles/write-good/TooWordy.yml @@ -137,10 +137,8 @@ tokens: - it seems that - it was - magnitude - - maximum - methodology - minimize - - minimum - modify - monitor - necessitate @@ -211,7 +209,6 @@ tokens: - until such time as - utilization - utilize - - validate - various different - what I mean to say is - whether or not