Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions content/master/get-started/get-started-with-composition.md
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,38 @@ crossplane-contrib-function-kcl True True xpkg.crossplane.io/cross
```
{{< /tab >}}

{{< tab "Pythonic" >}}
[Pythonic](https://github.com/crossplane-contrib/function-pythonic?tab=readme-ov-file#function-pythonic)
is an excellent choice for compositions with dynamic logic. The full flexibility and power of python is
available using a set of python classes with an elegant and terse syntax that hides the details of the low level
Crossplane function APIs.

Create this composition function to install Pythonic support:

```yaml
apiVersion: pkg.crossplane.io/v1
kind: Function
metadata:
name: function-pythonic
spec:
package: xpkg.crossplane.io/crossplane-contrib/function-pythonic:v0.3.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
function-pythonic True True xpkg.crossplane.io/crossplane-contrib/function-pythonic:v0.3.0 1m
```
{{< /tab >}}

{{</ tabs >}}

### Configure the composition
Expand Down Expand Up @@ -646,6 +678,52 @@ spec:
```
{{< /tab >}}

{{< tab "Pythonic" >}}
Create this composition to use Pythonic to configure Crossplane:

```yaml
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: app-pythonic
spec:
compositeTypeRef:
apiVersion: example.crossplane.io/v1
kind: App
mode: Pipeline
pipeline:
- step: create-deployment-and-service
functionRef:
name: function-pythonic
input:
apiVersion: pythonic.fn.crossplane.io/v1alpha1
kind: Composite
composite: |
class Composite(BaseComposite):
def compose(self):
labels = {'example.crossplane.io/app': self.metadata.name}

d = self.resources.deployment('apps/v1', 'Deployment')
d.metadata.labels = labels
d.spec.replicas = 2
d.spec.selector.matchLabels = labels
d.spec.template.metadata.labels = labels
d.spec.template.spec.containers[0].name = 'app'
d.spec.template.spec.containers[0].image = self.spec.image
d.spec.template.spec.containers[0].ports[0].containerPort = 80

s = self.resources.service('v1', 'Service')
s.metadata.labels = labels
s.spec.selector = labels
s.spec.ports[0].protocol = 'TCP'
s.spec.ports[0].port = 8080
s.spec.ports[0].targetPort = 80

self.status.replicas = d.status.availableReplicas
self.status.address = s.observed.spec.clusterIP
```
{{< /tab >}}

{{</ tabs >}}

Save the composition as `composition.yaml` and apply it:
Expand Down
115 changes: 113 additions & 2 deletions content/master/guides/connection-details-composition.md
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,34 @@ kubectl get -f fn.yaml
NAME INSTALLED HEALTHY PACKAGE AGE
function-kcl True True xpkg.crossplane.io/crossplane-contrib/function-kcl:v0.11.6 6s
```
{{< /tab>}}

{{< tab "Pythonic" >}}

Create this composition function to install [Pythonic](https://github.com/crossplane-contrib/function-pythonic?tab=readme-ov-file#function-pythonic) support:

```yaml
apiVersion: pkg.crossplane.io/v1
kind: Function
metadata:
name: function-pythonic
spec:
package: xpkg.crossplane.io/crossplane-contrib/function-pythonic:v0.3.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
function-pythonic True True xpkg.crossplane.io/crossplane-contrib/function-pythonic:v0.3.0 1m
```
{{< /tab >}}

{{< /tabs >}}
Expand Down Expand Up @@ -786,6 +814,67 @@ spec:

{{< /tab >}}

{{< tab "Pythonic" >}}

```yaml {label="comp-pythonic"}
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: useraccesskeys-pythonic
spec:
compositeTypeRef:
apiVersion: example.org/v1alpha1
kind: UserAccessKey
mode: Pipeline
pipeline:
- step: render-pythonic
functionRef:
name: function-pythonic
input:
apiVersion: pythonic.fn.crossplane.io/v1alpha1
kind: Composite
composite: |
class Composite(BaseComposite):
def compose(self):
self.connectionSecret = self.spec.writeConnectionSecretToRef

user = self.resources.user('iam.aws.m.upbound.io/v1beta1', 'User')
user.spec.forProvider = {}

for ix in range(2):
key = self.resources[f"access-key-{ix}"]('iam.aws.m.upbound.io/v1beta1', 'AccessKey')
key.spec.forProvider.user = user.status.atProvider.id
key.spec.writeConnectionSecretToRef.name = f"{self.metadata.name}-accesskey-{ix}"
self.connection[f"user-{ix}"] = key.connection.username
self.connection[f"password-{ix}"] = key.connection.password
```

<!-- vale write-good.Passive = NO -->
<!-- vale Google.WordList = NO -->
**How this Composition exposes connection details:**

* Each composed {{<hover label="comp-pythonic" line="26">}}AccessKey{{</hover>}} has
{{<hover label="comp-pythonic" line="28">}}writeConnectionSecretToRef{{</hover>}} set. This
tells each AccessKey to write its credentials to an individual Secret.
* Crossplane observes the connection details from each `AccessKey` and makes them
available to the composition when the function runs.
* The Secret reads `AccessKey`'s connection details via
{{<hover label="comp-pythonic" line="29">}}connection.username{{</hover>}} and
{{<hover label="comp-pythonic" line="30">}}connection.password{{</hover>}}.
* The function establishes the connection `Secret` name from the XR
{{<hover label="comp-pythonic" line="20">}}spec.writeConnectionSecretToRef{{</hover>}}
if it exists.
* The function automatically includes a `Secret` object in the XR's composed
resources that represents the XR's aggregated connection details.
* You don't need to create or compose this `Secret` yourself, it's done
automatically for you.
* In `function-pythonic`, connection details base64 encoding and decoding is handled
automatically for you.
<!-- vale Google.WordList = YES -->
<!-- vale write-good.Passive = YES -->

{{< /tab >}}

{{< /tabs >}}

Save the composition as `composition.yaml` and apply it:
Expand Down Expand Up @@ -955,6 +1044,28 @@ You don't need to manually compose a `Secret` resource yourself.
needed. Use patches to configure these values using data from the XR if
needed.

### Automatic aggregation (`function-pythonic`)

`function-pythonic` automatically observes connection details from
composed resources and creates the aggregated connection secret to
maintain backward compatibility with v1 behavior.

You don't need to manually compose a `Secret` resource yourself.

1. **Compose resources**: Create composed resources as usual in your
composition, such as IAM `User` and `AccessKeys`. These resources expose
their connection details in a `Secret`.

2. **Set `writeConnectionSecretToRef`**: Each composed resource that should have
connection details stored should have their `resource.spec.writeConnectionSecretToRef` set
in the composition.

3. **Define `connection`**: For each composed resource, assign the connection secret
values wanted to the aggregated secret using `self.connection[key] = resource.connection[key]`.

4. **Configure the `Secret`**: Set the XR `self.connectionSecret` fields
to override the aggregated secret's default name and namespace.

## Troubleshooting

### Composite resource's connection details Secret is empty
Expand Down Expand Up @@ -995,8 +1106,8 @@ For example, `function-python` requires you to convert connection details to
base64-encoded strings, while connection details in `function-go-templating` and
`function-kcl` are already encoded this way and require no conversion logic.

`function-patch-and-transform` handles encoding when automatically creating the
composed connection secret.
`function-patch-and-transform` and `function-pythonic` handle encoding when automatically
creating the composed connection secret.
<!-- vale write-good.Weasel = YES -->
<!-- vale Google.Colons = YES -->

Expand Down
1 change: 1 addition & 0 deletions content/master/learn/community-extension-projects.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ use by Crossplane adopters.
- [function-kcl](https://github.com/crossplane-contrib/function-kcl)
- [function-patch-and-transform](https://github.com/crossplane-contrib/function-patch-and-transform)
- [function-python](https://github.com/crossplane-contrib/function-python)
- [function-pythonic](https://github.com/crossplane-contrib/function-pythonic)
- [function-sequencer](https://github.com/crossplane-contrib/function-sequencer)
- [function-shell](https://github.com/crossplane-contrib/function-shell)
- [function-status-transformer](https://github.com/crossplane-contrib/function-status-transformer)
Expand Down
1 change: 1 addition & 0 deletions utils/vale/styles/Crossplane/crossplane-words.txt
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ PatchSets
ProviderConfig
ProviderConfigs
ProviderRevision
Pythonic
RunFunctionRequest
RunFunctionRequests
RunFunctionResponse
Expand Down