openova/platform/crossplane
hatiyildiz 31b03ce02a ci(pdm)+platform(crossplane): build workflow + XDynadotPoolAllocation composition (Phase 3+4 of #163)
CI workflow (.github/workflows/pool-domain-manager-build.yaml) mirrors
the marketplace-api / catalyst-api shape:

  - Triggers on push to core/pool-domain-manager/** + workflow_dispatch
  - Runs unit tests (reserved + dynadot — the integration suite needs a
    real Postgres which the workflow does not provide; full integration
    runs in test-bootstrap-api.yaml against an ephemeral CNPG)
  - Builds and pushes ghcr.io/openova-io/openova/pool-domain-manager:<sha>
  - Cosign-signs the image via Sigstore keyless OIDC (id-token: write)
  - Emits an SBOM attestation tied to the image digest
  - Manifest deployment is intentionally NOT in this workflow — PDM
    manifests live in the openova-private repo per the issue body, so
    the Flux Kustomization there picks up the new SHA via a follow-up
    private-repo commit (Phase 6 of #163)

Crossplane composition (platform/crossplane/compositions/xrd-pool-
allocation.yaml + composition-pool-allocation.yaml) wraps PDM as a
declarative Crossplane Resource:

  apiVersion: compose.openova.io/v1alpha1
  kind: XDynadotPoolAllocation
  spec:
    parameters:
      poolDomain:    omani.works
      subdomain:     omantel
      sovereignFQDN: omantel.omani.works
      loadBalancerIP: 1.2.3.4
      createdBy:     crossplane

The Composition uses provider-http (crossplane-contrib/provider-http) to
render the XR into a Reserve → Commit sequence of HTTP calls against
PDM's in-cluster service URL. Per docs/INVIOLABLE-PRINCIPLES.md #3 we use
provider-http rather than bespoke Go to keep the day-2 lifecycle
declarative. Operators who want to pre-allocate a name (e.g. reserve
'omantel.omani.works' for a Sovereign that hasn't been provisioned yet)
commit YAML to Git and Flux+Crossplane converge.

Refs: #163
2026-04-29 06:46:11 +02:00
..
chart fix(charts): drop dependencies block — wrappers carry values overlay only 2026-04-28 12:57:29 +02:00
compositions ci(pdm)+platform(crossplane): build workflow + XDynadotPoolAllocation composition (Phase 3+4 of #163) 2026-04-29 06:46:11 +02:00
blueprint.yaml feat(charts): G2 wrapper Helm charts for 11 bootstrap-kit components + blueprint-release CI 2026-04-28 12:51:06 +02:00
README.md docs(pass-48): crossplane OpenTofu/XRD group drift; PERSONAS clean 2026-04-28 00:10:48 +02:00

Crossplane

Day-2 cloud resource provisioning for Catalyst. Per-Sovereign on the management cluster (see docs/PLATFORM-TECH-STACK.md §3.2) — manages all non-Kubernetes resources for the entire Sovereign (host clusters, VPCs, DNS records, S3 buckets, third-party SaaS).

Crossplane is platform plumbing, never a user-facing surface. Users see "needs a database, pick existing or new" in the Catalyst console; Blueprint authors write Compositions; advanced users (sovereign-admins, OpenOva engineers) contribute Compositions upstream as Blueprints. End users do NOT write Crossplane Compositions in their Application configs. See docs/ARCHITECTURE.md §4 / §7 (the "no fourth surface" rule) and docs/BLUEPRINT-AUTHORING.md §8.

Status: Accepted | Updated: 2026-04-27


Overview

Crossplane provides Kubernetes-native cloud resource provisioning for day-2 operations. Terraform handles initial bootstrap; Crossplane manages ongoing infrastructure.


Architecture

flowchart TB
    subgraph K8s["Kubernetes"]
        subgraph Crossplane
            Controller[Crossplane Controller]
            Provider[Cloud Provider]
        end

        XR[Composite Resources]
        Claim[Claims]
    end

    subgraph Cloud["Cloud Provider"]
        Resources[Cloud Resources]
    end

    Claim --> XR
    XR --> Controller
    Controller --> Provider
    Provider --> Resources

OpenTofu vs Crossplane

Catalyst uses OpenTofu (the open-source Terraform fork) for bootstrap IaC, not Terraform. See docs/PLATFORM-TECH-STACK.md §3.2 and platform/opentofu/.

Aspect OpenTofu Crossplane
Phase Bootstrap (day-0/1) — Phase 0 of Sovereign provisioning, then archived Day-2+ operations
State External state file Kubernetes CRDs
Drift Manual detection Continuous reconciliation
Access CI/CD pipeline (catalyst-provisioner) K8s RBAC
Lifecycle Point-in-time GitOps continuous

Decision: Use OpenTofu for initial cluster bootstrap only (Phase 0). All subsequent infrastructure managed via Crossplane.


Supported Providers

Provider Status Crossplane Provider
Hetzner Cloud Available hcloud
Huawei Cloud Coming huaweicloud
Oracle Cloud Coming oci
AWS Coming aws
GCP Coming gcp
Azure Coming azure

Configuration

Provider Configuration

apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
  name: provider-hcloud
spec:
  package: xpkg.upbound.io/crossplane-contrib/provider-hcloud:v0.4.0
---
apiVersion: hcloud.crossplane.io/v1alpha1
kind: ProviderConfig
metadata:
  name: default
spec:
  credentials:
    source: Secret
    secretRef:
      namespace: crossplane-system
      name: hcloud-credentials
      key: token

Composite Resource Definition

apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
  name: xdatabases.compose.openova.io
spec:
  group: compose.openova.io                      # canonical XRD group per BLUEPRINT-AUTHORING §8
  names:
    kind: XDatabase
    plural: xdatabases
  versions:
    - name: v1alpha1
      served: true
      referenceable: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                size:
                  type: string
                  enum: [small, medium, large]

Composition

apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  name: database.hcloud.compose.openova.io
spec:
  compositeTypeRef:
    apiVersion: compose.openova.io/v1alpha1     # canonical XRD group per BLUEPRINT-AUTHORING §8
    kind: XDatabase
  resources:
    - name: server
      base:
        apiVersion: hcloud.crossplane.io/v1alpha1
        kind: Server
        spec:
          forProvider:
            serverType: cx21
            image: ubuntu-22.04

GitOps Integration

Crossplane resources are managed via Flux:

apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: crossplane
  namespace: flux-system
spec:
  interval: 10m
  sourceRef:
    kind: GitRepository
    name: crossplane
  path: ./deploy/prod
  prune: true

Catalyst Integration

Crossplane Compositions are referenced by Blueprints when an Application requires non-Kubernetes resources (cloud DBs, DNS records, S3 buckets, etc.). End users never see Crossplane directly — they see "needs a database" in the Blueprint's configSchema, rendered as a form in the Catalyst console. Advanced users author Crossplane Compositions and contribute them upstream as Blueprints. See docs/BLUEPRINT-AUTHORING.md §8.


Part of OpenOva