Skip to content

New Configuration: cluster-registration (wrap clusterbook-cluster-gen) — closes the k8s/cluster gap #30

Description

@patrick-hermann-sva

Motivation

We manage ClusterbookCluster CRs (clusterbook.stuttgart-things.com/v1alpha1) as raw YAML committed under ArgoCD app paths, e.g. clusters/labul/vsphere/platform-sthings/argocd/crossplane-dev1/cluster.yaml. That CR is an operator-backed abstraction handling IP allocation, DNS, ArgoCD cluster registration, kubeconfig secret wiring, and platform bootstrap (driven by the labels: toggle matrix consumed by ApplicationSets).

Our Configurations today cover VM provisioning (machinery/*) and k8s primitives (namespace, volume-claim, cloud-config), but nothing represents a cluster as a composable unit. A cluster-registration Configuration is the bridge between "we provisioned a node/VM" and "the platform is registered and bootstrapping in GitOps."

The hard part is already done: kcl/kubernetes/clusterbook-cluster-gen v0.3.0 is a mature module (schema validation for kind vs default clusterType, mutually-exclusive secret refs) that emits exactly this CR and is published to OCI. The wrapping pattern is also proven by cicd/ansible-run: namespaced XR → function-kcl (source: oci://…kcl-module) → provider-kubernetes Object → a derive-status KCL step that mirrors live CR status back onto the XR.

Value-add over raw YAML in argocd

  1. Typed XR API. The flat labels: toggles (cicd-platform/crossplane: 'true', storage-platform/openebs, …) become a structured spec.platform.{cicd,network,storage,security} of booleans — defaultable/validatable in the XRD.
  2. Status surfacing. XR reflects the operator's reconcile result (allocated IP / FQDN / registered), like ansible-run surfaces PipelineRun status.
  3. Composability. Becomes a building block for cluster provisioning + registration + bootstrap as one XR (see roadmap below).

Design decision to confirm

This file is currently pure GitOps (ArgoCD → ClusterbookCluster). A Configuration changes the chain to ArgoCD → XR → Crossplane → ClusterbookCluster, inserting Crossplane + provider-kubernetes as a dependency of cluster registration. On the management cluster this is fine (same in-cluster targeting as ansible-run). If we only ever want param-templating, an ApplicationSet/plugin over the existing KCL module is lighter — so this Configuration only pays off if we want cluster registration to be a composable Crossplane primitive.

Proposed shape (step 1, standalone)

  • New Configuration k8s/cluster-registration (or a new cluster/ category) — XRD apiextensions.crossplane.io/v2, scope: Namespaced.
  • Composition: function-kclclusterbook-cluster-gen OCI → provider-kubernetes Object (kubernetes.m.crossplane.io/v1alpha1, managementPolicies: [Observe, Create], no Delete) → derive-statusfunction-auto-ready.
  • Shared inputs (Vault address, network pool) default from an EnvironmentConfig; XR spec holds only the per-cluster delta.
  • xr-min / xr / xr-max examples, with crossplane-dev1 mirrored 1:1 as the realistic xr case.

Roadmap — compose with ESO + Rancher (later)

Mirrors how machinery grew (standalone configs → vm-provision router). Don't build the mega-Configuration first.

Cluster XR (namespaced)
  ├─ 1. PROVISION   OpenTofu Workspace (rancher2 TF)        → kube_config → connection Secret
  ├─ 2. SECRET      ESO ClusterSecretStore + ExternalSecret (Vault)
  └─ 3. REGISTER    function-kcl → clusterbook-cluster-gen  → provider-kubernetes Object → ClusterbookCluster

Building blocks that already exist:

  • ESO + Vault: kcl/crossplane/xplane-vault-config (eso-secret-store / eso-external-secret / eso-push-secret); machinery/virtual-machine already wires ClusterSecretStore + ExternalSecret.
  • OpenTofu route: machinery provisions via opentofu.m.upbound.io/v1beta1 Workspace — Rancher would follow the same pattern with the rancher2 TF provider (kube_config output → connection secret). Avoids an immature crossplane provider-rancher.
  • Rancher is the only genuinely new piece.

The module's existing kubeconfigSecretRef vs existingSecretRef split already models the two kubeconfig provenances with no module change:

  • Rancher-provisioned → kubeconfigSecretRef (managed mode).
  • Imported/external (kubeconfig in Vault via ESO) → existingSecretRef (enrich mode).

Build order

  1. cluster-registration (this issue) — standalone, closes the immediate gap.
  2. rancher-cluster (opentofu/rancher2) — standalone provisioning, the new piece.
  3. cluster router XR — composes registration + rancher + ESO, like vm-provision.

Gotchas to bank now

  • Ordering isn't sequential. Crossplane reconciles composed resources concurrently; registration must tolerate the kubeconfig Secret being absent initially (operator retries). Gate readiness, don't assume sequence.
  • kubernetes.m.crossplane.io/v1alpha1 Object has no deletionPolicy — control delete behavior via managementPolicies only.

Acceptance criteria (step 1)

  • k8s/cluster-registration Configuration renders locally (task render) for all three example XRs.
  • xr.yaml reproduces the existing crossplane-dev1 ClusterbookCluster.
  • Platform toggles modelled as a typed struct in the XRD.
  • README documents the cluster precondition (ClusterProviderConfig via spec.providerConfigRef) and the GitOps-vs-Crossplane ownership decision.
  • Configurations table in repo README.md updated.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions