diff --git a/content/v2.0-preview/get-started/get-started-with-composition.md b/content/v2.0-preview/get-started/get-started-with-composition.md index ebcd237c..6015defe 100644 --- a/content/v2.0-preview/get-started/get-started-with-composition.md +++ b/content/v2.0-preview/get-started/get-started-with-composition.md @@ -3,4 +3,734 @@ title: Get Started With Composition weight: 200 --- -TODO \ No newline at end of file +This guide shows how to create a new kind of custom resource named `App`. When a +user calls the custom resource API to create an `App`, Crossplane creates a +`Deployment` and a `Service`. + +**Crossplane calls this _composition_.** The `App` is _composed of_ the +`Deployment` and the `Service`. + + +{{}} +The guide shows how to configure composition using YAML, templated YAML, Python, +and KCL. You can pick your preferred language. +{{}} + +An `App` custom resource looks like this: + +```yaml +apiVersion: example.crossplane.io/v1 +kind: App +metadata: + namespace: default + name: my-app +spec: + image: nginx +status: + replicas: 2 # Copied from the Deployment's status + address: 10.0.0.1 # Copied from the Service's status +``` + +**The `App` is the custom API Crossplane users use to configure an app.** + +When users create an `App` Crossplane creates this `Deployment` and `Service`: + +```yaml +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + namespace: default + name: my-app-dhj3a + labels: + example.crossplane.io/app: my-app # Copied from the App's name +spec: + replicas: 2 + selector: + matchLabels: + example.crossplane.io/app: my-app # Copied from the App's name + template: + metadata: + labels: + example.crossplane.io/app: my-app # Copied from the App's name + spec: + containers: + - name: app + image: nginx # Copied from the App's spec + ports: + - containerPort: 80 +--- +apiVersion: v1 +kind: Service +metadata: + namespace: default + name: my-app-03mda + labels: + example.crossplane.io/app: my-app # Copied from the App's name +spec: + selector: + example.crossplane.io/app: my-app # Copied from the App's name + ports: + - protocol: TCP + port: 8080 + targetPort: 80 +``` + +Crossplane builds on Kubernetes, so users can use `kubectl` or any other tool +from the Kubernetes ecosystem to work with apps. + +{{}} +Kubernetes custom resources are just JSON REST APIs, so users can use any tool +that supports REST APIs to work with apps. +{{}} + +## Prerequisites + +This guide requires: + +* A Kubernetes cluster with at least 2 GB of RAM +* The Crossplane v2 preview [installed on the Kubernetes cluster]({{}}) + +## Create the custom resource + +Follow these steps to create a new kind of custom resource using Crossplane: + +1. [Define](#define-the-schema) the schema of the `App` custom resource +1. [Install](#install-the-function) the function you want to use to configure + how Crossplane composes apps +1. [Configure](#configure-the-composition) how Crossplane composes apps + +After you complete these steps you can +[use the new `App` custom resource](#use-the-custom-resource). + +### Define the schema + +Crossplane calls a custom resource that's powered by composition a _composite +resource_, or XR. + +{{}} +Kubernetes calls user-defined API resources _custom resources_. + +Crossplane calls user-defined API resources that use composition _composite +resources_. + +A composite resource is a kind of custom resource. +{{}} + +Create this _composite resource definition_ (XRD) to define the schema of the +new `App` composite resource (XR). + +```yaml +apiVersion: apiextensions.crossplane.io/v2alpha1 +kind: CompositeResourceDefinition +metadata: + name: apps.example.crossplane.io +spec: + scope: Namespaced + group: example.crossplane.io + names: + kind: App + plural: apps + versions: + - name: v1 + served: true + referenceable: true + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + properties: + image: + description: The app's OCI container image. + type: string + required: + - image + status: + type: object + properties: + replicas: + description: The number of available app replicas. + type: integer + address: + description: The app's IP address. + type: string +``` + +Save the XRD as `xrd.yaml` and apply it: + +```shell +kubectl apply -f xrd.yaml +``` + +Check that Crossplane has established the XRD: + +``` shell {copy-lines="1"} +kubectl get -f xrd.yaml +NAME ESTABLISHED OFFERED AGE +apps.example.crossplane.io True 21s +``` + +Now that Crossplane has established the XRD, Kubernetes is serving API requests +for the new `App` XR. + +Crossplane now knows it's responsible for the new `App` XR, but it doesn't know +what to do when you create or update one. You tell Crossplane what to do by +[installing a function](#install-the-function) and +[configuring a composition](#configure-the-composition). + +### Install the function + +You can use different _composition functions_ to configure what Crossplane does +when someone creates or updates a composite resource (XR). Composition functions +are like configuration language plugins. + +Pick what language to use to configure how Crossplane turns an `App` XR into a +`Deployment` and a `Service`. + +{{< tabs >}} + +{{< tab "YAML" >}} +YAML is a good choice for small, static compositions. It doesn't support loops +or conditionals. + +Create this composition function to install YAML support: + +```yaml +apiVersion: pkg.crossplane.io/v1 +kind: Function +metadata: + name: crossplane-contrib-function-patch-and-transform +spec: + package: xpkg.crossplane.io/crossplane-contrib/function-patch-and-transform:v0.8.2 +``` + +Save the function as `fn.yaml` and apply it: + +```shell +kubectl apply -f fn.yaml +``` + +Check that Crossplane installed the function: + +```shell {copy-lines="1"} +kubectl get -f fn.yaml +NAME INSTALLED HEALTHY PACKAGE AGE +crossplane-contrib-function-patch-and-transform True True xpkg.crossplane.io/crossplane-contrib/function-patch-and-transform:v0.8.2 10s +``` +{{< /tab >}} + +{{< tab "Templated YAML" >}} +Templated YAML is a good choice if you're used to writing +[Helm charts](https://helm.sh). + +Create this composition function to install templated YAML support: + +```yaml +apiVersion: pkg.crossplane.io/v1 +kind: Function +metadata: + name: crossplane-contrib-function-go-templating +spec: + package: xpkg.crossplane.io/crossplane-contrib/function-go-templating:v0.9.2 +``` + +Save the function as `fn.yaml` and apply it: + +```shell +kubectl apply -f fn.yaml +``` + +Check that Crossplane installed the function: + +```shell {copy-lines="1"} +kubectl get -f fn.yaml +NAME INSTALLED HEALTHY PACKAGE AGE +crossplane-contrib-function-go-templating True True xpkg.crossplane.io/crossplane-contrib/function-go-templating:v0.9.2 9s +``` +{{< /tab >}} + +{{< tab "Python" >}} +Python is a good choice for compositions with dynamic logic. You can use the +full [Python standard library](https://docs.python.org/3/library/index.html). + +Create this composition function to install Python support: + +```yaml +apiVersion: pkg.crossplane.io/v1 +kind: Function +metadata: + name: crossplane-contrib-function-python +spec: + package: xpkg.crossplane.io/crossplane-contrib/function-python:v0.1.0 +``` + +Save the function as `fn.yaml` and apply it: + +```shell +kubectl apply -f fn.yaml +``` + +Check that Crossplane installed the function: + +```shell {copy-lines="1"} +kubectl get -f fn.yaml +NAME INSTALLED HEALTHY PACKAGE AGE +crossplane-contrib-function-python True True xpkg.crossplane.io/crossplane-contrib/function-python:v0.1.0 12s +``` +{{< /tab >}} + +{{< tab "KCL" >}} +[KCL](https://kcl-lang.io) is a good choice for compositions with dynamic logic. +It's fast and sandboxed. + +Create this composition function to install KCL support: + +```yaml +apiVersion: pkg.crossplane.io/v1 +kind: Function +metadata: + name: crossplane-contrib-function-kcl +spec: + package: xpkg.crossplane.io/crossplane-contrib/function-kcl:v0.11.2 +``` + +Save the function as `fn.yaml` and apply it: + +```shell +kubectl apply -f fn.yaml +``` + +Check that Crossplane installed the function: + +```shell {copy-lines="1"} +kubectl get -f fn.yaml +NAME INSTALLED HEALTHY PACKAGE AGE +crossplane-contrib-function-kcl True True xpkg.crossplane.io/crossplane-contrib/function-kcl:v0.11.2 6s +``` +{{< /tab >}} + +{{}} + +### Configure the composition + +A composition tells Crossplane what functions to call when you create or +update a composite resource (XR). + +Create a composition to tell Crossplane what to do when you create or update an +`App` XR. + +{{< tabs >}} + +{{< tab "YAML" >}} +Create this composition to use YAML to configure Crossplane: + +```yaml +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +metadata: + name: app-yaml +spec: + compositeTypeRef: + apiVersion: example.crossplane.io/v1 + kind: App + mode: Pipeline + pipeline: + - step: create-deployment-and-service + functionRef: + name: crossplane-contrib-function-patch-and-transform + input: + apiVersion: pt.fn.crossplane.io/v1beta1 + kind: Resources + resources: + - name: deployment + base: + apiVersion: apps/v1 + kind: Deployment + spec: + replicas: 2 + template: + spec: + containers: + - name: app + ports: + - containerPort: 80 + patches: + - type: FromCompositeFieldPath + fromFieldPath: metadata.name + toFieldPath: metadata.labels[example.crossplane.io/app] + - type: FromCompositeFieldPath + fromFieldPath: metadata.name + toFieldPath: spec.selector.matchLabels[example.crossplane.io/app] + - type: FromCompositeFieldPath + fromFieldPath: metadata.name + toFieldPath: spec.template.metadata.labels[example.crossplane.io/app] + - type: FromCompositeFieldPath + fromFieldPath: spec.image + toFieldPath: spec.template.spec.containers[0].image + - type: ToCompositeFieldPath + fromFieldPath: status.availableReplicas + toFieldPath: status.replicas + readinessChecks: + - type: MatchCondition + matchCondition: + type: Available + status: "True" + - name: service + base: + apiVersion: v1 + kind: Service + spec: + ports: + - protocol: TCP + port: 8080 + targetPort: 80 + patches: + - type: FromCompositeFieldPath + fromFieldPath: metadata.name + toFieldPath: metadata.labels[example.crossplane.io/app] + - type: FromCompositeFieldPath + fromFieldPath: metadata.name + toFieldPath: spec.selector[example.crossplane.io/app] + - type: ToCompositeFieldPath + fromFieldPath: spec.clusterIP + toFieldPath: status.address + readinessChecks: + - type: NonEmpty + fieldPath: spec.clusterIP +``` +{{< /tab >}} + +{{< tab "Templated YAML" >}} +Create this composition to use templated YAML to configure Crossplane: + +```yaml +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +metadata: + name: app-templated-yaml +spec: + compositeTypeRef: + apiVersion: example.crossplane.io/v1 + kind: App + mode: Pipeline + pipeline: + - step: create-deployment-and-service + functionRef: + name: crossplane-contrib-function-go-templating + input: + apiVersion: gotemplating.fn.crossplane.io/v1beta1 + kind: GoTemplate + source: Inline + inline: + template: | + --- + apiVersion: apps/v1 + kind: Deployment + metadata: + annotations: + gotemplating.fn.crossplane.io/composition-resource-name: deployment + {{ if eq (.observed.resources.deployment | getResourceCondition "Available").Status "True" }} + gotemplating.fn.crossplane.io/ready: "True" + {{ end }} + labels: + example.crossplane.io/app: {{ .observed.composite.resource.metadata.name }} + spec: + replicas: 2 + selector: + matchLabels: + example.crossplane.io/app: {{ .observed.composite.resource.metadata.name }} + template: + metadata: + labels: + example.crossplane.io/app: {{ .observed.composite.resource.metadata.name }} + spec: + containers: + - name: app + image: {{ .observed.composite.resource.spec.image }} + ports: + - containerPort: 80 + --- + apiVersion: v1 + kind: Service + metadata: + annotations: + gotemplating.fn.crossplane.io/composition-resource-name: service + {{ if (get (getComposedResource . "service").spec "clusterIP") }} + gotemplating.fn.crossplane.io/ready: "True" + {{ end }} + labels: + example.crossplane.io/app: {{ .observed.composite.resource.metadata.name }} + spec: + selector: + example.crossplane.io/app: {{ .observed.composite.resource.metadata.name }} + ports: + - protocol: TCP + port: 8080 + targetPort: 80 + --- + apiVersion: example.crossplane.io/v1 + kind: App + status: + replicas: {{ get (getComposedResource . "deployment").status "availableReplicas" | default 0 }} + address: {{ get (getComposedResource . "service").spec "clusterIP" | default "" | quote }} +``` +{{< /tab >}} + +{{< tab "Python" >}} +Create this composition to use Python to configure Crossplane: + +```yaml +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +metadata: + name: app-python +spec: + compositeTypeRef: + apiVersion: example.crossplane.io/v1 + kind: App + mode: Pipeline + pipeline: + - step: create-deployment-and-service + functionRef: + name: crossplane-contrib-function-python + input: + apiVersion: python.fn.crossplane.io/v1beta1 + kind: Script + script: | + def compose(req, rsp): + observed_xr = req.observed.composite.resource + + rsp.desired.resources["deployment"].resource.update({ + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": { + "labels": {"example.crossplane.io/app": observed_xr["metadata"]["name"]}, + }, + "spec": { + "replicas": 2, + "selector": {"matchLabels": {"example.crossplane.io/app": observed_xr["metadata"]["name"]}}, + "template": { + "metadata": { + "labels": {"example.crossplane.io/app": observed_xr["metadata"]["name"]}, + }, + "spec": { + "containers": [{ + "name": "app", + "image": observed_xr["spec"]["image"], + "ports": [{"containerPort": 80}] + }], + }, + }, + }, + }) + + observed_deployment = req.observed.resources["deployment"].resource + if "status" in observed_deployment: + if "availableReplicas" in observed_deployment["status"]: + rsp.desired.composite.resource.get_or_create_struct("status")["replicas"] = observed_deployment["status"]["availableReplicas"] + if "conditions" in observed_deployment["status"]: + for condition in observed_deployment["status"]["conditions"]: + if condition["type"] == "Available" and condition["status"] == "True": + rsp.desired.resources["deployment"].ready = True + + rsp.desired.resources["service"].resource.update({ + "apiVersion": "v1", + "kind": "Service", + "metadata": { + "labels": {"example.crossplane.io/app": observed_xr["metadata"]["name"]}, + }, + "spec": { + "selector": {"example.crossplane.io/app": observed_xr["metadata"]["name"]}, + "ports": [{"protocol": "TCP", "port": 8080, "targetPort": 80}], + }, + }) + + observed_service = req.observed.resources["service"].resource + if "spec" in observed_service and "clusterIP" in observed_service["spec"]: + rsp.desired.composite.resource.get_or_create_struct("status")["address"] = observed_service["spec"]["clusterIP"] + rsp.desired.resources["service"].ready = True +``` + +{{}} +You can write your own function in Python. + +It's a good idea to write your own function for larger configurations. When you +write your own function you can write multiple files of Python. You don't embed +the Python in YAML, so it's easier to use a Python IDE. + +Read the [guide to writing a composition function in Python]({{}}). +{{}} +{{< /tab >}} + +{{< tab "KCL" >}} +Create this composition to use KCL to configure Crossplane: + +```yaml +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +metadata: + name: app-kcl +spec: + compositeTypeRef: + apiVersion: example.crossplane.io/v1 + kind: App + mode: Pipeline + pipeline: + - step: create-deployment-and-service + functionRef: + name: crossplane-contrib-function-kcl + input: + apiVersion: krm.kcl.dev/v1alpha1 + kind: KCLInput + spec: + source: | + observed_xr = option("params").oxr + + _desired_deployment = { + apiVersion = "apps/v1" + kind = "Deployment" + metadata = { + annotations = { + "krm.kcl.dev/composition-resource-name" = "deployment" + } + labels = {"example.crossplane.io/app" = observed_xr.metadata.name} + } + spec = { + replicas = 2 + selector.matchLabels = {"example.crossplane.io/app" = observed_xr.metadata.name} + template = { + metadata.labels = {"example.crossplane.io/app" = observed_xr.metadata.name} + spec.containers = [{ + name = "app" + image = observed_xr.spec.image + ports = [{containerPort = 80}] + }] + } + } + } + + observed_deployment = option("params").ocds["deployment"]?.Resource + if any_true([c.type == "Available" and c.status == "True" for c in observed_deployment?.status?.conditions or []]): + _desired_deployment.metadata.annotations["krm.kcl.dev/ready"] = "True" + + _desired_service = { + apiVersion = "v1" + kind = "Service" + metadata = { + annotations = { + "krm.kcl.dev/composition-resource-name" = "service" + } + labels = {"example.crossplane.io/app" = observed_xr.metadata.name} + } + spec = { + selector = {"example.crossplane.io/app" = observed_xr.metadata.name} + ports = [{protocol = "TCP", port = 8080, targetPort = 80}] + } + } + + observed_service = option("params").ocds["service"]?.Resource + if observed_service?.spec?.clusterIP: + _desired_service.metadata.annotations["krm.kcl.dev/ready"] = "True" + + _desired_xr = { + **option("params").dxr + + status.address = observed_service?.spec?.clusterIP or "" + status.replicas = observed_deployment?.status?.availableReplicas or 0 + } + + items = [_desired_deployment, _desired_service, _desired_xr] +``` +{{< /tab >}} + +{{}} + +Save the composition as `composition.yaml` and apply it: + +```shell +kubectl apply -f composition.yaml +``` + +{{}} +A composition can include multiple functions. + +Functions can change the results of earlier functions in the pipeline. +Crossplane uses the result returned by the last function. +{{}} + +## Use the custom resource + +Crossplane now understands `App` custom resources. + +Create an `App`: + +```yaml +apiVersion: example.crossplane.io/v1 +kind: App +metadata: + namespace: default + name: my-app +spec: + image: nginx +``` + +Save the `App` as `app.yaml` and apply it: + +```shell +kubectl apply -f app.yaml +``` + +Check that the `App` is ready: + +```shell {copy-lines="1"} +kubectl get -f app.yaml +NAME SYNCED READY COMPOSITION AGE +my-app True True app-yaml 56s +``` + +{{}} +The `COMPOSITION` column shows what composition the `App` is using. + +You can create multiple compositions for each kind of XR. +[Read the XR page]({{}}) to learn how to +select which composition Crossplane uses. +{{}} + +Check that Crossplane created a `Deployment` and a `Service`: + +```shell {copy-lines="1"} +kubectl get deploy,service -l example.crossplane.io/app=my-app +NAME READY UP-TO-DATE AVAILABLE AGE +deployment.apps/my-app-2r2rk 2/2 2 2 11m + +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +service/my-app-xfkzg ClusterIP 10.96.148.56 8080/TCP 11m +``` + +{{}} +Use `kubectl edit -f app.yaml` to edit the `App`'s image. Crossplane updates +the `Deployment`'s image to match. +{{}} + +Delete the `App`. + +```shell {copy-lines="1"} +kubectl delete -f app.yaml +``` + +When you delete the `App`, Crossplane deletes the `Deployment` and `Service`. + +## Next steps + +Managed resources (MRs) are ready-made Kubernetes custom resources. + +Crossplane has an extensive library of managed resources you can use to manage +almost any cloud provider, or cloud native software. + +[Get started with managed resources]({{}}) +to learn more about them. + +You can use MRs with composition. Try updating your `App` composition to include +an MR. diff --git a/content/v2.0-preview/get-started/get-started-with-managed-resources.md b/content/v2.0-preview/get-started/get-started-with-managed-resources.md index 414d5878..85498c0e 100644 --- a/content/v2.0-preview/get-started/get-started-with-managed-resources.md +++ b/content/v2.0-preview/get-started/get-started-with-managed-resources.md @@ -1,86 +1,109 @@ --- title: Get Started With Managed Resources -weight: 200 +weight: 300 --- -Connect Crossplane to AWS to create and manage cloud resources from Kubernetes -with [provider-upjet-aws](https://github.com/crossplane-contrib/provider-upjet-aws). +This guide shows how to install and use a new kind of custom resource called +`Bucket`. When a user calls the custom resource API to create a `Bucket`, +Crossplane creates a bucket in AWS S3. -A _managed resource_ is anything Crossplane creates and manages outside of the -control plane. +**Crossplane calls this a _managed resource_**. A managed resource is a +ready-made custom resource that manages something outside of the control plane. -This guide creates an AWS S3 bucket with Crossplane. The S3 bucket is a _managed resource_. +A `Bucket` managed resource looks like this: + +```yaml +apiVersion: s3.aws.m.upbound.io/v1beta1 +kind: Bucket +metadata: + namespace: default + name: crossplane-bucket-example +spec: + forProvider: + region: us-east-2 +``` + +{{}} +Kubernetes calls third party API resources _custom resources_. +{{}} ## Prerequisites -This quickstart requires: + +This guide requires: * A Kubernetes cluster with at least 2 GB of RAM * The Crossplane v2 preview [installed on the Kubernetes cluster]({{}}) * An AWS account with permissions to create an S3 storage bucket * AWS [access keys](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html#cli-configure-quickstart-creds) -## Install the AWS provider -Install the AWS S3 provider into the Kubernetes cluster with a Kubernetes -configuration file. +## Install support for the managed resource + +Follow these steps to install support for the `Bucket` managed resource: + +1. [Install](#install-the-provider) the provider +1. [Save](#save-the-providers-credentials) the provider's credentials as a secret +1. [Configure](#configure-the-provider) the provider to use the secret + +After you complete these steps you can +[use the `Bucket` managed resource](#use-the-managed-resource). + +### Install the provider + +A Crossplane _provider_ installs support for a set of related managed resources. +The AWS S3 provider installs support for all the AWS S3 managed resources. + +Create this provider to install the AWS S3 provider: ```yaml {label="provider",copy-lines="all"} apiVersion: pkg.crossplane.io/v1 kind: Provider metadata: - name: provider-aws-s3 + name: crossplane-contrib-provider-aws-s3 spec: package: xpkg.crossplane.io/crossplane-contrib/provider-aws-s3:v1.22.0-crossplane-v2-preview.0 ``` -Save this to a file called `provider.yaml`, then apply it with: +Save this as `provider.yaml` and apply it: + ```shell {label="kube-apply-provider",copy-lines="all"} kubectl apply -f provider.yaml ``` -The Crossplane {{< hover label="provider" line="2" >}}Provider{{}} -installs the Kubernetes _Custom Resource Definitions_ (CRDs) representing AWS S3 -services. These CRDs allow you to create AWS resources directly inside -Kubernetes. - -Verify the provider installed with `kubectl get providers`. - +Check that Crossplane installed the provider: ```shell {copy-lines="1",label="getProvider"} kubectl get providers NAME INSTALLED HEALTHY PACKAGE AGE crossplane-contrib-provider-family-aws True True xpkg.crossplane.io/crossplane-contrib/provider-family-aws:v1.22.0-crossplane-v2-preview.0 27s -provider-aws-s3 True True xpkg.crossplane.io/crossplane-contrib/provider-aws-s3:v1.22.0-crossplane-v2-preview.0 31s +crossplane-contrib-provider-aws-s3 True True xpkg.crossplane.io/crossplane-contrib/provider-aws-s3:v1.22.0-crossplane-v2-preview.0 31s ``` -The S3 Provider installs a second Provider, the +{{}} +The S3 provider installs a second provider, the {{}}crossplane-contrib-provider-family-aws{{}}. The family provider manages authentication to AWS across all AWS family -Providers. +providers. +{{}} -You can view the new CRDs with `kubectl get crds`. -Every CRD maps to a unique AWS service Crossplane can provision and manage. +Crossplane installed the AWS S3 provider. The provider needs credentials to +connect to AWS. Before you can use managed resources, you have to +[save the provider's credentials](#save-the-providers-credentials) and +[configure the provider to use them](#configure-the-provider). -{{< hint "tip" >}} -See details about all the supported CRDs in the -[provider examples](https://github.com/crossplane-contrib/provider-upjet-aws/tree/main/examples). -{{< /hint >}} +### Save the provider's credentials -## Create a Kubernetes secret for AWS -The provider requires credentials to create and manage AWS resources. -Providers use a Kubernetes _Secret_ to connect the credentials to the provider. +The provider needs credentials to create and manage AWS resources. Providers use +a Kubernetes _secret_ to connect the credentials to the provider. -Generate a Kubernetes _Secret_ from your AWS key-pair and -then configure the Provider to use it. +Generate a secret from your AWS key-pair. -### Generate an AWS key-pair file -For basic user authentication, use an AWS Access keys key-pair file. - -{{< hint "tip" >}} +{{}} The [AWS documentation](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html#cli-configure-quickstart-creds) provides information on how to generate AWS Access keys. -{{< /hint >}} +{{}} -Create a text file containing the AWS account `aws_access_key_id` and `aws_secret_access_key`. +Create a file containing the AWS account `aws_access_key_id` and +`aws_secret_access_key`: {{< editCode >}} ```ini {copy-lines="all"} @@ -90,32 +113,36 @@ aws_secret_access_key = $@$@ ``` {{< /editCode >}} -Save this text file as `aws-credentials.txt`. +Save the text file as `aws-credentials.ini`. -{{< hint "note" >}} -The [Authentication](https://docs.upbound.io/providers/provider-aws/authentication/) section of the AWS Provider documentation describes other authentication methods. -{{< /hint >}} +{{}} +The [Authentication](https://docs.upbound.io/providers/provider-aws/authentication/) +section of the AWS Provider documentation describes other authentication methods. +{{}} -### Create a Kubernetes secret with the AWS credentials -A Kubernetes generic secret has a name and contents. -Use -{{< hover label="kube-create-secret" line="1">}}kubectl create secret{{}} -to generate the secret object named -{{< hover label="kube-create-secret" line="2">}}aws-secret{{< /hover >}} -in the {{< hover label="kube-create-secret" line="3">}}crossplane-system{{}} namespace. - -Use the {{< hover label="kube-create-secret" line="4">}}--from-file={{}} argument to set the value to the contents of the {{< hover label="kube-create-secret" line="4">}}aws-credentials.txt{{< /hover >}} file. +Create a secret from the text file: ```shell {label="kube-create-secret",copy-lines="all"} -kubectl create secret \ -generic aws-secret \ --n crossplane-system \ ---from-file=creds=./aws-credentials.txt +kubectl create secret generic aws-secret \ + --namespace=crossplane-system \ + --from-file=creds=./aws-credentials.ini ``` -## Create a ProviderConfig -A {{< hover label="providerconfig" line="2">}}ProviderConfig{{}} -customizes the settings of the AWS Provider: +{{}} +Crossplane providers don't have to store their credentials in a secret. They +can load their credentials from various sources. +{{}} + +Next, [configure the provider](#configure-the-provider) to use the credentials. + +### Configure the provider + +A {{< hover label="providerconfig" line="2">}}provider configuration{{}} +customizes the settings of the AWS Provider. + +All providers need a configuration to tell them where to load credentials. + +Create this provider configuration: ```yaml {label="providerconfig",copy-lines="all"} apiVersion: aws.upbound.io/v1beta1 @@ -131,20 +158,21 @@ spec: key: creds ``` -Save this to a file called `providerconfig.yaml`, then apply it with: +Save the provider configuration as `providerconfig.yaml` and apply it: ```shell {label="kube-apply-providerconfig",copy-lines="all"} kubectl apply -f providerconfig.yaml ``` -This attaches the AWS credentials, saved as a Kubernetes secret, as a -{{< hover label="providerconfig" line="8">}}secretRef{{}}. +This tells the provider to load credentials from +[the secret](#save-the-providers-credentials). -## Create a managed resource -{{< hint "note" >}} -AWS S3 bucket names must be globally unique. To generate a unique name the example uses a random hash. -Any unique name is acceptable. -{{< /hint >}} +## Use the managed resource + +{{}} +AWS S3 bucket names must be globally unique. This example uses `generateName` to +generate a random name. Any unique name is acceptable. +{{}} ```yaml {label="bucket"} apiVersion: s3.aws.m.upbound.io/v1beta1 @@ -155,57 +183,48 @@ metadata: spec: forProvider: region: us-east-2 - providerConfigRef: - name: default ``` -Save this to a file called `bucket.yaml`, then apply it with: +Save the bucket to `bucket.yaml` and apply it: ```shell {label="kube-create-bucket",copy-lines="all"} kubectl create -f bucket.yaml ``` -The {{< hover label="bucket" line="5">}}metadata.generateName{{< /hover >}} gives a -pattern that Kubernetes will use to create a unique name for the bucket in S3. -The generated name will look like `crossplane-bucket-`. - -Use `kubectl -n default get buckets.s3.aws.m.upbound.io` to verify Crossplane created the bucket. - -{{< hint "tip" >}} -Crossplane created the bucket when the values `READY` and `SYNCED` are `True`. -This may take up to 5 minutes. -{{< /hint >}} +Check that Crossplane created the bucket: ```shell {copy-lines="1"} -kubectl -n default get buckets.s3.aws.m.upbound.io +kubectl get buckets.s3.aws.m.upbound.io NAME SYNCED READY EXTERNAL-NAME AGE crossplane-bucket-7tfcj True True crossplane-bucket-7tfcj 3m4s ``` -## Delete the managed resource -When you are finished with your S3 bucket, use `kubectl -n default -delete buckets.s3.aws.m.upbound.io ` to remove the bucket. +{{}} +Crossplane created the bucket when the values `READY` and `SYNCED` are `True`. +{{}} + +Delete the bucket: ```shell {copy-lines="1"} -kubectl -n default delete buckets.s3.aws.m.upbound.io crossplane-bucket-7tfcj +kubectl delete buckets.s3.aws.m.upbound.io crossplane-bucket-7tfcj bucket.s3.aws.m.upbound.io "crossplane-bucket-7tfcj" deleted ``` -{{< hint "important" >}} +When you delete the bucket managed resource, Crossplane deletes the S3 bucket +from AWS. + +{{}} Make sure to delete the S3 bucket before uninstalling the provider or shutting down your control plane. If those are no longer running, they can't clean up any managed resources and you would need to do so manually. -{{< /hint >}} +{{}} -## Composing managed resources -Crossplane allows you to compose **any type of resource** into custom APIs for +## Next steps + +Crossplane allows you to compose **any kind of resource** into custom APIs for your users, which includes managed resources. Enjoy the freedom that Crossplane gives you to compose the diverse set of resources your applications need for their unique environments, scenarios, and requirements. Follow [Get Started with Composition]({{}}) to learn more about how composition works. - -## Next steps -* Join the [Crossplane Slack](https://slack.crossplane.io/) and connect with - Crossplane users and contributors. diff --git a/content/v2.0-preview/whats-crossplane/_index.md b/content/v2.0-preview/whats-crossplane/_index.md index c0802615..67036cff 100644 --- a/content/v2.0-preview/whats-crossplane/_index.md +++ b/content/v2.0-preview/whats-crossplane/_index.md @@ -44,7 +44,7 @@ the [Kubebuilder documentation](https://book.kubebuilder.io) to see what's involved in writing a controller. {{}} -# Crossplane components +## Crossplane components Crossplane has three major components: @@ -55,7 +55,7 @@ Crossplane has three major components: You can use all three components to build your control plane, or pick only the ones you need. -## Composition +### Composition Composition lets you build custom APIs to control your cloud native software. @@ -66,9 +66,9 @@ extend Kubernetes with new custom resources. controller.** The controller is the software that reacts when a user calls the custom resource API. -Say you want your control plane to serve an `Application` custom resource API. -When someone creates an `Application`, the control plane should create a -Kubernetes `Deployment` and a `Service`. +Say you want your control plane to serve an `App` custom resource API. When +someone creates an `App`, the control plane should create a Kubernetes +`Deployment` and a `Service`. **If there's not already a controller that does what you want - and exposes the API you want - you have to write the controller yourself.** @@ -78,8 +78,8 @@ flowchart TD user(User) subgraph control [Control Plane] - api(Application API) - controller[Your Application Controller] + api(App API) + controller[Your App Controller] deployment(Deployment API) service(Service API) end @@ -101,7 +101,7 @@ flowchart TD user(User) subgraph control [Control Plane] - api(Application API) + api(App API) subgraph crossplane [Composition Engine] fn(Python Function) @@ -138,7 +138,7 @@ build new custom resource APIs powered by managed resources. Follow [Get Started with Composition]({{}}) to see how composition works. -## Managed resources +### Managed resources Managed resources (MRs) are ready-made Kubernetes custom resources. @@ -181,7 +181,7 @@ flowchart TD user(User) subgraph control [Control Plane] - api(Application API) + api(App API) subgraph crossplane [Composition Engine] fn(Python Function) @@ -223,7 +223,7 @@ GCP, Terraform, Helm, GitHub, etc to support Crossplane v2 soon. {{}} -## Package manager +### Package manager The Crossplane package manager lets you install new managed resources and composition functions. diff --git a/utils/vale/styles/Crossplane/allowed-jargon.txt b/utils/vale/styles/Crossplane/allowed-jargon.txt index 5bbe6b27..699e73d4 100644 --- a/utils/vale/styles/Crossplane/allowed-jargon.txt +++ b/utils/vale/styles/Crossplane/allowed-jargon.txt @@ -73,6 +73,7 @@ ReplicaSets RPC RPCs RSS +sandboxed SCSS SDK SDKs @@ -93,6 +94,7 @@ Subnet subnets Substrings syscall +templated TLS tolerations UI diff --git a/utils/vale/styles/Crossplane/crossplane-words.txt b/utils/vale/styles/Crossplane/crossplane-words.txt index 9db50501..86039f1f 100644 --- a/utils/vale/styles/Crossplane/crossplane-words.txt +++ b/utils/vale/styles/Crossplane/crossplane-words.txt @@ -16,6 +16,7 @@ config CONTRIBUTING.md ControllerConfig ControllerConfigs +CRs CRDs Crossplane crossplane-admin @@ -42,6 +43,7 @@ function-environment-configs function-extra-resources function-go-templating function-patch-and-transform +function-template-python HealthyPackageRevision Helm-like ImageConfig