diff --git a/README.md b/README.md index 7660d90..868b1d4 100644 --- a/README.md +++ b/README.md @@ -145,7 +145,7 @@ metadata: name: basic spec: params: - custom_key: custom_value + custom_key: custom_value source: oci://ghcr.io/kcl-lang/crossplane-xnetwork-kcl-function ``` @@ -331,7 +331,7 @@ metadata: spec: source: | import base64 - + # Omit other logic ocds = option("params").ocds details = { @@ -363,6 +363,83 @@ spec: } ``` +### Extra resources +By defining one or more special `ExtraResources`, you can ask Crossplane to retrieve additional resources from the local cluster and make them available to your templates. See the [docs](https://github.com/crossplane/crossplane/blob/main/design/design-doc-composition-functions-extra-resources.md) for more information. + +```yaml +apiVersion: krm.kcl.dev/v1alpha1 +kind: KCLInput +metadata: + name: basic +spec: + source: | + # Omit other logic + details = { + apiVersion: "meta.krm.kcl.dev/v1alpha1" + kind: "ExtraResources" + requirements = { + foo = { + apiVersion: "example.com/v1beta1", + kind: "Foo", + matchLabels: { + "foo": "bar" + } + }, + bar = { + apiVersion: "example.com/v1beta1", + kind: "Bar", + matchName: "my-bar" + } + } + } + + # Omit other composite logics. + items = [ + details + # Omit other return resources. + ] +``` +You can retrieve the extra resources either via labels with `matchLabels` or via name with `matchName: somename`. + +This will result in Crossplane receiving the requested resources and make them available with the following format. +```yaml +foo: +- Resource: + apiVersion: example.com/v1beta1 + kind: Foo + metadata: + labels: + foo: bar + # Omitted for brevity +- Resource: + apiVersion: example.com/v1beta1 + kind: Foo + metadata: + labels: + foo: bar + # Omit for brevity +bar: +- Resource: + apiVersion: example.com/v1beta1 + kind: Bar + metadata: + name: my-bar + # Omitted for brevity +``` +You can access the retrieved resources in your code like this: +```yaml +apiVersion: krm.kcl.dev/v1alpha1 +kind: KCLInput +metadata: + name: basic +spec: + source: | + er = option("params")?.extraResources + + name = er?.bar[0]?.Resource?.metadata?.name or "" + # Omit other logic +``` + ### Patching the XR status field You can read the XR, patch it with the status field and return the new patched XR in the `item` result like this diff --git a/examples/default/extra_resources/Makefile b/examples/default/extra_resources/Makefile new file mode 100644 index 0000000..f17dba7 --- /dev/null +++ b/examples/default/extra_resources/Makefile @@ -0,0 +1,2 @@ +run: + crossplane render --verbose xr.yaml composition.yaml functions.yaml -r --extra-resources extra_resources.yaml diff --git a/examples/default/extra_resources/README.md b/examples/default/extra_resources/README.md new file mode 100644 index 0000000..380cf1d --- /dev/null +++ b/examples/default/extra_resources/README.md @@ -0,0 +1,62 @@ +# Example Manifests + +You can run your function locally and test it using `crossplane render` +with these example manifests. + +```shell +# Run the function locally +$ go run . --insecure --debug +``` + +```shell +# Then, in another terminal, call it with these example manifests +$ crossplane render --verbose xr.yaml composition.yaml functions.yaml -r --extra-resources extra_resources.yaml +--- +--- +apiVersion: example.crossplane.io/v1beta1 +kind: XR +metadata: + name: example +status: + conditions: + - lastTransitionTime: "2024-01-01T00:00:00Z" + message: 'Unready resources: another-awesome-dev-bucket, my-awesome-dev-bucket' + reason: Creating + status: "False" + type: Ready +--- +apiVersion: example/v1alpha1 +kind: Foo +metadata: + annotations: + crossplane.io/composition-resource-name: another-awesome-dev-bucket + generateName: example- + labels: + crossplane.io/composite: example + name: another-awesome-dev-bucket + ownerReferences: + - apiVersion: example.crossplane.io/v1beta1 + blockOwnerDeletion: true + controller: true + kind: XR + name: example + uid: "" +--- +apiVersion: example/v1alpha1 +kind: Foo +metadata: + annotations: + crossplane.io/composition-resource-name: my-awesome-dev-bucket + generateName: example- + labels: + crossplane.io/composite: example + name: my-awesome-dev-bucket + ownerReferences: + - apiVersion: example.crossplane.io/v1beta1 + blockOwnerDeletion: true + controller: true + kind: XR + name: example + uid: "" + +``` diff --git a/examples/default/extra_resources/composition.yaml b/examples/default/extra_resources/composition.yaml new file mode 100644 index 0000000..44a88e2 --- /dev/null +++ b/examples/default/extra_resources/composition.yaml @@ -0,0 +1,54 @@ +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +metadata: + name: function-template-go +spec: + compositeTypeRef: + apiVersion: example.crossplane.io/v1beta1 + kind: XR + mode: Pipeline + pipeline: + - step: normal + functionRef: + name: kcl-function + input: + apiVersion: krm.kcl.dev/v1alpha1 + kind: KCLInput + metadata: + annotations: + "krm.kcl.dev/default_ready": "True" + name: basic + spec: + source: | + oxr = option("params").oxr + er = option("params")?.extraResources + + foo = [{ + apiVersion: "example/v1alpha1" + kind: "Foo" + metadata = { + name: k.Resource.metadata.name + } + } for k in er?.bucket] if er?.bucket else [] + + dxr = { + **oxr + } + + details = { + apiVersion: "meta.krm.kcl.dev/v1alpha1" + kind: "ExtraResources" + requirements = { + bucket = { + apiVersion: "s3.aws.upbound.io/v1beta1", + kind: "Bucket", + matchLabels: { + "foo": "bar" + } + } + } + } + items = [ + details + dxr + ] + foo diff --git a/examples/default/extra_resources/extra_resources.yaml b/examples/default/extra_resources/extra_resources.yaml new file mode 100644 index 0000000..8d5a780 --- /dev/null +++ b/examples/default/extra_resources/extra_resources.yaml @@ -0,0 +1,29 @@ +apiVersion: s3.aws.upbound.io/v1beta1 +kind: Bucket +metadata: + annotations: + crossplane.io/external-name: my-awesome-dev-bucket + labels: + foo: bar + name: my-awesome-dev-bucket +spec: + forProvider: + region: us-west-1 +status: + atProvider: + id: random-bucket-id +--- +apiVersion: s3.aws.upbound.io/v1beta1 +kind: Bucket +metadata: + annotations: + crossplane.io/external-name: my-awesome-dev-bucket + labels: + foo: bar + name: another-awesome-dev-bucket +spec: + forProvider: + region: us-west-1 +status: + atProvider: + id: random-bucket-id diff --git a/examples/default/extra_resources/functions.yaml b/examples/default/extra_resources/functions.yaml new file mode 100644 index 0000000..d5679cb --- /dev/null +++ b/examples/default/extra_resources/functions.yaml @@ -0,0 +1,9 @@ +apiVersion: pkg.crossplane.io/v1beta1 +kind: Function +metadata: + name: kcl-function + annotations: + # This tells crossplane render to connect to the function locally. + render.crossplane.io/runtime: Development +spec: + package: xpkg.upbound.io/crossplane-contrib/function-kcl:latest diff --git a/examples/default/extra_resources/xr.yaml b/examples/default/extra_resources/xr.yaml new file mode 100644 index 0000000..67aa59f --- /dev/null +++ b/examples/default/extra_resources/xr.yaml @@ -0,0 +1,6 @@ +apiVersion: example.crossplane.io/v1beta1 +kind: XR +metadata: + name: example +spec: + count: 1