From e92a870f69e8fd56159a8df0e2fc8a9a8ecbf321 Mon Sep 17 00:00:00 2001 From: Pete Lumbis Date: Wed, 24 May 2023 13:08:35 -0400 Subject: [PATCH 01/17] WIP Signed-off-by: Pete Lumbis --- content/v1.12/concepts/managed-resources.md | 144 ++++++++++++++++++ content/v1.12/concepts/providers.md | 5 +- .../styles/Crossplane/spelling-exceptions.txt | 1 + 3 files changed, 148 insertions(+), 2 deletions(-) diff --git a/content/v1.12/concepts/managed-resources.md b/content/v1.12/concepts/managed-resources.md index 0950b8b85..0830c09e1 100644 --- a/content/v1.12/concepts/managed-resources.md +++ b/content/v1.12/concepts/managed-resources.md @@ -3,6 +3,150 @@ title: Managed Resources weight: 102 --- +A _managed resource_ (`MR`) represents an external service in a Provider. When +asking a Provider to create an external resource, the Provider creates a managed resource +inside the Kubernetes cluster. Every external service managed by Crossplane maps +to a managed resource. + +{{< hint "note" >}} +Crossplane calls the object inside Kubernetes a _managed resource_ and the +external object inside the Provider an _external resource_. +{{< /hint >}} + +Examples of managed resources include: +* Amazon AWS EC2 [`Instance`](https://marketplace.upbound.io/providers/upbound/provider-aws/latest/resources/ec2.aws.upbound.io/Instance/v1beta1) +* Google Cloud GKE [`Cluster`](https://marketplace.upbound.io/providers/upbound/provider-gcp/latest/resources/container.gcp.upbound.io/Cluster/v1beta1) +* Microsoft Azure Postgre [`Database`](https://marketplace.upbound.io/providers/upbound/provider-azure/latest/resources/dbforpostgresql.azure.upbound.io/Database/v1beta1) + +{{< hint "tip" >}} + +You can create individual managed resources, but Crossplane recommends using +[Compositions]({{}}) and Claims to create +managed resources. +{{< /hint >}} + +## Managed resource fields + +The Provider defines the group, Kind and version of a managed resource. The +Provider also define the available settings of a managed resource. + +### Group, kind and version +Each managed resource is a unique API endpoint with their own +group, Kind and version. + +For example the [Upbound AWS +Provider](https://marketplace.upbound.io/providers/upbound/provider-aws/latest/) +defines the {{}}Instance{{}} Kind from the +group {{}}ec2.aws.upbound.io{{}} + +```yaml {label="gkv"} +apiVersion: ec2.aws.upbound.io/v1beta1 +kind: Instance +``` + + +### deletionPolicy + + +A managed resource's `deletionPolicy` tells the Provider what to do after +deleting the managed resource. If the `deletionPolicy` is `delete` the Provider +deletes the external resource as well. If the `deletionPolicy` is `orphan` + +#### Options +* `deletionPolicy: delete` - Delete the external resource when deleting the managed resource. _Default value_ +* `deletionPolicy: orphan` - Leave the external resource when deleting the managed resource. + + +### forProvider + + +The {{}}spec.forProvider{{}} of a managed resource maps to the parameters of the +external resource. + +For example, when creating an AWS EC2 instance the Provider supports defining the +AWS {{}}region{{}} and the VM size, +called the {{}}instanceType{{}}. + +```yaml {label="forProvider"} +apiVersion: ec2.aws.upbound.io/v1beta1 +kind: Instance +spec: + forProvider: + region: us-west-1 + instanceType: t2.micro +``` + +{{< hint "note" >}} +The Provider defines the settings and their valid values. Providers also define +the required values in the `forProvider` definition. + +Refer to the documentation for your specific Provider for details. +{{< /hint >}} + + + +### managementPolicy + + + +### providerConfigRef + + +The `providerConfigRef` on a managed resource tells the Provider which +[ProviderConfig]({{}}) to +use when creating the managed resource. + +A ProviderConfig commonly defines the authentication to use when communicating to +the Provider. + +{{< hint "important" >}} +If `providerConfigRef` isn't applied, Providers use the ProviderConfig named `default`. +{{< /hint >}} + +For example, a managed resource references a ProviderConfig named {{}}user-keys{{}}. + +This matches the {{}}name{{}} of a ProviderConfig. + +```yaml {label="pcref"} +apiVersion: ec2.aws.upbound.io/v1beta1 +kind: Instance +spec: + forProvider: + # Removed for brevity + providerConfigRef: user-keys +``` + +```yaml {label="pc"} +apiVersion: aws.crossplane.io/v1beta1 +kind: ProviderConfig +metadata: + name: user-keys +# Removed for brevity +``` + +{{< hint "tip" >}} +Different managed resources can reference different ProviderConfigs. This allows +different managed resources to authenticate with different credentials with the +same Provider. +{{< /hint >}} + + +### providerRef + + +Crossplane deprecated the `providerRef` field in `crossplane-runtime` +[v0.10.0](https://github.com/crossplane/crossplane-runtime/releases/tag/v0.10.0). +Managed resources using `providerRef`must use `providerConfigRef`. + + +### publishConnectionDetailsTo + + + +### writeConnectionSecretToRef + + A Managed Resource (MR) is Crossplane's representation of a resource in an external system - most commonly a cloud provider. Managed Resources are opinionated, Crossplane Resource Model ([XRM]({{}})) compliant Kubernetes diff --git a/content/v1.12/concepts/providers.md b/content/v1.12/concepts/providers.md index ebd4ab5b5..49170bb4b 100644 --- a/content/v1.12/concepts/providers.md +++ b/content/v1.12/concepts/providers.md @@ -330,8 +330,9 @@ authentication for that Provider. -ProviderConfig objects apply to individual Managed Resources. A single -Provider can authenticate with multiple users or accounts through +ProviderConfig objects apply to individual managed resources with a +[`providerConfigRef`]({{}}). +A single Provider can authenticate with multiple users or accounts through ProviderConfigs. diff --git a/utils/vale/styles/Crossplane/spelling-exceptions.txt b/utils/vale/styles/Crossplane/spelling-exceptions.txt index 99d9ee263..022c15c25 100644 --- a/utils/vale/styles/Crossplane/spelling-exceptions.txt +++ b/utils/vale/styles/Crossplane/spelling-exceptions.txt @@ -20,6 +20,7 @@ minikube namespace namespaces Netlify +Postgre proselint semver shortcode From 417efd96798618df8097b95ae604ee9260f7b96a Mon Sep 17 00:00:00 2001 From: Pete Lumbis Date: Thu, 25 May 2023 09:15:06 -0600 Subject: [PATCH 02/17] wip Signed-off-by: Pete Lumbis --- .../guides/import-existing-resources.md | 200 ++++++++++++++++++ content/v1.12/concepts/managed-resources.md | 40 +++- 2 files changed, 235 insertions(+), 5 deletions(-) create mode 100644 content/knowledge-base/guides/import-existing-resources.md diff --git a/content/knowledge-base/guides/import-existing-resources.md b/content/knowledge-base/guides/import-existing-resources.md new file mode 100644 index 000000000..0114189d8 --- /dev/null +++ b/content/knowledge-base/guides/import-existing-resources.md @@ -0,0 +1,200 @@ +--- +title: Import Existing Resources +weight: 200 +--- + +If you have resources that are already provisioned in a Provider, +you can import them as managed resources and let Crossplane manage them. + +Crossplane can import resources either [manually]({{}}) or [automatically]({{}}). + + +## Import resources manually + +Crossplane can discover and import existing Provider resources by matching the +`external-name` annotation in a managed resource. + +To import an existing external resource in a Provider, create a new managed +resource with a `metadata.annotations.crossplane.io/external-name` value. Set +the `external-name` to the name of the resource in the Provider. + +Leave the `spec.forProvider` field empty. Crossplane imports the settings and +applies them to the managed resource. + +For example, to import an existing GCP Network named `my-existing-network`, +create a new managed resource and use the +{{}}my-existing-network{{}} in the +annotation. + +```yaml {label="annotation"} +apiVersion: compute.gcp.crossplane.io/v1beta1 +kind: Network +metadata: + annotations: + crossplane.io/external-name: my-existing-network +``` + + + +## Import resources automatically + + +What +you need to do is to enter the name of the external resource as well as the +required fields on the managed resource. For example, let's say I have a GCP +Network provisioned from GCP console and I would like to migrate it to +Crossplane. Here is the YAML that I need to create: + +```yaml +apiVersion: compute.gcp.crossplane.io/v1beta1 +kind: Network +metadata: + name: foo-network + annotations: + crossplane.io/external-name: existing-network +spec: + forProvider: {} + providerConfigRef: + name: default +``` + +Crossplane will check whether a GCP Network called `existing-network` exists, +and if it does, then the optional fields under `forProvider` will be filled with +the values that are fetched from the provider. + +Note that if a resource has required fields, you must fill those fields or the +creation of the managed resource will be rejected. So, in those cases, you will +need to enter the name of the resource as well as the required fields. + +### Alternative Import Procedure: Start with ObserveOnly + +Directly importing existing managed resources approach has the following caveats: + +1. You must provide all the required fields in the spec of the resource with +correct values even though they're not used for importing the resource. A wrong +value for a required field result in a configuration update which isn't +desired. +2. Any typos in the external name annotation or mistakes in the identifying +arguments, such as the `region`, results in the creation of a new resource +instead of importing the existing one. + +Instead of manually creating resources you can import the resource with an +`ObserveOnly` management policy. + +Crossplane imports `ObserveOnly` resources but never changes or deletes the +resource. + +{{< hint "important" >}} +Management policies including `ObserveOnly` are experimental. They must be +explicitly enabled. +See the management policies section for more details. +{{< /hint >}} + +To configure an `ObserveOnly` resource: + +1. Create a new resource with an {{}}ObserveOnly{{}} + management policy. + 1. With the + {{}}crossplane.io/external-name{{}} + annotation set to the external name of the resource to import. + 1. Only provide the identifying arguments (for example, + {{}}region{{}}) in the spec + of the resource. + +```yaml {label="oo"} +apiVersion: sql.gcp.upbound.io/v1beta1 +kind: DatabaseInstance +metadata: + annotations: + crossplane.io/external-name: existing-database-instance + name: existing-database-instance +spec: + managementPolicy: ObserveOnly + forProvider: + region: "us-central1" +``` + +Crossplane discovers the managed resource and populates the +{{}}status.atProvider{{}} +with the observed state. + +```yaml {label="ooPopulated"} +apiVersion: sql.gcp.upbound.io/v1beta1 +kind: DatabaseInstance +metadata: + annotations: + crossplane.io/external-name: existing-database-instance + name: existing-database-instance +spec: + managementPolicy: ObserveOnly + forProvider: + region: us-central1 +status: + atProvider: + connectionName: crossplane-playground:us-central1:existing-database-instance + databaseVersion: POSTGRES_14 + deletionProtection: true + firstIpAddress: 35.184.74.79 + id: existing-database-instance + publicIpAddress: 35.184.74.79 + region: us-central1 + + settings: + - activationPolicy: ALWAYS + availabilityType: REGIONAL + diskSize: 100 + + pricingPlan: PER_USE + tier: db-custom-4-26624 + version: 4 + conditions: + - lastTransitionTime: "2023-02-22T07:16:51Z" + reason: Available + status: "True" + type: Ready + - lastTransitionTime: "2023-02-22T07:16:51Z" + reason: ReconcileSuccess + status: "True" + type: Synced +``` + +To allow Crossplane to control and change the `ObserveOnly` resource, edit the +policy. + +Change the {{}}ObserveOnly{{}} field +to {{}}FullControl{{}}. + +Copy any required parameter values from +{{}}status.atProvider{{}} and provide them +in {{}}spec.forProvider{{}}. + +```yaml {label="fc"} +apiVersion: sql.gcp.upbound.io/v1beta1 +kind: DatabaseInstance +metadata: + annotations: + crossplane.io/external-name: existing-database-instance + name: existing-database-instance +spec: + managementPolicy: Full + forProvider: + databaseVersion: POSTGRES_14 + region: us-central1 + settings: + - diskSize: 100 + tier: db-custom-4-26624 +status: + atProvider: + + conditions: + - lastTransitionTime: "2023-02-22T07:16:51Z" + reason: Available + status: "True" + type: Ready + - lastTransitionTime: "2023-02-22T11:16:45Z" + reason: ReconcileSuccess + status: "True" + type: Synced +``` \ No newline at end of file diff --git a/content/v1.12/concepts/managed-resources.md b/content/v1.12/concepts/managed-resources.md index 0830c09e1..c99350746 100644 --- a/content/v1.12/concepts/managed-resources.md +++ b/content/v1.12/concepts/managed-resources.md @@ -50,18 +50,19 @@ kind: Instance A managed resource's `deletionPolicy` tells the Provider what to do after deleting the managed resource. If the `deletionPolicy` is `delete` the Provider -deletes the external resource as well. If the `deletionPolicy` is `orphan` +deletes the external resource as well. If the `deletionPolicy` is `orphan` the +Provider deletes the managed resource but doesn't delete the remote resource. #### Options -* `deletionPolicy: delete` - Delete the external resource when deleting the managed resource. _Default value_ +* `deletionPolicy: delete` - **Default** - Delete the external resource when deleting the managed resource. * `deletionPolicy: orphan` - Leave the external resource when deleting the managed resource. ### forProvider -The {{}}spec.forProvider{{}} of a managed resource maps to the parameters of the -external resource. +The {{}}spec.forProvider{{}} of a +managed resource maps to the parameters of the external resource. For example, when creating an AWS EC2 instance the Provider supports defining the AWS {{}}region{{}} and the VM size, @@ -80,7 +81,7 @@ spec: The Provider defines the settings and their valid values. Providers also define the required values in the `forProvider` definition. -Refer to the documentation for your specific Provider for details. +Refer to the documentation of your specific Provider for details. {{< /hint >}} @@ -88,6 +89,34 @@ Refer to the documentation for your specific Provider for details. ### managementPolicy +{{}} +The managed resource `managementPolicy` option is an alpha feature. + +Enable the `managementPolicy` in a provider with `--enable-management-policies` +in a +[ControllerConfig]({{}}). +{{< /hint >}} + +A `managementPolicy` determines if Crossplane can make changes to managed +resources. The `ObserveOnly` policy imports existing external resources not +originally created by Crossplane. +This allows new managed resources to reference +the `ObserveOnly` resource, for example, a shared database or network. +The `ObserveOnly` policy can also place existing resources under the control of +Crossplane. + +Read the [Import Existing Resources]({{}}) guide for more +information. + +#### Options +* `managementPolicy: FullControl` - **Default** - Crossplane can create, change + and delete the managed resource. +* `managementPolicy: ObserveOnly` - Crossplane only imports the details of the + external resource, but doesn't make any changes to the managed resource. + + + ### providerConfigRef @@ -163,6 +192,7 @@ _composed_ into higher level, opinionated Custom Resources that Crossplane calls Composite Resources or XRs - not used directly. See the [Composition]({{}}) documentation for more information. +## Annotations ## Syntax Crossplane API conventions extend the Kubernetes API conventions for the schema From 9cda0387a3da36159313e192dbf26ada5b6bf856 Mon Sep 17 00:00:00 2001 From: Pete Lumbis Date: Thu, 25 May 2023 11:57:56 -0600 Subject: [PATCH 03/17] Remove importing resource guide out of concepts and into KB guide Signed-off-by: Pete Lumbis --- .../guides/import-existing-resources.md | 187 ++++++++++++------ 1 file changed, 125 insertions(+), 62 deletions(-) diff --git a/content/knowledge-base/guides/import-existing-resources.md b/content/knowledge-base/guides/import-existing-resources.md index 0114189d8..b700e6ff4 100644 --- a/content/knowledge-base/guides/import-existing-resources.md +++ b/content/knowledge-base/guides/import-existing-resources.md @@ -4,13 +4,15 @@ weight: 200 --- If you have resources that are already provisioned in a Provider, -you can import them as managed resources and let Crossplane manage them. +you can import them as managed resources and let Crossplane manage them. +A managed resource's [`managementPolicy`]({{}}) field enables importing +external resources into Crossplane. Crossplane can import resources either [manually]({{}}) or [automatically]({{}}). - ## Import resources manually Crossplane can discover and import existing Provider resources by matching the @@ -20,10 +22,8 @@ To import an existing external resource in a Provider, create a new managed resource with a `metadata.annotations.crossplane.io/external-name` value. Set the `external-name` to the name of the resource in the Provider. -Leave the `spec.forProvider` field empty. Crossplane imports the settings and -applies them to the managed resource. - -For example, to import an existing GCP Network named `my-existing-network`, +For example, to import an existing GCP Network named +{{}}my-existing-network{{}}, create a new managed resource and use the {{}}my-existing-network{{}} in the annotation. @@ -36,89 +36,136 @@ metadata: crossplane.io/external-name: my-existing-network ``` +The {{}}metadata.name{{}} +field can be anything you want. For example, +{{}}imported-network{{}}. +{{< hint "note" >}} +This name is the +name of the Kubernetes object. It's not related to the resource name inside the +Provider. +{{< /hint >}} -## Import resources automatically - +```yaml {label="name"} +apiVersion: compute.gcp.crossplane.io/v1beta1 +kind: Network +metadata: + annotations: + name: imported-network + crossplane.io/external-name: my-existing-network +``` -What -you need to do is to enter the name of the external resource as well as the -required fields on the managed resource. For example, let's say I have a GCP -Network provisioned from GCP console and I would like to migrate it to -Crossplane. Here is the YAML that I need to create: +Leave the +{{}}spec.forProvider{{}} field empty. +Crossplane imports the settings and automatically applies them to the managed +resource. -```yaml +```yaml {label="fp",copy-lines="all"} apiVersion: compute.gcp.crossplane.io/v1beta1 kind: Network metadata: - name: foo-network annotations: - crossplane.io/external-name: existing-network + name: imported-network + crossplane.io/external-name: my-existing-network spec: forProvider: {} - providerConfigRef: - name: default ``` -Crossplane will check whether a GCP Network called `existing-network` exists, -and if it does, then the optional fields under `forProvider` will be filled with -the values that are fetched from the provider. - -Note that if a resource has required fields, you must fill those fields or the -creation of the managed resource will be rejected. So, in those cases, you will -need to enter the name of the resource as well as the required fields. +{{< hint "important" >}} +If the managed resource has _required_ fields in the +{{}}spec.forProvider{{}} you must add it to +the `forProvider` field. -### Alternative Import Procedure: Start with ObserveOnly +The values of those fields must match what's inside the Provider or Crossplane +overwrites the existing values. +{{< /hint >}} -Directly importing existing managed resources approach has the following caveats: +Crossplane now controls and manages this imported resource. Any changes to the +managed resource `spec` changes the external resource. -1. You must provide all the required fields in the spec of the resource with -correct values even though they're not used for importing the resource. A wrong -value for a required field result in a configuration update which isn't -desired. -2. Any typos in the external name annotation or mistakes in the identifying -arguments, such as the `region`, results in the creation of a new resource -instead of importing the existing one. +## Import resources automatically -Instead of manually creating resources you can import the resource with an -`ObserveOnly` management policy. +Automatically import external resources with the +`ObserveOnly` [`managementPolicy`]({{}}). Crossplane imports `ObserveOnly` resources but never changes or deletes the -resource. +resources. -{{< hint "important" >}} -Management policies including `ObserveOnly` are experimental. They must be -explicitly enabled. -See the management policies section for more details. +{{}} +The managed resource `managementPolicy` option is an alpha feature. + +Enable the `managementPolicy` in a provider with `--enable-management-policies` +in a +[ControllerConfig]({{}}). {{< /hint >}} -To configure an `ObserveOnly` resource: + +### Apply the ObserveOnly managementPolicy + -1. Create a new resource with an {{}}ObserveOnly{{}} - management policy. - 1. With the - {{}}crossplane.io/external-name{{}} - annotation set to the external name of the resource to import. - 1. Only provide the identifying arguments (for example, - {{}}region{{}}) in the spec - of the resource. +Create a new managed resource with {{}}managementPolicy: ObserveOnly{{}} set. -```yaml {label="oo"} +```yaml {label="oo-policy"} +apiVersion: sql.gcp.upbound.io/v1beta1 +kind: DatabaseInstance +spec: + managementPolicy: ObserveOnly +``` + +### Add the external-name annotation +Add the {{}}external-name{{}} annotation +for the resource. This name must match the name inside the Provider. + +```yaml {label="oo-ex-name"} apiVersion: sql.gcp.upbound.io/v1beta1 kind: DatabaseInstance metadata: annotations: - crossplane.io/external-name: existing-database-instance - name: existing-database-instance + crossplane.io/external-name: my-external-database +spec: + managementPolicy: ObserveOnly +``` + +### Create a Kubernetes object name +Create a {{}}name{{}} to use for the +Kubernetes object. + +```yaml {label="oo-name"} +apiVersion: sql.gcp.upbound.io/v1beta1 +kind: DatabaseInstance +metadata: + name: my-imported-database + annotations: + crossplane.io/external-name: my-external-database +spec: + managementPolicy: ObserveOnly +``` + +### Identify a specific external resource +If more than one resource inside the Provider shares the same name, identify the +specific resource with a unique +{{}}spec.forProvider{{}} field. +For example, the +{{}}region{{}}. + +```yaml {label="oo-region"} +apiVersion: sql.gcp.upbound.io/v1beta1 +kind: DatabaseInstance +metadata: + name: my-imported-database + annotations: + crossplane.io/external-name: my-external-database spec: managementPolicy: ObserveOnly forProvider: region: "us-central1" ``` +### View the discovered resource Crossplane discovers the managed resource and populates the {{}}status.atProvider{{}} -with the observed state. +fields with the values from the external resource. ```yaml {label="ooPopulated"} apiVersion: sql.gcp.upbound.io/v1beta1 @@ -140,12 +187,12 @@ status: id: existing-database-instance publicIpAddress: 35.184.74.79 region: us-central1 - + # Removed for brevity settings: - activationPolicy: ALWAYS availabilityType: REGIONAL diskSize: 100 - + # Removed for brevity pricingPlan: PER_USE tier: db-custom-4-26624 version: 4 @@ -159,17 +206,24 @@ status: status: "True" type: Synced ``` + +## Control imported ObserveOnly resources + -To allow Crossplane to control and change the `ObserveOnly` resource, edit the -policy. +Crossplane can take active control of `ObserveOnly` imported resources by +changing the `managementPolicy` after import. -Change the {{}}ObserveOnly{{}} field -to {{}}FullControl{{}}. +Change the {{}}managementPolicy{{}} field +of the managed resource to {{}}Full{{}}. Copy any required parameter values from {{}}status.atProvider{{}} and provide them in {{}}spec.forProvider{{}}. +{{< hint "tip" >}} +Manually copy the important `spec.atProvider` values to `spec.forProvider`. +{{< /hint >}} + ```yaml {label="fc"} apiVersion: sql.gcp.upbound.io/v1beta1 kind: DatabaseInstance @@ -187,7 +241,13 @@ spec: tier: db-custom-4-26624 status: atProvider: - + databaseVersion: POSTGRES_14 + region: us-central1 + # Removed for brevity + settings: + - diskSize: 100 + tier: db-custom-4-26624 + # Removed for brevity conditions: - lastTransitionTime: "2023-02-22T07:16:51Z" reason: Available @@ -197,4 +257,7 @@ status: reason: ReconcileSuccess status: "True" type: Synced -``` \ No newline at end of file +``` + +Crossplane now fully manages the imported resource. Any changes are reflected +inside the Provider. \ No newline at end of file From e3ad0f4df858040365ed3b0b77b4217fd691fc2c Mon Sep 17 00:00:00 2001 From: Pete Lumbis Date: Thu, 25 May 2023 17:38:30 -0600 Subject: [PATCH 04/17] vale fixes Signed-off-by: Pete Lumbis --- .../knowledge-base/guides/import-existing-resources.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/content/knowledge-base/guides/import-existing-resources.md b/content/knowledge-base/guides/import-existing-resources.md index b700e6ff4..b564d91ec 100644 --- a/content/knowledge-base/guides/import-existing-resources.md +++ b/content/knowledge-base/guides/import-existing-resources.md @@ -214,7 +214,8 @@ Crossplane can take active control of `ObserveOnly` imported resources by changing the `managementPolicy` after import. Change the {{}}managementPolicy{{}} field -of the managed resource to {{}}Full{{}}. +of the managed resource to +{{}}FullControl{{}}. Copy any required parameter values from {{}}status.atProvider{{}} and provide them @@ -232,7 +233,7 @@ metadata: crossplane.io/external-name: existing-database-instance name: existing-database-instance spec: - managementPolicy: Full + managementPolicy: FullControl forProvider: databaseVersion: POSTGRES_14 region: us-central1 @@ -259,5 +260,5 @@ status: type: Synced ``` -Crossplane now fully manages the imported resource. Any changes are reflected -inside the Provider. \ No newline at end of file +Crossplane now fully manages the imported resource. Crossplane applies any +changes to the managed resource in the Provider's external resource. \ No newline at end of file From 5518fd51dbfdd0375c9c1a83fa1621c78a7c1457 Mon Sep 17 00:00:00 2001 From: Pete Lumbis Date: Thu, 25 May 2023 17:38:40 -0600 Subject: [PATCH 05/17] wip Signed-off-by: Pete Lumbis --- content/v1.12/concepts/managed-resources.md | 474 ++------------------ 1 file changed, 46 insertions(+), 428 deletions(-) diff --git a/content/v1.12/concepts/managed-resources.md b/content/v1.12/concepts/managed-resources.md index c99350746..4dbd307d0 100644 --- a/content/v1.12/concepts/managed-resources.md +++ b/content/v1.12/concepts/managed-resources.md @@ -4,9 +4,9 @@ weight: 102 --- A _managed resource_ (`MR`) represents an external service in a Provider. When -asking a Provider to create an external resource, the Provider creates a managed resource -inside the Kubernetes cluster. Every external service managed by Crossplane maps -to a managed resource. +asking a Provider to create an external resource, the Provider creates a managed +resource inside the Kubernetes cluster. Every external service managed by +Crossplane maps to a managed resource. {{< hint "note" >}} Crossplane calls the object inside Kubernetes a _managed resource_ and the @@ -64,9 +64,18 @@ Provider deletes the managed resource but doesn't delete the remote resource. The {{}}spec.forProvider{{}} of a managed resource maps to the parameters of the external resource. -For example, when creating an AWS EC2 instance the Provider supports defining the -AWS {{}}region{{}} and the VM size, -called the {{}}instanceType{{}}. +For example, when creating an AWS EC2 instance, the Provider supports defining +the AWS {{}}region{{}} and the VM +size, called the +{{}}instanceType{{}}. + +{{< hint "note" >}} +The Provider defines the settings and their valid values. Providers also define +the required values in the `forProvider` definition. + +Refer to the documentation of your specific Provider for details. +{{< /hint >}} + ```yaml {label="forProvider"} apiVersion: ec2.aws.upbound.io/v1beta1 @@ -77,14 +86,14 @@ spec: instanceType: t2.micro ``` -{{< hint "note" >}} -The Provider defines the settings and their valid values. Providers also define -the required values in the `forProvider` definition. - -Refer to the documentation of your specific Provider for details. +{{< hint "important">}} +Crossplane considers the `forProvider` field of a managed resource +the "source of truth." Crossplane overrides any changes made to a managed +resource outside of Crossplane. If a user makes a change inside a +Provider's web console, Crossplane reverts that change back to what's +configured in the `forProvider` setting. {{< /hint >}} - ### managementPolicy @@ -105,9 +114,11 @@ the `ObserveOnly` resource, for example, a shared database or network. The `ObserveOnly` policy can also place existing resources under the control of Crossplane. +{{< hint "tip" >}} Read the [Import Existing Resources]({{}}) guide for more -information. +information on using the `managementPolicy` to import existing resources. +{{< /hint >}} #### Options * `managementPolicy: FullControl` - **Default** - Crossplane can create, change @@ -116,7 +127,6 @@ information. external resource, but doesn't make any changes to the managed resource. - ### providerConfigRef @@ -125,8 +135,8 @@ The `providerConfigRef` on a managed resource tells the Provider which [ProviderConfig]({{}}) to use when creating the managed resource. -A ProviderConfig commonly defines the authentication to use when communicating to -the Provider. +Use a ProviderConfig to define the authentication method to use when +communicating to the Provider. {{< hint "important" >}} If `providerConfigRef` isn't applied, Providers use the ProviderConfig named `default`. @@ -172,239 +182,42 @@ Managed resources using `providerRef`must use `providerConfigRef`. ### publishConnectionDetailsTo - -### writeConnectionSecretToRef - +When a Provider creates a managed resource it may generate resource-specific +details, like usernames, passwords or connection details like an IP address. -A Managed Resource (MR) is Crossplane's representation of a resource in an -external system - most commonly a cloud provider. Managed Resources are -opinionated, Crossplane Resource Model ([XRM]({{}})) compliant Kubernetes -Custom Resources that are installed by a Crossplane [provider]({{}}). +Crossplane stores these details in a Kubernetes Secret object -For example, `RDSInstance` in the AWS Provider corresponds to an actual RDS -Instance in AWS. There is a one-to-one relationship and the changes on managed -resources are reflected directly on the corresponding resource in the provider. -Similarly, the `Database` types in the SQL provider represent a PostgreSQL or -MySQL database. -Managed Resources are the building blocks of Crossplane. They're designed to be -_composed_ into higher level, opinionated Custom Resources that Crossplane calls -Composite Resources or XRs - not used directly. See the -[Composition]({{}}) documentation for more information. -## Annotations -## Syntax - -Crossplane API conventions extend the Kubernetes API conventions for the schema -of Crossplane managed resources. Following is an example of a managed resource: - -{{< tabs >}} -{{< tab "AWS" >}} - -The AWS provider supports provisioning an [RDS][rds] instance via the `RDSInstance` -managed resource it adds to Crossplane. - -```yaml -apiVersion: database.aws.crossplane.io/v1beta1 -kind: RDSInstance -metadata: - name: rdspostgresql -spec: - forProvider: - region: us-east-1 - dbInstanceClass: db.t2.small - masterUsername: masteruser - allocatedStorage: 20 - engine: postgres - engineVersion: "12" - skipFinalSnapshotBeforeDeletion: true - writeConnectionSecretToRef: - namespace: crossplane-system - name: aws-rdspostgresql-conn -``` - -```console -kubectl apply -f https://raw.githubusercontent.com/crossplane/crossplane/release-1.10/docs/snippets/provision/aws.yaml -``` - -Creating the above instance will cause Crossplane to provision an RDS instance -on AWS. You can view the progress with the following command: - -```console -kubectl get rdsinstance rdspostgresql -``` - -When provisioning is complete, you should see `READY: True` in the output. You -can take a look at its connection secret that is referenced under -`spec.writeConnectionSecretToRef`: - -```console -kubectl describe secret aws-rdspostgresql-conn -n crossplane-system -``` - -You can then delete the `RDSInstance`: - -```console -kubectl delete rdsinstance rdspostgresql -``` - -{{< /tab >}} -{{< tab "GCP" >}} - -The GCP provider supports provisioning a [CloudSQL][cloudsql] instance with the -`CloudSQLInstance` managed resource it adds to Crossplane. - -```yaml -apiVersion: database.gcp.crossplane.io/v1beta1 -kind: CloudSQLInstance -metadata: - name: cloudsqlpostgresql -spec: - forProvider: - databaseVersion: POSTGRES_12 - region: us-central1 - settings: - tier: db-custom-1-3840 - dataDiskType: PD_SSD - dataDiskSizeGb: 10 - writeConnectionSecretToRef: - namespace: crossplane-system - name: cloudsqlpostgresql-conn -``` - -```console -kubectl apply -f https://raw.githubusercontent.com/crossplane/crossplane/release-1.10/docs/snippets/provision/gcp.yaml -``` - -Creating the above instance will cause Crossplane to provision a CloudSQL -instance on GCP. You can view the progress with the following command: - -```console -kubectl get cloudsqlinstance cloudsqlpostgresql -``` - -When provisioning is complete, you should see `READY: True` in the output. You -can take a look at its connection secret that is referenced under -`spec.writeConnectionSecretToRef`: - -```console -kubectl describe secret cloudsqlpostgresql-conn -n crossplane-system -``` - -You can then delete the `CloudSQLInstance`: - -```console -kubectl delete cloudsqlinstance cloudsqlpostgresql -``` + +### writeConnectionSecretToRef + -{{< /tab >}} -{{< tab "Azure" >}} +{{< hint "important" >}} +The Crossplane community recommends using `publishConnectionDetailsTo` over +`writeConnectionSecretToRef`. +{{< /hint >}} -The Azure provider supports provisioning an [Azure Database for PostgreSQL] -instance with the `PostgreSQLServer` managed resource it adds to Crossplane. -> Note: provisioning an Azure Database for PostgreSQL requires the presence of a -> [Resource Group] in your Azure account. We go ahead and provision a new -> `ResourceGroup` here in case you do not already have a suitable one in your -> account. +`writeConnectionSecretToRef`: A reference to the secret that you want this + managed resource to write its connection secret that you'd be able to mount to + your pods in the same namespace. For `RDSInstance`, this secret would contain + `endpoint`, `username` and `password`. -```yaml -apiVersion: azure.crossplane.io/v1alpha3 -kind: ResourceGroup -metadata: - name: sqlserverpostgresql-rg -spec: - location: West US 2 ---- -apiVersion: database.azure.crossplane.io/v1beta1 -kind: PostgreSQLServer -metadata: - name: sqlserverpostgresql -spec: - forProvider: - administratorLogin: myadmin - resourceGroupNameRef: - name: sqlserverpostgresql-rg - location: West US 2 - sslEnforcement: Disabled - version: "9.6" - sku: - tier: GeneralPurpose - capacity: 2 - family: Gen5 - storageProfile: - storageMB: 20480 - writeConnectionSecretToRef: - namespace: crossplane-system - name: sqlserverpostgresql-conn -``` +## Annotations -```console -kubectl apply -f https://raw.githubusercontent.com/crossplane/crossplane/release-1.10/docs/snippets/provision/azure.yaml -``` +## Status fields -Creating the above instance will cause Crossplane to provision a PostgreSQL -database instance on Azure. You can view the progress with the following -command: + +### atProvider + +https://github.com/crossplane/docs/issues/272 -```console -kubectl get postgresqlserver sqlserverpostgresql -``` When provisioning is complete, you should see `READY: True` in the output. You can take a look at its connection secret that is referenced under `spec.writeConnectionSecretToRef`: -```console -kubectl describe secret sqlserverpostgresql-conn -n crossplane-system -``` - -You can then delete the `PostgreSQLServer`: - -```console -kubectl delete postgresqlserver sqlserverpostgresql -kubectl delete resourcegroup sqlserverpostgresql-rg -``` - -{{< /tab >}} -{{< /tabs >}} - -In Kubernetes, `spec` top field represents the desired state of the user. -Crossplane adheres to that and has its own conventions about how the fields -under `spec` should look like. - -* `writeConnectionSecretToRef`: A reference to the secret that you want this - managed resource to write its connection secret that you'd be able to mount to - your pods in the same namespace. For `RDSInstance`, this secret would contain - `endpoint`, `username` and `password`. - -* `providerConfigRef`: Reference to the `ProviderConfig` resource that will - provide information regarding authentication of Crossplane to the provider. - `ProviderConfig` resources refer to `Secret` and potentially contain other - information regarding authentication. The `providerConfigRef` is defaulted to - a `ProviderConfig` named `default` if omitted. - -* `deletionPolicy`: Enum to specify whether the actual cloud resource should be - deleted when this managed resource is deleted in Kubernetes API server. - Possible values are `Delete` (the default) and `Orphan`. - -* `managementPolicy`: Enum to specify the level of control Crossplane has over - the external resource. - Possible values are `FullControl` (the default) and `ObserveOnly`. - {{}} - `managementPolicy` is an experimental feature, see the management policies - section below for further details. - {{< /hint >}} - -* `forProvider`: While the rest of the fields relate to how Crossplane should - behave, the fields under `forProvider` are solely used to configure the actual - external resource. In most of the cases, the field names correspond to the - what exists in provider's API Reference. - - The objects under `forProvider` field can get huge depending on the provider - API. For example, GCP `ServiceAccount` has only a few fields while GCP - `CloudSQLInstance` has over 100 fields that you can configure. - ### Versioning Crossplane closely follows the [Kubernetes API versioning @@ -442,21 +255,6 @@ apiVersion: sqs.aws.crossplane.io/v1beta1 kind: Queue ``` -## Behavior - -As a general rule, managed resource controllers try not to make any decision -that is not specified by the user in the desired state since managed resources -are the lowest level primitives that operate directly on the cloud provider -APIs. - -### Continuous Reconciliation - -Crossplane providers continuously reconcile the managed resource to achieve the -desired state. The parameters under `spec` are considered the one and only -source of truth for the external resource. This means that if someone changed a -configuration in the UI of the provider, like AWS Console, Crossplane will -change it back to what's given under `spec`. - #### Connection Details Some Crossplane resources support writing connection details - things like URLs, @@ -601,186 +399,6 @@ does not create the external resource until the referenced object is ready. In this example, creation call of Azure MySQL Server will not be made until referenced `ResourceGroup` has its `status.condition` named `Ready` to be true. -## Management Policies - -Crossplane offers a set of management policies that allow you to define the -level of control it has over external resources. You can configure these -policies using the `spec.managementPolicy` field in the managed resource -definition. The available policies include: - -- `FullControl (Default)`: With this policy, Crossplane fully manages and -controls the external resource. -- `ObserveOnly`: With the ObserveOnly policy, Crossplane only observes the -external resource without making any changes or deletions. - -{{}} -Management policies are an experimental feature, and the API is -subject to change. -{{< /hint >}} - -To use management policies, you must enable them with the -`--enable-management-policies` flag when starting the provider controller. - -## Importing Existing Resources - -If you have some resources that are already provisioned in the cloud provider, -you can import them as managed resources and let Crossplane manage them. What -you need to do is to enter the name of the external resource as well as the -required fields on the managed resource. For example, let's say I have a GCP -Network provisioned from GCP console and I would like to migrate it to -Crossplane. Here is the YAML that I need to create: - -```yaml -apiVersion: compute.gcp.crossplane.io/v1beta1 -kind: Network -metadata: - name: foo-network - annotations: - crossplane.io/external-name: existing-network -spec: - forProvider: {} - providerConfigRef: - name: default -``` - -Crossplane will check whether a GCP Network called `existing-network` exists, -and if it does, then the optional fields under `forProvider` will be filled with -the values that are fetched from the provider. - -Note that if a resource has required fields, you must fill those fields or the -creation of the managed resource will be rejected. So, in those cases, you will -need to enter the name of the resource as well as the required fields. - -### Alternative Import Procedure: Start with ObserveOnly - -Directly importing existing managed resources approach has the following caveats: - -1. You must provide all the required fields in the spec of the resource with -correct values even though they're not used for importing the resource. A wrong -value for a required field result in a configuration update which isn't -desired. -2. Any typos in the external name annotation or mistakes in the identifying -arguments, such as the `region`, results in the creation of a new resource -instead of importing the existing one. - -Instead of manually creating resources you can import the resource with an -`ObserveOnly` management policy. - -Crossplane imports `ObserveOnly` resources but never changes or deletes the -resource. - -{{< hint "important" >}} -Management policies including `ObserveOnly` are experimental. They must be -explicitly enabled. -See the management policies section for more details. -{{< /hint >}} - -To configure an `ObserveOnly` resource: - -1. Create a new resource with an {{}}ObserveOnly{{}} - management policy. - 1. With the - {{}}crossplane.io/external-name{{}} - annotation set to the external name of the resource to import. - 1. Only provide the identifying arguments (for example, - {{}}region{{}}) in the spec - of the resource. - -```yaml {label="oo"} -apiVersion: sql.gcp.upbound.io/v1beta1 -kind: DatabaseInstance -metadata: - annotations: - crossplane.io/external-name: existing-database-instance - name: existing-database-instance -spec: - managementPolicy: ObserveOnly - forProvider: - region: "us-central1" -``` - -Crossplane discovers the managed resource and populates the -{{}}status.atProvider{{}} -with the observed state. - -```yaml {label="ooPopulated"} -apiVersion: sql.gcp.upbound.io/v1beta1 -kind: DatabaseInstance -metadata: - annotations: - crossplane.io/external-name: existing-database-instance - name: existing-database-instance -spec: - managementPolicy: ObserveOnly - forProvider: - region: us-central1 -status: - atProvider: - connectionName: crossplane-playground:us-central1:existing-database-instance - databaseVersion: POSTGRES_14 - deletionProtection: true - firstIpAddress: 35.184.74.79 - id: existing-database-instance - publicIpAddress: 35.184.74.79 - region: us-central1 - - settings: - - activationPolicy: ALWAYS - availabilityType: REGIONAL - diskSize: 100 - - pricingPlan: PER_USE - tier: db-custom-4-26624 - version: 4 - conditions: - - lastTransitionTime: "2023-02-22T07:16:51Z" - reason: Available - status: "True" - type: Ready - - lastTransitionTime: "2023-02-22T07:16:51Z" - reason: ReconcileSuccess - status: "True" - type: Synced -``` - -To allow Crossplane to control and change the `ObserveOnly` resource, edit the -policy. - -Change the {{}}ObserveOnly{{}} field -to {{}}FullControl{{}}. - -Copy any required parameter values from -{{}}status.atProvider{{}} and provide them -in {{}}spec.forProvider{{}}. - -```yaml {label="fc"} -apiVersion: sql.gcp.upbound.io/v1beta1 -kind: DatabaseInstance -metadata: - annotations: - crossplane.io/external-name: existing-database-instance - name: existing-database-instance -spec: - managementPolicy: Full - forProvider: - databaseVersion: POSTGRES_14 - region: us-central1 - settings: - - diskSize: 100 - tier: db-custom-4-26624 -status: - atProvider: - - conditions: - - lastTransitionTime: "2023-02-22T07:16:51Z" - reason: Available - status: "True" - type: Ready - - lastTransitionTime: "2023-02-22T11:16:45Z" - reason: ReconcileSuccess - status: "True" - type: Synced -``` ## Backup and Restore From c9851685ada272c0ddd1b0b640486adc760d7c1b Mon Sep 17 00:00:00 2001 From: Pete Lumbis Date: Wed, 31 May 2023 13:48:14 -0400 Subject: [PATCH 06/17] section rewrite and update. Resolves #453 and #272 Signed-off-by: Pete Lumbis --- content/v1.12/concepts/managed-resources.md | 737 ++++++++++++++------ 1 file changed, 530 insertions(+), 207 deletions(-) diff --git a/content/v1.12/concepts/managed-resources.md b/content/v1.12/concepts/managed-resources.md index 4dbd307d0..3f8d02da4 100644 --- a/content/v1.12/concepts/managed-resources.md +++ b/content/v1.12/concepts/managed-resources.md @@ -51,7 +51,7 @@ kind: Instance A managed resource's `deletionPolicy` tells the Provider what to do after deleting the managed resource. If the `deletionPolicy` is `delete` the Provider deletes the external resource as well. If the `deletionPolicy` is `orphan` the -Provider deletes the managed resource but doesn't delete the remote resource. +Provider deletes the managed resource but doesn't delete the external resource. #### Options * `deletionPolicy: delete` - **Default** - Delete the external resource when deleting the managed resource. @@ -71,7 +71,7 @@ size, called the {{< hint "note" >}} The Provider defines the settings and their valid values. Providers also define -the required values in the `forProvider` definition. +required and optional values in the `forProvider` definition. Refer to the documentation of your specific Provider for details. {{< /hint >}} @@ -94,6 +94,115 @@ Provider's web console, Crossplane reverts that change back to what's configured in the `forProvider` setting. {{< /hint >}} +Providers add any settings not manually set to the `forProvider` field of the +created managed resource object. +Use `kubectl describe ` to view the applied values. + +#### Referencing other resources + +Some fields in a managed resource may depend on values from other managed +resources. For example a VM may need the name of a virtual network to use. + +Managed resources can reference other managed resources by external name, name +reference or selector. + +##### Matching by external name + +When matching a resource by name Crossplane looks for the name of the external +resource in the Provider. + +For example, a AWS VPC object named `my-test-vpc` has the external name +`vpc-01353cfe93950a8ff`. + +```shell +kubectl get vpc +NAME READY SYNCED EXTERNAL-NAME AGE +my-test-vpc True True vpc-01353cfe93950a8ff 49m +``` + +To match the VPC by name, use the external name. For example, creating a Subnet +managed resource attached to this VPC. + +```yaml +apiVersion: ec2.aws.upbound.io/v1beta1 +kind: Subnet +spec: + forProvider: + # Removed for brevity + vpcId: vpc-01353cfe93950a8ff +``` + +##### Matching by name reference + +To match a resource based on the name of the managed resource and not the +external resource name inside the Provider, use a `nameRef`. + +For example, a AWS VPC object named `my-test-vpc` has the external name +`vpc-01353cfe93950a8ff`. + +```shell +kubectl get vpc +NAME READY SYNCED EXTERNAL-NAME AGE +my-test-vpc True True vpc-01353cfe93950a8ff 49m +``` + +To match the VPC by name reference, use the managed resource name. For example, +creating a Subnet managed resource attached to this VPC. + +```yaml +apiVersion: ec2.aws.upbound.io/v1beta1 +kind: Subnet +spec: + forProvider: + # Removed for brevity + vpcIdRef: + name: my-test-vpc +``` + + +##### Matching by selector + +Matching by selector is the most flexible matching method. + +{{}} +The [Composition]({{}}) section covers the +`matchControllerRef` selector. +{{}} + +Use `matchLabels` to match the labels applied to a resource. For example, this +Subnet resource only matches VPC resources with the label +`my-label: label-value`. + +```yaml +apiVersion: ec2.aws.upbound.io/v1beta1 +kind: Subnet +spec: + forProvider: + # Removed for brevity + vpcIdSelector: + matchLabels: + my-label: label-value +``` + + +#### Immutable fields + +Some providers don't support changing the fields of some managed resources after +creation. For example, you can't change the `region` of an Amazon AWS +`RDSInstance`. These fields are _immutable fields_. Amazon requires you delete +and recreate the resource. + +Crossplane allows you to edit the immutable field of a managed resource, but +doesn't apply the change. Crossplane never deletes a resource based on a +`forProvider` change. + +{{}} +Crossplane behaves differently than other tools like Terraform. Terraform +deletes and recreates a resource to change an immutable field. Crossplane only +deletes managed resources if the Kubernetes deletes the managed resource object. +{{< /hint >}} + + ### managementPolicy @@ -142,8 +251,8 @@ communicating to the Provider. If `providerConfigRef` isn't applied, Providers use the ProviderConfig named `default`. {{< /hint >}} -For example, a managed resource references a ProviderConfig named {{}}user-keys{{}}. +For example, a managed resource references a ProviderConfig named +{{}}user-keys{{}}. This matches the {{}}name{{}} of a ProviderConfig. @@ -165,8 +274,8 @@ metadata: ``` {{< hint "tip" >}} -Different managed resources can reference different ProviderConfigs. This allows -different managed resources to authenticate with different credentials with the +Each managed resource can reference different ProviderConfigs. This allows +different managed resources to authenticate with different credentials to the same Provider. {{< /hint >}} @@ -176,249 +285,463 @@ same Provider. Crossplane deprecated the `providerRef` field in `crossplane-runtime` [v0.10.0](https://github.com/crossplane/crossplane-runtime/releases/tag/v0.10.0). -Managed resources using `providerRef`must use `providerConfigRef`. +Managed resources using `providerRef`must use [`providerConfigRef`](#providerconfigref). + -### publishConnectionDetailsTo +### writeConnectionSecretToRef When a Provider creates a managed resource it may generate resource-specific details, like usernames, passwords or connection details like an IP address. -Crossplane stores these details in a Kubernetes Secret object +Crossplane stores these details in a Kubernetes Secret object specified by the +`writeConnectionSecretToRef` values. +For example, when creating an AWS RDS database instance with the Crossplane +[community AWS +provider](https://marketplace.upbound.io/providers/crossplane-contrib/provider-aws/v0.40.0) +generates an endpoint, password, port and username data. The Provider saves +these variables in the +{{}}writeConnectionSecretToRef.name{{}} +field. +```yaml {label="secretname"} +apiVersion: database.aws.crossplane.io/v1beta1 +kind: RDSInstance +metadata: + name: my-rds-instance +spec: + forProvider: + # Removed for brevity + writeConnectionSecretToRef: + name: rds-secret +``` + +Viewing the Secret object shows the saved fields. + +```yaml {copy-lines="1"} +kubectl describe secret rds-secret +Name: rds-secret +# Removed for brevity +Data +==== +port: 4 bytes +username: 10 bytes +endpoint: 54 bytes +password: 27 bytes +``` + +{{}} +The Provider determines the data written to the Secret object. Refer to the +specific Provider documentation for the generated Secret data. +{{< /hint >}} -### writeConnectionSecretToRef +### publishConnectionDetailsTo -{{< hint "important" >}} -The Crossplane community recommends using `publishConnectionDetailsTo` over -`writeConnectionSecretToRef`. +The `publishConnectionDetailsTo` field expands on +[`writeConnectionSecretToRef`](#writeconnectionsecrettoref) supporting storing +managed resource information as a Kubernetes Secret object or in an external +secrets store like [HashiCorp Vault](https://www.vaultproject.io/). + +Using `publishConnectionDetailsTo` requires enabling Crossplane +External Secrets Stores (ESS). Enable ESS inside a Provider with a +[ControllerConfig]({{}}) and +in Crossplane with the `--enable-external-secret-stores` argument. + +{{< hint "note" >}} +Not all Providers support `publishConnectionDetailsTo`. Check your Provider +documentation for details. {{< /hint >}} +#### Publish secrets to Kubernetes + +To publish the data generated by a managed resource as a Kubernetes Secret +object provide a +{{}}publishConnectionDetailsTo.name{{< /hover >}} + +```yaml {label="k8secret"} +apiVersion: rds.aws.upbound.io/v1beta1 +kind: Instance +spec: + forProvider: + # Removed for brevity + publishConnectionDetailsTo: + name: rds-kubernetes-secret +``` + +Crossplane can apply labels and annotations to the Kubernetes secret as well +using +{{}}publishConnectionDetailsTo.metadata{{}}. + +```yaml {label="k8label"} +apiVersion: rds.aws.upbound.io/v1beta1 +kind: Instance +spec: + forProvider: + # Removed for brevity + publishConnectionDetailsTo: + name: rds-kubernetes-secret + metadata: + labels: + label-tag: label-value + annotations: + annotation-tag: annotation-value +``` + +#### Publish secrets to an external secrets store + +Publishing secrets data to an external secret store like +[HashiCorp Vault](https://www.vaultproject.io/) relies on a +{{}}publishConnectionDetailsTo.configRef{{}}. + +The +{{}}configRef.name{{}} references a +{{}}StoreConfig{{}} +object. + +```yaml {label="configref"} +apiVersion: rds.aws.upbound.io/v1beta1 +kind: Instance +spec: + forProvider: + # Removed for brevity + publishConnectionDetailsTo: + name: rds-kubernetes-secret + configRef: + name: my-vault-storeconfig +``` + +```yaml {label="storeconfig"} +apiVersion: secrets.crossplane.io/v1alpha1 +kind: StoreConfig +metadata: + name: my-vault-storeconfig +# Removed for brevity +``` + +{{}} +Read the +[Vault as an External Secrets Store]({{}}) +guide for details on using StoreConfig objects. +{{< /hint >}} -`writeConnectionSecretToRef`: A reference to the secret that you want this - managed resource to write its connection secret that you'd be able to mount to - your pods in the same namespace. For `RDSInstance`, this secret would contain - `endpoint`, `username` and `password`. ## Annotations -## Status fields +Crossplane applies a standard set of Kubernetes `annotations` to managed +resources. + +{{}} +| Annotation | Definition | +| --- | --- | +| `crossplane.io/external-name` | The name of the managed resource inside the Provider. | +| `crossplane.io/external-create-pending` | The timestamp of the last time the managed resource was in the `PENDING` state. | +| `crossplane.io/external-create-succeeded` | The timestamp of the last time the Provider successfully created the managed resource. | +| `crossplane.io/external-create-failed` | The timestamp of the last time the Provider failed to create the managed resource. | +| `crossplane.io/paused` | Indicates Crossplane isn't reconciling this resource. Read the [Pause Annotation](#pause) for more details. | +| `crossplane.io/composition-resource-name` | For managed resource created by a Composition, this is the Composition's `resources.name` value. | +{{
}} + +### Naming external resources +By default Providers give external resources the same name as the Kubernetes +object. + +For example, a managed resource named +{{}}my-rds-instance{{}} has +the name `my-rds-instance` as an external resource inside the Provider's +environment. + +```yaml {label="external-name"} +apiVersion: database.aws.crossplane.io/v1beta1 +kind: RDSInstance +metadata: + name: my-rds-instance +``` + +```shell +kubectl get rdsinstance +NAME READY SYNCED EXTERNAL-NAME AGE +my-rds-instance True True my-rds-instance 11m +``` - -### atProvider - -https://github.com/crossplane/docs/issues/272 - - -When provisioning is complete, you should see `READY: True` in the output. You -can take a look at its connection secret that is referenced under -`spec.writeConnectionSecretToRef`: - -### Versioning - -Crossplane closely follows the [Kubernetes API versioning -conventions][api-versioning] for the CRDs that it deploys. In short, for -`vXbeta` and `vX` versions, you can expect that either automatic migration or -instructions for manual migration will be provided when a new version of that -CRD schema is released. - -In practice, we suggest the following guidelines to provider developers: -* Every new kind has to be introduced as `v1alpha1` with no exception. -* Breaking changes require a version change, i.e. `v1alpha1` needs to become - `v1alpha2`. - * Alpha resources don't require automatic conversions or manual instructions - but it's recommended that manual instructions are provided. - * Beta resources require at least manual instructions but it's recommended - that conversion webhooks are used so that users can upgrade without any - hands-on operation. - * Stable resources require conversion webhooks. -* As long as the developer feels comfortable with the guarantees above, they can - bump the version to beta or stable given that the CRD shape adheres to the - Crossplane Resource Model (XRM) specifications for managed resources - [here][managed-api-patterns]. -* It's suggested that the bump from Alpha to Beta or from Beta to Stable happen - after a bake period which includes at least one release. - -### Grouping - -In general, managed resources are high fidelity resources meaning they will -provide parameters and behaviors that are provided by the external resource API. -This applies to grouping of resources, too. For example, `Queue` appears under -`sqs` API group in AWS,so, its `APIVersion` and `Kind` look like the following: +Managed resource created with a `crossplane.io/external-name` +annotation already provided use the annotation value as the external +resource name. -```yaml -apiVersion: sqs.aws.crossplane.io/v1beta1 -kind: Queue +For example, the Provider creates managed resource named +{{< hover label="custom-name" line="6">}}my-rds-instance{{
}} but uses +the name {{}}my-custom-name{{}} +for the external resource inside AWS. + +```yaml {label="custom-name"} +apiVersion: database.aws.crossplane.io/v1beta1 +kind: RDSInstance +metadata: + name: my-rds-instance + annotations: + crossplane.io/external-name: my-custom-namee ``` -#### Connection Details +```shell +kubectl get rdsinstance +NAME READY SYNCED EXTERNAL-NAME AGE +my-rds-instance True True my-custom-name 11m +``` -Some Crossplane resources support writing connection details - things like URLs, -usernames, endpoints, and passwords to a Kubernetes `Secret`. You can specify -the secret to write by setting the `spec.writeConnectionSecretToRef` field. Note -that while all managed resources have a `writeConnectionSecretToRef` field, not -all managed resources actually have connection details to write - many will -write an empty `Secret`. +### Creation annotations -> Which managed resources have connection details and what connection details -> they have is currently undocumented. This is tracked in [this -> issue][issue-1143]. +Providers create new managed resources with the +`crossplane.io/external-create-pending` annotation. -#### Immutable Properties +The Provider applies the `crossplane.io/external-create-succeeded` or +`crossplane.io/external-create-failed` annotation after making the external API +call and receiving a response. -There are configuration parameters in external resources that cloud providers do -not allow to be changed. For example, in AWS, you cannot change the region of an -`RDSInstance`. +{{}} +If a Provider restarts before creating the `succeed` or `fail` annotations the +Provider can't reconcile the manged resource. -Some infrastructure tools such as Terraform delete and recreate the resource to -accommodate those changes but Crossplane does not take that route. Unless the -managed resource is deleted and its `deletionPolicy` is `Delete`, its controller -never deletes the external resource in the provider. +Read Crossplane [issue +#3037](https://github.com/crossplane/crossplane/issues/3037#issuecomment-1110142427) +for more details +{{< /hint >}} -> Kubernetes does not yet support immutable fields for custom resources. This -> means Crossplane will allow immutable fields to be changed, but will not -> actually make the desired change. This is tracked in [this issue][issue-727]. -#### Pausing Reconciliations -If a managed resource being reconciled by the [managed reconciler], has the -`crossplane.io/paused` annotation with its value set to `true` as in the -following example, then further reconciliations are paused on that resource -after emitting an event with the type `Synced`, the status `False`, -and the reason `ReconcilePaused`: -```yaml +### Pause +Manually applying the `crossplane.io/paused` annotation causes the Provider to +stop reconciling the managed resource. + +Pausing a resource is useful when modifying Providers or preventing +race-conditions when editing Kubernetes objects. + +Apply a {{}}crossplane.io/paused: "true"{{}} +annotation to a managed resource to pause reconciliation. + +{{< hint "note" >}} +Only the value `"true"` pauses reconciliation. +{{< /hint >}} + +```yaml {label="pause"} apiVersion: ec2.aws.upbound.io/v1beta1 -kind: VPC +kind: Instance metadata: - name: paused-vpc + name: my-rds-instance annotations: crossplane.io/paused: "true" -... +spec: + forProvider: + region: us-west-1 + instanceType: t2.micro ``` -Reconciliations on the managed resource will resume once the -`crossplane.io/paused` annotation is removed or its value is set -to anything other than `true`. -### External Name +Remove the annotation to resume reconciliation. + +## Finalizers +Crossplane applies a +[Finalizer](https://kubernetes.io/docs/concepts/overview/working-with-objects/finalizers/) +on managed resources to control their deletion. + +{{< hint "note" >}} +Kubernetes can't delete objects with Finalizers. +{{}} + +When a Crossplane deletes managed resource the Provider begins deleting the +external resource, but the managed resource remains until the external +resource is fully deleted. + +When the external resource is fully deleted Crossplane removes the Finalizer and +deletes the managed resource object. + +## Conditions + +Crossplane has a standard set of `Conditions` for a managed +resource. View the `Conditions` of a managed resource with +`kubectl describe ` + + +{{}} +Providers may define their own custom `Conditions`. +{{}} -By default the name of the managed resource is used as the name of the external -cloud resource that will show up in your cloud console. To specify a different -external name, Crossplane has a special annotation to represent the name of the -external resource. For example, I would like to have a `CloudSQLInstance` with -an external name that is different than its managed resource name: + +### Available +`Reason: Available` indicates the Provider created the managed resource. ```yaml -apiVersion: database.gcp.crossplane.io/v1beta1 -kind: CloudSQLInstance -metadata: - name: foodb - annotations: - crossplane.io/external-name: my-special-db -spec: - ... +Conditions: + Type: Ready + Status: True + Reason: Available ``` +### Creating -When you create this managed resource, you will see that the name of -`CloudSQLInstance` in GCP console will be `my-special-db`. - -If the annotation is not given, Crossplane will fill it with the name of the -managed resource by default. In cases where provider doesn't allow you to name -the resource, like AWS VPC, the controller creates the resource and sets -external annotation to be the name that the cloud provider chose. So, you would -see something like `vpc-28dsnh3` as the value of `crossplane.io/external-name` -annotation of your AWS `VPC` resource even if you added your own custom external -name during creation. - -### Late Initialization - -For some of the optional fields, users rely on the default that the cloud -provider chooses for them. Since Crossplane treats the managed resource as the -source of the truth, values of those fields need to exist in `spec` of the -managed resource. So, in each reconciliation, Crossplane will fill the value of -a field that is left empty by the user but is assigned a value by the provider. -For example, there could be two fields like `region` and `availabilityZone` and -you might want to give only `region` and leave the availability zone to be -chosen by the cloud provider. In that case, if the provider assigns an -availability zone, Crossplane gets that value and fills `availabilityZone`. Note -that if the field is already filled, the controller won't override its value. - -### Deletion - -When a deletion request is made for a managed resource, its controller starts -the deletion process immediately. However, the managed resource is kept in the -Kubernetes API (via a finalizer) until the controller confirms the external -resource in the cloud is gone. So you can be sure that if the managed resource -is deleted, then the external cloud resource is also deleted. Any errors that -happen during deletion will be added to the `status` of the managed resource, so -you can troubleshoot any issues. - -## Dependencies - -In many cases, an external resource refers to another one for a specific -configuration. For example, you could want your Azure Kubernetes cluster in a -specific Virtual Network. External resources have specific fields for these -relations, however, they usually require the information to be supplied in -different formats. In Azure MySQL, you might be required to enter only the name -of the Virtual Network while in Azure Kubernetes, it could be required to enter -a string in a specific format that includes other information such as resource -group name. - -In Crossplane, users have 3 fields to refer to another resource. Here is an -example from Azure MySQL managed resource referring to an Azure Resource Group: +`Reason: Creating` indicates the Provider is attempting to create the managed +resource. ```yaml -spec: - forProvider: - resourceGroupName: foo-res-group - resourceGroupNameRef: - name: resourcegroup - resourceGroupNameSelector: - matchLabels: - app: prod +Conditions: + Type: Ready + Status: False + Reason: Creating +``` + +### Deleting +`Reason: Deleting` indicates the Provider is attempting to delete the managed +resource. + +```yaml +Conditions: + Type: Ready + Status: False + Reason: Deleting +``` + + +### Paused +`Reason: ReconcilePaused` indicates the managed resource has a [Pause](#paused) +annotation +```yaml +Conditions: + Type: Synced + Status: False + Reason: ReconcilePaused +``` + + +### ReconcileError + +`Reason: ReconcileError` indicates Crossplane encountered an error while +reconciling the managed resource. The `Message:` value of the `Condition` helps +identify the Crossplane error. + +```yaml +Conditions: + Type: Synced + Status: False + Reason: ReconcileError +``` + + + + +### Success +`Reason: ReconcileSuccess` indicates the Provider created and is monitoring the +managed resource. + +```yaml +Conditions: + Type: Synced + Status: True + Reason: ReconcileSuccess ``` -In this example, the user provided only a set of labels to select a -`ResourceGroup` managed resource that already exists in the cluster via -`resourceGroupNameSelector`. Then after a specific `ResourceGroup` is selected, -`resourceGroupNameRef` is filled with the name of that `ResourceGroup` managed -resource. Then in the last step, Crossplane fills the actual `resourceGroupName` -field with whatever format Azure accepts it. Once a dependency is resolved, the -controller never changes it. - -Users are able to specify any of these three fields: - -- Selector to select via labels -- Reference to point to a determined managed resource -- Actual value that will be submitted to the provider - -It's important to note that in case a reference exists, the managed resource -does not create the external resource until the referenced object is ready. In -this example, creation call of Azure MySQL Server will not be made until -referenced `ResourceGroup` has its `status.condition` named `Ready` to be true. - - -## Backup and Restore - -Crossplane adheres to Kubernetes conventions as much as possible and one of the -advantages we gain is backup & restore ability with tools that work with native -Kubernetes types, like [Velero][velero]. - -If you'd like to backup and restore manually, you can simply export them and -save YAMLs in your file system. When you reload them, as we've discovered in -import section, their `crossplane.io/external-name` annotation and required -fields are there and those are enough to import a resource. The tool you're -using needs to store `annotations` and `spec` fields, which most tools do -including Velero. - -[rds]: https://aws.amazon.com/rds/ -[cloudsql]: https://cloud.google.com/sql -[api-versioning]: https://kubernetes.io/docs/reference/using-api/#api-versioning#api-versioning -[velero]: https://velero.io/ -[issue-727]: https://github.com/crossplane/crossplane/issues/727 -[issue-1143]: https://github.com/crossplane/crossplane/issues/1143 -[managed-api-patterns]: https://github.com/crossplane/crossplane/blob/release-1.10/design/one-pager-managed-resource-api-design.md -[managed reconciler]: https://github.com/crossplane/crossplane-runtime/blob/84e629b9589852df1322ff1eae4c6e7639cf6e99/pkg/reconciler/managed/reconciler.go#L637 -[management policies]: #management-policies \ No newline at end of file +### Unavailable +`Reason: Unavailable` indicates Crossplane expects the managed resource to be +available, but the Provider reports the resource is unhealthy. + +```yaml +Conditions: + Type: Ready + Status: False + Reason: Unavailable +``` + +### Unknown +`Reason: Unknown` indicates the Provider has an unexpected error with the +managed resource. The `conditions.message` provides more information on what +went wrong. +```yaml +Conditions: + Type: Unknown + Status: False + Reason: Unknown +``` + + +### Upjet Provider conditions +[Upjet](https://github.com/upbound/upjet), the open source tool to generate +Crossplane Providers, also has a set of standard `Conditions`. + + + +#### AsyncOperation + + +Some resources may take more than a minute to create. Upjet based providers can +complete their Kubernetes command before creating the managed resource by using +an asynchronous operation. + + +##### Finished +The `Reason: Finished` indicates the asynchronous operation completed +successfully. + +```yaml +Conditions: + Type: AsyncOperation + Status: True + Reason: Finished +``` + + +##### Ongoing + +`Reason: Ongoing` indicates the managed resource operation is still in progress. + +```yaml +Conditions: + Type: AsyncOperation + Status: True + Reason: Ongoing +``` + + +#### LastAsyncOperation + + +The Upjet `Type: LastAsyncOperation` captures the previous asynchronous +operation status as either `Success` or a failure `Reason`. + + +##### ApplyFailure + + +`Reason: ApplyFailure` indicates the Provider failed to apply a setting to the +managed resource. The `conditions.message` provides more information on what +went wrong. + +```yaml +Conditions: + Type: LastAsyncOperation + Status: False + Reason: ApplyFailure +``` + + +##### DestroyFailure + + +`Reason: DestroyFailure` indicates the Provider failed to delete the managed +resource. The `conditions.message` provides more information on what +went wrong. + +```yaml +Conditions: + Type: LastAsyncOperation + Status: False + Reason: DestroyFailure +``` + +##### Success +`Reason: Success` indicates the Provider successfully created the managed +resource asynchronously. + +```yaml +Conditions: + Type: LastAsyncOperation + Status: True + Reason: Success +``` \ No newline at end of file From 9c6dd58b4b5ae10d2f27f7cb5fc62a88be6f5892 Mon Sep 17 00:00:00 2001 From: Pete Lumbis Date: Wed, 31 May 2023 13:48:50 -0400 Subject: [PATCH 07/17] + finalizer, Subnet, Upjet Signed-off-by: Pete Lumbis --- utils/vale/styles/Crossplane/spelling-exceptions.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/utils/vale/styles/Crossplane/spelling-exceptions.txt b/utils/vale/styles/Crossplane/spelling-exceptions.txt index 022c15c25..b077483e4 100644 --- a/utils/vale/styles/Crossplane/spelling-exceptions.txt +++ b/utils/vale/styles/Crossplane/spelling-exceptions.txt @@ -9,6 +9,7 @@ Datastore editCode Enum Env +finalizer finalizers GCP's Geekdocs @@ -28,9 +29,11 @@ shortcodes SLAs stdin stdout +Subnet tolerations Upbound Upbound's +Upjet Velero VSCode Webpack From ed6f12a848438820ae3ab64e2b204e012a3a9f47 Mon Sep 17 00:00:00 2001 From: Pete Lumbis Date: Wed, 31 May 2023 13:49:07 -0400 Subject: [PATCH 08/17] -failure, -remains Signed-off-by: Pete Lumbis --- utils/vale/styles/alex/ProfanityUnlikely.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/utils/vale/styles/alex/ProfanityUnlikely.yml b/utils/vale/styles/alex/ProfanityUnlikely.yml index 812cf5d23..8e8b5524f 100644 --- a/utils/vale/styles/alex/ProfanityUnlikely.yml +++ b/utils/vale/styles/alex/ProfanityUnlikely.yml @@ -105,7 +105,6 @@ tokens: - execution - executioner - explosion - - failure - fairies - fairy - faith @@ -202,7 +201,6 @@ tokens: - redlight - refugee - reject - - remains - republican - roach - robber From 18c8165509e1a9482eaa28ec03d67bdc89bfd210 Mon Sep 17 00:00:00 2001 From: Pete Lumbis Date: Wed, 31 May 2023 13:49:31 -0400 Subject: [PATCH 09/17] -successfully Signed-off-by: Pete Lumbis --- utils/vale/styles/write-good/Weasel.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/utils/vale/styles/write-good/Weasel.yml b/utils/vale/styles/write-good/Weasel.yml index 19553f1cd..a0818e7e9 100644 --- a/utils/vale/styles/write-good/Weasel.yml +++ b/utils/vale/styles/write-good/Weasel.yml @@ -174,7 +174,6 @@ tokens: - sternly - stupidly - substantially - - successfully - suddenly - surprisingly - suspiciously From 1a7a39ad157638ef5573e0aba8b406ed02ef90b3 Mon Sep 17 00:00:00 2001 From: Pete Lumbis Date: Wed, 31 May 2023 13:58:32 -0400 Subject: [PATCH 10/17] sync to master and v1.11 Signed-off-by: Pete Lumbis --- content/master/concepts/managed-resources.md | 1133 ++++++++++-------- content/v1.11/concepts/managed-resources.md | 1019 ++++++++++------ 2 files changed, 1271 insertions(+), 881 deletions(-) diff --git a/content/master/concepts/managed-resources.md b/content/master/concepts/managed-resources.md index 0950b8b85..3f8d02da4 100644 --- a/content/master/concepts/managed-resources.md +++ b/content/master/concepts/managed-resources.md @@ -3,630 +3,745 @@ title: Managed Resources weight: 102 --- -A Managed Resource (MR) is Crossplane's representation of a resource in an -external system - most commonly a cloud provider. Managed Resources are -opinionated, Crossplane Resource Model ([XRM]({{}})) compliant Kubernetes -Custom Resources that are installed by a Crossplane [provider]({{}}). +A _managed resource_ (`MR`) represents an external service in a Provider. When +asking a Provider to create an external resource, the Provider creates a managed +resource inside the Kubernetes cluster. Every external service managed by +Crossplane maps to a managed resource. + +{{< hint "note" >}} +Crossplane calls the object inside Kubernetes a _managed resource_ and the +external object inside the Provider an _external resource_. +{{< /hint >}} -For example, `RDSInstance` in the AWS Provider corresponds to an actual RDS -Instance in AWS. There is a one-to-one relationship and the changes on managed -resources are reflected directly on the corresponding resource in the provider. -Similarly, the `Database` types in the SQL provider represent a PostgreSQL or -MySQL database. +Examples of managed resources include: +* Amazon AWS EC2 [`Instance`](https://marketplace.upbound.io/providers/upbound/provider-aws/latest/resources/ec2.aws.upbound.io/Instance/v1beta1) +* Google Cloud GKE [`Cluster`](https://marketplace.upbound.io/providers/upbound/provider-gcp/latest/resources/container.gcp.upbound.io/Cluster/v1beta1) +* Microsoft Azure Postgre [`Database`](https://marketplace.upbound.io/providers/upbound/provider-azure/latest/resources/dbforpostgresql.azure.upbound.io/Database/v1beta1) -Managed Resources are the building blocks of Crossplane. They're designed to be -_composed_ into higher level, opinionated Custom Resources that Crossplane calls -Composite Resources or XRs - not used directly. See the -[Composition]({{}}) documentation for more information. +{{< hint "tip" >}} -## Syntax +You can create individual managed resources, but Crossplane recommends using +[Compositions]({{}}) and Claims to create +managed resources. +{{< /hint >}} -Crossplane API conventions extend the Kubernetes API conventions for the schema -of Crossplane managed resources. Following is an example of a managed resource: +## Managed resource fields -{{< tabs >}} -{{< tab "AWS" >}} +The Provider defines the group, Kind and version of a managed resource. The +Provider also define the available settings of a managed resource. -The AWS provider supports provisioning an [RDS][rds] instance via the `RDSInstance` -managed resource it adds to Crossplane. +### Group, kind and version +Each managed resource is a unique API endpoint with their own +group, Kind and version. -```yaml -apiVersion: database.aws.crossplane.io/v1beta1 -kind: RDSInstance -metadata: - name: rdspostgresql +For example the [Upbound AWS +Provider](https://marketplace.upbound.io/providers/upbound/provider-aws/latest/) +defines the {{}}Instance{{}} Kind from the +group {{}}ec2.aws.upbound.io{{}} + +```yaml {label="gkv"} +apiVersion: ec2.aws.upbound.io/v1beta1 +kind: Instance +``` + + +### deletionPolicy + + +A managed resource's `deletionPolicy` tells the Provider what to do after +deleting the managed resource. If the `deletionPolicy` is `delete` the Provider +deletes the external resource as well. If the `deletionPolicy` is `orphan` the +Provider deletes the managed resource but doesn't delete the external resource. + +#### Options +* `deletionPolicy: delete` - **Default** - Delete the external resource when deleting the managed resource. +* `deletionPolicy: orphan` - Leave the external resource when deleting the managed resource. + + +### forProvider + + +The {{}}spec.forProvider{{}} of a +managed resource maps to the parameters of the external resource. + +For example, when creating an AWS EC2 instance, the Provider supports defining +the AWS {{}}region{{}} and the VM +size, called the +{{}}instanceType{{}}. + +{{< hint "note" >}} +The Provider defines the settings and their valid values. Providers also define +required and optional values in the `forProvider` definition. + +Refer to the documentation of your specific Provider for details. +{{< /hint >}} + + +```yaml {label="forProvider"} +apiVersion: ec2.aws.upbound.io/v1beta1 +kind: Instance spec: forProvider: - region: us-east-1 - dbInstanceClass: db.t2.small - masterUsername: masteruser - allocatedStorage: 20 - engine: postgres - engineVersion: "12" - skipFinalSnapshotBeforeDeletion: true - writeConnectionSecretToRef: - namespace: crossplane-system - name: aws-rdspostgresql-conn + region: us-west-1 + instanceType: t2.micro ``` -```console -kubectl apply -f https://raw.githubusercontent.com/crossplane/crossplane/release-1.10/docs/snippets/provision/aws.yaml +{{< hint "important">}} +Crossplane considers the `forProvider` field of a managed resource +the "source of truth." Crossplane overrides any changes made to a managed +resource outside of Crossplane. If a user makes a change inside a +Provider's web console, Crossplane reverts that change back to what's +configured in the `forProvider` setting. +{{< /hint >}} + +Providers add any settings not manually set to the `forProvider` field of the +created managed resource object. +Use `kubectl describe ` to view the applied values. + +#### Referencing other resources + +Some fields in a managed resource may depend on values from other managed +resources. For example a VM may need the name of a virtual network to use. + +Managed resources can reference other managed resources by external name, name +reference or selector. + +##### Matching by external name + +When matching a resource by name Crossplane looks for the name of the external +resource in the Provider. + +For example, a AWS VPC object named `my-test-vpc` has the external name +`vpc-01353cfe93950a8ff`. + +```shell +kubectl get vpc +NAME READY SYNCED EXTERNAL-NAME AGE +my-test-vpc True True vpc-01353cfe93950a8ff 49m ``` -Creating the above instance will cause Crossplane to provision an RDS instance -on AWS. You can view the progress with the following command: +To match the VPC by name, use the external name. For example, creating a Subnet +managed resource attached to this VPC. + +```yaml +apiVersion: ec2.aws.upbound.io/v1beta1 +kind: Subnet +spec: + forProvider: + # Removed for brevity + vpcId: vpc-01353cfe93950a8ff +``` + +##### Matching by name reference -```console -kubectl get rdsinstance rdspostgresql +To match a resource based on the name of the managed resource and not the +external resource name inside the Provider, use a `nameRef`. + +For example, a AWS VPC object named `my-test-vpc` has the external name +`vpc-01353cfe93950a8ff`. + +```shell +kubectl get vpc +NAME READY SYNCED EXTERNAL-NAME AGE +my-test-vpc True True vpc-01353cfe93950a8ff 49m ``` -When provisioning is complete, you should see `READY: True` in the output. You -can take a look at its connection secret that is referenced under -`spec.writeConnectionSecretToRef`: +To match the VPC by name reference, use the managed resource name. For example, +creating a Subnet managed resource attached to this VPC. + +```yaml +apiVersion: ec2.aws.upbound.io/v1beta1 +kind: Subnet +spec: + forProvider: + # Removed for brevity + vpcIdRef: + name: my-test-vpc +``` + + +##### Matching by selector + +Matching by selector is the most flexible matching method. + +{{}} +The [Composition]({{}}) section covers the +`matchControllerRef` selector. +{{}} + +Use `matchLabels` to match the labels applied to a resource. For example, this +Subnet resource only matches VPC resources with the label +`my-label: label-value`. -```console -kubectl describe secret aws-rdspostgresql-conn -n crossplane-system +```yaml +apiVersion: ec2.aws.upbound.io/v1beta1 +kind: Subnet +spec: + forProvider: + # Removed for brevity + vpcIdSelector: + matchLabels: + my-label: label-value ``` -You can then delete the `RDSInstance`: -```console -kubectl delete rdsinstance rdspostgresql +#### Immutable fields + +Some providers don't support changing the fields of some managed resources after +creation. For example, you can't change the `region` of an Amazon AWS +`RDSInstance`. These fields are _immutable fields_. Amazon requires you delete +and recreate the resource. + +Crossplane allows you to edit the immutable field of a managed resource, but +doesn't apply the change. Crossplane never deletes a resource based on a +`forProvider` change. + +{{}} +Crossplane behaves differently than other tools like Terraform. Terraform +deletes and recreates a resource to change an immutable field. Crossplane only +deletes managed resources if the Kubernetes deletes the managed resource object. +{{< /hint >}} + + + +### managementPolicy + + +{{}} +The managed resource `managementPolicy` option is an alpha feature. + +Enable the `managementPolicy` in a provider with `--enable-management-policies` +in a +[ControllerConfig]({{}}). +{{< /hint >}} + +A `managementPolicy` determines if Crossplane can make changes to managed +resources. The `ObserveOnly` policy imports existing external resources not +originally created by Crossplane. +This allows new managed resources to reference +the `ObserveOnly` resource, for example, a shared database or network. +The `ObserveOnly` policy can also place existing resources under the control of +Crossplane. + +{{< hint "tip" >}} +Read the [Import Existing Resources]({{}}) guide for more +information on using the `managementPolicy` to import existing resources. +{{< /hint >}} + +#### Options +* `managementPolicy: FullControl` - **Default** - Crossplane can create, change + and delete the managed resource. +* `managementPolicy: ObserveOnly` - Crossplane only imports the details of the + external resource, but doesn't make any changes to the managed resource. + + + +### providerConfigRef + + +The `providerConfigRef` on a managed resource tells the Provider which +[ProviderConfig]({{}}) to +use when creating the managed resource. + +Use a ProviderConfig to define the authentication method to use when +communicating to the Provider. + +{{< hint "important" >}} +If `providerConfigRef` isn't applied, Providers use the ProviderConfig named `default`. +{{< /hint >}} + +For example, a managed resource references a ProviderConfig named +{{}}user-keys{{}}. + +This matches the {{}}name{{}} of a ProviderConfig. + +```yaml {label="pcref"} +apiVersion: ec2.aws.upbound.io/v1beta1 +kind: Instance +spec: + forProvider: + # Removed for brevity + providerConfigRef: user-keys ``` -{{< /tab >}} -{{< tab "GCP" >}} +```yaml {label="pc"} +apiVersion: aws.crossplane.io/v1beta1 +kind: ProviderConfig +metadata: + name: user-keys +# Removed for brevity +``` + +{{< hint "tip" >}} +Each managed resource can reference different ProviderConfigs. This allows +different managed resources to authenticate with different credentials to the +same Provider. +{{< /hint >}} -The GCP provider supports provisioning a [CloudSQL][cloudsql] instance with the -`CloudSQLInstance` managed resource it adds to Crossplane. + +### providerRef + -```yaml -apiVersion: database.gcp.crossplane.io/v1beta1 -kind: CloudSQLInstance +Crossplane deprecated the `providerRef` field in `crossplane-runtime` +[v0.10.0](https://github.com/crossplane/crossplane-runtime/releases/tag/v0.10.0). +Managed resources using `providerRef`must use [`providerConfigRef`](#providerconfigref). + + + +### writeConnectionSecretToRef + + +When a Provider creates a managed resource it may generate resource-specific +details, like usernames, passwords or connection details like an IP address. + +Crossplane stores these details in a Kubernetes Secret object specified by the +`writeConnectionSecretToRef` values. + +For example, when creating an AWS RDS database instance with the Crossplane +[community AWS +provider](https://marketplace.upbound.io/providers/crossplane-contrib/provider-aws/v0.40.0) +generates an endpoint, password, port and username data. The Provider saves +these variables in the +{{}}writeConnectionSecretToRef.name{{}} +field. + +```yaml {label="secretname"} +apiVersion: database.aws.crossplane.io/v1beta1 +kind: RDSInstance metadata: - name: cloudsqlpostgresql + name: my-rds-instance spec: forProvider: - databaseVersion: POSTGRES_12 - region: us-central1 - settings: - tier: db-custom-1-3840 - dataDiskType: PD_SSD - dataDiskSizeGb: 10 + # Removed for brevity writeConnectionSecretToRef: - namespace: crossplane-system - name: cloudsqlpostgresql-conn + name: rds-secret ``` -```console -kubectl apply -f https://raw.githubusercontent.com/crossplane/crossplane/release-1.10/docs/snippets/provision/gcp.yaml +Viewing the Secret object shows the saved fields. + +```yaml {copy-lines="1"} +kubectl describe secret rds-secret +Name: rds-secret +# Removed for brevity +Data +==== +port: 4 bytes +username: 10 bytes +endpoint: 54 bytes +password: 27 bytes ``` -Creating the above instance will cause Crossplane to provision a CloudSQL -instance on GCP. You can view the progress with the following command: +{{}} +The Provider determines the data written to the Secret object. Refer to the +specific Provider documentation for the generated Secret data. +{{< /hint >}} -```console -kubectl get cloudsqlinstance cloudsqlpostgresql -``` + +### publishConnectionDetailsTo + + +The `publishConnectionDetailsTo` field expands on +[`writeConnectionSecretToRef`](#writeconnectionsecrettoref) supporting storing +managed resource information as a Kubernetes Secret object or in an external +secrets store like [HashiCorp Vault](https://www.vaultproject.io/). -When provisioning is complete, you should see `READY: True` in the output. You -can take a look at its connection secret that is referenced under -`spec.writeConnectionSecretToRef`: +Using `publishConnectionDetailsTo` requires enabling Crossplane +External Secrets Stores (ESS). Enable ESS inside a Provider with a +[ControllerConfig]({{}}) and +in Crossplane with the `--enable-external-secret-stores` argument. -```console -kubectl describe secret cloudsqlpostgresql-conn -n crossplane-system +{{< hint "note" >}} +Not all Providers support `publishConnectionDetailsTo`. Check your Provider +documentation for details. +{{< /hint >}} + +#### Publish secrets to Kubernetes + +To publish the data generated by a managed resource as a Kubernetes Secret +object provide a +{{}}publishConnectionDetailsTo.name{{< /hover >}} + +```yaml {label="k8secret"} +apiVersion: rds.aws.upbound.io/v1beta1 +kind: Instance +spec: + forProvider: + # Removed for brevity + publishConnectionDetailsTo: + name: rds-kubernetes-secret ``` -You can then delete the `CloudSQLInstance`: +Crossplane can apply labels and annotations to the Kubernetes secret as well +using +{{}}publishConnectionDetailsTo.metadata{{}}. -```console -kubectl delete cloudsqlinstance cloudsqlpostgresql +```yaml {label="k8label"} +apiVersion: rds.aws.upbound.io/v1beta1 +kind: Instance +spec: + forProvider: + # Removed for brevity + publishConnectionDetailsTo: + name: rds-kubernetes-secret + metadata: + labels: + label-tag: label-value + annotations: + annotation-tag: annotation-value ``` -{{< /tab >}} -{{< tab "Azure" >}} +#### Publish secrets to an external secrets store -The Azure provider supports provisioning an [Azure Database for PostgreSQL] -instance with the `PostgreSQLServer` managed resource it adds to Crossplane. +Publishing secrets data to an external secret store like +[HashiCorp Vault](https://www.vaultproject.io/) relies on a +{{}}publishConnectionDetailsTo.configRef{{}}. -> Note: provisioning an Azure Database for PostgreSQL requires the presence of a -> [Resource Group] in your Azure account. We go ahead and provision a new -> `ResourceGroup` here in case you do not already have a suitable one in your -> account. +The +{{}}configRef.name{{}} references a +{{}}StoreConfig{{}} +object. -```yaml -apiVersion: azure.crossplane.io/v1alpha3 -kind: ResourceGroup -metadata: - name: sqlserverpostgresql-rg -spec: - location: West US 2 ---- -apiVersion: database.azure.crossplane.io/v1beta1 -kind: PostgreSQLServer -metadata: - name: sqlserverpostgresql +```yaml {label="configref"} +apiVersion: rds.aws.upbound.io/v1beta1 +kind: Instance spec: forProvider: - administratorLogin: myadmin - resourceGroupNameRef: - name: sqlserverpostgresql-rg - location: West US 2 - sslEnforcement: Disabled - version: "9.6" - sku: - tier: GeneralPurpose - capacity: 2 - family: Gen5 - storageProfile: - storageMB: 20480 - writeConnectionSecretToRef: - namespace: crossplane-system - name: sqlserverpostgresql-conn + # Removed for brevity + publishConnectionDetailsTo: + name: rds-kubernetes-secret + configRef: + name: my-vault-storeconfig ``` -```console -kubectl apply -f https://raw.githubusercontent.com/crossplane/crossplane/release-1.10/docs/snippets/provision/azure.yaml +```yaml {label="storeconfig"} +apiVersion: secrets.crossplane.io/v1alpha1 +kind: StoreConfig +metadata: + name: my-vault-storeconfig +# Removed for brevity ``` -Creating the above instance will cause Crossplane to provision a PostgreSQL -database instance on Azure. You can view the progress with the following -command: +{{}} +Read the +[Vault as an External Secrets Store]({{}}) +guide for details on using StoreConfig objects. +{{< /hint >}} -```console -kubectl get postgresqlserver sqlserverpostgresql -``` -When provisioning is complete, you should see `READY: True` in the output. You -can take a look at its connection secret that is referenced under -`spec.writeConnectionSecretToRef`: +## Annotations -```console -kubectl describe secret sqlserverpostgresql-conn -n crossplane-system -``` +Crossplane applies a standard set of Kubernetes `annotations` to managed +resources. -You can then delete the `PostgreSQLServer`: +{{}} +| Annotation | Definition | +| --- | --- | +| `crossplane.io/external-name` | The name of the managed resource inside the Provider. | +| `crossplane.io/external-create-pending` | The timestamp of the last time the managed resource was in the `PENDING` state. | +| `crossplane.io/external-create-succeeded` | The timestamp of the last time the Provider successfully created the managed resource. | +| `crossplane.io/external-create-failed` | The timestamp of the last time the Provider failed to create the managed resource. | +| `crossplane.io/paused` | Indicates Crossplane isn't reconciling this resource. Read the [Pause Annotation](#pause) for more details. | +| `crossplane.io/composition-resource-name` | For managed resource created by a Composition, this is the Composition's `resources.name` value. | +{{
}} -```console -kubectl delete postgresqlserver sqlserverpostgresql -kubectl delete resourcegroup sqlserverpostgresql-rg +### Naming external resources +By default Providers give external resources the same name as the Kubernetes +object. + +For example, a managed resource named +{{}}my-rds-instance{{}} has +the name `my-rds-instance` as an external resource inside the Provider's +environment. + +```yaml {label="external-name"} +apiVersion: database.aws.crossplane.io/v1beta1 +kind: RDSInstance +metadata: + name: my-rds-instance ``` -{{< /tab >}} -{{< /tabs >}} - -In Kubernetes, `spec` top field represents the desired state of the user. -Crossplane adheres to that and has its own conventions about how the fields -under `spec` should look like. - -* `writeConnectionSecretToRef`: A reference to the secret that you want this - managed resource to write its connection secret that you'd be able to mount to - your pods in the same namespace. For `RDSInstance`, this secret would contain - `endpoint`, `username` and `password`. - -* `providerConfigRef`: Reference to the `ProviderConfig` resource that will - provide information regarding authentication of Crossplane to the provider. - `ProviderConfig` resources refer to `Secret` and potentially contain other - information regarding authentication. The `providerConfigRef` is defaulted to - a `ProviderConfig` named `default` if omitted. - -* `deletionPolicy`: Enum to specify whether the actual cloud resource should be - deleted when this managed resource is deleted in Kubernetes API server. - Possible values are `Delete` (the default) and `Orphan`. - -* `managementPolicy`: Enum to specify the level of control Crossplane has over - the external resource. - Possible values are `FullControl` (the default) and `ObserveOnly`. - {{}} - `managementPolicy` is an experimental feature, see the management policies - section below for further details. - {{< /hint >}} - -* `forProvider`: While the rest of the fields relate to how Crossplane should - behave, the fields under `forProvider` are solely used to configure the actual - external resource. In most of the cases, the field names correspond to the - what exists in provider's API Reference. - - The objects under `forProvider` field can get huge depending on the provider - API. For example, GCP `ServiceAccount` has only a few fields while GCP - `CloudSQLInstance` has over 100 fields that you can configure. - -### Versioning - -Crossplane closely follows the [Kubernetes API versioning -conventions][api-versioning] for the CRDs that it deploys. In short, for -`vXbeta` and `vX` versions, you can expect that either automatic migration or -instructions for manual migration will be provided when a new version of that -CRD schema is released. - -In practice, we suggest the following guidelines to provider developers: -* Every new kind has to be introduced as `v1alpha1` with no exception. -* Breaking changes require a version change, i.e. `v1alpha1` needs to become - `v1alpha2`. - * Alpha resources don't require automatic conversions or manual instructions - but it's recommended that manual instructions are provided. - * Beta resources require at least manual instructions but it's recommended - that conversion webhooks are used so that users can upgrade without any - hands-on operation. - * Stable resources require conversion webhooks. -* As long as the developer feels comfortable with the guarantees above, they can - bump the version to beta or stable given that the CRD shape adheres to the - Crossplane Resource Model (XRM) specifications for managed resources - [here][managed-api-patterns]. -* It's suggested that the bump from Alpha to Beta or from Beta to Stable happen - after a bake period which includes at least one release. - -### Grouping - -In general, managed resources are high fidelity resources meaning they will -provide parameters and behaviors that are provided by the external resource API. -This applies to grouping of resources, too. For example, `Queue` appears under -`sqs` API group in AWS,so, its `APIVersion` and `Kind` look like the following: +```shell +kubectl get rdsinstance +NAME READY SYNCED EXTERNAL-NAME AGE +my-rds-instance True True my-rds-instance 11m +``` -```yaml -apiVersion: sqs.aws.crossplane.io/v1beta1 -kind: Queue +Managed resource created with a `crossplane.io/external-name` +annotation already provided use the annotation value as the external +resource name. + +For example, the Provider creates managed resource named +{{< hover label="custom-name" line="6">}}my-rds-instance{{
}} but uses +the name {{}}my-custom-name{{}} +for the external resource inside AWS. + +```yaml {label="custom-name"} +apiVersion: database.aws.crossplane.io/v1beta1 +kind: RDSInstance +metadata: + name: my-rds-instance + annotations: + crossplane.io/external-name: my-custom-namee ``` -## Behavior +```shell +kubectl get rdsinstance +NAME READY SYNCED EXTERNAL-NAME AGE +my-rds-instance True True my-custom-name 11m +``` -As a general rule, managed resource controllers try not to make any decision -that is not specified by the user in the desired state since managed resources -are the lowest level primitives that operate directly on the cloud provider -APIs. +### Creation annotations -### Continuous Reconciliation +Providers create new managed resources with the +`crossplane.io/external-create-pending` annotation. -Crossplane providers continuously reconcile the managed resource to achieve the -desired state. The parameters under `spec` are considered the one and only -source of truth for the external resource. This means that if someone changed a -configuration in the UI of the provider, like AWS Console, Crossplane will -change it back to what's given under `spec`. +The Provider applies the `crossplane.io/external-create-succeeded` or +`crossplane.io/external-create-failed` annotation after making the external API +call and receiving a response. -#### Connection Details +{{}} +If a Provider restarts before creating the `succeed` or `fail` annotations the +Provider can't reconcile the manged resource. -Some Crossplane resources support writing connection details - things like URLs, -usernames, endpoints, and passwords to a Kubernetes `Secret`. You can specify -the secret to write by setting the `spec.writeConnectionSecretToRef` field. Note -that while all managed resources have a `writeConnectionSecretToRef` field, not -all managed resources actually have connection details to write - many will -write an empty `Secret`. +Read Crossplane [issue +#3037](https://github.com/crossplane/crossplane/issues/3037#issuecomment-1110142427) +for more details +{{< /hint >}} -> Which managed resources have connection details and what connection details -> they have is currently undocumented. This is tracked in [this -> issue][issue-1143]. -#### Immutable Properties +### Pause +Manually applying the `crossplane.io/paused` annotation causes the Provider to +stop reconciling the managed resource. -There are configuration parameters in external resources that cloud providers do -not allow to be changed. For example, in AWS, you cannot change the region of an -`RDSInstance`. +Pausing a resource is useful when modifying Providers or preventing +race-conditions when editing Kubernetes objects. -Some infrastructure tools such as Terraform delete and recreate the resource to -accommodate those changes but Crossplane does not take that route. Unless the -managed resource is deleted and its `deletionPolicy` is `Delete`, its controller -never deletes the external resource in the provider. +Apply a {{}}crossplane.io/paused: "true"{{}} +annotation to a managed resource to pause reconciliation. -> Kubernetes does not yet support immutable fields for custom resources. This -> means Crossplane will allow immutable fields to be changed, but will not -> actually make the desired change. This is tracked in [this issue][issue-727]. +{{< hint "note" >}} +Only the value `"true"` pauses reconciliation. +{{< /hint >}} -#### Pausing Reconciliations -If a managed resource being reconciled by the [managed reconciler], has the -`crossplane.io/paused` annotation with its value set to `true` as in the -following example, then further reconciliations are paused on that resource -after emitting an event with the type `Synced`, the status `False`, -and the reason `ReconcilePaused`: -```yaml +```yaml {label="pause"} apiVersion: ec2.aws.upbound.io/v1beta1 -kind: VPC +kind: Instance metadata: - name: paused-vpc + name: my-rds-instance annotations: crossplane.io/paused: "true" -... +spec: + forProvider: + region: us-west-1 + instanceType: t2.micro ``` -Reconciliations on the managed resource will resume once the -`crossplane.io/paused` annotation is removed or its value is set -to anything other than `true`. -### External Name +Remove the annotation to resume reconciliation. -By default the name of the managed resource is used as the name of the external -cloud resource that will show up in your cloud console. To specify a different -external name, Crossplane has a special annotation to represent the name of the -external resource. For example, I would like to have a `CloudSQLInstance` with -an external name that is different than its managed resource name: +## Finalizers +Crossplane applies a +[Finalizer](https://kubernetes.io/docs/concepts/overview/working-with-objects/finalizers/) +on managed resources to control their deletion. + +{{< hint "note" >}} +Kubernetes can't delete objects with Finalizers. +{{}} + +When a Crossplane deletes managed resource the Provider begins deleting the +external resource, but the managed resource remains until the external +resource is fully deleted. + +When the external resource is fully deleted Crossplane removes the Finalizer and +deletes the managed resource object. + +## Conditions + +Crossplane has a standard set of `Conditions` for a managed +resource. View the `Conditions` of a managed resource with +`kubectl describe ` + + +{{}} +Providers may define their own custom `Conditions`. +{{}} + + +### Available +`Reason: Available` indicates the Provider created the managed resource. ```yaml -apiVersion: database.gcp.crossplane.io/v1beta1 -kind: CloudSQLInstance -metadata: - name: foodb - annotations: - crossplane.io/external-name: my-special-db -spec: - ... +Conditions: + Type: Ready + Status: True + Reason: Available ``` +### Creating -When you create this managed resource, you will see that the name of -`CloudSQLInstance` in GCP console will be `my-special-db`. - -If the annotation is not given, Crossplane will fill it with the name of the -managed resource by default. In cases where provider doesn't allow you to name -the resource, like AWS VPC, the controller creates the resource and sets -external annotation to be the name that the cloud provider chose. So, you would -see something like `vpc-28dsnh3` as the value of `crossplane.io/external-name` -annotation of your AWS `VPC` resource even if you added your own custom external -name during creation. - -### Late Initialization - -For some of the optional fields, users rely on the default that the cloud -provider chooses for them. Since Crossplane treats the managed resource as the -source of the truth, values of those fields need to exist in `spec` of the -managed resource. So, in each reconciliation, Crossplane will fill the value of -a field that is left empty by the user but is assigned a value by the provider. -For example, there could be two fields like `region` and `availabilityZone` and -you might want to give only `region` and leave the availability zone to be -chosen by the cloud provider. In that case, if the provider assigns an -availability zone, Crossplane gets that value and fills `availabilityZone`. Note -that if the field is already filled, the controller won't override its value. - -### Deletion - -When a deletion request is made for a managed resource, its controller starts -the deletion process immediately. However, the managed resource is kept in the -Kubernetes API (via a finalizer) until the controller confirms the external -resource in the cloud is gone. So you can be sure that if the managed resource -is deleted, then the external cloud resource is also deleted. Any errors that -happen during deletion will be added to the `status` of the managed resource, so -you can troubleshoot any issues. - -## Dependencies - -In many cases, an external resource refers to another one for a specific -configuration. For example, you could want your Azure Kubernetes cluster in a -specific Virtual Network. External resources have specific fields for these -relations, however, they usually require the information to be supplied in -different formats. In Azure MySQL, you might be required to enter only the name -of the Virtual Network while in Azure Kubernetes, it could be required to enter -a string in a specific format that includes other information such as resource -group name. - -In Crossplane, users have 3 fields to refer to another resource. Here is an -example from Azure MySQL managed resource referring to an Azure Resource Group: +`Reason: Creating` indicates the Provider is attempting to create the managed +resource. ```yaml -spec: - forProvider: - resourceGroupName: foo-res-group - resourceGroupNameRef: - name: resourcegroup - resourceGroupNameSelector: - matchLabels: - app: prod +Conditions: + Type: Ready + Status: False + Reason: Creating ``` -In this example, the user provided only a set of labels to select a -`ResourceGroup` managed resource that already exists in the cluster via -`resourceGroupNameSelector`. Then after a specific `ResourceGroup` is selected, -`resourceGroupNameRef` is filled with the name of that `ResourceGroup` managed -resource. Then in the last step, Crossplane fills the actual `resourceGroupName` -field with whatever format Azure accepts it. Once a dependency is resolved, the -controller never changes it. - -Users are able to specify any of these three fields: +### Deleting +`Reason: Deleting` indicates the Provider is attempting to delete the managed +resource. -- Selector to select via labels -- Reference to point to a determined managed resource -- Actual value that will be submitted to the provider +```yaml +Conditions: + Type: Ready + Status: False + Reason: Deleting +``` -It's important to note that in case a reference exists, the managed resource -does not create the external resource until the referenced object is ready. In -this example, creation call of Azure MySQL Server will not be made until -referenced `ResourceGroup` has its `status.condition` named `Ready` to be true. -## Management Policies +### Paused +`Reason: ReconcilePaused` indicates the managed resource has a [Pause](#paused) +annotation +```yaml +Conditions: + Type: Synced + Status: False + Reason: ReconcilePaused +``` -Crossplane offers a set of management policies that allow you to define the -level of control it has over external resources. You can configure these -policies using the `spec.managementPolicy` field in the managed resource -definition. The available policies include: + +### ReconcileError + +`Reason: ReconcileError` indicates Crossplane encountered an error while +reconciling the managed resource. The `Message:` value of the `Condition` helps +identify the Crossplane error. -- `FullControl (Default)`: With this policy, Crossplane fully manages and -controls the external resource. -- `ObserveOnly`: With the ObserveOnly policy, Crossplane only observes the -external resource without making any changes or deletions. +```yaml +Conditions: + Type: Synced + Status: False + Reason: ReconcileError +``` -{{}} -Management policies are an experimental feature, and the API is -subject to change. -{{< /hint >}} -To use management policies, you must enable them with the -`--enable-management-policies` flag when starting the provider controller. -## Importing Existing Resources -If you have some resources that are already provisioned in the cloud provider, -you can import them as managed resources and let Crossplane manage them. What -you need to do is to enter the name of the external resource as well as the -required fields on the managed resource. For example, let's say I have a GCP -Network provisioned from GCP console and I would like to migrate it to -Crossplane. Here is the YAML that I need to create: +### Success +`Reason: ReconcileSuccess` indicates the Provider created and is monitoring the +managed resource. ```yaml -apiVersion: compute.gcp.crossplane.io/v1beta1 -kind: Network -metadata: - name: foo-network - annotations: - crossplane.io/external-name: existing-network -spec: - forProvider: {} - providerConfigRef: - name: default +Conditions: + Type: Synced + Status: True + Reason: ReconcileSuccess ``` -Crossplane will check whether a GCP Network called `existing-network` exists, -and if it does, then the optional fields under `forProvider` will be filled with -the values that are fetched from the provider. +### Unavailable +`Reason: Unavailable` indicates Crossplane expects the managed resource to be +available, but the Provider reports the resource is unhealthy. -Note that if a resource has required fields, you must fill those fields or the -creation of the managed resource will be rejected. So, in those cases, you will -need to enter the name of the resource as well as the required fields. +```yaml +Conditions: + Type: Ready + Status: False + Reason: Unavailable +``` -### Alternative Import Procedure: Start with ObserveOnly +### Unknown +`Reason: Unknown` indicates the Provider has an unexpected error with the +managed resource. The `conditions.message` provides more information on what +went wrong. +```yaml +Conditions: + Type: Unknown + Status: False + Reason: Unknown +``` -Directly importing existing managed resources approach has the following caveats: -1. You must provide all the required fields in the spec of the resource with -correct values even though they're not used for importing the resource. A wrong -value for a required field result in a configuration update which isn't -desired. -2. Any typos in the external name annotation or mistakes in the identifying -arguments, such as the `region`, results in the creation of a new resource -instead of importing the existing one. +### Upjet Provider conditions +[Upjet](https://github.com/upbound/upjet), the open source tool to generate +Crossplane Providers, also has a set of standard `Conditions`. -Instead of manually creating resources you can import the resource with an -`ObserveOnly` management policy. -Crossplane imports `ObserveOnly` resources but never changes or deletes the -resource. + +#### AsyncOperation + -{{< hint "important" >}} -Management policies including `ObserveOnly` are experimental. They must be -explicitly enabled. -See the management policies section for more details. -{{< /hint >}} +Some resources may take more than a minute to create. Upjet based providers can +complete their Kubernetes command before creating the managed resource by using +an asynchronous operation. -To configure an `ObserveOnly` resource: -1. Create a new resource with an {{}}ObserveOnly{{}} - management policy. - 1. With the - {{}}crossplane.io/external-name{{}} - annotation set to the external name of the resource to import. - 1. Only provide the identifying arguments (for example, - {{}}region{{}}) in the spec - of the resource. +##### Finished +The `Reason: Finished` indicates the asynchronous operation completed +successfully. -```yaml {label="oo"} -apiVersion: sql.gcp.upbound.io/v1beta1 -kind: DatabaseInstance -metadata: - annotations: - crossplane.io/external-name: existing-database-instance - name: existing-database-instance -spec: - managementPolicy: ObserveOnly - forProvider: - region: "us-central1" +```yaml +Conditions: + Type: AsyncOperation + Status: True + Reason: Finished ``` -Crossplane discovers the managed resource and populates the -{{}}status.atProvider{{}} -with the observed state. -```yaml {label="ooPopulated"} -apiVersion: sql.gcp.upbound.io/v1beta1 -kind: DatabaseInstance -metadata: - annotations: - crossplane.io/external-name: existing-database-instance - name: existing-database-instance -spec: - managementPolicy: ObserveOnly - forProvider: - region: us-central1 -status: - atProvider: - connectionName: crossplane-playground:us-central1:existing-database-instance - databaseVersion: POSTGRES_14 - deletionProtection: true - firstIpAddress: 35.184.74.79 - id: existing-database-instance - publicIpAddress: 35.184.74.79 - region: us-central1 - - settings: - - activationPolicy: ALWAYS - availabilityType: REGIONAL - diskSize: 100 - - pricingPlan: PER_USE - tier: db-custom-4-26624 - version: 4 - conditions: - - lastTransitionTime: "2023-02-22T07:16:51Z" - reason: Available - status: "True" - type: Ready - - lastTransitionTime: "2023-02-22T07:16:51Z" - reason: ReconcileSuccess - status: "True" - type: Synced +##### Ongoing + +`Reason: Ongoing` indicates the managed resource operation is still in progress. + +```yaml +Conditions: + Type: AsyncOperation + Status: True + Reason: Ongoing ``` -To allow Crossplane to control and change the `ObserveOnly` resource, edit the -policy. + +#### LastAsyncOperation + -Change the {{}}ObserveOnly{{}} field -to {{}}FullControl{{}}. +The Upjet `Type: LastAsyncOperation` captures the previous asynchronous +operation status as either `Success` or a failure `Reason`. -Copy any required parameter values from -{{}}status.atProvider{{}} and provide them -in {{}}spec.forProvider{{}}. + +##### ApplyFailure + -```yaml {label="fc"} -apiVersion: sql.gcp.upbound.io/v1beta1 -kind: DatabaseInstance -metadata: - annotations: - crossplane.io/external-name: existing-database-instance - name: existing-database-instance -spec: - managementPolicy: Full - forProvider: - databaseVersion: POSTGRES_14 - region: us-central1 - settings: - - diskSize: 100 - tier: db-custom-4-26624 -status: - atProvider: - - conditions: - - lastTransitionTime: "2023-02-22T07:16:51Z" - reason: Available - status: "True" - type: Ready - - lastTransitionTime: "2023-02-22T11:16:45Z" - reason: ReconcileSuccess - status: "True" - type: Synced +`Reason: ApplyFailure` indicates the Provider failed to apply a setting to the +managed resource. The `conditions.message` provides more information on what +went wrong. + +```yaml +Conditions: + Type: LastAsyncOperation + Status: False + Reason: ApplyFailure ``` -## Backup and Restore - -Crossplane adheres to Kubernetes conventions as much as possible and one of the -advantages we gain is backup & restore ability with tools that work with native -Kubernetes types, like [Velero][velero]. - -If you'd like to backup and restore manually, you can simply export them and -save YAMLs in your file system. When you reload them, as we've discovered in -import section, their `crossplane.io/external-name` annotation and required -fields are there and those are enough to import a resource. The tool you're -using needs to store `annotations` and `spec` fields, which most tools do -including Velero. - -[rds]: https://aws.amazon.com/rds/ -[cloudsql]: https://cloud.google.com/sql -[api-versioning]: https://kubernetes.io/docs/reference/using-api/#api-versioning#api-versioning -[velero]: https://velero.io/ -[issue-727]: https://github.com/crossplane/crossplane/issues/727 -[issue-1143]: https://github.com/crossplane/crossplane/issues/1143 -[managed-api-patterns]: https://github.com/crossplane/crossplane/blob/release-1.10/design/one-pager-managed-resource-api-design.md -[managed reconciler]: https://github.com/crossplane/crossplane-runtime/blob/84e629b9589852df1322ff1eae4c6e7639cf6e99/pkg/reconciler/managed/reconciler.go#L637 -[management policies]: #management-policies \ No newline at end of file + +##### DestroyFailure + + +`Reason: DestroyFailure` indicates the Provider failed to delete the managed +resource. The `conditions.message` provides more information on what +went wrong. + +```yaml +Conditions: + Type: LastAsyncOperation + Status: False + Reason: DestroyFailure +``` + +##### Success +`Reason: Success` indicates the Provider successfully created the managed +resource asynchronously. + +```yaml +Conditions: + Type: LastAsyncOperation + Status: True + Reason: Success +``` \ No newline at end of file diff --git a/content/v1.11/concepts/managed-resources.md b/content/v1.11/concepts/managed-resources.md index cd581f27e..3f8d02da4 100644 --- a/content/v1.11/concepts/managed-resources.md +++ b/content/v1.11/concepts/managed-resources.md @@ -3,470 +3,745 @@ title: Managed Resources weight: 102 --- -A Managed Resource (MR) is Crossplane's representation of a resource in an -external system - most commonly a cloud provider. Managed Resources are -opinionated, Crossplane Resource Model ([XRM]({{}})) compliant Kubernetes -Custom Resources that are installed by a Crossplane [provider]({{}}). +A _managed resource_ (`MR`) represents an external service in a Provider. When +asking a Provider to create an external resource, the Provider creates a managed +resource inside the Kubernetes cluster. Every external service managed by +Crossplane maps to a managed resource. -For example, `RDSInstance` in the AWS Provider corresponds to an actual RDS -Instance in AWS. There is a one-to-one relationship and the changes on managed -resources are reflected directly on the corresponding resource in the provider. -Similarly, the `Database` types in the SQL provider represent a PostgreSQL or -MySQL database. +{{< hint "note" >}} +Crossplane calls the object inside Kubernetes a _managed resource_ and the +external object inside the Provider an _external resource_. +{{< /hint >}} -Managed Resources are the building blocks of Crossplane. They're designed to be -_composed_ into higher level, opinionated Custom Resources that Crossplane calls -Composite Resources or XRs - not used directly. See the -[Composition]({{}}) documentation for more information. +Examples of managed resources include: +* Amazon AWS EC2 [`Instance`](https://marketplace.upbound.io/providers/upbound/provider-aws/latest/resources/ec2.aws.upbound.io/Instance/v1beta1) +* Google Cloud GKE [`Cluster`](https://marketplace.upbound.io/providers/upbound/provider-gcp/latest/resources/container.gcp.upbound.io/Cluster/v1beta1) +* Microsoft Azure Postgre [`Database`](https://marketplace.upbound.io/providers/upbound/provider-azure/latest/resources/dbforpostgresql.azure.upbound.io/Database/v1beta1) -## Syntax +{{< hint "tip" >}} -Crossplane API conventions extend the Kubernetes API conventions for the schema -of Crossplane managed resources. Following is an example of a managed resource: +You can create individual managed resources, but Crossplane recommends using +[Compositions]({{}}) and Claims to create +managed resources. +{{< /hint >}} -{{< tabs >}} -{{< tab "AWS" >}} +## Managed resource fields -The AWS provider supports provisioning an [RDS][rds] instance via the `RDSInstance` -managed resource it adds to Crossplane. +The Provider defines the group, Kind and version of a managed resource. The +Provider also define the available settings of a managed resource. -```yaml -apiVersion: database.aws.crossplane.io/v1beta1 -kind: RDSInstance -metadata: - name: rdspostgresql +### Group, kind and version +Each managed resource is a unique API endpoint with their own +group, Kind and version. + +For example the [Upbound AWS +Provider](https://marketplace.upbound.io/providers/upbound/provider-aws/latest/) +defines the {{}}Instance{{}} Kind from the +group {{}}ec2.aws.upbound.io{{}} + +```yaml {label="gkv"} +apiVersion: ec2.aws.upbound.io/v1beta1 +kind: Instance +``` + + +### deletionPolicy + + +A managed resource's `deletionPolicy` tells the Provider what to do after +deleting the managed resource. If the `deletionPolicy` is `delete` the Provider +deletes the external resource as well. If the `deletionPolicy` is `orphan` the +Provider deletes the managed resource but doesn't delete the external resource. + +#### Options +* `deletionPolicy: delete` - **Default** - Delete the external resource when deleting the managed resource. +* `deletionPolicy: orphan` - Leave the external resource when deleting the managed resource. + + +### forProvider + + +The {{}}spec.forProvider{{}} of a +managed resource maps to the parameters of the external resource. + +For example, when creating an AWS EC2 instance, the Provider supports defining +the AWS {{}}region{{}} and the VM +size, called the +{{}}instanceType{{}}. + +{{< hint "note" >}} +The Provider defines the settings and their valid values. Providers also define +required and optional values in the `forProvider` definition. + +Refer to the documentation of your specific Provider for details. +{{< /hint >}} + + +```yaml {label="forProvider"} +apiVersion: ec2.aws.upbound.io/v1beta1 +kind: Instance spec: forProvider: - region: us-east-1 - dbInstanceClass: db.t2.small - masterUsername: masteruser - allocatedStorage: 20 - engine: postgres - engineVersion: "12" - skipFinalSnapshotBeforeDeletion: true - writeConnectionSecretToRef: - namespace: crossplane-system - name: aws-rdspostgresql-conn + region: us-west-1 + instanceType: t2.micro ``` -```console -kubectl apply -f https://raw.githubusercontent.com/crossplane/crossplane/release-1.10/docs/snippets/provision/aws.yaml +{{< hint "important">}} +Crossplane considers the `forProvider` field of a managed resource +the "source of truth." Crossplane overrides any changes made to a managed +resource outside of Crossplane. If a user makes a change inside a +Provider's web console, Crossplane reverts that change back to what's +configured in the `forProvider` setting. +{{< /hint >}} + +Providers add any settings not manually set to the `forProvider` field of the +created managed resource object. +Use `kubectl describe ` to view the applied values. + +#### Referencing other resources + +Some fields in a managed resource may depend on values from other managed +resources. For example a VM may need the name of a virtual network to use. + +Managed resources can reference other managed resources by external name, name +reference or selector. + +##### Matching by external name + +When matching a resource by name Crossplane looks for the name of the external +resource in the Provider. + +For example, a AWS VPC object named `my-test-vpc` has the external name +`vpc-01353cfe93950a8ff`. + +```shell +kubectl get vpc +NAME READY SYNCED EXTERNAL-NAME AGE +my-test-vpc True True vpc-01353cfe93950a8ff 49m ``` -Creating the above instance will cause Crossplane to provision an RDS instance -on AWS. You can view the progress with the following command: +To match the VPC by name, use the external name. For example, creating a Subnet +managed resource attached to this VPC. + +```yaml +apiVersion: ec2.aws.upbound.io/v1beta1 +kind: Subnet +spec: + forProvider: + # Removed for brevity + vpcId: vpc-01353cfe93950a8ff +``` + +##### Matching by name reference + +To match a resource based on the name of the managed resource and not the +external resource name inside the Provider, use a `nameRef`. -```console -kubectl get rdsinstance rdspostgresql +For example, a AWS VPC object named `my-test-vpc` has the external name +`vpc-01353cfe93950a8ff`. + +```shell +kubectl get vpc +NAME READY SYNCED EXTERNAL-NAME AGE +my-test-vpc True True vpc-01353cfe93950a8ff 49m ``` -When provisioning is complete, you should see `READY: True` in the output. You -can take a look at its connection secret that is referenced under -`spec.writeConnectionSecretToRef`: +To match the VPC by name reference, use the managed resource name. For example, +creating a Subnet managed resource attached to this VPC. + +```yaml +apiVersion: ec2.aws.upbound.io/v1beta1 +kind: Subnet +spec: + forProvider: + # Removed for brevity + vpcIdRef: + name: my-test-vpc +``` + + +##### Matching by selector + +Matching by selector is the most flexible matching method. + +{{}} +The [Composition]({{}}) section covers the +`matchControllerRef` selector. +{{}} + +Use `matchLabels` to match the labels applied to a resource. For example, this +Subnet resource only matches VPC resources with the label +`my-label: label-value`. -```console -kubectl describe secret aws-rdspostgresql-conn -n crossplane-system +```yaml +apiVersion: ec2.aws.upbound.io/v1beta1 +kind: Subnet +spec: + forProvider: + # Removed for brevity + vpcIdSelector: + matchLabels: + my-label: label-value ``` -You can then delete the `RDSInstance`: -```console -kubectl delete rdsinstance rdspostgresql +#### Immutable fields + +Some providers don't support changing the fields of some managed resources after +creation. For example, you can't change the `region` of an Amazon AWS +`RDSInstance`. These fields are _immutable fields_. Amazon requires you delete +and recreate the resource. + +Crossplane allows you to edit the immutable field of a managed resource, but +doesn't apply the change. Crossplane never deletes a resource based on a +`forProvider` change. + +{{}} +Crossplane behaves differently than other tools like Terraform. Terraform +deletes and recreates a resource to change an immutable field. Crossplane only +deletes managed resources if the Kubernetes deletes the managed resource object. +{{< /hint >}} + + + +### managementPolicy + + +{{}} +The managed resource `managementPolicy` option is an alpha feature. + +Enable the `managementPolicy` in a provider with `--enable-management-policies` +in a +[ControllerConfig]({{}}). +{{< /hint >}} + +A `managementPolicy` determines if Crossplane can make changes to managed +resources. The `ObserveOnly` policy imports existing external resources not +originally created by Crossplane. +This allows new managed resources to reference +the `ObserveOnly` resource, for example, a shared database or network. +The `ObserveOnly` policy can also place existing resources under the control of +Crossplane. + +{{< hint "tip" >}} +Read the [Import Existing Resources]({{}}) guide for more +information on using the `managementPolicy` to import existing resources. +{{< /hint >}} + +#### Options +* `managementPolicy: FullControl` - **Default** - Crossplane can create, change + and delete the managed resource. +* `managementPolicy: ObserveOnly` - Crossplane only imports the details of the + external resource, but doesn't make any changes to the managed resource. + + + +### providerConfigRef + + +The `providerConfigRef` on a managed resource tells the Provider which +[ProviderConfig]({{}}) to +use when creating the managed resource. + +Use a ProviderConfig to define the authentication method to use when +communicating to the Provider. + +{{< hint "important" >}} +If `providerConfigRef` isn't applied, Providers use the ProviderConfig named `default`. +{{< /hint >}} + +For example, a managed resource references a ProviderConfig named +{{}}user-keys{{}}. + +This matches the {{}}name{{}} of a ProviderConfig. + +```yaml {label="pcref"} +apiVersion: ec2.aws.upbound.io/v1beta1 +kind: Instance +spec: + forProvider: + # Removed for brevity + providerConfigRef: user-keys +``` + +```yaml {label="pc"} +apiVersion: aws.crossplane.io/v1beta1 +kind: ProviderConfig +metadata: + name: user-keys +# Removed for brevity ``` -{{< /tab >}} -{{< tab "GCP" >}} +{{< hint "tip" >}} +Each managed resource can reference different ProviderConfigs. This allows +different managed resources to authenticate with different credentials to the +same Provider. +{{< /hint >}} -The GCP provider supports provisioning a [CloudSQL][cloudsql] instance with the -`CloudSQLInstance` managed resource it adds to Crossplane. + +### providerRef + -```yaml -apiVersion: database.gcp.crossplane.io/v1beta1 -kind: CloudSQLInstance +Crossplane deprecated the `providerRef` field in `crossplane-runtime` +[v0.10.0](https://github.com/crossplane/crossplane-runtime/releases/tag/v0.10.0). +Managed resources using `providerRef`must use [`providerConfigRef`](#providerconfigref). + + + +### writeConnectionSecretToRef + + +When a Provider creates a managed resource it may generate resource-specific +details, like usernames, passwords or connection details like an IP address. + +Crossplane stores these details in a Kubernetes Secret object specified by the +`writeConnectionSecretToRef` values. + +For example, when creating an AWS RDS database instance with the Crossplane +[community AWS +provider](https://marketplace.upbound.io/providers/crossplane-contrib/provider-aws/v0.40.0) +generates an endpoint, password, port and username data. The Provider saves +these variables in the +{{}}writeConnectionSecretToRef.name{{}} +field. + +```yaml {label="secretname"} +apiVersion: database.aws.crossplane.io/v1beta1 +kind: RDSInstance metadata: - name: cloudsqlpostgresql + name: my-rds-instance spec: forProvider: - databaseVersion: POSTGRES_12 - region: us-central1 - settings: - tier: db-custom-1-3840 - dataDiskType: PD_SSD - dataDiskSizeGb: 10 + # Removed for brevity writeConnectionSecretToRef: - namespace: crossplane-system - name: cloudsqlpostgresql-conn + name: rds-secret ``` -```console -kubectl apply -f https://raw.githubusercontent.com/crossplane/crossplane/release-1.10/docs/snippets/provision/gcp.yaml +Viewing the Secret object shows the saved fields. + +```yaml {copy-lines="1"} +kubectl describe secret rds-secret +Name: rds-secret +# Removed for brevity +Data +==== +port: 4 bytes +username: 10 bytes +endpoint: 54 bytes +password: 27 bytes ``` -Creating the above instance will cause Crossplane to provision a CloudSQL -instance on GCP. You can view the progress with the following command: +{{}} +The Provider determines the data written to the Secret object. Refer to the +specific Provider documentation for the generated Secret data. +{{< /hint >}} + + +### publishConnectionDetailsTo + + +The `publishConnectionDetailsTo` field expands on +[`writeConnectionSecretToRef`](#writeconnectionsecrettoref) supporting storing +managed resource information as a Kubernetes Secret object or in an external +secrets store like [HashiCorp Vault](https://www.vaultproject.io/). + +Using `publishConnectionDetailsTo` requires enabling Crossplane +External Secrets Stores (ESS). Enable ESS inside a Provider with a +[ControllerConfig]({{}}) and +in Crossplane with the `--enable-external-secret-stores` argument. -```console -kubectl get cloudsqlinstance cloudsqlpostgresql +{{< hint "note" >}} +Not all Providers support `publishConnectionDetailsTo`. Check your Provider +documentation for details. +{{< /hint >}} + +#### Publish secrets to Kubernetes + +To publish the data generated by a managed resource as a Kubernetes Secret +object provide a +{{}}publishConnectionDetailsTo.name{{< /hover >}} + +```yaml {label="k8secret"} +apiVersion: rds.aws.upbound.io/v1beta1 +kind: Instance +spec: + forProvider: + # Removed for brevity + publishConnectionDetailsTo: + name: rds-kubernetes-secret ``` -When provisioning is complete, you should see `READY: True` in the output. You -can take a look at its connection secret that is referenced under -`spec.writeConnectionSecretToRef`: +Crossplane can apply labels and annotations to the Kubernetes secret as well +using +{{}}publishConnectionDetailsTo.metadata{{}}. -```console -kubectl describe secret cloudsqlpostgresql-conn -n crossplane-system +```yaml {label="k8label"} +apiVersion: rds.aws.upbound.io/v1beta1 +kind: Instance +spec: + forProvider: + # Removed for brevity + publishConnectionDetailsTo: + name: rds-kubernetes-secret + metadata: + labels: + label-tag: label-value + annotations: + annotation-tag: annotation-value ``` -You can then delete the `CloudSQLInstance`: +#### Publish secrets to an external secrets store + +Publishing secrets data to an external secret store like +[HashiCorp Vault](https://www.vaultproject.io/) relies on a +{{}}publishConnectionDetailsTo.configRef{{}}. + +The +{{}}configRef.name{{}} references a +{{}}StoreConfig{{}} +object. + +```yaml {label="configref"} +apiVersion: rds.aws.upbound.io/v1beta1 +kind: Instance +spec: + forProvider: + # Removed for brevity + publishConnectionDetailsTo: + name: rds-kubernetes-secret + configRef: + name: my-vault-storeconfig +``` -```console -kubectl delete cloudsqlinstance cloudsqlpostgresql +```yaml {label="storeconfig"} +apiVersion: secrets.crossplane.io/v1alpha1 +kind: StoreConfig +metadata: + name: my-vault-storeconfig +# Removed for brevity ``` -{{< /tab >}} -{{< tab "Azure" >}} +{{}} +Read the +[Vault as an External Secrets Store]({{}}) +guide for details on using StoreConfig objects. +{{< /hint >}} -The Azure provider supports provisioning an [Azure Database for PostgreSQL] -instance with the `PostgreSQLServer` managed resource it adds to Crossplane. -> Note: provisioning an Azure Database for PostgreSQL requires the presence of a -> [Resource Group] in your Azure account. We go ahead and provision a new -> `ResourceGroup` here in case you do not already have a suitable one in your -> account. +## Annotations -```yaml -apiVersion: azure.crossplane.io/v1alpha3 -kind: ResourceGroup +Crossplane applies a standard set of Kubernetes `annotations` to managed +resources. + +{{}} +| Annotation | Definition | +| --- | --- | +| `crossplane.io/external-name` | The name of the managed resource inside the Provider. | +| `crossplane.io/external-create-pending` | The timestamp of the last time the managed resource was in the `PENDING` state. | +| `crossplane.io/external-create-succeeded` | The timestamp of the last time the Provider successfully created the managed resource. | +| `crossplane.io/external-create-failed` | The timestamp of the last time the Provider failed to create the managed resource. | +| `crossplane.io/paused` | Indicates Crossplane isn't reconciling this resource. Read the [Pause Annotation](#pause) for more details. | +| `crossplane.io/composition-resource-name` | For managed resource created by a Composition, this is the Composition's `resources.name` value. | +{{
}} + +### Naming external resources +By default Providers give external resources the same name as the Kubernetes +object. + +For example, a managed resource named +{{}}my-rds-instance{{}} has +the name `my-rds-instance` as an external resource inside the Provider's +environment. + +```yaml {label="external-name"} +apiVersion: database.aws.crossplane.io/v1beta1 +kind: RDSInstance metadata: - name: sqlserverpostgresql-rg -spec: - location: West US 2 ---- -apiVersion: database.azure.crossplane.io/v1beta1 -kind: PostgreSQLServer + name: my-rds-instance +``` + +```shell +kubectl get rdsinstance +NAME READY SYNCED EXTERNAL-NAME AGE +my-rds-instance True True my-rds-instance 11m +``` + +Managed resource created with a `crossplane.io/external-name` +annotation already provided use the annotation value as the external +resource name. + +For example, the Provider creates managed resource named +{{< hover label="custom-name" line="6">}}my-rds-instance{{
}} but uses +the name {{}}my-custom-name{{}} +for the external resource inside AWS. + +```yaml {label="custom-name"} +apiVersion: database.aws.crossplane.io/v1beta1 +kind: RDSInstance metadata: - name: sqlserverpostgresql -spec: - forProvider: - administratorLogin: myadmin - resourceGroupNameRef: - name: sqlserverpostgresql-rg - location: West US 2 - sslEnforcement: Disabled - version: "9.6" - sku: - tier: GeneralPurpose - capacity: 2 - family: Gen5 - storageProfile: - storageMB: 20480 - writeConnectionSecretToRef: - namespace: crossplane-system - name: sqlserverpostgresql-conn + name: my-rds-instance + annotations: + crossplane.io/external-name: my-custom-namee ``` -```console -kubectl apply -f https://raw.githubusercontent.com/crossplane/crossplane/release-1.10/docs/snippets/provision/azure.yaml +```shell +kubectl get rdsinstance +NAME READY SYNCED EXTERNAL-NAME AGE +my-rds-instance True True my-custom-name 11m ``` -Creating the above instance will cause Crossplane to provision a PostgreSQL -database instance on Azure. You can view the progress with the following -command: +### Creation annotations + +Providers create new managed resources with the +`crossplane.io/external-create-pending` annotation. + +The Provider applies the `crossplane.io/external-create-succeeded` or +`crossplane.io/external-create-failed` annotation after making the external API +call and receiving a response. + +{{}} +If a Provider restarts before creating the `succeed` or `fail` annotations the +Provider can't reconcile the manged resource. + +Read Crossplane [issue +#3037](https://github.com/crossplane/crossplane/issues/3037#issuecomment-1110142427) +for more details +{{< /hint >}} + -```console -kubectl get postgresqlserver sqlserverpostgresql +### Pause +Manually applying the `crossplane.io/paused` annotation causes the Provider to +stop reconciling the managed resource. + +Pausing a resource is useful when modifying Providers or preventing +race-conditions when editing Kubernetes objects. + +Apply a {{}}crossplane.io/paused: "true"{{}} +annotation to a managed resource to pause reconciliation. + +{{< hint "note" >}} +Only the value `"true"` pauses reconciliation. +{{< /hint >}} + +```yaml {label="pause"} +apiVersion: ec2.aws.upbound.io/v1beta1 +kind: Instance +metadata: + name: my-rds-instance + annotations: + crossplane.io/paused: "true" +spec: + forProvider: + region: us-west-1 + instanceType: t2.micro ``` -When provisioning is complete, you should see `READY: True` in the output. You -can take a look at its connection secret that is referenced under -`spec.writeConnectionSecretToRef`: +Remove the annotation to resume reconciliation. + +## Finalizers +Crossplane applies a +[Finalizer](https://kubernetes.io/docs/concepts/overview/working-with-objects/finalizers/) +on managed resources to control their deletion. -```console -kubectl describe secret sqlserverpostgresql-conn -n crossplane-system +{{< hint "note" >}} +Kubernetes can't delete objects with Finalizers. +{{}} + +When a Crossplane deletes managed resource the Provider begins deleting the +external resource, but the managed resource remains until the external +resource is fully deleted. + +When the external resource is fully deleted Crossplane removes the Finalizer and +deletes the managed resource object. + +## Conditions + +Crossplane has a standard set of `Conditions` for a managed +resource. View the `Conditions` of a managed resource with +`kubectl describe ` + + +{{}} +Providers may define their own custom `Conditions`. +{{}} + + +### Available +`Reason: Available` indicates the Provider created the managed resource. + +```yaml +Conditions: + Type: Ready + Status: True + Reason: Available ``` +### Creating -You can then delete the `PostgreSQLServer`: +`Reason: Creating` indicates the Provider is attempting to create the managed +resource. -```console -kubectl delete postgresqlserver sqlserverpostgresql -kubectl delete resourcegroup sqlserverpostgresql-rg +```yaml +Conditions: + Type: Ready + Status: False + Reason: Creating ``` -{{< /tab >}} -{{< /tabs >}} - -In Kubernetes, `spec` top field represents the desired state of the user. -Crossplane adheres to that and has its own conventions about how the fields -under `spec` should look like. - -* `writeConnectionSecretToRef`: A reference to the secret that you want this - managed resource to write its connection secret that you'd be able to mount to - your pods in the same namespace. For `RDSInstance`, this secret would contain - `endpoint`, `username` and `password`. - -* `providerConfigRef`: Reference to the `ProviderConfig` resource that will - provide information regarding authentication of Crossplane to the provider. - `ProviderConfig` resources refer to `Secret` and potentially contain other - information regarding authentication. The `providerConfigRef` is defaulted to - a `ProviderConfig` named `default` if omitted. - -* `deletionPolicy`: Enum to specify whether the actual cloud resource should be - deleted when this managed resource is deleted in Kubernetes API server. - Possible values are `Delete` (the default) and `Orphan`. - -* `forProvider`: While the rest of the fields relate to how Crossplane should - behave, the fields under `forProvider` are solely used to configure the actual - external resource. In most of the cases, the field names correspond to the - what exists in provider's API Reference. - - The objects under `forProvider` field can get huge depending on the provider - API. For example, GCP `ServiceAccount` has only a few fields while GCP - `CloudSQLInstance` has over 100 fields that you can configure. - -### Versioning - -Crossplane closely follows the [Kubernetes API versioning -conventions][api-versioning] for the CRDs that it deploys. In short, for -`vXbeta` and `vX` versions, you can expect that either automatic migration or -instructions for manual migration will be provided when a new version of that -CRD schema is released. - -In practice, we suggest the following guidelines to provider developers: -* Every new kind has to be introduced as `v1alpha1` with no exception. -* Breaking changes require a version change, i.e. `v1alpha1` needs to become - `v1alpha2`. - * Alpha resources don't require automatic conversions or manual instructions - but it's recommended that manual instructions are provided. - * Beta resources require at least manual instructions but it's recommended - that conversion webhooks are used so that users can upgrade without any - hands-on operation. - * Stable resources require conversion webhooks. -* As long as the developer feels comfortable with the guarantees above, they can - bump the version to beta or stable given that the CRD shape adheres to the - Crossplane Resource Model (XRM) specifications for managed resources - [here][managed-api-patterns]. -* It's suggested that the bump from Alpha to Beta or from Beta to Stable happen - after a bake period which includes at least one release. - -### Grouping - -In general, managed resources are high fidelity resources meaning they will -provide parameters and behaviors that are provided by the external resource API. -This applies to grouping of resources, too. For example, `Queue` appears under -`sqs` API group in AWS,so, its `APIVersion` and `Kind` look like the following: +### Deleting +`Reason: Deleting` indicates the Provider is attempting to delete the managed +resource. ```yaml -apiVersion: sqs.aws.crossplane.io/v1beta1 -kind: Queue +Conditions: + Type: Ready + Status: False + Reason: Deleting ``` -## Behavior -As a general rule, managed resource controllers try not to make any decision -that is not specified by the user in the desired state since managed resources -are the lowest level primitives that operate directly on the cloud provider -APIs. +### Paused +`Reason: ReconcilePaused` indicates the managed resource has a [Pause](#paused) +annotation +```yaml +Conditions: + Type: Synced + Status: False + Reason: ReconcilePaused +``` -### Continuous Reconciliation + +### ReconcileError + +`Reason: ReconcileError` indicates Crossplane encountered an error while +reconciling the managed resource. The `Message:` value of the `Condition` helps +identify the Crossplane error. -Crossplane providers continuously reconcile the managed resource to achieve the -desired state. The parameters under `spec` are considered the one and only -source of truth for the external resource. This means that if someone changed a -configuration in the UI of the provider, like AWS Console, Crossplane will -change it back to what's given under `spec`. +```yaml +Conditions: + Type: Synced + Status: False + Reason: ReconcileError +``` -#### Connection Details -Some Crossplane resources support writing connection details - things like URLs, -usernames, endpoints, and passwords to a Kubernetes `Secret`. You can specify -the secret to write by setting the `spec.writeConnectionSecretToRef` field. Note -that while all managed resources have a `writeConnectionSecretToRef` field, not -all managed resources actually have connection details to write - many will -write an empty `Secret`. -> Which managed resources have connection details and what connection details -> they have is currently undocumented. This is tracked in [this -> issue][issue-1143]. -#### Immutable Properties +### Success +`Reason: ReconcileSuccess` indicates the Provider created and is monitoring the +managed resource. -There are configuration parameters in external resources that cloud providers do -not allow to be changed. For example, in AWS, you cannot change the region of an -`RDSInstance`. +```yaml +Conditions: + Type: Synced + Status: True + Reason: ReconcileSuccess +``` -Some infrastructure tools such as Terraform delete and recreate the resource to -accommodate those changes but Crossplane does not take that route. Unless the -managed resource is deleted and its `deletionPolicy` is `Delete`, its controller -never deletes the external resource in the provider. +### Unavailable +`Reason: Unavailable` indicates Crossplane expects the managed resource to be +available, but the Provider reports the resource is unhealthy. -> Kubernetes does not yet support immutable fields for custom resources. This -> means Crossplane will allow immutable fields to be changed, but will not -> actually make the desired change. This is tracked in [this issue][issue-727]. +```yaml +Conditions: + Type: Ready + Status: False + Reason: Unavailable +``` -#### Pausing Reconciliations -If a managed resource being reconciled by the [managed reconciler], has the -`crossplane.io/paused` annotation with its value set to `true` as in the -following example, then further reconciliations are paused on that resource -after emitting an event with the type `Synced`, the status `False`, -and the reason `ReconcilePaused`: +### Unknown +`Reason: Unknown` indicates the Provider has an unexpected error with the +managed resource. The `conditions.message` provides more information on what +went wrong. ```yaml -apiVersion: ec2.aws.upbound.io/v1beta1 -kind: VPC -metadata: - name: paused-vpc - annotations: - crossplane.io/paused: "true" -... +Conditions: + Type: Unknown + Status: False + Reason: Unknown ``` -Reconciliations on the managed resource will resume once the -`crossplane.io/paused` annotation is removed or its value is set -to anything other than `true`. -### External Name -By default the name of the managed resource is used as the name of the external -cloud resource that will show up in your cloud console. To specify a different -external name, Crossplane has a special annotation to represent the name of the -external resource. For example, I would like to have a `CloudSQLInstance` with -an external name that is different than its managed resource name: +### Upjet Provider conditions +[Upjet](https://github.com/upbound/upjet), the open source tool to generate +Crossplane Providers, also has a set of standard `Conditions`. + + + +#### AsyncOperation + + +Some resources may take more than a minute to create. Upjet based providers can +complete their Kubernetes command before creating the managed resource by using +an asynchronous operation. + + +##### Finished +The `Reason: Finished` indicates the asynchronous operation completed +successfully. ```yaml -apiVersion: database.gcp.crossplane.io/v1beta1 -kind: CloudSQLInstance -metadata: - name: foodb - annotations: - crossplane.io/external-name: my-special-db -spec: - ... +Conditions: + Type: AsyncOperation + Status: True + Reason: Finished ``` -When you create this managed resource, you will see that the name of -`CloudSQLInstance` in GCP console will be `my-special-db`. - -If the annotation is not given, Crossplane will fill it with the name of the -managed resource by default. In cases where provider doesn't allow you to name -the resource, like AWS VPC, the controller creates the resource and sets -external annotation to be the name that the cloud provider chose. So, you would -see something like `vpc-28dsnh3` as the value of `crossplane.io/external-name` -annotation of your AWS `VPC` resource even if you added your own custom external -name during creation. - -### Late Initialization - -For some of the optional fields, users rely on the default that the cloud -provider chooses for them. Since Crossplane treats the managed resource as the -source of the truth, values of those fields need to exist in `spec` of the -managed resource. So, in each reconciliation, Crossplane will fill the value of -a field that is left empty by the user but is assigned a value by the provider. -For example, there could be two fields like `region` and `availabilityZone` and -you might want to give only `region` and leave the availability zone to be -chosen by the cloud provider. In that case, if the provider assigns an -availability zone, Crossplane gets that value and fills `availabilityZone`. Note -that if the field is already filled, the controller won't override its value. - -### Deletion - -When a deletion request is made for a managed resource, its controller starts -the deletion process immediately. However, the managed resource is kept in the -Kubernetes API (via a finalizer) until the controller confirms the external -resource in the cloud is gone. So you can be sure that if the managed resource -is deleted, then the external cloud resource is also deleted. Any errors that -happen during deletion will be added to the `status` of the managed resource, so -you can troubleshoot any issues. - -## Dependencies - -In many cases, an external resource refers to another one for a specific -configuration. For example, you could want your Azure Kubernetes cluster in a -specific Virtual Network. External resources have specific fields for these -relations, however, they usually require the information to be supplied in -different formats. In Azure MySQL, you might be required to enter only the name -of the Virtual Network while in Azure Kubernetes, it could be required to enter -a string in a specific format that includes other information such as resource -group name. - -In Crossplane, users have 3 fields to refer to another resource. Here is an -example from Azure MySQL managed resource referring to an Azure Resource Group: + +##### Ongoing + +`Reason: Ongoing` indicates the managed resource operation is still in progress. ```yaml -spec: - forProvider: - resourceGroupName: foo-res-group - resourceGroupNameRef: - name: resourcegroup - resourceGroupNameSelector: - matchLabels: - app: prod +Conditions: + Type: AsyncOperation + Status: True + Reason: Ongoing ``` -In this example, the user provided only a set of labels to select a -`ResourceGroup` managed resource that already exists in the cluster via -`resourceGroupNameSelector`. Then after a specific `ResourceGroup` is selected, -`resourceGroupNameRef` is filled with the name of that `ResourceGroup` managed -resource. Then in the last step, Crossplane fills the actual `resourceGroupName` -field with whatever format Azure accepts it. Once a dependency is resolved, the -controller never changes it. + +#### LastAsyncOperation + -Users are able to specify any of these three fields: +The Upjet `Type: LastAsyncOperation` captures the previous asynchronous +operation status as either `Success` or a failure `Reason`. -- Selector to select via labels -- Reference to point to a determined managed resource -- Actual value that will be submitted to the provider + +##### ApplyFailure + -It's important to note that in case a reference exists, the managed resource -does not create the external resource until the referenced object is ready. In -this example, creation call of Azure MySQL Server will not be made until -referenced `ResourceGroup` has its `status.condition` named `Ready` to be true. +`Reason: ApplyFailure` indicates the Provider failed to apply a setting to the +managed resource. The `conditions.message` provides more information on what +went wrong. + +```yaml +Conditions: + Type: LastAsyncOperation + Status: False + Reason: ApplyFailure +``` -## Importing Existing Resources + +##### DestroyFailure + -If you have some resources that are already provisioned in the cloud provider, -you can import them as managed resources and let Crossplane manage them. What -you need to do is to enter the name of the external resource as well as the -required fields on the managed resource. For example, let's say I have a GCP -Network provisioned from GCP console and I would like to migrate it to -Crossplane. Here is the YAML that I need to create: +`Reason: DestroyFailure` indicates the Provider failed to delete the managed +resource. The `conditions.message` provides more information on what +went wrong. ```yaml -apiVersion: compute.gcp.crossplane.io/v1beta1 -kind: Network -metadata: - name: foo-network - annotations: - crossplane.io/external-name: existing-network -spec: - forProvider: {} - providerConfigRef: - name: default +Conditions: + Type: LastAsyncOperation + Status: False + Reason: DestroyFailure ``` -Crossplane will check whether a GCP Network called `existing-network` exists, -and if it does, then the optional fields under `forProvider` will be filled with -the values that are fetched from the provider. - -Note that if a resource has required fields, you must fill those fields or the -creation of the managed resource will be rejected. So, in those cases, you will -need to enter the name of the resource as well as the required fields. - -## Backup and Restore - -Crossplane adheres to Kubernetes conventions as much as possible and one of the -advantages we gain is backup & restore ability with tools that work with native -Kubernetes types, like [Velero][velero]. - -If you'd like to backup and restore manually, you can simply export them and -save YAMLs in your file system. When you reload them, as we've discovered in -import section, their `crossplane.io/external-name` annotation and required -fields are there and those are enough to import a resource. The tool you're -using needs to store `annotations` and `spec` fields, which most tools do -including Velero. - -[rds]: https://aws.amazon.com/rds/ -[cloudsql]: https://cloud.google.com/sql -[api-versioning]: https://kubernetes.io/docs/reference/using-api/#api-versioning#api-versioning -[velero]: https://velero.io/ -[issue-727]: https://github.com/crossplane/crossplane/issues/727 -[issue-1143]: https://github.com/crossplane/crossplane/issues/1143 -[managed-api-patterns]: https://github.com/crossplane/crossplane/blob/release-1.10/design/one-pager-managed-resource-api-design.md -[managed reconciler]: https://github.com/crossplane/crossplane-runtime/blob/84e629b9589852df1322ff1eae4c6e7639cf6e99/pkg/reconciler/managed/reconciler.go#L637 \ No newline at end of file +##### Success +`Reason: Success` indicates the Provider successfully created the managed +resource asynchronously. + +```yaml +Conditions: + Type: LastAsyncOperation + Status: True + Reason: Success +``` \ No newline at end of file From 670ef9ef76222408289e213a5c96cd8e3fda8420 Mon Sep 17 00:00:00 2001 From: Pete Lumbis Date: Wed, 31 May 2023 14:05:56 -0400 Subject: [PATCH 11/17] Update anchors to managementpolicy Signed-off-by: Pete Lumbis --- .../knowledge-base/guides/import-existing-resources.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/content/knowledge-base/guides/import-existing-resources.md b/content/knowledge-base/guides/import-existing-resources.md index b564d91ec..13c8e4dae 100644 --- a/content/knowledge-base/guides/import-existing-resources.md +++ b/content/knowledge-base/guides/import-existing-resources.md @@ -5,9 +5,8 @@ weight: 200 If you have resources that are already provisioned in a Provider, you can import them as managed resources and let Crossplane manage them. -A managed resource's [`managementPolicy`]({{}}) field enables importing -external resources into Crossplane. +A managed resource's [`managementPolicy`]({{}}) +field enables importing external resources into Crossplane. Crossplane can import resources either [manually]({{}}) or [automatically]({{}}). +`ObserveOnly` [`managementPolicy`]({{}}). Crossplane imports `ObserveOnly` resources but never changes or deletes the resources. From d99e2686debf3b18707f040a0e3b30346a9c96ed Mon Sep 17 00:00:00 2001 From: Pete Lumbis Date: Tue, 27 Jun 2023 09:58:56 -0400 Subject: [PATCH 12/17] fix baseURL for deploy previews Signed-off-by: Pete Lumbis --- netlify_build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netlify_build.sh b/netlify_build.sh index 3c3329acc..c2dc940c0 100644 --- a/netlify_build.sh +++ b/netlify_build.sh @@ -12,5 +12,5 @@ cat config.yaml if [ "$CONTEXT" = "production" ]; then hugo --minify --baseURL https://docs.crossplane.io/ else -hugo --minify --baseURL $DEPLOY_URL +hugo --minify --baseURL $DEPLOY_URL/ fi \ No newline at end of file From 92296426c77b2ed9f12ae358aff1caad39404b57 Mon Sep 17 00:00:00 2001 From: Pete Lumbis Date: Tue, 27 Jun 2023 10:04:45 -0400 Subject: [PATCH 13/17] Update styling of Postgre to Postgre SQL Signed-off-by: Pete Lumbis --- content/master/concepts/managed-resources.md | 2 +- content/v1.11/concepts/managed-resources.md | 2 +- content/v1.12/concepts/managed-resources.md | 2 +- utils/vale/styles/Crossplane/spelling-exceptions.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/content/master/concepts/managed-resources.md b/content/master/concepts/managed-resources.md index 3f8d02da4..a238f5c0c 100644 --- a/content/master/concepts/managed-resources.md +++ b/content/master/concepts/managed-resources.md @@ -16,7 +16,7 @@ external object inside the Provider an _external resource_. Examples of managed resources include: * Amazon AWS EC2 [`Instance`](https://marketplace.upbound.io/providers/upbound/provider-aws/latest/resources/ec2.aws.upbound.io/Instance/v1beta1) * Google Cloud GKE [`Cluster`](https://marketplace.upbound.io/providers/upbound/provider-gcp/latest/resources/container.gcp.upbound.io/Cluster/v1beta1) -* Microsoft Azure Postgre [`Database`](https://marketplace.upbound.io/providers/upbound/provider-azure/latest/resources/dbforpostgresql.azure.upbound.io/Database/v1beta1) +* Microsoft Azure PostgreSQL [`Database`](https://marketplace.upbound.io/providers/upbound/provider-azure/latest/resources/dbforpostgresql.azure.upbound.io/Database/v1beta1) {{< hint "tip" >}} diff --git a/content/v1.11/concepts/managed-resources.md b/content/v1.11/concepts/managed-resources.md index 3f8d02da4..a238f5c0c 100644 --- a/content/v1.11/concepts/managed-resources.md +++ b/content/v1.11/concepts/managed-resources.md @@ -16,7 +16,7 @@ external object inside the Provider an _external resource_. Examples of managed resources include: * Amazon AWS EC2 [`Instance`](https://marketplace.upbound.io/providers/upbound/provider-aws/latest/resources/ec2.aws.upbound.io/Instance/v1beta1) * Google Cloud GKE [`Cluster`](https://marketplace.upbound.io/providers/upbound/provider-gcp/latest/resources/container.gcp.upbound.io/Cluster/v1beta1) -* Microsoft Azure Postgre [`Database`](https://marketplace.upbound.io/providers/upbound/provider-azure/latest/resources/dbforpostgresql.azure.upbound.io/Database/v1beta1) +* Microsoft Azure PostgreSQL [`Database`](https://marketplace.upbound.io/providers/upbound/provider-azure/latest/resources/dbforpostgresql.azure.upbound.io/Database/v1beta1) {{< hint "tip" >}} diff --git a/content/v1.12/concepts/managed-resources.md b/content/v1.12/concepts/managed-resources.md index 3f8d02da4..a238f5c0c 100644 --- a/content/v1.12/concepts/managed-resources.md +++ b/content/v1.12/concepts/managed-resources.md @@ -16,7 +16,7 @@ external object inside the Provider an _external resource_. Examples of managed resources include: * Amazon AWS EC2 [`Instance`](https://marketplace.upbound.io/providers/upbound/provider-aws/latest/resources/ec2.aws.upbound.io/Instance/v1beta1) * Google Cloud GKE [`Cluster`](https://marketplace.upbound.io/providers/upbound/provider-gcp/latest/resources/container.gcp.upbound.io/Cluster/v1beta1) -* Microsoft Azure Postgre [`Database`](https://marketplace.upbound.io/providers/upbound/provider-azure/latest/resources/dbforpostgresql.azure.upbound.io/Database/v1beta1) +* Microsoft Azure PostgreSQL [`Database`](https://marketplace.upbound.io/providers/upbound/provider-azure/latest/resources/dbforpostgresql.azure.upbound.io/Database/v1beta1) {{< hint "tip" >}} diff --git a/utils/vale/styles/Crossplane/spelling-exceptions.txt b/utils/vale/styles/Crossplane/spelling-exceptions.txt index b077483e4..abe0f0132 100644 --- a/utils/vale/styles/Crossplane/spelling-exceptions.txt +++ b/utils/vale/styles/Crossplane/spelling-exceptions.txt @@ -21,7 +21,7 @@ minikube namespace namespaces Netlify -Postgre +PostgreSQL proselint semver shortcode From 2416c837a2b4039d239d53f0c400c22b0d9ca408 Mon Sep 17 00:00:00 2001 From: Pete Lumbis Date: Tue, 27 Jun 2023 11:32:46 -0400 Subject: [PATCH 14/17] updates based on PR feedback Signed-off-by: Pete Lumbis --- .../guides/import-existing-resources.md | 71 +++++++++++++------ 1 file changed, 48 insertions(+), 23 deletions(-) diff --git a/content/knowledge-base/guides/import-existing-resources.md b/content/knowledge-base/guides/import-existing-resources.md index 13c8e4dae..085c94bc5 100644 --- a/content/knowledge-base/guides/import-existing-resources.md +++ b/content/knowledge-base/guides/import-existing-resources.md @@ -15,11 +15,11 @@ Crossplane can import resources either [manually]({{}}my-existing-network{{}}, @@ -27,7 +27,7 @@ create a new managed resource and use the {{}}my-existing-network{{}} in the annotation. -```yaml {label="annotation"} +```yaml {label="annotation",copy-lines="none"} apiVersion: compute.gcp.crossplane.io/v1beta1 kind: Network metadata: @@ -45,7 +45,7 @@ name of the Kubernetes object. It's not related to the resource name inside the Provider. {{< /hint >}} -```yaml {label="name"} +```yaml {label="name",copy-lines="none"} apiVersion: compute.gcp.crossplane.io/v1beta1 kind: Network metadata: @@ -59,6 +59,15 @@ Leave the Crossplane imports the settings and automatically applies them to the managed resource. +{{< hint "important" >}} +If the managed resource has _required_ fields in the +{{}}spec.forProvider{{}} you must add it to +the `forProvider` field. + +The values of those fields must match what's inside the Provider or Crossplane +overwrites the existing values. +{{< /hint >}} + ```yaml {label="fp",copy-lines="all"} apiVersion: compute.gcp.crossplane.io/v1beta1 kind: Network @@ -70,14 +79,6 @@ spec: forProvider: {} ``` -{{< hint "important" >}} -If the managed resource has _required_ fields in the -{{}}spec.forProvider{{}} you must add it to -the `forProvider` field. - -The values of those fields must match what's inside the Provider or Crossplane -overwrites the existing values. -{{< /hint >}} Crossplane now controls and manages this imported resource. Any changes to the managed resource `spec` changes the external resource. @@ -102,9 +103,17 @@ in a ### Apply the ObserveOnly managementPolicy -Create a new managed resource with {{}}managementPolicy: ObserveOnly{{}} set. - -```yaml {label="oo-policy"} +Create a new managed resource matching the +{{}}apiVersion{{}} and +{{}}kind{{}} of the resource +to import and add +{{}}managementPolicy: ObserveOnly{{}} to the +{{}}spec{{}} + +For example, to import a GCP SQL DatabaseInstance, create a new resource with +the {{}}managementPolicy: ObserveOnly{{}} +set. +```yaml {label="oo-policy",copy-lines="none"} apiVersion: sql.gcp.upbound.io/v1beta1 kind: DatabaseInstance spec: @@ -112,10 +121,17 @@ spec: ``` ### Add the external-name annotation -Add the {{}}external-name{{}} annotation -for the resource. This name must match the name inside the Provider. +Add the {{}}crossplane.io/external-name{{}} +annotation for the resource. This name must match the name inside the Provider. -```yaml {label="oo-ex-name"} +For example, for a GCP database named +{{}}my-external-database{{}}, apply +the +{{}}crossplane.io/external-name{{}} +annotation with the value +{{}}my-external-database{{}}. + +```yaml {label="oo-ex-name",copy-lines="none"} apiVersion: sql.gcp.upbound.io/v1beta1 kind: DatabaseInstance metadata: @@ -129,7 +145,10 @@ spec: Create a {{}}name{{}} to use for the Kubernetes object. -```yaml {label="oo-name"} +For example, name the Kubernetes object +{{}}my-imported-database{{}}. + +```yaml {label="oo-name",copy-lines="none"} apiVersion: sql.gcp.upbound.io/v1beta1 kind: DatabaseInstance metadata: @@ -144,8 +163,9 @@ spec: If more than one resource inside the Provider shares the same name, identify the specific resource with a unique {{}}spec.forProvider{{}} field. -For example, the -{{}}region{{}}. + +For example, only import the GCP SQL database in the +{{}}us-central1{{}} region. ```yaml {label="oo-region"} apiVersion: sql.gcp.upbound.io/v1beta1 @@ -160,12 +180,17 @@ spec: region: "us-central1" ``` +### Apply the managed resource + +Apply the new managed resource. Crossplane syncs the status of the external +resource in the cloud with the newly created managed resource. + ### View the discovered resource Crossplane discovers the managed resource and populates the {{}}status.atProvider{{}} fields with the values from the external resource. -```yaml {label="ooPopulated"} +```yaml {label="ooPopulated",copy-lines="none"} apiVersion: sql.gcp.upbound.io/v1beta1 kind: DatabaseInstance metadata: From 51ee41271652a6cb8af6a15f9ca8ebad7d8398cb Mon Sep 17 00:00:00 2001 From: Pete Lumbis Date: Tue, 27 Jun 2023 12:55:25 -0400 Subject: [PATCH 15/17] Edits based on PR feedback Signed-off-by: Pete Lumbis --- content/master/concepts/managed-resources.md | 113 ++++++++++--------- content/v1.11/concepts/managed-resources.md | 113 ++++++++++--------- content/v1.12/concepts/managed-resources.md | 113 ++++++++++--------- 3 files changed, 186 insertions(+), 153 deletions(-) diff --git a/content/master/concepts/managed-resources.md b/content/master/concepts/managed-resources.md index a238f5c0c..3e1420639 100644 --- a/content/master/concepts/managed-resources.md +++ b/content/master/concepts/managed-resources.md @@ -4,8 +4,8 @@ weight: 102 --- A _managed resource_ (`MR`) represents an external service in a Provider. When -asking a Provider to create an external resource, the Provider creates a managed -resource inside the Kubernetes cluster. Every external service managed by +users create a new managed resource, the Provider reacts by creating an external +resource inside the Provider's environment. Every external service managed by Crossplane maps to a managed resource. {{< hint "note" >}} @@ -27,19 +27,19 @@ managed resources. ## Managed resource fields -The Provider defines the group, Kind and version of a managed resource. The +The Provider defines the group, kind and version of a managed resource. The Provider also define the available settings of a managed resource. ### Group, kind and version Each managed resource is a unique API endpoint with their own -group, Kind and version. +group, kind and version. For example the [Upbound AWS Provider](https://marketplace.upbound.io/providers/upbound/provider-aws/latest/) -defines the {{}}Instance{{}} Kind from the +defines the {{}}Instance{{}} kind from the group {{}}ec2.aws.upbound.io{{}} -```yaml {label="gkv"} +```yaml {label="gkv",copy-lines="none} apiVersion: ec2.aws.upbound.io/v1beta1 kind: Instance ``` @@ -77,9 +77,10 @@ Refer to the documentation of your specific Provider for details. {{< /hint >}} -```yaml {label="forProvider"} +```yaml {label="forProvider",copy-lines="none} apiVersion: ec2.aws.upbound.io/v1beta1 kind: Instance +# Removed for brevity spec: forProvider: region: us-west-1 @@ -88,9 +89,9 @@ spec: {{< hint "important">}} Crossplane considers the `forProvider` field of a managed resource -the "source of truth." Crossplane overrides any changes made to a managed -resource outside of Crossplane. If a user makes a change inside a -Provider's web console, Crossplane reverts that change back to what's +the "source of truth" for external resources. Crossplane overrides any changes +made to an external resource outside of Crossplane. If a user makes a change +inside a Provider's web console, Crossplane reverts that change back to what's configured in the `forProvider` setting. {{< /hint >}} @@ -114,7 +115,7 @@ resource in the Provider. For example, a AWS VPC object named `my-test-vpc` has the external name `vpc-01353cfe93950a8ff`. -```shell +```shell {copy-lines="1" kubectl get vpc NAME READY SYNCED EXTERNAL-NAME AGE my-test-vpc True True vpc-01353cfe93950a8ff 49m @@ -123,7 +124,7 @@ my-test-vpc True True vpc-01353cfe93950a8ff 49m To match the VPC by name, use the external name. For example, creating a Subnet managed resource attached to this VPC. -```yaml +```yaml {copy-lines="none} apiVersion: ec2.aws.upbound.io/v1beta1 kind: Subnet spec: @@ -140,7 +141,7 @@ external resource name inside the Provider, use a `nameRef`. For example, a AWS VPC object named `my-test-vpc` has the external name `vpc-01353cfe93950a8ff`. -```shell +```shell {copy-lines="1"} kubectl get vpc NAME READY SYNCED EXTERNAL-NAME AGE my-test-vpc True True vpc-01353cfe93950a8ff 49m @@ -149,7 +150,7 @@ my-test-vpc True True vpc-01353cfe93950a8ff 49m To match the VPC by name reference, use the managed resource name. For example, creating a Subnet managed resource attached to this VPC. -```yaml +```yaml {copy-lines="none"} apiVersion: ec2.aws.upbound.io/v1beta1 kind: Subnet spec: @@ -173,7 +174,7 @@ Use `matchLabels` to match the labels applied to a resource. For example, this Subnet resource only matches VPC resources with the label `my-label: label-value`. -```yaml +```yaml {copy-lines="none"} apiVersion: ec2.aws.upbound.io/v1beta1 kind: Subnet spec: @@ -197,9 +198,13 @@ doesn't apply the change. Crossplane never deletes a resource based on a `forProvider` change. {{}} + Crossplane behaves differently than other tools like Terraform. Terraform deletes and recreates a resource to change an immutable field. Crossplane only -deletes managed resources if the Kubernetes deletes the managed resource object. +deletes an external resource if their corresponding managed +resource object is deleted from Kubernetes and the `deletionPolicy` is +`delete`. + {{< /hint >}} @@ -256,7 +261,7 @@ For example, a managed resource references a ProviderConfig named This matches the {{}}name{{}} of a ProviderConfig. -```yaml {label="pcref"} +```yaml {label="pcref",copy-lines="none"}} apiVersion: ec2.aws.upbound.io/v1beta1 kind: Instance spec: @@ -302,11 +307,13 @@ For example, when creating an AWS RDS database instance with the Crossplane [community AWS provider](https://marketplace.upbound.io/providers/crossplane-contrib/provider-aws/v0.40.0) generates an endpoint, password, port and username data. The Provider saves -these variables in the -{{}}writeConnectionSecretToRef.name{{}} +these variables in the Kubernetes secret +{{}}rds-secret{{}}, referenced by +the +{{}}writeConnectionSecretToRef{{}} field. -```yaml {label="secretname"} +```yaml {label="secretname",copy-lines="none"} apiVersion: database.aws.crossplane.io/v1beta1 kind: RDSInstance metadata: @@ -362,7 +369,7 @@ To publish the data generated by a managed resource as a Kubernetes Secret object provide a {{}}publishConnectionDetailsTo.name{{< /hover >}} -```yaml {label="k8secret"} +```yaml {label="k8secret",copy-lines="none"} apiVersion: rds.aws.upbound.io/v1beta1 kind: Instance spec: @@ -376,7 +383,7 @@ Crossplane can apply labels and annotations to the Kubernetes secret as well using {{}}publishConnectionDetailsTo.metadata{{}}. -```yaml {label="k8label"} +```yaml {label="k8label",copy-lines="none"} apiVersion: rds.aws.upbound.io/v1beta1 kind: Instance spec: @@ -402,7 +409,7 @@ The {{}}StoreConfig{{}} object. -```yaml {label="configref"} +```yaml {label="configref",copy-lines="none"} apiVersion: rds.aws.upbound.io/v1beta1 kind: Instance spec: @@ -414,7 +421,7 @@ spec: name: my-vault-storeconfig ``` -```yaml {label="storeconfig"} +```yaml {label="storeconfig",copy-lines="none"} apiVersion: secrets.crossplane.io/v1alpha1 kind: StoreConfig metadata: @@ -438,9 +445,10 @@ resources. | Annotation | Definition | | --- | --- | | `crossplane.io/external-name` | The name of the managed resource inside the Provider. | -| `crossplane.io/external-create-pending` | The timestamp of the last time the managed resource was in the `PENDING` state. | -| `crossplane.io/external-create-succeeded` | The timestamp of the last time the Provider successfully created the managed resource. | -| `crossplane.io/external-create-failed` | The timestamp of the last time the Provider failed to create the managed resource. | +| `crossplane.io/external-create-pending` | The timestamp of when Crossplane +began creating a new managed resource. | +| `crossplane.io/external-create-succeeded` | The timestamp of when the Provider successfully created the managed resource. | +| `crossplane.io/external-create-failed` | The timestamp of when the Provider failed to create the managed resource. | | `crossplane.io/paused` | Indicates Crossplane isn't reconciling this resource. Read the [Pause Annotation](#pause) for more details. | | `crossplane.io/composition-resource-name` | For managed resource created by a Composition, this is the Composition's `resources.name` value. | {{}} @@ -454,7 +462,7 @@ For example, a managed resource named the name `my-rds-instance` as an external resource inside the Provider's environment. -```yaml {label="external-name"} +```yaml {label="external-name,copy-lines="none"} apiVersion: database.aws.crossplane.io/v1beta1 kind: RDSInstance metadata: @@ -476,7 +484,7 @@ For example, the Provider creates managed resource named the name {{}}my-custom-name{{}} for the external resource inside AWS. -```yaml {label="custom-name"} +```yaml {label="custom-name",copy-lines="none"} apiVersion: database.aws.crossplane.io/v1beta1 kind: RDSInstance metadata: @@ -485,7 +493,7 @@ metadata: crossplane.io/external-name: my-custom-namee ``` -```shell +```shell {copy-lines="1"} kubectl get rdsinstance NAME READY SYNCED EXTERNAL-NAME AGE my-rds-instance True True my-custom-name 11m @@ -548,7 +556,7 @@ on managed resources to control their deletion. Kubernetes can't delete objects with Finalizers. {{}} -When a Crossplane deletes managed resource the Provider begins deleting the +When Crossplane deletes a managed resource the Provider begins deleting the external resource, but the managed resource remains until the external resource is fully deleted. @@ -568,9 +576,10 @@ Providers may define their own custom `Conditions`. ### Available -`Reason: Available` indicates the Provider created the managed resource. +`Reason: Available` indicates the Provider created the managed resource and it's +ready for use. -```yaml +```yaml {copy-lines="none"} Conditions: Type: Ready Status: True @@ -581,7 +590,7 @@ Conditions: `Reason: Creating` indicates the Provider is attempting to create the managed resource. -```yaml +```yaml {copy-lines="none"} Conditions: Type: Ready Status: False @@ -592,18 +601,20 @@ Conditions: `Reason: Deleting` indicates the Provider is attempting to delete the managed resource. -```yaml +```yaml {copy-lines="none"} Conditions: Type: Ready Status: False Reason: Deleting ``` - -### Paused + +### ReconcilePaused + `Reason: ReconcilePaused` indicates the managed resource has a [Pause](#paused) annotation -```yaml + +```yaml {copy-lines="none"} Conditions: Type: Synced Status: False @@ -617,21 +628,20 @@ Conditions: reconciling the managed resource. The `Message:` value of the `Condition` helps identify the Crossplane error. -```yaml +```yaml {copy-lines="none"} Conditions: Type: Synced Status: False Reason: ReconcileError ``` - - - -### Success + +### ReconcileSuccess + `Reason: ReconcileSuccess` indicates the Provider created and is monitoring the managed resource. -```yaml +```yaml {copy-lines="none"} Conditions: Type: Synced Status: True @@ -642,7 +652,7 @@ Conditions: `Reason: Unavailable` indicates Crossplane expects the managed resource to be available, but the Provider reports the resource is unhealthy. -```yaml +```yaml {copy-lines="none"} Conditions: Type: Ready Status: False @@ -653,7 +663,8 @@ Conditions: `Reason: Unknown` indicates the Provider has an unexpected error with the managed resource. The `conditions.message` provides more information on what went wrong. -```yaml + +```yaml {copy-lines="none"} Conditions: Type: Unknown Status: False @@ -679,7 +690,7 @@ an asynchronous operation. The `Reason: Finished` indicates the asynchronous operation completed successfully. -```yaml +```yaml {copy-lines="none"} Conditions: Type: AsyncOperation Status: True @@ -691,7 +702,7 @@ Conditions: `Reason: Ongoing` indicates the managed resource operation is still in progress. -```yaml +```yaml {copy-lines="none"} Conditions: Type: AsyncOperation Status: True @@ -713,7 +724,7 @@ operation status as either `Success` or a failure `Reason`. managed resource. The `conditions.message` provides more information on what went wrong. -```yaml +```yaml {copy-lines="none"} Conditions: Type: LastAsyncOperation Status: False @@ -728,7 +739,7 @@ Conditions: resource. The `conditions.message` provides more information on what went wrong. -```yaml +```yaml {copy-lines="none"} Conditions: Type: LastAsyncOperation Status: False @@ -739,7 +750,7 @@ Conditions: `Reason: Success` indicates the Provider successfully created the managed resource asynchronously. -```yaml +```yaml {copy-lines="none"} Conditions: Type: LastAsyncOperation Status: True diff --git a/content/v1.11/concepts/managed-resources.md b/content/v1.11/concepts/managed-resources.md index a238f5c0c..3e1420639 100644 --- a/content/v1.11/concepts/managed-resources.md +++ b/content/v1.11/concepts/managed-resources.md @@ -4,8 +4,8 @@ weight: 102 --- A _managed resource_ (`MR`) represents an external service in a Provider. When -asking a Provider to create an external resource, the Provider creates a managed -resource inside the Kubernetes cluster. Every external service managed by +users create a new managed resource, the Provider reacts by creating an external +resource inside the Provider's environment. Every external service managed by Crossplane maps to a managed resource. {{< hint "note" >}} @@ -27,19 +27,19 @@ managed resources. ## Managed resource fields -The Provider defines the group, Kind and version of a managed resource. The +The Provider defines the group, kind and version of a managed resource. The Provider also define the available settings of a managed resource. ### Group, kind and version Each managed resource is a unique API endpoint with their own -group, Kind and version. +group, kind and version. For example the [Upbound AWS Provider](https://marketplace.upbound.io/providers/upbound/provider-aws/latest/) -defines the {{}}Instance{{}} Kind from the +defines the {{}}Instance{{}} kind from the group {{}}ec2.aws.upbound.io{{}} -```yaml {label="gkv"} +```yaml {label="gkv",copy-lines="none} apiVersion: ec2.aws.upbound.io/v1beta1 kind: Instance ``` @@ -77,9 +77,10 @@ Refer to the documentation of your specific Provider for details. {{< /hint >}} -```yaml {label="forProvider"} +```yaml {label="forProvider",copy-lines="none} apiVersion: ec2.aws.upbound.io/v1beta1 kind: Instance +# Removed for brevity spec: forProvider: region: us-west-1 @@ -88,9 +89,9 @@ spec: {{< hint "important">}} Crossplane considers the `forProvider` field of a managed resource -the "source of truth." Crossplane overrides any changes made to a managed -resource outside of Crossplane. If a user makes a change inside a -Provider's web console, Crossplane reverts that change back to what's +the "source of truth" for external resources. Crossplane overrides any changes +made to an external resource outside of Crossplane. If a user makes a change +inside a Provider's web console, Crossplane reverts that change back to what's configured in the `forProvider` setting. {{< /hint >}} @@ -114,7 +115,7 @@ resource in the Provider. For example, a AWS VPC object named `my-test-vpc` has the external name `vpc-01353cfe93950a8ff`. -```shell +```shell {copy-lines="1" kubectl get vpc NAME READY SYNCED EXTERNAL-NAME AGE my-test-vpc True True vpc-01353cfe93950a8ff 49m @@ -123,7 +124,7 @@ my-test-vpc True True vpc-01353cfe93950a8ff 49m To match the VPC by name, use the external name. For example, creating a Subnet managed resource attached to this VPC. -```yaml +```yaml {copy-lines="none} apiVersion: ec2.aws.upbound.io/v1beta1 kind: Subnet spec: @@ -140,7 +141,7 @@ external resource name inside the Provider, use a `nameRef`. For example, a AWS VPC object named `my-test-vpc` has the external name `vpc-01353cfe93950a8ff`. -```shell +```shell {copy-lines="1"} kubectl get vpc NAME READY SYNCED EXTERNAL-NAME AGE my-test-vpc True True vpc-01353cfe93950a8ff 49m @@ -149,7 +150,7 @@ my-test-vpc True True vpc-01353cfe93950a8ff 49m To match the VPC by name reference, use the managed resource name. For example, creating a Subnet managed resource attached to this VPC. -```yaml +```yaml {copy-lines="none"} apiVersion: ec2.aws.upbound.io/v1beta1 kind: Subnet spec: @@ -173,7 +174,7 @@ Use `matchLabels` to match the labels applied to a resource. For example, this Subnet resource only matches VPC resources with the label `my-label: label-value`. -```yaml +```yaml {copy-lines="none"} apiVersion: ec2.aws.upbound.io/v1beta1 kind: Subnet spec: @@ -197,9 +198,13 @@ doesn't apply the change. Crossplane never deletes a resource based on a `forProvider` change. {{}} + Crossplane behaves differently than other tools like Terraform. Terraform deletes and recreates a resource to change an immutable field. Crossplane only -deletes managed resources if the Kubernetes deletes the managed resource object. +deletes an external resource if their corresponding managed +resource object is deleted from Kubernetes and the `deletionPolicy` is +`delete`. + {{< /hint >}} @@ -256,7 +261,7 @@ For example, a managed resource references a ProviderConfig named This matches the {{}}name{{}} of a ProviderConfig. -```yaml {label="pcref"} +```yaml {label="pcref",copy-lines="none"}} apiVersion: ec2.aws.upbound.io/v1beta1 kind: Instance spec: @@ -302,11 +307,13 @@ For example, when creating an AWS RDS database instance with the Crossplane [community AWS provider](https://marketplace.upbound.io/providers/crossplane-contrib/provider-aws/v0.40.0) generates an endpoint, password, port and username data. The Provider saves -these variables in the -{{}}writeConnectionSecretToRef.name{{}} +these variables in the Kubernetes secret +{{}}rds-secret{{}}, referenced by +the +{{}}writeConnectionSecretToRef{{}} field. -```yaml {label="secretname"} +```yaml {label="secretname",copy-lines="none"} apiVersion: database.aws.crossplane.io/v1beta1 kind: RDSInstance metadata: @@ -362,7 +369,7 @@ To publish the data generated by a managed resource as a Kubernetes Secret object provide a {{}}publishConnectionDetailsTo.name{{< /hover >}} -```yaml {label="k8secret"} +```yaml {label="k8secret",copy-lines="none"} apiVersion: rds.aws.upbound.io/v1beta1 kind: Instance spec: @@ -376,7 +383,7 @@ Crossplane can apply labels and annotations to the Kubernetes secret as well using {{}}publishConnectionDetailsTo.metadata{{}}. -```yaml {label="k8label"} +```yaml {label="k8label",copy-lines="none"} apiVersion: rds.aws.upbound.io/v1beta1 kind: Instance spec: @@ -402,7 +409,7 @@ The {{}}StoreConfig{{}} object. -```yaml {label="configref"} +```yaml {label="configref",copy-lines="none"} apiVersion: rds.aws.upbound.io/v1beta1 kind: Instance spec: @@ -414,7 +421,7 @@ spec: name: my-vault-storeconfig ``` -```yaml {label="storeconfig"} +```yaml {label="storeconfig",copy-lines="none"} apiVersion: secrets.crossplane.io/v1alpha1 kind: StoreConfig metadata: @@ -438,9 +445,10 @@ resources. | Annotation | Definition | | --- | --- | | `crossplane.io/external-name` | The name of the managed resource inside the Provider. | -| `crossplane.io/external-create-pending` | The timestamp of the last time the managed resource was in the `PENDING` state. | -| `crossplane.io/external-create-succeeded` | The timestamp of the last time the Provider successfully created the managed resource. | -| `crossplane.io/external-create-failed` | The timestamp of the last time the Provider failed to create the managed resource. | +| `crossplane.io/external-create-pending` | The timestamp of when Crossplane +began creating a new managed resource. | +| `crossplane.io/external-create-succeeded` | The timestamp of when the Provider successfully created the managed resource. | +| `crossplane.io/external-create-failed` | The timestamp of when the Provider failed to create the managed resource. | | `crossplane.io/paused` | Indicates Crossplane isn't reconciling this resource. Read the [Pause Annotation](#pause) for more details. | | `crossplane.io/composition-resource-name` | For managed resource created by a Composition, this is the Composition's `resources.name` value. | {{}} @@ -454,7 +462,7 @@ For example, a managed resource named the name `my-rds-instance` as an external resource inside the Provider's environment. -```yaml {label="external-name"} +```yaml {label="external-name,copy-lines="none"} apiVersion: database.aws.crossplane.io/v1beta1 kind: RDSInstance metadata: @@ -476,7 +484,7 @@ For example, the Provider creates managed resource named the name {{}}my-custom-name{{}} for the external resource inside AWS. -```yaml {label="custom-name"} +```yaml {label="custom-name",copy-lines="none"} apiVersion: database.aws.crossplane.io/v1beta1 kind: RDSInstance metadata: @@ -485,7 +493,7 @@ metadata: crossplane.io/external-name: my-custom-namee ``` -```shell +```shell {copy-lines="1"} kubectl get rdsinstance NAME READY SYNCED EXTERNAL-NAME AGE my-rds-instance True True my-custom-name 11m @@ -548,7 +556,7 @@ on managed resources to control their deletion. Kubernetes can't delete objects with Finalizers. {{}} -When a Crossplane deletes managed resource the Provider begins deleting the +When Crossplane deletes a managed resource the Provider begins deleting the external resource, but the managed resource remains until the external resource is fully deleted. @@ -568,9 +576,10 @@ Providers may define their own custom `Conditions`. ### Available -`Reason: Available` indicates the Provider created the managed resource. +`Reason: Available` indicates the Provider created the managed resource and it's +ready for use. -```yaml +```yaml {copy-lines="none"} Conditions: Type: Ready Status: True @@ -581,7 +590,7 @@ Conditions: `Reason: Creating` indicates the Provider is attempting to create the managed resource. -```yaml +```yaml {copy-lines="none"} Conditions: Type: Ready Status: False @@ -592,18 +601,20 @@ Conditions: `Reason: Deleting` indicates the Provider is attempting to delete the managed resource. -```yaml +```yaml {copy-lines="none"} Conditions: Type: Ready Status: False Reason: Deleting ``` - -### Paused + +### ReconcilePaused + `Reason: ReconcilePaused` indicates the managed resource has a [Pause](#paused) annotation -```yaml + +```yaml {copy-lines="none"} Conditions: Type: Synced Status: False @@ -617,21 +628,20 @@ Conditions: reconciling the managed resource. The `Message:` value of the `Condition` helps identify the Crossplane error. -```yaml +```yaml {copy-lines="none"} Conditions: Type: Synced Status: False Reason: ReconcileError ``` - - - -### Success + +### ReconcileSuccess + `Reason: ReconcileSuccess` indicates the Provider created and is monitoring the managed resource. -```yaml +```yaml {copy-lines="none"} Conditions: Type: Synced Status: True @@ -642,7 +652,7 @@ Conditions: `Reason: Unavailable` indicates Crossplane expects the managed resource to be available, but the Provider reports the resource is unhealthy. -```yaml +```yaml {copy-lines="none"} Conditions: Type: Ready Status: False @@ -653,7 +663,8 @@ Conditions: `Reason: Unknown` indicates the Provider has an unexpected error with the managed resource. The `conditions.message` provides more information on what went wrong. -```yaml + +```yaml {copy-lines="none"} Conditions: Type: Unknown Status: False @@ -679,7 +690,7 @@ an asynchronous operation. The `Reason: Finished` indicates the asynchronous operation completed successfully. -```yaml +```yaml {copy-lines="none"} Conditions: Type: AsyncOperation Status: True @@ -691,7 +702,7 @@ Conditions: `Reason: Ongoing` indicates the managed resource operation is still in progress. -```yaml +```yaml {copy-lines="none"} Conditions: Type: AsyncOperation Status: True @@ -713,7 +724,7 @@ operation status as either `Success` or a failure `Reason`. managed resource. The `conditions.message` provides more information on what went wrong. -```yaml +```yaml {copy-lines="none"} Conditions: Type: LastAsyncOperation Status: False @@ -728,7 +739,7 @@ Conditions: resource. The `conditions.message` provides more information on what went wrong. -```yaml +```yaml {copy-lines="none"} Conditions: Type: LastAsyncOperation Status: False @@ -739,7 +750,7 @@ Conditions: `Reason: Success` indicates the Provider successfully created the managed resource asynchronously. -```yaml +```yaml {copy-lines="none"} Conditions: Type: LastAsyncOperation Status: True diff --git a/content/v1.12/concepts/managed-resources.md b/content/v1.12/concepts/managed-resources.md index a238f5c0c..3e1420639 100644 --- a/content/v1.12/concepts/managed-resources.md +++ b/content/v1.12/concepts/managed-resources.md @@ -4,8 +4,8 @@ weight: 102 --- A _managed resource_ (`MR`) represents an external service in a Provider. When -asking a Provider to create an external resource, the Provider creates a managed -resource inside the Kubernetes cluster. Every external service managed by +users create a new managed resource, the Provider reacts by creating an external +resource inside the Provider's environment. Every external service managed by Crossplane maps to a managed resource. {{< hint "note" >}} @@ -27,19 +27,19 @@ managed resources. ## Managed resource fields -The Provider defines the group, Kind and version of a managed resource. The +The Provider defines the group, kind and version of a managed resource. The Provider also define the available settings of a managed resource. ### Group, kind and version Each managed resource is a unique API endpoint with their own -group, Kind and version. +group, kind and version. For example the [Upbound AWS Provider](https://marketplace.upbound.io/providers/upbound/provider-aws/latest/) -defines the {{}}Instance{{}} Kind from the +defines the {{}}Instance{{}} kind from the group {{}}ec2.aws.upbound.io{{}} -```yaml {label="gkv"} +```yaml {label="gkv",copy-lines="none} apiVersion: ec2.aws.upbound.io/v1beta1 kind: Instance ``` @@ -77,9 +77,10 @@ Refer to the documentation of your specific Provider for details. {{< /hint >}} -```yaml {label="forProvider"} +```yaml {label="forProvider",copy-lines="none} apiVersion: ec2.aws.upbound.io/v1beta1 kind: Instance +# Removed for brevity spec: forProvider: region: us-west-1 @@ -88,9 +89,9 @@ spec: {{< hint "important">}} Crossplane considers the `forProvider` field of a managed resource -the "source of truth." Crossplane overrides any changes made to a managed -resource outside of Crossplane. If a user makes a change inside a -Provider's web console, Crossplane reverts that change back to what's +the "source of truth" for external resources. Crossplane overrides any changes +made to an external resource outside of Crossplane. If a user makes a change +inside a Provider's web console, Crossplane reverts that change back to what's configured in the `forProvider` setting. {{< /hint >}} @@ -114,7 +115,7 @@ resource in the Provider. For example, a AWS VPC object named `my-test-vpc` has the external name `vpc-01353cfe93950a8ff`. -```shell +```shell {copy-lines="1" kubectl get vpc NAME READY SYNCED EXTERNAL-NAME AGE my-test-vpc True True vpc-01353cfe93950a8ff 49m @@ -123,7 +124,7 @@ my-test-vpc True True vpc-01353cfe93950a8ff 49m To match the VPC by name, use the external name. For example, creating a Subnet managed resource attached to this VPC. -```yaml +```yaml {copy-lines="none} apiVersion: ec2.aws.upbound.io/v1beta1 kind: Subnet spec: @@ -140,7 +141,7 @@ external resource name inside the Provider, use a `nameRef`. For example, a AWS VPC object named `my-test-vpc` has the external name `vpc-01353cfe93950a8ff`. -```shell +```shell {copy-lines="1"} kubectl get vpc NAME READY SYNCED EXTERNAL-NAME AGE my-test-vpc True True vpc-01353cfe93950a8ff 49m @@ -149,7 +150,7 @@ my-test-vpc True True vpc-01353cfe93950a8ff 49m To match the VPC by name reference, use the managed resource name. For example, creating a Subnet managed resource attached to this VPC. -```yaml +```yaml {copy-lines="none"} apiVersion: ec2.aws.upbound.io/v1beta1 kind: Subnet spec: @@ -173,7 +174,7 @@ Use `matchLabels` to match the labels applied to a resource. For example, this Subnet resource only matches VPC resources with the label `my-label: label-value`. -```yaml +```yaml {copy-lines="none"} apiVersion: ec2.aws.upbound.io/v1beta1 kind: Subnet spec: @@ -197,9 +198,13 @@ doesn't apply the change. Crossplane never deletes a resource based on a `forProvider` change. {{}} + Crossplane behaves differently than other tools like Terraform. Terraform deletes and recreates a resource to change an immutable field. Crossplane only -deletes managed resources if the Kubernetes deletes the managed resource object. +deletes an external resource if their corresponding managed +resource object is deleted from Kubernetes and the `deletionPolicy` is +`delete`. + {{< /hint >}} @@ -256,7 +261,7 @@ For example, a managed resource references a ProviderConfig named This matches the {{}}name{{}} of a ProviderConfig. -```yaml {label="pcref"} +```yaml {label="pcref",copy-lines="none"}} apiVersion: ec2.aws.upbound.io/v1beta1 kind: Instance spec: @@ -302,11 +307,13 @@ For example, when creating an AWS RDS database instance with the Crossplane [community AWS provider](https://marketplace.upbound.io/providers/crossplane-contrib/provider-aws/v0.40.0) generates an endpoint, password, port and username data. The Provider saves -these variables in the -{{}}writeConnectionSecretToRef.name{{}} +these variables in the Kubernetes secret +{{}}rds-secret{{}}, referenced by +the +{{}}writeConnectionSecretToRef{{}} field. -```yaml {label="secretname"} +```yaml {label="secretname",copy-lines="none"} apiVersion: database.aws.crossplane.io/v1beta1 kind: RDSInstance metadata: @@ -362,7 +369,7 @@ To publish the data generated by a managed resource as a Kubernetes Secret object provide a {{}}publishConnectionDetailsTo.name{{< /hover >}} -```yaml {label="k8secret"} +```yaml {label="k8secret",copy-lines="none"} apiVersion: rds.aws.upbound.io/v1beta1 kind: Instance spec: @@ -376,7 +383,7 @@ Crossplane can apply labels and annotations to the Kubernetes secret as well using {{}}publishConnectionDetailsTo.metadata{{}}. -```yaml {label="k8label"} +```yaml {label="k8label",copy-lines="none"} apiVersion: rds.aws.upbound.io/v1beta1 kind: Instance spec: @@ -402,7 +409,7 @@ The {{}}StoreConfig{{}} object. -```yaml {label="configref"} +```yaml {label="configref",copy-lines="none"} apiVersion: rds.aws.upbound.io/v1beta1 kind: Instance spec: @@ -414,7 +421,7 @@ spec: name: my-vault-storeconfig ``` -```yaml {label="storeconfig"} +```yaml {label="storeconfig",copy-lines="none"} apiVersion: secrets.crossplane.io/v1alpha1 kind: StoreConfig metadata: @@ -438,9 +445,10 @@ resources. | Annotation | Definition | | --- | --- | | `crossplane.io/external-name` | The name of the managed resource inside the Provider. | -| `crossplane.io/external-create-pending` | The timestamp of the last time the managed resource was in the `PENDING` state. | -| `crossplane.io/external-create-succeeded` | The timestamp of the last time the Provider successfully created the managed resource. | -| `crossplane.io/external-create-failed` | The timestamp of the last time the Provider failed to create the managed resource. | +| `crossplane.io/external-create-pending` | The timestamp of when Crossplane +began creating a new managed resource. | +| `crossplane.io/external-create-succeeded` | The timestamp of when the Provider successfully created the managed resource. | +| `crossplane.io/external-create-failed` | The timestamp of when the Provider failed to create the managed resource. | | `crossplane.io/paused` | Indicates Crossplane isn't reconciling this resource. Read the [Pause Annotation](#pause) for more details. | | `crossplane.io/composition-resource-name` | For managed resource created by a Composition, this is the Composition's `resources.name` value. | {{}} @@ -454,7 +462,7 @@ For example, a managed resource named the name `my-rds-instance` as an external resource inside the Provider's environment. -```yaml {label="external-name"} +```yaml {label="external-name,copy-lines="none"} apiVersion: database.aws.crossplane.io/v1beta1 kind: RDSInstance metadata: @@ -476,7 +484,7 @@ For example, the Provider creates managed resource named the name {{}}my-custom-name{{}} for the external resource inside AWS. -```yaml {label="custom-name"} +```yaml {label="custom-name",copy-lines="none"} apiVersion: database.aws.crossplane.io/v1beta1 kind: RDSInstance metadata: @@ -485,7 +493,7 @@ metadata: crossplane.io/external-name: my-custom-namee ``` -```shell +```shell {copy-lines="1"} kubectl get rdsinstance NAME READY SYNCED EXTERNAL-NAME AGE my-rds-instance True True my-custom-name 11m @@ -548,7 +556,7 @@ on managed resources to control their deletion. Kubernetes can't delete objects with Finalizers. {{}} -When a Crossplane deletes managed resource the Provider begins deleting the +When Crossplane deletes a managed resource the Provider begins deleting the external resource, but the managed resource remains until the external resource is fully deleted. @@ -568,9 +576,10 @@ Providers may define their own custom `Conditions`. ### Available -`Reason: Available` indicates the Provider created the managed resource. +`Reason: Available` indicates the Provider created the managed resource and it's +ready for use. -```yaml +```yaml {copy-lines="none"} Conditions: Type: Ready Status: True @@ -581,7 +590,7 @@ Conditions: `Reason: Creating` indicates the Provider is attempting to create the managed resource. -```yaml +```yaml {copy-lines="none"} Conditions: Type: Ready Status: False @@ -592,18 +601,20 @@ Conditions: `Reason: Deleting` indicates the Provider is attempting to delete the managed resource. -```yaml +```yaml {copy-lines="none"} Conditions: Type: Ready Status: False Reason: Deleting ``` - -### Paused + +### ReconcilePaused + `Reason: ReconcilePaused` indicates the managed resource has a [Pause](#paused) annotation -```yaml + +```yaml {copy-lines="none"} Conditions: Type: Synced Status: False @@ -617,21 +628,20 @@ Conditions: reconciling the managed resource. The `Message:` value of the `Condition` helps identify the Crossplane error. -```yaml +```yaml {copy-lines="none"} Conditions: Type: Synced Status: False Reason: ReconcileError ``` - - - -### Success + +### ReconcileSuccess + `Reason: ReconcileSuccess` indicates the Provider created and is monitoring the managed resource. -```yaml +```yaml {copy-lines="none"} Conditions: Type: Synced Status: True @@ -642,7 +652,7 @@ Conditions: `Reason: Unavailable` indicates Crossplane expects the managed resource to be available, but the Provider reports the resource is unhealthy. -```yaml +```yaml {copy-lines="none"} Conditions: Type: Ready Status: False @@ -653,7 +663,8 @@ Conditions: `Reason: Unknown` indicates the Provider has an unexpected error with the managed resource. The `conditions.message` provides more information on what went wrong. -```yaml + +```yaml {copy-lines="none"} Conditions: Type: Unknown Status: False @@ -679,7 +690,7 @@ an asynchronous operation. The `Reason: Finished` indicates the asynchronous operation completed successfully. -```yaml +```yaml {copy-lines="none"} Conditions: Type: AsyncOperation Status: True @@ -691,7 +702,7 @@ Conditions: `Reason: Ongoing` indicates the managed resource operation is still in progress. -```yaml +```yaml {copy-lines="none"} Conditions: Type: AsyncOperation Status: True @@ -713,7 +724,7 @@ operation status as either `Success` or a failure `Reason`. managed resource. The `conditions.message` provides more information on what went wrong. -```yaml +```yaml {copy-lines="none"} Conditions: Type: LastAsyncOperation Status: False @@ -728,7 +739,7 @@ Conditions: resource. The `conditions.message` provides more information on what went wrong. -```yaml +```yaml {copy-lines="none"} Conditions: Type: LastAsyncOperation Status: False @@ -739,7 +750,7 @@ Conditions: `Reason: Success` indicates the Provider successfully created the managed resource asynchronously. -```yaml +```yaml {copy-lines="none"} Conditions: Type: LastAsyncOperation Status: True From abdafdd7b883ad8799da11592e66099c48818f58 Mon Sep 17 00:00:00 2001 From: Pete Lumbis Date: Tue, 27 Jun 2023 13:07:34 -0400 Subject: [PATCH 16/17] fix copy-lines attributes Signed-off-by: Pete Lumbis --- content/master/concepts/managed-resources.md | 10 +++++----- content/v1.11/concepts/managed-resources.md | 10 +++++----- content/v1.12/concepts/managed-resources.md | 10 +++++----- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/content/master/concepts/managed-resources.md b/content/master/concepts/managed-resources.md index 3e1420639..1baad3101 100644 --- a/content/master/concepts/managed-resources.md +++ b/content/master/concepts/managed-resources.md @@ -39,7 +39,7 @@ Provider](https://marketplace.upbound.io/providers/upbound/provider-aws/latest/) defines the {{}}Instance{{}} kind from the group {{}}ec2.aws.upbound.io{{}} -```yaml {label="gkv",copy-lines="none} +```yaml {label="gkv",copy-lines="none"} apiVersion: ec2.aws.upbound.io/v1beta1 kind: Instance ``` @@ -77,7 +77,7 @@ Refer to the documentation of your specific Provider for details. {{< /hint >}} -```yaml {label="forProvider",copy-lines="none} +```yaml {label="forProvider",copy-lines="none"} apiVersion: ec2.aws.upbound.io/v1beta1 kind: Instance # Removed for brevity @@ -124,7 +124,7 @@ my-test-vpc True True vpc-01353cfe93950a8ff 49m To match the VPC by name, use the external name. For example, creating a Subnet managed resource attached to this VPC. -```yaml {copy-lines="none} +```yaml {copy-lines="none"} apiVersion: ec2.aws.upbound.io/v1beta1 kind: Subnet spec: @@ -462,7 +462,7 @@ For example, a managed resource named the name `my-rds-instance` as an external resource inside the Provider's environment. -```yaml {label="external-name,copy-lines="none"} +```yaml {label="external-name",copy-lines="none"} apiVersion: database.aws.crossplane.io/v1beta1 kind: RDSInstance metadata: @@ -518,7 +518,7 @@ for more details {{< /hint >}} -### Pause +### Paused Manually applying the `crossplane.io/paused` annotation causes the Provider to stop reconciling the managed resource. diff --git a/content/v1.11/concepts/managed-resources.md b/content/v1.11/concepts/managed-resources.md index 3e1420639..1baad3101 100644 --- a/content/v1.11/concepts/managed-resources.md +++ b/content/v1.11/concepts/managed-resources.md @@ -39,7 +39,7 @@ Provider](https://marketplace.upbound.io/providers/upbound/provider-aws/latest/) defines the {{}}Instance{{}} kind from the group {{}}ec2.aws.upbound.io{{}} -```yaml {label="gkv",copy-lines="none} +```yaml {label="gkv",copy-lines="none"} apiVersion: ec2.aws.upbound.io/v1beta1 kind: Instance ``` @@ -77,7 +77,7 @@ Refer to the documentation of your specific Provider for details. {{< /hint >}} -```yaml {label="forProvider",copy-lines="none} +```yaml {label="forProvider",copy-lines="none"} apiVersion: ec2.aws.upbound.io/v1beta1 kind: Instance # Removed for brevity @@ -124,7 +124,7 @@ my-test-vpc True True vpc-01353cfe93950a8ff 49m To match the VPC by name, use the external name. For example, creating a Subnet managed resource attached to this VPC. -```yaml {copy-lines="none} +```yaml {copy-lines="none"} apiVersion: ec2.aws.upbound.io/v1beta1 kind: Subnet spec: @@ -462,7 +462,7 @@ For example, a managed resource named the name `my-rds-instance` as an external resource inside the Provider's environment. -```yaml {label="external-name,copy-lines="none"} +```yaml {label="external-name",copy-lines="none"} apiVersion: database.aws.crossplane.io/v1beta1 kind: RDSInstance metadata: @@ -518,7 +518,7 @@ for more details {{< /hint >}} -### Pause +### Paused Manually applying the `crossplane.io/paused` annotation causes the Provider to stop reconciling the managed resource. diff --git a/content/v1.12/concepts/managed-resources.md b/content/v1.12/concepts/managed-resources.md index 3e1420639..1baad3101 100644 --- a/content/v1.12/concepts/managed-resources.md +++ b/content/v1.12/concepts/managed-resources.md @@ -39,7 +39,7 @@ Provider](https://marketplace.upbound.io/providers/upbound/provider-aws/latest/) defines the {{}}Instance{{}} kind from the group {{}}ec2.aws.upbound.io{{}} -```yaml {label="gkv",copy-lines="none} +```yaml {label="gkv",copy-lines="none"} apiVersion: ec2.aws.upbound.io/v1beta1 kind: Instance ``` @@ -77,7 +77,7 @@ Refer to the documentation of your specific Provider for details. {{< /hint >}} -```yaml {label="forProvider",copy-lines="none} +```yaml {label="forProvider",copy-lines="none"} apiVersion: ec2.aws.upbound.io/v1beta1 kind: Instance # Removed for brevity @@ -124,7 +124,7 @@ my-test-vpc True True vpc-01353cfe93950a8ff 49m To match the VPC by name, use the external name. For example, creating a Subnet managed resource attached to this VPC. -```yaml {copy-lines="none} +```yaml {copy-lines="none"} apiVersion: ec2.aws.upbound.io/v1beta1 kind: Subnet spec: @@ -462,7 +462,7 @@ For example, a managed resource named the name `my-rds-instance` as an external resource inside the Provider's environment. -```yaml {label="external-name,copy-lines="none"} +```yaml {label="external-name",copy-lines="none"} apiVersion: database.aws.crossplane.io/v1beta1 kind: RDSInstance metadata: @@ -518,7 +518,7 @@ for more details {{< /hint >}} -### Pause +### Paused Manually applying the `crossplane.io/paused` annotation causes the Provider to stop reconciling the managed resource. From abd125038f79fefb6ab923b2da3f191ff9a3d9ad Mon Sep 17 00:00:00 2001 From: Pete Lumbis Date: Tue, 27 Jun 2023 13:12:12 -0400 Subject: [PATCH 17/17] fix anchor links Signed-off-by: Pete Lumbis --- content/master/concepts/managed-resources.md | 2 +- content/v1.11/concepts/managed-resources.md | 2 +- content/v1.12/concepts/managed-resources.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/content/master/concepts/managed-resources.md b/content/master/concepts/managed-resources.md index 1baad3101..f6404554d 100644 --- a/content/master/concepts/managed-resources.md +++ b/content/master/concepts/managed-resources.md @@ -449,7 +449,7 @@ resources. began creating a new managed resource. | | `crossplane.io/external-create-succeeded` | The timestamp of when the Provider successfully created the managed resource. | | `crossplane.io/external-create-failed` | The timestamp of when the Provider failed to create the managed resource. | -| `crossplane.io/paused` | Indicates Crossplane isn't reconciling this resource. Read the [Pause Annotation](#pause) for more details. | +| `crossplane.io/paused` | Indicates Crossplane isn't reconciling this resource. Read the [Pause Annotation](#paused) for more details. | | `crossplane.io/composition-resource-name` | For managed resource created by a Composition, this is the Composition's `resources.name` value. | {{}} diff --git a/content/v1.11/concepts/managed-resources.md b/content/v1.11/concepts/managed-resources.md index 1baad3101..f6404554d 100644 --- a/content/v1.11/concepts/managed-resources.md +++ b/content/v1.11/concepts/managed-resources.md @@ -449,7 +449,7 @@ resources. began creating a new managed resource. | | `crossplane.io/external-create-succeeded` | The timestamp of when the Provider successfully created the managed resource. | | `crossplane.io/external-create-failed` | The timestamp of when the Provider failed to create the managed resource. | -| `crossplane.io/paused` | Indicates Crossplane isn't reconciling this resource. Read the [Pause Annotation](#pause) for more details. | +| `crossplane.io/paused` | Indicates Crossplane isn't reconciling this resource. Read the [Pause Annotation](#paused) for more details. | | `crossplane.io/composition-resource-name` | For managed resource created by a Composition, this is the Composition's `resources.name` value. | {{}} diff --git a/content/v1.12/concepts/managed-resources.md b/content/v1.12/concepts/managed-resources.md index 1baad3101..f6404554d 100644 --- a/content/v1.12/concepts/managed-resources.md +++ b/content/v1.12/concepts/managed-resources.md @@ -449,7 +449,7 @@ resources. began creating a new managed resource. | | `crossplane.io/external-create-succeeded` | The timestamp of when the Provider successfully created the managed resource. | | `crossplane.io/external-create-failed` | The timestamp of when the Provider failed to create the managed resource. | -| `crossplane.io/paused` | Indicates Crossplane isn't reconciling this resource. Read the [Pause Annotation](#pause) for more details. | +| `crossplane.io/paused` | Indicates Crossplane isn't reconciling this resource. Read the [Pause Annotation](#paused) for more details. | | `crossplane.io/composition-resource-name` | For managed resource created by a Composition, this is the Composition's `resources.name` value. | {{}}