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/README.md b/README.md index 7a194b2..d8a8080 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 without the need for a Configuration package. + +```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 +``` + +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 +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. 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. #### 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 @@ -451,9 +479,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 +512,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 +593,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 +744,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 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",