Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

singleton-example #163

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
97 changes: 97 additions & 0 deletions example/singleton-resource/composition.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
---
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: xsingletonresources.example.crossplane.io
spec:
compositeTypeRef:
apiVersion: example.crossplane.io/v1
kind: XSingletonResource
mode: Pipeline
pipeline:
- step: check-resource-existence-create
functionRef:
name: function-go-templating
input:
apiVersion: gotemplating.fn.crossplane.io/v1beta1
kind: GoTemplate
source: Inline
inline:
template: |-
{{- $resourceSpec := index .observed.composite.resource.spec.resource }}
{{- $readyStatus := false -}}
{{- $externalName := "" -}}
---
apiVersion: meta.gotemplating.fn.crossplane.io/v1alpha1
kind: ExtraResources
requirements:
resources:
apiVersion: {{ $resourceSpec.apiVersion }}
kind: {{ $resourceSpec.kind }}
matchName: {{ $resourceSpec.metadata.name }}
{{- with .extraResources }}
{{- $someExtraResources := index . "resources" }}
{{- range $i, $extraResource := $someExtraResources.items }}
{{- $resourceName := $extraResource.resource.metadata.name }}
{{- if eq $resourceName $resourceSpec.metadata.name }}
{{- $extraResourcesStatus := fromJson ($extraResource.resource.status.conditions | toJson) }}
{{- if gt (len $extraResourcesStatus) 1 }}
{{- $syncConditionReadyStatus := index $extraResourcesStatus 1 }}
{{- if eq $syncConditionReadyStatus.status "True" }} # Assuming "True" indicates readiness
{{- $readyStatus = true -}} # Set readyStatus to true if condition is met
{{ $externalName = index $extraResource.resource.metadata.annotations "crossplane.io/external-name" }}
{{- else }}
{{- $readyStatus = false -}}
{{- end }}
{{- else }}
{{- $readyStatus = false -}}
{{- end }}
{{- else }}
{{- $readyStatus = false -}}
{{- end }}
{{- end }}
{{- end }}
{{- if $readyStatus }}
---
apiVersion: {{ $resourceSpec.apiVersion }}
kind: {{ $resourceSpec.kind }}
metadata:
name: {{ $resourceSpec.metadata.name }}-readonly-{{ $.observed.composite.resource.metadata.name }}
annotations:
crossplane.io/external-name: {{ $externalName }}
gotemplating.fn.crossplane.io/composition-resource-name: {{ $resourceSpec.metadata.name }}
spec:
{{- toYaml $resourceSpec.spec | nindent 2 }}
managementPolicies: ["Observe"]
---
apiVersion: example.crossplane.io/v1
kind: XSingletonResource
metadata:
name: {{ $.observed.composite.resource.metadata.name }}
status:
externalResourceStatus: "Already exist {{ $resourceSpec.metadata.name }} of kind {{ $resourceSpec.kind }}"
externalName: {{ $externalName }}
{{- else }}
---
apiVersion: {{ $resourceSpec.apiVersion }}
kind: {{ $resourceSpec.kind }}
metadata:
name: {{ $resourceSpec.metadata.name }}
annotations:
crossplane.io/external-name: {{ $resourceSpec.metadata.name }}
gotemplating.fn.crossplane.io/composition-resource-name: {{ $resourceSpec.metadata.name }}
spec:
{{- toYaml $resourceSpec.spec | nindent 2 }}
managementPolicies: ["Observe", "Create"]
---
apiVersion: example.crossplane.io/v1
kind: XSingletonResource
metadata:
name: {{ $.observed.composite.resource.metadata.name }}
status:
externalResourceStatus: "resource name {{ $resourceSpec.metadata.name }} of kind {{ $resourceSpec.kind }} not exist. Creating.."
externalName: {{ $externalName }}
{{- end }}
- step: automatically-detect-ready-composed-resources
functionRef:
name: function-auto-ready
43 changes: 43 additions & 0 deletions example/singleton-resource/definition.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
name: xsingletonresources.example.crossplane.io
spec:
group: example.crossplane.io
defaultCompositeDeletePolicy: Foreground
names:
kind: XSingletonResource
plural: xsingletonresources
singular: xsingletonresource
claimNames:
kind: SingletonResource
plural: singletonresources
singular: singletonresource
versions:
- name: v1
served: true
referenceable: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
required:
- resource
properties:
resource:
description: Freeform field for resource
type: object
x-kubernetes-preserve-unknown-fields: true
status:
description: XRStatus defines the observed state of XR.
type: object
properties:
externalResourceStatus:
type: string
description: status of the resource
externalName:
type: string
description: external name of the MR
14 changes: 14 additions & 0 deletions example/singleton-resource/functions.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
apiVersion: pkg.crossplane.io/v1beta1
kind: Function
metadata:
name: function-go-templating
spec:
package: xpkg.upbound.io/crossplane-contrib/function-go-templating:v0.9.1
---
apiVersion: pkg.crossplane.io/v1beta1
kind: Function
metadata:
name: function-auto-ready
spec:
package: xpkg.upbound.io/crossplane-contrib/function-auto-ready:v0.4.1
136 changes: 136 additions & 0 deletions example/singleton-resource/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# How It Works

## Overview
This mechanism leverages Crossplane Composition, Go templating, and management policies to dynamically determine whether a cloud resource should be observed (imported) or created (managed). It uses Crossplane's ExtraResources feature, a powerful Go templating capability, to check if a requested resource already exists in the cloud and handle it accordingly.

1. **ExtraResources** fetch MR information
2. **Imported (Observed Only)** if it already exists in the cloud.
3. **Created and Managed** if it does not exist.

The mechanism leverages **Crossplane Composition** and **management policies** to determine whether the resource should be observed or created.

---

## Step-by-Step Execution

### 1️⃣ Extracting the Requested Resource Specification
The template begins by extracting the required resource details from the **observed composite resource**:

```yaml
{{- $resourceSpec := index .observed.composite.resource.spec.resource }}
```

- `$resourceSpec` holds information such as `apiVersion`, `kind`, `metadata.name`, and `spec`.
- A default **ready status (`$readyStatus`)** is set to `false`.
- An empty **external name (`$externalName`)** is initialized.

---

### 2️⃣ Checking for Existing Resources
The logic then checks whether this resource already exists in **extraResources**:

```yaml
{{- with .extraResources }}
{{- $someExtraResources := index . "resources" }}
{{- range $i, $extraResource := $someExtraResources.items }}
{{- $resourceName := $extraResource.resource.metadata.name }}
```

For each `extraResource`:
- If the `metadata.name` matches `$resourceSpec.metadata.name`, it means an instance of this resource may already exist.
- The **status conditions** are parsed:

```yaml
{{- $extraResourcesStatus := fromJson ($extraResource.resource.status.conditions | toJson) }}
```

- If there is more than one condition:
- The **second condition** (`syncConditionReadyStatus`) is checked to determine if it is `"True"` (i.e., the resource is ready).
- If **ready**, `$readyStatus` is set to `true`, and the `external-name` annotation is extracted:

```yaml
{{ $externalName = index $extraResource.resource.metadata.annotations "crossplane.io/external-name" }}
```

- If the conditions are missing or not `"True"`, `$readyStatus` remains `false`.

---

### 3️⃣ Handling the Resource Based on Readiness

#### ✅ **Case 1: Resource Already Exists**
If `$readyStatus` is `true`:

- A **read-only Crossplane resource** is created, importing the existing cloud resource using an `external-name`:

```yaml
apiVersion: {{ $resourceSpec.apiVersion }}
kind: {{ $resourceSpec.kind }}
metadata:
name: {{ $resourceSpec.metadata.name }}-readonly-{{ $.observed.composite.resource.metadata.name }}
annotations:
crossplane.io/external-name: {{ $externalName }}
spec:
managementPolicies: ["Observe"]
```

- A **singleton status resource** (`XSingletonResource`) is updated to indicate that the resource already exists:

```yaml
apiVersion: example.crossplane.io/v1
kind: XSingletonResource
metadata:
name: {{ $.observed.composite.resource.metadata.name }}
status:
externalResourceStatus: "Already exist {{ $resourceSpec.metadata.name }} of kind {{ $resourceSpec.kind }}"
externalName: {{ $externalName }}
```

---

#### ❌ **Case 2: Resource Does Not Exist**
If `$readyStatus` is `false`:

- A **new resource is created and managed** by Crossplane with `managementPolicies: ["Observe", "Create"]`:

```yaml
apiVersion: {{ $resourceSpec.apiVersion }}
kind: {{ $resourceSpec.kind }}
metadata:
name: {{ $resourceSpec.metadata.name }}
annotations:
crossplane.io/external-name: {{ $resourceSpec.metadata.name }}
spec:
managementPolicies: ["Observe", "Create"]
```

- A **singleton status resource** (`XSingletonResource`) is updated to indicate that the resource is being created:

```yaml
apiVersion: example.crossplane.io/v1
kind: XSingletonResource
metadata:
name: {{ $.observed.composite.resource.metadata.name }}
status:
externalResourceStatus: "resource name {{ $resourceSpec.metadata.name }} of kind {{ $resourceSpec.kind }} not exist. Creating.."
externalName: {{ $externalName }}
```

---

## Summary

| Condition | Action Taken |
|-----------|-------------|
| **Resource exists** in cloud (determined via `extraResources`) | Import as **read-only** with `"Observe"` policy. |
| **Resource does not exist** | Create a new resource and manage it with `"Observe", "Create"` policies. |

### Key Benefits
✅ Prevents duplicate resource creation in the cloud.
✅ Ensures the correct resource is always referenced.
✅ Keeps resources in **read-only mode** to prevent accidental modifications.
✅ Allows claims to share the same cloud resource while maintaining unique metadata inside Kubernetes.

---

This explanation should provide a clear understanding of how the template works. Let me know if you need any refinements! 🚀
16 changes: 16 additions & 0 deletions example/singleton-resource/xr1.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
apiVersion: example.crossplane.io/v1
kind: XSingletonResource
metadata:
name: example-xr1
spec:
resource:
apiVersion: azure.upbound.io/v1beta1
kind: ResourceGroup
metadata:
name: testrg
spec:
forProvider:
location: West Europe
providerConfigRef:
name: default
16 changes: 16 additions & 0 deletions example/singleton-resource/xr2.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
apiVersion: example.crossplane.io/v1
kind: XSingletonResource
metadata:
name: example-xr2
spec:
resource:
apiVersion: azure.upbound.io/v1beta1
kind: ResourceGroup
metadata:
name: testrg
spec:
forProvider:
location: West Europe
providerConfigRef:
name: default
16 changes: 16 additions & 0 deletions example/singleton-resource/xr3.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
apiVersion: example.crossplane.io/v1
kind: XSingletonResource
metadata:
name: example-xr3
spec:
resource:
apiVersion: azure.upbound.io/v1beta1
kind: ResourceGroup
metadata:
name: testrg
spec:
forProvider:
location: West Europe
providerConfigRef:
name: default