From 02b805be842b36c8b16c13fa915fc9b4556d136d Mon Sep 17 00:00:00 2001 From: Steven Borrelli Date: Fri, 6 Mar 2026 10:30:26 +0100 Subject: [PATCH 1/4] format md files Signed-off-by: Steven Borrelli --- .github/ISSUE_TEMPLATE/bug_report.md | 7 +++++-- .github/ISSUE_TEMPLATE/feature_request.md | 5 ++++- .github/PULL_REQUEST_TEMPLATE.md | 2 +- package-configuration/crossplane.yaml | 4 ++-- package-lock.json | 4 ++-- package.json | 2 +- 6 files changed, 15 insertions(+), 9 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index d834757..ab3574d 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -3,6 +3,7 @@ name: Bug Report about: Help us diagnose and fix bugs in this Function labels: bug --- + ### What happened? + - ### How can we reproduce it? + ### What environment did it happen in? -Function version: + +Function version: ### What problem are you facing? + ### How could this Function help solve your problem? + diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index d893e4d..15684cc 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -19,7 +19,7 @@ We love pull requests that fix an open issue. If yours does, use the below line to indicate which issue it fixes, for example "Fixes #500". --> -Fixes # +Fixes # I have: diff --git a/package-configuration/crossplane.yaml b/package-configuration/crossplane.yaml index fb95b73..19540b7 100644 --- a/package-configuration/crossplane.yaml +++ b/package-configuration/crossplane.yaml @@ -22,12 +22,12 @@ spec: - apiVersion: pkg.crossplane.io/v1 kind: Function package: ghcr.io/crossplane/function-template-typescript - version: ">=v0.3.0" + version: ">=v0.4.0" # Automatically set ready status for resources - apiVersion: pkg.crossplane.io/v1 kind: Function package: xpkg.upbound.io/crossplane-contrib/function-auto-ready - version: ">=v0.6.0" + version: ">=v0.6.1" # Example provider dependency # - apiVersion: pkg.crossplane.io/v1 # kind: Provider diff --git a/package-lock.json b/package-lock.json index 68f367a..d5ebcf8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "function-template-typescript", - "version": "0.3.0", + "version": "0.4.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "function-template-typescript", - "version": "0.3.0", + "version": "0.4.0", "license": "Apache-2.0", "dependencies": { "@crossplane-org/function-sdk-typescript": "^0.5.0", diff --git a/package.json b/package.json index 0e1e416..a723875 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "function-template-typescript", - "version": "0.3.0", + "version": "0.4.0", "description": "Template for Crossplane function in Typescript", "keywords": [ "crossplane", From 1f7a898959268775c0da5de9e2dd49806f3d4454 Mon Sep 17 00:00:00 2001 From: Steven Borrelli Date: Fri, 6 Mar 2026 10:31:29 +0100 Subject: [PATCH 2/4] update README and xrd Signed-off-by: Steven Borrelli --- README.md | 133 +++++++++++++++--- .../apis/apps/definition.yaml | 2 +- 2 files changed, 116 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 7a194b2..8b078b2 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ This repository is a template for building Crossplane Composition functions in T - [Build the Crossplane Function Package](#build-the-crossplane-function-package) - [Update the Function Package Metadata](#update-the-function-package-metadata) - [Building the Function Package](#building-the-function-package) + - [Installing the Function Package Directly without a Configuration Package](#installing-the-function-package-directly-without-a-configuration-package) - [Configuration Package](#configuration-package) - [Updating the `crossplane.yaml` File](#updating-the-crossplaneyaml-file) - [Build the Configuration Package](#build-the-configuration-package) @@ -32,6 +33,8 @@ This repository is a template for building Crossplane Composition functions in T - [Key SDK Functions](#key-sdk-functions) - [Example: Creating a Resource](#example-creating-a-resource) - [Using Kubernetes Models](#using-kubernetes-models) + - [Checking Resource Conditions (SDK v0.5.0+)](#checking-resource-conditions-sdk-v050) + - [Detecting Crossplane Capabilities (SDK v0.5.0+)](#detecting-crossplane-capabilities-sdk-v050) - [Testing Your Function](#testing-your-function) - [TypeScript Configuration](#typescript-configuration) - [GitHub Actions](#github-actions) @@ -83,19 +86,19 @@ kind: Configuration metadata: name: crossplane-configuration-template-typescript spec: - package: ghcr.io/crossplane/configuration-template-typescript:v0.3.0 + package: ghcr.io/crossplane/configuration-template-typescript:v0.4.0 ``` Once installed, confirm that the package and dependencies are installed: ```shell -$ kubectl get pkg +$ kubectl get pkg NAME INSTALLED HEALTHY PACKAGE AGE -configuration.pkg.crossplane.io/crossplane-configuration-template-typescript True True ghcr.io/crossplane/configuration-template-typescript:v0.3.0 49m +configuration.pkg.crossplane.io/crossplane-configuration-template-typescript True True ghcr.io/crossplane/configuration-template-typescript:v0.4.0 49m NAME INSTALLED HEALTHY PACKAGE AGE -function.pkg.crossplane.io/crossplane-contrib-function-auto-ready True True xpkg.upbound.io/crossplane-contrib/function-auto-ready:v0.6.0 48m -function.pkg.crossplane.io/crossplane-function-template-typescript True True ghcr.io/crossplane/function-template-typescript:v0.3.0 49m +function.pkg.crossplane.io/crossplane-contrib-function-auto-ready True True xpkg.upbound.io/crossplane-contrib/function-auto-ready:v0.6.1 48m +function.pkg.crossplane.io/crossplane-function-template-typescript True True ghcr.io/crossplane/function-template-typescript:v0.4.0 49m ``` ### Deploy the Example Manifest @@ -335,8 +338,8 @@ packed into a Function Package: ```shell $ tree _build/docker_images _build/docker_images -├── function-template-typescript-runtime-amd64-v0.3.0.tar -└── function-template-typescript-runtime-arm64-v0.3.0.tar +├── function-template-typescript-runtime-amd64-v0.4.0.tar +└── function-template-typescript-runtime-arm64-v0.4.0.tar ``` The Dockerfile uses a multi-stage build: @@ -372,8 +375,8 @@ the `_build/xpkg` directory: ```shell $ tree _build/xpkg _build/xpkg -├── function-template-typescript-amd64-v0.3.0.xpkg -└── function-template-typescript-arm64-v0.3.0.xpkg +├── function-template-typescript-amd64-v0.4.0.xpkg +└── function-template-typescript-arm64-v0.4.0.xpkg ``` These packages can be pushed to any Docker Registry using `crossplane xpkg push`. Update the `XPKG_REPO` in the [env](env) @@ -387,11 +390,36 @@ npm run function-xpkg-push npm run function-build-all ``` +### Installing the Function Package Directly without a Configuration Package + +The function package can be installed directly without the need for a Configuration +package. This installation method requires the application of the +CompositionResourceDefinition Composition manifests. + +```yaml +apiVersion: pkg.crossplane.io/v1 +kind: Function +metadata: + name: crossplane-function-template-typescript +spec: + package: ghcr.io/crossplane/function-template-typescript:v0.4.0 +``` + +Next, apply the CompositeResourceDefinition and Composition manifests: + +```shell +$ kubectl apply -f package-configuration/apis/apps +composition.apiextensions.crossplane.io/apps.platform.upbound.io created +compositeresourcedefinition.apiextensions.crossplane.io/apps.platform.upbound.io created +``` + ### Configuration Package -With the Function package created, the Configuration -Package can be generated. This package will install the Function -Package as a dependency. +Crossplane configuration packages install CompositeResourceDefinition and Composition manifests +and install Functions and Providers as dependencies. + +With the Function package created, the Configuration Package can be generated. We'll +update the Configuration package metadata to include our function as a dependency. #### Updating the `crossplane.yaml` File @@ -412,7 +440,7 @@ spec: - apiVersion: pkg.crossplane.io/v1 kind: Function package: ghcr.io/crossplane/function-template-typescript - version: '>=v0.3.0' + version: '>=v0.4.0' ``` A Crossplane Composition requires a `CompositeResourceDefinition` (XRD) and `Composite`. These @@ -435,7 +463,6 @@ Update the value with the name that represents the Docker registry and image whe name: crossplane-function-template-typescript step: app ``` - #### Build the Configuration Package Build the Crossplane configuration package: @@ -451,9 +478,9 @@ images and the Configuration package image: ```shell $ tree _build/xpkg _build/xpkg -├── configuration-template-typescript-v0.3.0.xpkg -├── function-template-typescript-amd64-v0.3.0.xpkg -└── function-template-typescript-arm64-v0.3.0.xpkg +├── configuration-template-typescript-v0.4.0.xpkg +├── function-template-typescript-amd64-v0.4.0.xpkg +└── function-template-typescript-arm64-v0.4.0.xpkg ``` Push this package to a Docker registry: @@ -484,13 +511,26 @@ export class Function implements FunctionHandler { The SDK provides helper functions for working with Crossplane resources: +**Resource Access:** + - `getObservedCompositeResource(req)` - Get the observed composite resource (XR) - `getDesiredCompositeResource(req)` - Get the desired composite resource - `getObservedComposedResources(req)` - Get observed composed resources - `getDesiredComposedResources(req)` - Get desired composed resources - `setDesiredComposedResources(rsp, resources)` - Set desired composed resources + +**Resource Conversion:** + - `fromModel(model)` - Convert a Kubernetes model to a Crossplane Resource (added in SDK v0.4.0) - `Resource.fromJSON()` - Create resources from JSON (legacy method, use `fromModel` for kubernetes-models) + +**Status and Conditions (added in SDK v0.5.0):** + +- `getCondition(resource, type)` - Extract a status condition by type from a resource. Returns a condition object with `status` set to `"Unknown"` if the condition is not found. Useful for checking resource readiness and health. +- `hasCapability(req, capability)` - Check if a specific Crossplane capability is available. Use the `Capability` enum to check for features like `CAPABILITY_REQUIRED_RESOURCES` or `CAPABILITY_REQUIRED_SCHEMAS`. This helps ensure compatibility with different Crossplane versions. + +**Response Management:** + - `normal(rsp, message)` - Add a normal condition to the response - `fatal(rsp, message)` - Add a fatal condition to the response - `to(req)` - Create a minimal response from a request @@ -552,6 +592,61 @@ desiredComposed['my-pod'] = fromModel(pod); **Note**: As of SDK v0.4.0, the `fromModel` helper function provides a cleaner way to convert kubernetes-models objects to Crossplane Resources. The legacy approach using `Resource.fromJSON({ resource: pod.toJSON() })` is still supported but `fromModel` is now the recommended method. +### Checking Resource Conditions (SDK v0.5.0+) + +The SDK provides the `getCondition` function to extract status conditions from resources: + +```typescript +import { + getCondition, + getObservedComposedResources, +} from '@crossplane-org/function-sdk-typescript'; + +// Get observed composed resources +const observedComposed = getObservedComposedResources(req); +const observedDeployment = observedComposed['my-deployment']; + +if (observedDeployment?.resource) { + // Check if the deployment is available + const availableCondition = getCondition(observedDeployment.resource, 'Available'); + const progressingCondition = getCondition(observedDeployment.resource, 'Progressing'); + + logger?.info( + { + deployment: observedDeployment.resource.metadata?.name, + available: availableCondition.status, // "True", "False", or "Unknown" + progressing: progressingCondition.status, + }, + 'Deployment conditions' + ); +} +``` + +The `getCondition` function returns a condition object with `status` set to `"Unknown"` if the condition type is not found, making it safe to use even when resources are newly created. + +### Detecting Crossplane Capabilities (SDK v0.5.0+) + +Use `hasCapability` to check what features are supported by the Crossplane version: + +```typescript +import { hasCapability, Capability } from '@crossplane-org/function-sdk-typescript'; + +// Check for specific capabilities +const capabilities = { + requiredResources: hasCapability(req, Capability.CAPABILITY_REQUIRED_RESOURCES), + requiredSchemas: hasCapability(req, Capability.CAPABILITY_REQUIRED_SCHEMAS), +}; + +logger?.info({ capabilities }, 'Crossplane capabilities detected'); + +// Conditionally enable features based on capabilities +if (capabilities.requiredSchemas) { + // Use schema validation features available in Crossplane 2.2.0+ +} +``` + +This allows your function to gracefully adapt to different Crossplane versions and use features when available. + ### Testing Your Function Create YAML test cases in the [test-cases/](test-cases/) directory. Each test case defines: @@ -648,7 +743,9 @@ Manual workflow for creating Git tags: ### Production Dependencies -- `@crossplane-org/function-sdk-typescript` - Crossplane function SDK (v0.4.0+ includes `fromModel` helper) +- `@crossplane-org/function-sdk-typescript` - Crossplane function SDK + - v0.4.0+: `fromModel` helper for converting kubernetes-models + - v0.5.0+: `getCondition` and `hasCapability` for status checking and capability detection - `commander` - CLI argument parsing - `pino` - Structured logging - `kubernetes-models` - Type-safe Kubernetes resource models diff --git a/package-configuration/apis/apps/definition.yaml b/package-configuration/apis/apps/definition.yaml index 453b560..929ad3d 100644 --- a/package-configuration/apis/apps/definition.yaml +++ b/package-configuration/apis/apps/definition.yaml @@ -1,4 +1,4 @@ -apiVersion: apiextensions.crossplane.io/v1 +apiVersion: apiextensions.crossplane.io/v2 kind: CompositeResourceDefinition metadata: name: apps.platform.upbound.io From 5826222b5bae3312e6b5f9ad4aa2be6869702e0a Mon Sep 17 00:00:00 2001 From: Steven Borrelli Date: Fri, 6 Mar 2026 10:34:25 +0100 Subject: [PATCH 3/4] typo Signed-off-by: Steven Borrelli --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8b078b2..0edf099 100644 --- a/README.md +++ b/README.md @@ -394,7 +394,7 @@ npm run function-build-all The function package can be installed directly without the need for a Configuration package. This installation method requires the application of the -CompositionResourceDefinition Composition manifests. +CompositeResourceDefinition and Composition manifests. ```yaml apiVersion: pkg.crossplane.io/v1 From bc6662453dcb89848a1f3fbd0abff9a265b9821e Mon Sep 17 00:00:00 2001 From: Steven Borrelli Date: Fri, 6 Mar 2026 10:39:14 +0100 Subject: [PATCH 4/4] update readme Signed-off-by: Steven Borrelli --- README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 0edf099..d8a8080 100644 --- a/README.md +++ b/README.md @@ -392,9 +392,7 @@ npm run function-build-all ### Installing the Function Package Directly without a Configuration Package -The function package can be installed directly without the need for a Configuration -package. This installation method requires the application of the -CompositeResourceDefinition and Composition manifests. +The Function package can be installed without the need for a Configuration package. ```yaml apiVersion: pkg.crossplane.io/v1 @@ -405,7 +403,10 @@ spec: package: ghcr.io/crossplane/function-template-typescript:v0.4.0 ``` -Next, apply the CompositeResourceDefinition and Composition manifests: +The Function package will not install the +related CompositeResourceDefinition and Composition manifests. + +Apply the CompositeResourceDefinition and Composition manifests: ```shell $ kubectl apply -f package-configuration/apis/apps @@ -415,8 +416,7 @@ compositeresourcedefinition.apiextensions.crossplane.io/apps.platform.upbound.io ### Configuration Package -Crossplane configuration packages install CompositeResourceDefinition and Composition manifests -and install Functions and Providers as dependencies. +Crossplane configuration packages install CompositeResourceDefinition and Composition manifests. The Configuration will install Function and Provider packages as dependencies. With the Function package created, the Configuration Package can be generated. We'll update the Configuration package metadata to include our function as a dependency. @@ -463,6 +463,7 @@ Update the value with the name that represents the Docker registry and image whe name: crossplane-function-template-typescript step: app ``` + #### Build the Configuration Package Build the Crossplane configuration package: