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..085c94bc5 --- /dev/null +++ b/content/knowledge-base/guides/import-existing-resources.md @@ -0,0 +1,287 @@ +--- +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. +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 +`crossplane.io/external-name` annotation in a managed resource. + +To import an existing external resource in a Provider, create a new managed +resource with the `crossplane.io/external-name` annotation. Set the annotation +value to the name of the resource in the Provider. + +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",copy-lines="none"} +apiVersion: compute.gcp.crossplane.io/v1beta1 +kind: Network +metadata: + annotations: + 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 >}} + +```yaml {label="name",copy-lines="none"} +apiVersion: compute.gcp.crossplane.io/v1beta1 +kind: Network +metadata: + annotations: + name: imported-network + crossplane.io/external-name: my-existing-network +``` + +Leave the +{{}}spec.forProvider{{}} field empty. +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 +metadata: + annotations: + name: imported-network + crossplane.io/external-name: my-existing-network +spec: + forProvider: {} +``` + + +Crossplane now controls and manages this imported resource. Any changes to the +managed resource `spec` changes the external resource. + +## Import resources automatically + +Automatically import external resources with the +`ObserveOnly` [`managementPolicy`]({{}}). + +Crossplane imports `ObserveOnly` resources but never changes or deletes the +resources. + +{{}} +The managed resource `managementPolicy` option is an alpha feature. + +Enable the `managementPolicy` in a provider with `--enable-management-policies` +in a +[ControllerConfig]({{}}). +{{< /hint >}} + + +### Apply the ObserveOnly managementPolicy + + +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: + managementPolicy: ObserveOnly +``` + +### Add the external-name annotation +Add the {{}}crossplane.io/external-name{{}} +annotation for the resource. This name must match the name inside the Provider. + +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: + annotations: + crossplane.io/external-name: my-external-database +spec: + managementPolicy: ObserveOnly +``` + +### Create a Kubernetes object name +Create a {{}}name{{}} to use for the +Kubernetes object. + +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: + 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, only import the GCP SQL database in the +{{}}us-central1{{}} 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" +``` + +### 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",copy-lines="none"} +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 + # Removed for brevity + settings: + - activationPolicy: ALWAYS + availabilityType: REGIONAL + diskSize: 100 + # Removed for brevity + 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 +``` + +## Control imported ObserveOnly resources + + +Crossplane can take active control of `ObserveOnly` imported resources by +changing the `managementPolicy` after import. + +Change the {{}}managementPolicy{{}} field +of the managed resource to +{{}}FullControl{{}}. + +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 +metadata: + annotations: + crossplane.io/external-name: existing-database-instance + name: existing-database-instance +spec: + managementPolicy: FullControl + forProvider: + databaseVersion: POSTGRES_14 + region: us-central1 + settings: + - diskSize: 100 + 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 + status: "True" + type: Ready + - lastTransitionTime: "2023-02-22T11:16:45Z" + reason: ReconcileSuccess + status: "True" + type: Synced +``` + +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 diff --git a/content/master/concepts/managed-resources.md b/content/master/concepts/managed-resources.md index 0950b8b85..f6404554d 100644 --- a/content/master/concepts/managed-resources.md +++ b/content/master/concepts/managed-resources.md @@ -3,630 +3,756 @@ 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 +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" >}} +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 PostgreSQL [`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",copy-lines="none"} +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",copy-lines="none"} +apiVersion: ec2.aws.upbound.io/v1beta1 +kind: Instance +# Removed for brevity 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" 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 >}} + +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 {copy-lines="1" +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 {copy-lines="none"} +apiVersion: ec2.aws.upbound.io/v1beta1 +kind: Subnet +spec: + forProvider: + # Removed for brevity + vpcId: vpc-01353cfe93950a8ff +``` -```console -kubectl get rdsinstance rdspostgresql +##### 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 {copy-lines="1"} +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 {copy-lines="none"} +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. -```console -kubectl describe secret aws-rdspostgresql-conn -n crossplane-system +{{}} +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 {copy-lines="none"} +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 an external resource if their corresponding managed +resource object is deleted from Kubernetes and the `deletionPolicy` is +`delete`. + +{{< /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",copy-lines="none"}} +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 >}} + + +### 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`](#providerconfigref). -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 + +### 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 Kubernetes secret +{{}}rds-secret{{}}, referenced by +the +{{}}writeConnectionSecretToRef{{}} +field. + +```yaml {label="secretname",copy-lines="none"} +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 + -```console -kubectl get cloudsqlinstance cloudsqlpostgresql -``` +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",copy-lines="none"} +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",copy-lines="none"} +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",copy-lines="none"} +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",copy-lines="none"} +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 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](#paused) 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",copy-lines="none"} +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: - -```yaml -apiVersion: sqs.aws.crossplane.io/v1beta1 -kind: Queue +```shell +kubectl get rdsinstance +NAME READY SYNCED EXTERNAL-NAME AGE +my-rds-instance True True my-rds-instance 11m ``` -## Behavior +Managed resource created with a `crossplane.io/external-name` +annotation already provided use the annotation value as the external +resource name. -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. +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. -### Continuous Reconciliation +```yaml {label="custom-name",copy-lines="none"} +apiVersion: database.aws.crossplane.io/v1beta1 +kind: RDSInstance +metadata: + name: my-rds-instance + annotations: + crossplane.io/external-name: my-custom-namee +``` -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`. +```shell {copy-lines="1"} +kubectl get rdsinstance +NAME READY SYNCED EXTERNAL-NAME AGE +my-rds-instance True True my-custom-name 11m +``` -#### Connection Details +### Creation annotations -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`. +Providers create new managed resources with the +`crossplane.io/external-create-pending` annotation. -> Which managed resources have connection details and what connection details -> they have is currently undocumented. This is tracked in [this -> issue][issue-1143]. +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. -#### Immutable Properties +{{}} +If a Provider restarts before creating the `succeed` or `fail` annotations the +Provider can't reconcile the manged 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`. +Read Crossplane [issue +#3037](https://github.com/crossplane/crossplane/issues/3037#issuecomment-1110142427) +for more details +{{< /hint >}} -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. -> 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]. +### Paused +Manually applying the `crossplane.io/paused` annotation causes the Provider to +stop reconciling the managed resource. -#### 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 +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. -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. -```yaml -apiVersion: database.gcp.crossplane.io/v1beta1 -kind: CloudSQLInstance -metadata: - name: foodb - annotations: - crossplane.io/external-name: my-special-db -spec: - ... -``` +{{< hint "note" >}} +Kubernetes can't delete objects with Finalizers. +{{}} -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: - -```yaml -spec: - forProvider: - resourceGroupName: foo-res-group - resourceGroupNameRef: - name: resourcegroup - resourceGroupNameSelector: - matchLabels: - app: prod -``` +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. -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. +When the external resource is fully deleted Crossplane removes the Finalizer and +deletes the managed resource object. -Users are able to specify any of these three fields: +## Conditions -- Selector to select via labels -- Reference to point to a determined managed resource -- Actual value that will be submitted to the provider +Crossplane has a standard set of `Conditions` for a managed +resource. View the `Conditions` of a managed resource with +`kubectl describe ` -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 +{{}} +Providers may define their own custom `Conditions`. +{{}} -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. +### Available +`Reason: Available` indicates the Provider created the managed resource and it's +ready for use. -{{}} -Management policies are an experimental feature, and the API is -subject to change. -{{< /hint >}} +```yaml {copy-lines="none"} +Conditions: + Type: Ready + Status: True + Reason: Available +``` +### Creating -To use management policies, you must enable them with the -`--enable-management-policies` flag when starting the provider controller. +`Reason: Creating` indicates the Provider is attempting to create the managed +resource. -## Importing Existing Resources +```yaml {copy-lines="none"} +Conditions: + Type: Ready + Status: False + Reason: Creating +``` -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: +### Deleting +`Reason: Deleting` indicates the Provider is attempting to delete 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 +```yaml {copy-lines="none"} +Conditions: + Type: Ready + Status: False + Reason: Deleting ``` -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. + +### ReconcilePaused + +`Reason: ReconcilePaused` indicates the managed resource has a [Pause](#paused) +annotation + +```yaml {copy-lines="none"} +Conditions: + Type: Synced + Status: False + Reason: ReconcilePaused +``` -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. + +### 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 {copy-lines="none"} +Conditions: + Type: Synced + Status: False + Reason: ReconcileError +``` -### Alternative Import Procedure: Start with ObserveOnly + +### ReconcileSuccess + +`Reason: ReconcileSuccess` indicates the Provider created and is monitoring the +managed resource. + +```yaml {copy-lines="none"} +Conditions: + Type: Synced + Status: True + Reason: ReconcileSuccess +``` -Directly importing existing managed resources approach has the following caveats: +### Unavailable +`Reason: Unavailable` indicates Crossplane expects the managed resource to be +available, but the Provider reports the resource is unhealthy. -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. +```yaml {copy-lines="none"} +Conditions: + Type: Ready + Status: False + Reason: Unavailable +``` -Instead of manually creating resources you can import the resource with an -`ObserveOnly` management policy. +### Unknown +`Reason: Unknown` indicates the Provider has an unexpected error with the +managed resource. The `conditions.message` provides more information on what +went wrong. -Crossplane imports `ObserveOnly` resources but never changes or deletes the -resource. +```yaml {copy-lines="none"} +Conditions: + Type: Unknown + Status: False + Reason: Unknown +``` -{{< 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: +### Upjet Provider conditions +[Upjet](https://github.com/upbound/upjet), the open source tool to generate +Crossplane Providers, also has a set of standard `Conditions`. -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" + +#### 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 {copy-lines="none"} +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 {copy-lines="none"} +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 {copy-lines="none"} +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 {copy-lines="none"} +Conditions: + Type: LastAsyncOperation + Status: False + Reason: DestroyFailure +``` + +##### Success +`Reason: Success` indicates the Provider successfully created the managed +resource asynchronously. + +```yaml {copy-lines="none"} +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..f6404554d 100644 --- a/content/v1.11/concepts/managed-resources.md +++ b/content/v1.11/concepts/managed-resources.md @@ -3,470 +3,756 @@ 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 +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. -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 PostgreSQL [`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",copy-lines="none"} +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",copy-lines="none"} +apiVersion: ec2.aws.upbound.io/v1beta1 +kind: Instance +# Removed for brevity 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" 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 >}} + +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 {copy-lines="1" +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 {copy-lines="none"} +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 {copy-lines="1"} +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 {copy-lines="none"} +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. +{{}} -```console -kubectl describe secret aws-rdspostgresql-conn -n crossplane-system +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 {copy-lines="none"} +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 an external resource if their corresponding managed +resource object is deleted from Kubernetes and the `deletionPolicy` is +`delete`. + +{{< /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",copy-lines="none"}} +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 >}} + + +### 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`](#providerconfigref). + -The GCP provider supports provisioning a [CloudSQL][cloudsql] instance with the -`CloudSQLInstance` managed resource it adds to Crossplane. + +### writeConnectionSecretToRef + -```yaml -apiVersion: database.gcp.crossplane.io/v1beta1 -kind: CloudSQLInstance +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 Kubernetes secret +{{}}rds-secret{{}}, referenced by +the +{{}}writeConnectionSecretToRef{{}} +field. + +```yaml {label="secretname",copy-lines="none"} +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 +``` + +{{}} +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. + +{{< 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",copy-lines="none"} +apiVersion: rds.aws.upbound.io/v1beta1 +kind: Instance +spec: + forProvider: + # Removed for brevity + publishConnectionDetailsTo: + name: rds-kubernetes-secret ``` -Creating the above instance will cause Crossplane to provision a CloudSQL -instance on GCP. You can view the progress with the following command: +Crossplane can apply labels and annotations to the Kubernetes secret as well +using +{{}}publishConnectionDetailsTo.metadata{{}}. -```console -kubectl get cloudsqlinstance cloudsqlpostgresql +```yaml {label="k8label",copy-lines="none"} +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 ``` -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`: +#### 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{{}}. -```console -kubectl describe secret cloudsqlpostgresql-conn -n crossplane-system +The +{{}}configRef.name{{}} references a +{{}}StoreConfig{{}} +object. + +```yaml {label="configref",copy-lines="none"} +apiVersion: rds.aws.upbound.io/v1beta1 +kind: Instance +spec: + forProvider: + # Removed for brevity + publishConnectionDetailsTo: + name: rds-kubernetes-secret + configRef: + name: my-vault-storeconfig ``` -You can then delete the `CloudSQLInstance`: +```yaml {label="storeconfig",copy-lines="none"} +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 >}} + + +## Annotations + +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 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](#paused) 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. -```console -kubectl delete cloudsqlinstance cloudsqlpostgresql +```yaml {label="external-name",copy-lines="none"} +apiVersion: database.aws.crossplane.io/v1beta1 +kind: RDSInstance +metadata: + name: my-rds-instance ``` -{{< /tab >}} -{{< tab "Azure" >}} +```shell +kubectl get rdsinstance +NAME READY SYNCED EXTERNAL-NAME AGE +my-rds-instance True True my-rds-instance 11m +``` -The Azure provider supports provisioning an [Azure Database for PostgreSQL] -instance with the `PostgreSQLServer` managed resource it adds to Crossplane. +Managed resource created with a `crossplane.io/external-name` +annotation already provided use the annotation value as the external +resource name. -> 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. +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 -apiVersion: azure.crossplane.io/v1alpha3 -kind: ResourceGroup +```yaml {label="custom-name",copy-lines="none"} +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 + annotations: + crossplane.io/external-name: my-custom-namee +``` + +```shell {copy-lines="1"} +kubectl get rdsinstance +NAME READY SYNCED EXTERNAL-NAME AGE +my-rds-instance True True my-custom-name 11m +``` + +### 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 >}} + + +### Paused +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: sqlserverpostgresql + name: my-rds-instance + annotations: + crossplane.io/paused: "true" 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 + region: us-west-1 + instanceType: t2.micro ``` -```console -kubectl apply -f https://raw.githubusercontent.com/crossplane/crossplane/release-1.10/docs/snippets/provision/azure.yaml +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 Crossplane deletes a 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 and it's +ready for use. + +```yaml {copy-lines="none"} +Conditions: + Type: Ready + Status: True + Reason: Available ``` +### Creating -Creating the above instance will cause Crossplane to provision a PostgreSQL -database instance on Azure. You can view the progress with the following -command: +`Reason: Creating` indicates the Provider is attempting to create the managed +resource. -```console -kubectl get postgresqlserver sqlserverpostgresql +```yaml {copy-lines="none"} +Conditions: + Type: Ready + Status: False + Reason: Creating ``` -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`: +### Deleting +`Reason: Deleting` indicates the Provider is attempting to delete the managed +resource. -```console -kubectl describe secret sqlserverpostgresql-conn -n crossplane-system +```yaml {copy-lines="none"} +Conditions: + Type: Ready + Status: False + Reason: Deleting ``` -You can then delete the `PostgreSQLServer`: + +### ReconcilePaused + +`Reason: ReconcilePaused` indicates the managed resource has a [Pause](#paused) +annotation + +```yaml {copy-lines="none"} +Conditions: + Type: Synced + Status: False + Reason: ReconcilePaused +``` -```console -kubectl delete postgresqlserver sqlserverpostgresql -kubectl delete resourcegroup sqlserverpostgresql-rg + +### 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 {copy-lines="none"} +Conditions: + Type: Synced + Status: False + Reason: ReconcileError ``` -{{< /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: - -```yaml -apiVersion: sqs.aws.crossplane.io/v1beta1 -kind: Queue + +### ReconcileSuccess + +`Reason: ReconcileSuccess` indicates the Provider created and is monitoring the +managed resource. + +```yaml {copy-lines="none"} +Conditions: + Type: Synced + Status: True + Reason: ReconcileSuccess ``` -## Behavior +### Unavailable +`Reason: Unavailable` indicates Crossplane expects the managed resource to be +available, but the Provider reports the resource is unhealthy. -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. +```yaml {copy-lines="none"} +Conditions: + Type: Ready + Status: False + Reason: Unavailable +``` -### Continuous Reconciliation +### Unknown +`Reason: Unknown` indicates the Provider has an unexpected error with the +managed resource. The `conditions.message` provides more information on what +went wrong. -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 {copy-lines="none"} +Conditions: + Type: Unknown + Status: False + Reason: Unknown +``` -#### 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`. +### Upjet Provider conditions +[Upjet](https://github.com/upbound/upjet), the open source tool to generate +Crossplane Providers, also has a set of standard `Conditions`. -> 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 + +#### AsyncOperation + -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`. +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. -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. -> 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]. +##### Finished +The `Reason: Finished` indicates the asynchronous operation completed +successfully. -#### 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 -apiVersion: ec2.aws.upbound.io/v1beta1 -kind: VPC -metadata: - name: paused-vpc - annotations: - crossplane.io/paused: "true" -... +```yaml {copy-lines="none"} +Conditions: + Type: AsyncOperation + Status: True + Reason: Finished ``` -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: +##### Ongoing -```yaml -apiVersion: database.gcp.crossplane.io/v1beta1 -kind: CloudSQLInstance -metadata: - name: foodb - annotations: - crossplane.io/external-name: my-special-db -spec: - ... +`Reason: Ongoing` indicates the managed resource operation is still in progress. + +```yaml {copy-lines="none"} +Conditions: + Type: AsyncOperation + Status: True + Reason: Ongoing ``` -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: - -```yaml -spec: - forProvider: - resourceGroupName: foo-res-group - resourceGroupNameRef: - name: resourcegroup - resourceGroupNameSelector: - matchLabels: - app: prod + +#### 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 {copy-lines="none"} +Conditions: + Type: LastAsyncOperation + Status: False + Reason: ApplyFailure ``` -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. - -## 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 + +##### DestroyFailure + + +`Reason: DestroyFailure` indicates the Provider failed to delete the managed +resource. The `conditions.message` provides more information on what +went wrong. + +```yaml {copy-lines="none"} +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 {copy-lines="none"} +Conditions: + Type: LastAsyncOperation + Status: True + Reason: Success +``` \ 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 0950b8b85..f6404554d 100644 --- a/content/v1.12/concepts/managed-resources.md +++ b/content/v1.12/concepts/managed-resources.md @@ -3,630 +3,756 @@ 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 +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" >}} +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 PostgreSQL [`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",copy-lines="none"} +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",copy-lines="none"} +apiVersion: ec2.aws.upbound.io/v1beta1 +kind: Instance +# Removed for brevity 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" 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 >}} + +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 {copy-lines="1" +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 {copy-lines="none"} +apiVersion: ec2.aws.upbound.io/v1beta1 +kind: Subnet +spec: + forProvider: + # Removed for brevity + vpcId: vpc-01353cfe93950a8ff +``` -```console -kubectl get rdsinstance rdspostgresql +##### 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 {copy-lines="1"} +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 {copy-lines="none"} +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. -```console -kubectl describe secret aws-rdspostgresql-conn -n crossplane-system +{{}} +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 {copy-lines="none"} +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 an external resource if their corresponding managed +resource object is deleted from Kubernetes and the `deletionPolicy` is +`delete`. + +{{< /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",copy-lines="none"}} +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 >}} + + +### 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`](#providerconfigref). -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 + +### 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 Kubernetes secret +{{}}rds-secret{{}}, referenced by +the +{{}}writeConnectionSecretToRef{{}} +field. + +```yaml {label="secretname",copy-lines="none"} +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 + -```console -kubectl get cloudsqlinstance cloudsqlpostgresql -``` +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",copy-lines="none"} +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",copy-lines="none"} +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",copy-lines="none"} +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",copy-lines="none"} +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 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](#paused) 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",copy-lines="none"} +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: - -```yaml -apiVersion: sqs.aws.crossplane.io/v1beta1 -kind: Queue +```shell +kubectl get rdsinstance +NAME READY SYNCED EXTERNAL-NAME AGE +my-rds-instance True True my-rds-instance 11m ``` -## Behavior +Managed resource created with a `crossplane.io/external-name` +annotation already provided use the annotation value as the external +resource name. -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. +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. -### Continuous Reconciliation +```yaml {label="custom-name",copy-lines="none"} +apiVersion: database.aws.crossplane.io/v1beta1 +kind: RDSInstance +metadata: + name: my-rds-instance + annotations: + crossplane.io/external-name: my-custom-namee +``` -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`. +```shell {copy-lines="1"} +kubectl get rdsinstance +NAME READY SYNCED EXTERNAL-NAME AGE +my-rds-instance True True my-custom-name 11m +``` -#### Connection Details +### Creation annotations -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`. +Providers create new managed resources with the +`crossplane.io/external-create-pending` annotation. -> Which managed resources have connection details and what connection details -> they have is currently undocumented. This is tracked in [this -> issue][issue-1143]. +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. -#### Immutable Properties +{{}} +If a Provider restarts before creating the `succeed` or `fail` annotations the +Provider can't reconcile the manged 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`. +Read Crossplane [issue +#3037](https://github.com/crossplane/crossplane/issues/3037#issuecomment-1110142427) +for more details +{{< /hint >}} -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. -> 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]. +### Paused +Manually applying the `crossplane.io/paused` annotation causes the Provider to +stop reconciling the managed resource. -#### 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 +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. -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. -```yaml -apiVersion: database.gcp.crossplane.io/v1beta1 -kind: CloudSQLInstance -metadata: - name: foodb - annotations: - crossplane.io/external-name: my-special-db -spec: - ... -``` +{{< hint "note" >}} +Kubernetes can't delete objects with Finalizers. +{{}} -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: - -```yaml -spec: - forProvider: - resourceGroupName: foo-res-group - resourceGroupNameRef: - name: resourcegroup - resourceGroupNameSelector: - matchLabels: - app: prod -``` +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. -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. +When the external resource is fully deleted Crossplane removes the Finalizer and +deletes the managed resource object. -Users are able to specify any of these three fields: +## Conditions -- Selector to select via labels -- Reference to point to a determined managed resource -- Actual value that will be submitted to the provider +Crossplane has a standard set of `Conditions` for a managed +resource. View the `Conditions` of a managed resource with +`kubectl describe ` -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 +{{}} +Providers may define their own custom `Conditions`. +{{}} -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. +### Available +`Reason: Available` indicates the Provider created the managed resource and it's +ready for use. -{{}} -Management policies are an experimental feature, and the API is -subject to change. -{{< /hint >}} +```yaml {copy-lines="none"} +Conditions: + Type: Ready + Status: True + Reason: Available +``` +### Creating -To use management policies, you must enable them with the -`--enable-management-policies` flag when starting the provider controller. +`Reason: Creating` indicates the Provider is attempting to create the managed +resource. -## Importing Existing Resources +```yaml {copy-lines="none"} +Conditions: + Type: Ready + Status: False + Reason: Creating +``` -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: +### Deleting +`Reason: Deleting` indicates the Provider is attempting to delete 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 +```yaml {copy-lines="none"} +Conditions: + Type: Ready + Status: False + Reason: Deleting ``` -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. + +### ReconcilePaused + +`Reason: ReconcilePaused` indicates the managed resource has a [Pause](#paused) +annotation + +```yaml {copy-lines="none"} +Conditions: + Type: Synced + Status: False + Reason: ReconcilePaused +``` -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. + +### 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 {copy-lines="none"} +Conditions: + Type: Synced + Status: False + Reason: ReconcileError +``` -### Alternative Import Procedure: Start with ObserveOnly + +### ReconcileSuccess + +`Reason: ReconcileSuccess` indicates the Provider created and is monitoring the +managed resource. + +```yaml {copy-lines="none"} +Conditions: + Type: Synced + Status: True + Reason: ReconcileSuccess +``` -Directly importing existing managed resources approach has the following caveats: +### Unavailable +`Reason: Unavailable` indicates Crossplane expects the managed resource to be +available, but the Provider reports the resource is unhealthy. -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. +```yaml {copy-lines="none"} +Conditions: + Type: Ready + Status: False + Reason: Unavailable +``` -Instead of manually creating resources you can import the resource with an -`ObserveOnly` management policy. +### Unknown +`Reason: Unknown` indicates the Provider has an unexpected error with the +managed resource. The `conditions.message` provides more information on what +went wrong. -Crossplane imports `ObserveOnly` resources but never changes or deletes the -resource. +```yaml {copy-lines="none"} +Conditions: + Type: Unknown + Status: False + Reason: Unknown +``` -{{< 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: +### Upjet Provider conditions +[Upjet](https://github.com/upbound/upjet), the open source tool to generate +Crossplane Providers, also has a set of standard `Conditions`. -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" + +#### 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 {copy-lines="none"} +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 {copy-lines="none"} +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 {copy-lines="none"} +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 {copy-lines="none"} +Conditions: + Type: LastAsyncOperation + Status: False + Reason: DestroyFailure +``` + +##### Success +`Reason: Success` indicates the Provider successfully created the managed +resource asynchronously. + +```yaml {copy-lines="none"} +Conditions: + Type: LastAsyncOperation + Status: True + Reason: Success +``` \ No newline at end of file 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/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 diff --git a/utils/vale/styles/Crossplane/spelling-exceptions.txt b/utils/vale/styles/Crossplane/spelling-exceptions.txt index 319cb8bef..c7909e5b5 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 @@ -21,6 +22,7 @@ namespace namespaced namespaces Netlify +PostgreSQL proselint semver shortcode @@ -28,12 +30,14 @@ shortcodes SLAs stdin stdout +Subnet subnet syscall tolerations untrusted Upbound Upbound's +Upjet Velero VSCode Webpack diff --git a/utils/vale/styles/alex/ProfanityUnlikely.yml b/utils/vale/styles/alex/ProfanityUnlikely.yml index 4e79b0109..407fe3fec 100644 --- a/utils/vale/styles/alex/ProfanityUnlikely.yml +++ b/utils/vale/styles/alex/ProfanityUnlikely.yml @@ -104,7 +104,6 @@ tokens: - execution - executioner - explosion - - failure - fairies - fairy - faith @@ -201,7 +200,6 @@ tokens: - redlight - refugee - reject - - remains - republican - roach - robber 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