Merge pull request #901 from negz/get-started-composition

[v2] Add a new 'Get Started With Composition' guide
This commit is contained in:
Nic Cope 2025-03-28 16:50:59 -07:00 committed by GitHub
commit 017ee79d92
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 858 additions and 105 deletions

View File

@ -3,4 +3,734 @@ title: Get Started With Composition
weight: 200
---
TODO
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`.
{{<hint "tip">}}
The guide shows how to configure composition using YAML, templated YAML, Python,
and KCL. You can pick your preferred language.
{{</hint>}}
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.
{{<hint "tip">}}
Kubernetes custom resources are just JSON REST APIs, so users can use any tool
that supports REST APIs to work with apps.
{{</hint>}}
## Prerequisites
This guide requires:
* A Kubernetes cluster with at least 2 GB of RAM
* The Crossplane v2 preview [installed on the Kubernetes cluster]({{<ref "install">}})
## 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.
{{<hint "note">}}
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.
{{</hint>}}
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 >}}
{{</ tabs >}}
### 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
```
{{<hint "tip">}}
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]({{<ref "../guides/write-a-composition-function-in-python">}}).
{{</hint>}}
{{< /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 >}}
{{</ tabs >}}
Save the composition as `composition.yaml` and apply it:
```shell
kubectl apply -f composition.yaml
```
{{<hint "note">}}
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.
{{</hint>}}
## 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
```
{{<hint "note">}}
The `COMPOSITION` column shows what composition the `App` is using.
You can create multiple compositions for each kind of XR.
[Read the XR page]({{<ref "../concepts/composite-resources">}}) to learn how to
select which composition Crossplane uses.
{{</hint>}}
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 <none> 8080/TCP 11m
```
{{<hint "tip">}}
Use `kubectl edit -f app.yaml` to edit the `App`'s image. Crossplane updates
the `Deployment`'s image to match.
{{</hint>}}
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]({{<ref "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.

View File

@ -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
```
{{<hint "note">}}
Kubernetes calls third party API resources _custom resources_.
{{</hint>}}
## 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]({{<ref "install">}})
* 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{{</hover>}}
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
{{<hint "note">}}
The S3 provider installs a second provider, the
{{<hover label="getProvider" line="4">}}crossplane-contrib-provider-family-aws{{</hover >}}.
The family provider manages authentication to AWS across all AWS family
Providers.
providers.
{{</hint>}}
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" >}}
{{<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 >}}
{{</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 = $@<aws_secret_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 >}}
{{<hint "note">}}
The [Authentication](https://docs.upbound.io/providers/provider-aws/authentication/)
section of the AWS Provider documentation describes other authentication methods.
{{</hint>}}
### 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{{</hover >}}
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{{</ hover >}} namespace.
Use the {{< hover label="kube-create-secret" line="4">}}--from-file={{</hover>}} 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{{</ hover >}}
customizes the settings of the AWS Provider:
{{<hint "important">}}
Crossplane providers don't have to store their credentials in a secret. They
can load their credentials from various sources.
{{</hint>}}
Next, [configure the provider](#configure-the-provider) to use the credentials.
### Configure the provider
A {{< hover label="providerconfig" line="2">}}provider configuration{{</ hover >}}
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{{</ hover>}}.
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
{{<hint "note">}}
AWS S3 bucket names must be globally unique. This example uses `generateName` to
generate a random name. Any unique name is acceptable.
{{</hint>}}
```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-<hash>`.
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 <bucketname>` to remove the bucket.
{{<hint "tip">}}
Crossplane created the bucket when the values `READY` and `SYNCED` are `True`.
{{</hint>}}
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.
{{<hint "important">}}
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 >}}
{{</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]({{<ref "../get-started/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.

View File

@ -44,7 +44,7 @@ the [Kubebuilder documentation](https://book.kubebuilder.io) to see what's
involved in writing a controller.
{{</hint>}}
# 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]({{<ref "../get-started/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.
<!-- vale gitlab.FutureTense = YES -->
{{</hint>}}
## Package manager
### Package manager
The Crossplane package manager lets you install new managed resources and
composition functions.

View File

@ -73,6 +73,7 @@ ReplicaSets
RPC
RPCs
RSS
sandboxed
SCSS
SDK
SDKs
@ -93,6 +94,7 @@ Subnet
subnets
Substrings
syscall
templated
TLS
tolerations
UI

View File

@ -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