Skip to main content

loom-crossplane

Cloud-native infrastructure management with Crossplane via Kubernetes APIs.

Source facts

Repository
cosmix/loom
Last source activity
August 29, 2026 at 21:19
Detected SKILL.md language
English
Stars
61
Forks
4

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
loom-crossplane
description
Cloud-native infrastructure management with Crossplane via Kubernetes APIs.
allowed-tools
["Read","Grep","Glob","Edit","Write","Bash"]
triggers
["crossplane","XRD","composition","claim","provider","managed resource","composite resource","infrastructure API","platform engineering","platform API","infrastructure abstraction","self-service infrastructure","kubernetes infrastructure","cloud control plane"]
# Crossplane Infrastructure Management Crossplane extends Kubernetes to manage cloud infrastructure using declarative APIs. It enables platform teams to build internal cloud platforms with self-service capabilities. ## Architecture Overview ### Core Components 1. **Providers**: Kubernetes controllers that provision infrastructure in external systems (AWS, GCP, Azure, etc.) 2. **Managed Resources (MRs)**: Custom resources representing external infrastructure (S3 buckets, RDS instances, etc.) 3. **Composite Resources (XRs)**: Higher-level abstractions composed of multiple managed resources 4. **Composite Resource Definitions (XRDs)**: Schemas defining composite resource types 5. **Compositions**: Templates that map XRs to managed resources with transformation logic 6. **Claims**: Namespace-scoped resources that provision composite resources for application teams 7. **Composition Functions**: Extension points for complex transformation logic ### Resource Hierarchy ```text Claim (namespace-scoped) -> Composite Resource (cluster-scoped) -> Managed Resources -> Cloud Infrastructure ``` ## Installation and Setup ### Install Crossplane ```bash # Add Crossplane Helm repository helm repo add crossplane-stable https://charts.crossplane.io/stable helm repo update # Install Crossplane # Composition functions are enabled by default since v1.14 — no feature-gate flag needed. helm install crossplane \ crossplane-stable/crossplane \ --namespace crossplane-system \ --create-namespace \ --wait # Verify installation kubectl get pods -n crossplane-system ``` ### Install Crossplane CLI ```bash # Install CLI for local development curl -sL https://raw.githubusercontent.com/crossplane/crossplane/master/install.sh | sh sudo mv crossplane /usr/local/bin/ # Verify CLI crossplane --version ``` ## Provider Configuration ### AWS Provider > **v2 note:** `ControllerConfig` (`pkg.crossplane.io/v1alpha1`) was deprecated in v1.11 and **removed in Crossplane v2**. Configure provider runtime with `DeploymentRuntimeConfig` (`pkg.crossplane.io/v1beta1`, beta-enabled by default since v1.14) referenced via `runtimeConfigRef`. Migrate any existing config with `crossplane beta convert deployment-runtime controller-config.yaml -o deployment-runtime-config.yaml` before upgrading. ```yaml # providers/aws-provider.yaml apiVersion: pkg.crossplane.io/v1 kind: Provider metadata: name: provider-aws-s3 spec: package: xpkg.upbound.io/upbound/provider-aws-s3:v1.1.0 runtimeConfigRef: name: aws-runtime --- apiVersion: pkg.crossplane.io/v1 kind: Provider metadata: name: provider-aws-rds spec: package: xpkg.upbound.io/upbound/provider-aws-rds:v1.1.0 runtimeConfigRef: name: aws-runtime --- apiVersion: pkg.crossplane.io/v1beta1 kind: DeploymentRuntimeConfig metadata: name: aws-runtime spec: deploymentTemplate: spec: selector: {} template: spec: securityContext: fsGroup: 2000 containers: - name: package-runtime args: - --poll=1m - --max-reconcile-rate=100 ``` ### Provider Authentication ```bash # Create AWS credentials secret kubectl create secret generic aws-creds \ -n crossplane-system \ --from-file=creds=/path/to/aws-credentials.txt # credentials.txt format: # [default] # aws_access_key_id = AKIAIOSFODNN7EXAMPLE # aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY ``` ```yaml # providers/aws-provider-config.yaml apiVersion: aws.upbound.io/v1beta1 kind: ProviderConfig metadata: name: default spec: credentials: source: Secret secretRef: namespace: crossplane-system name: aws-creds key: creds ``` ### GCP Provider ```yaml # providers/gcp-provider.yaml apiVersion: pkg.crossplane.io/v1 kind: Provider metadata: name: provider-gcp-storage spec: package: xpkg.upbound.io/upbound/provider-gcp-storage:v1.1.0 --- apiVersion: gcp.upbound.io/v1beta1 kind: ProviderConfig metadata: name: default spec: projectID: my-gcp-project credentials: source: Secret secretRef: namespace: crossplane-system name: gcp-creds key: creds.json ``` ## Managed Resources ### Direct Managed Resource Usage ```yaml # managed-resources/s3-bucket.yaml apiVersion: s3.aws.upbound.io/v1beta1 kind: Bucket metadata: name: my-app-data-bucket spec: forProvider: region: us-west-2 tags: Environment: production ManagedBy: crossplane providerConfigRef: name: default deletionPolicy: Delete ``` ```yaml # managed-resources/rds-instance.yaml apiVersion: rds.aws.upbound.io/v1beta1 kind: Instance metadata: name: my-postgres-db spec: forProvider: region: us-west-2 allocatedStorage: 20 engine: postgres engineVersion: "14.7" instanceClass: db.t3.micro dbName: myappdb username: dbadmin passwordSecretRef: namespace: crossplane-system name: db-password key: password skipFinalSnapshot: true publiclyAccessible: false vpcSecurityGroupIdSelector: matchLabels: role: database providerConfigRef: name: default writeConnectionSecretToRef: namespace: production name: postgres-connection ``` ## Composite Resource Definitions (XRDs) ### Database XRD The XRD **is your platform API** — the OpenAPI schema is your only guardrail against consumer misconfiguration. Enforce with `enum`, `minimum`/`maximum`, `pattern`, `default`, and `required`. `spec.group` and `spec.names` are **immutable**: changing them requires deleting/recreating the XRD, which cascades to all its XRs. **v1 (below) vs v2:** the v1 form uses `claimNames` (cluster-scoped XR + namespaced claim) and `connectionSecretKeys`. New XRDs should use `apiextensions.crossplane.io/v2` — **namespaced by default** (`scope: Namespaced`), **no claims** (`claimNames` gone), and `connectionSecretKeys` **does not apply** (aggregate via `function-patch-and-transform`'s `writeConnectionSecretToRef.patches`). ```yaml # xrds/database-xrd.yaml (v1 legacy-cluster form with claims) apiVersion: apiextensions.crossplane.io/v1 kind: CompositeResourceDefinition metadata: name: xpostgresqlinstances.database.example.com # must be <plural>.<group> spec: group: database.example.com names: { kind: XPostgreSQLInstance, plural: xpostgresqlinstances } claimNames: { kind: PostgreSQLInstance, plural: postgresqlinstances } # v1 only connectionSecretKeys: [username, password, endpoint, port] # v1 only versions: - name: v1alpha1 served: true referenceable: true # exactly one version must be referenceable schema: openAPIV3Schema: type: object properties: spec: type: object properties: parameters: type: object properties: storageGB: { type: integer, default: 20, minimum: 20, maximum: 1000 } size: { type: string, enum: [small, medium, large], default: small } networkRef: # nested object with its own required field type: object properties: { id: { type: string } } required: [id] required: [size, networkRef] required: [parameters] status: # ToCompositeFieldPath patches write here type: object properties: address: { type: string } ``` For the **v2** form, drop `claimNames`/`connectionSecretKeys` and add `scope: Namespaced` (XRs then live in a namespace and follow standard Kubernetes RBAC): ```yaml apiVersion: apiextensions.crossplane.io/v2 kind: CompositeResourceDefinition spec: scope: Namespaced # default in v2; also LegacyCluster (v1 behavior) or Cluster group: platform.example.com names: { kind: XAppPlatform, plural: xappplatforms } # ... versions/schema as above ``` ## Compositions A Composition implements an XRD's API by templating one or more resources. **On v2, `mode: Pipeline` (with `function-patch-and-transform`) is the ONLY supported form** — native `mode: Resources` (`spec.resources`/`spec.patchSets`) was deprecated in v1.17 and removed in v2. See the full Pipeline example under *Composition Functions*; migrate legacy compositions with `crossplane beta convert pipeline-composition old.yaml -o new.yaml`. ### Patch & Transform vocabulary These patch/transform types are the workhorses of both legacy `mode: Resources` **and** `function-patch-and-transform` input — the syntax is identical, so knowledge transfers verbatim. | Patch `type` | Direction | Use | | ----------------------- | ----------------- | ------------------------------------------------------ | | `FromCompositeFieldPath`| XR → composed MR | Push a user parameter onto a managed resource field | | `ToCompositeFieldPath` | composed MR → XR | Surface `status.atProvider.*` back to the XR status | | `CombineFromComposite` | many XR → one MR | Build a value (e.g. name) from multiple fields via `fmt`| | `PatchSet` (+`patchSets`)| — | Reuse a named patch group across resources (e.g. common tags)| | Transform `type` | Use / gotcha | | ---------------- | ---------------------------------------------------------------------------- | | `map` | Enum → value (`small`→`db.t3.micro`). **Errors if key absent** — prefer `match` with `fallbackTo` (see *Expert Practices → Idioms*) | | `string` | `fmt: "%s-connection"` for derived names — must stay deterministic across reconciles | | `math` | Scale a numeric field | | `convert` | Type coercion (string↔int↔bool) | ```yaml # Inside mode: Pipeline -> function-patch-and-transform input (see Composition Functions) resources: - name: rds-instance base: apiVersion: rds.aws.upbound.io/v1beta1 kind: Instance spec: forProvider: engine: postgres username: dbadmin # Selectors let Crossplane resolve refs & order deps automatically: vpcSecurityGroupIdSelector: { matchControllerRef: true } dbSubnetGroupNameSelector: { matchControllerRef: true } writeConnectionSecretToRef: { namespace: crossplane-system } patches: - type: PatchSet patchSetName: common-tags - type: FromCompositeFieldPath fromFieldPath: spec.parameters.size toFieldPath: spec.forProvider.instanceClass transforms: - type: map map: { small: db.t3.micro, medium: db.t3.medium, large: db.m5.large } - type: FromCompositeFieldPath # derive a stable secret name from UID fromFieldPath: metadata.uid toFieldPath: spec.writeConnectionSecretToRef.name transforms: [{ type: string, string: { fmt: "%s-connection" } }] - type: ToCompositeFieldPath # surface endpoint to XR status fromFieldPath: status.atProvider.endpoint toFieldPath: status.address - type: FromCompositeFieldPath # LOAD-BEARING patch: fail loud, don't skip fromFieldPath: spec.parameters.networkRef.id toFieldPath: spec.forProvider.vpcId policy: { fromFieldPath: Required } ``` **Environment-driven variation** (dev/staging/prod) is just a `map` transform per field (e.g. `environment → multiAz`, `→ numCacheClusters`, `→ backupRetentionDays`). Conditional resource **inclusion** (create a cache only when `enableCache=true`) is NOT expressible in patch-and-transform — it needs a templating function (see *Conditional Resource Creation* and *Expert Practices*). ## Claims (Self-Service Resources) ### Database Claim ```yaml # claims/my-app-database.yaml apiVersion: database.example.com/v1alpha1 kind: PostgreSQLInstance metadata: name: my-app-db namespace: production spec: parameters: size: medium storageGB: 100 engineVersion: "14.7" highAvailability: true backupRetentionDays: 30 networkRef: id: vpc-0a1b2c3d4e5f6g7h8 compositionSelector: matchLabels: provider: aws database: postgresql writeConnectionSecretToRef: name: my-app-db-connection ``` ### Application Platform Claim ```yaml # claims/my-app-platform.yaml apiVersion: platform.example.com/v1alpha1 kind: AppPlatform metadata: name: my-application namespace: team-alpha spec: parameters: environment: prod appName: my-app region: us-west-2 databaseSize: large enableCache: true compositionSelector: matchLabels: provider: aws writeConnectionSecretToRef: name: my-app-platform-secrets ``` ## Composition Functions Composition Functions enable complex transformation logic using WebAssembly or container-based functions. ### Function Configuration ```yaml # compositions/postgres-with-functions.yaml apiVersion: apiextensions.crossplane.io/v1 kind: Composition metadata: name: postgres.function-based.aws.database.example.com spec: compositeTypeRef: apiVersion: database.example.com/v1alpha1 kind: XPostgreSQLInstance mode: Pipeline pipeline: # Step 1: Patch and transform resources # Note: function-auto-ready only marks resources Ready — it does NOT generate # passwords or accept a Resources input. For secret/password generation use # function-kcl (random string) or External Secrets Operator; never write a # literal placeholder into a Secret. - step: patch-and-transform functionRef: name: function-patch-and-transform input: apiVersion: pt.fn.crossplane.io/v1beta1 kind: Resources patchSets: - name: common-tags patches: - type: FromCompositeFieldPath fromFieldPath: metadata.labels[crossplane.io/claim-name] toFieldPath: spec.forProvider.tags.ClaimName - type: FromCompositeFieldPath fromFieldPath: metadata.labels[crossplane.io/claim-namespace] toFieldPath: spec.forProvider.tags.ClaimNamespace resources: - name: rds-instance base: apiVersion: rds.aws.upbound.io/v1beta1 kind: Instance spec: forProvider: engine: postgres username: dbadmin skipFinalSnapshot: true patches: - type: PatchSet patchSetName: common-tags - type: FromCompositeFieldPath fromFieldPath: spec.parameters.size toFieldPath: spec.forProvider.instanceClass transforms: - type: map map: small: db.t3.micro medium: db.t3.medium large: db.m5.large # Step 2: Mark as ready
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub