Quick Start (ControlPlane): C5C3 + K-ORC on Kind
The shortest path from git clone to an authenticated Keystone API call driven by a single c5c3 ControlPlane CR. Where the Quick Start stops at a hand-applied Keystone CR, here one ControlPlane CR makes the c5c3-operator provision the MariaDB, Memcached, and Keystone children, mint the admin application credential through K-ORC, mirror it to OpenBao, and register the identity catalog — all reconciled to an aggregate Ready.
Prerequisites
Same toolchain as the Quick Start, plus an internet connection (K-ORC is cloned from GitHub at a pinned tag). Docker Desktop needs ample CPU/memory — the ControlPlane provisions a production-shaped (Galera) MariaDB, heavier than the single-replica database the per-service path uses.
make install-test-deps
export PATH="${HOME}/.local/bin:${PATH}"Step 1 — Clone
git clone https://github.com/c5c3/forge.git
cd forgeStep 2 — Cluster + ControlPlane stack
KIND_HOST_PORT=8443 WITH_CONTROLPLANE=true make deploy-infraWITH_CONTROLPLANE=true brings up the shared infrastructure and then the ControlPlane operator stack (keystone-operator, K-ORC, c5c3-operator) from the published charts — but not the ControlPlane CR itself; you create and apply that in Step 3. In this mode the ControlPlane provisions its own MariaDB/Memcached (managed mode), so deploy-infra does not create the shared ones. KIND_HOST_PORT=8443 maps the Gateway to a non-privileged host port for macOS; on Linux with rootful Docker drop the override and use port 443. Expect 5–10 minutes.
Step 3 — Create the ControlPlane CR
Apply a ControlPlane CR — the c5c3-operator reconciles it into the whole stack. You only supply openStackRelease and the services.keystone block: the defaulting webhook fills the infrastructure and admin-credential references with their well-known names — openstack-db (managed MariaDB), openstack-memcached (managed Memcached), keystone-db (DB-credential placeholder — in managed mode the operator projects a per-ControlPlane {name}-keystone-db-credentials Secret and points the Keystone CR at it instead), keystone-admin / password (admin-password placeholder — in managed mode the operator projects a per-ControlPlane {name}-keystone-admin-credentials Secret and points the Keystone CR at it instead), k-orc-clouds-yaml with cloud entry admin (K-ORC clouds.yaml) — which match the Secrets and clusters the infrastructure layer (Step 2) seeds. The c5c3-operator seeds the K-ORC bootstrap clouds.yaml per-CR, deriving the in-cluster Keystone auth URL from the CR's own name, so the CR name is no longer pinned by a pre-seeded clouds.yaml; to use a different name, pass CONTROLPLANE_NAME=foo to Step 2 — it renames the bundled CR and seeds the matching admin password. The defaulting only fills the names/references; the operator still consumes the pre-seeded Secret content (DB credentials, admin password) and materialises the bootstrap clouds.yaml itself, so it does not invent credentials.
# controlplane.yaml
apiVersion: c5c3.io/v1alpha1
kind: ControlPlane
metadata:
name: controlplane
namespace: openstack
spec:
openStackRelease: "2025.2"
services:
keystone:
replicas: 1
# Drop publicEndpoint on the default port 443 — the operator then derives
# https://keystone.127-0-0-1.nip.io/v3 from the gateway hostname.
publicEndpoint: https://keystone.127-0-0-1.nip.io:8443/v3
gateway:
parentRef:
name: openstack-gw
hostname: keystone.127-0-0-1.nip.io
path: /kubectl apply -f controlplane.yamlEquivalent fully-expanded form (what the webhook defaults to)
# controlplane.yaml
apiVersion: c5c3.io/v1alpha1
kind: ControlPlane
metadata:
name: controlplane
namespace: openstack
spec:
openStackRelease: "2025.2"
region: RegionOne
infrastructure:
database:
clusterRef:
name: openstack-db # MariaDB the operator provisions (managed mode)
database: keystone
secretRef:
name: keystone-db # placeholder default — the operator replaces it
# with {name}-keystone-db-credentials (managed mode)
cache:
clusterRef:
name: openstack-memcached
backend: dogpile.cache.pymemcache
services:
keystone:
replicas: 1
publicEndpoint: https://keystone.127-0-0-1.nip.io:8443/v3
gateway:
parentRef:
name: openstack-gw
hostname: keystone.127-0-0-1.nip.io
path: /
korc:
adminCredential:
cloudCredentialsRef:
cloudName: admin # entry in the operator-materialised k-orc-clouds-yaml Secret
secretName: k-orc-clouds-yaml
passwordSecretRef:
name: keystone-admin # spec-level/brownfield default — in managed mode the
# operator projects {name}-keystone-admin-credentials
# and points the Keystone child at it instead
key: password
applicationCredential:
rotation:
mode: PasswordDrivenStep 4 — Watch the chain reconcile
The aggregate Ready flips to True once all five sub-conditions are met, in dependency order:
InfrastructureReady → KeystoneReady → KORCReady → AdminCredentialReady → CatalogReadykubectl get controlplane controlplane -n openstack \
-o jsonpath='{range .status.conditions[*]}{.type}={.status} ({.reason}){"\n"}{end}'Wait for the aggregate condition:
kubectl wait controlplane/controlplane -n openstack \
--for=condition=Ready --timeout=15mStep 5 — Verify
The ControlPlane exposes the projected Keystone through the shared Envoy Gateway at https://keystone.127-0-0-1.nip.io:8443/v3 — the same path as the per-service Quick Start, no port-forward.
curl -k https://keystone.127-0-0-1.nip.io:8443/v3Then issue a token with the admin password:
export OS_AUTH_URL=https://keystone.127-0-0-1.nip.io:8443/v3
export OS_USERNAME=admin
export OS_PASSWORD=$(kubectl get secret controlplane-keystone-admin-credentials -n openstack -o jsonpath='{.data.password}' | base64 -d)
export OS_PROJECT_NAME=admin
export OS_USER_DOMAIN_NAME=Default
export OS_PROJECT_DOMAIN_NAME=Default
openstack --insecure token issueThe admin password is read from the operator-owned per-ControlPlane Secret
controlplane-keystone-admin-credentials(named{ControlPlane name}-keystone-admin-credentials). In managed mode the c5c3-operator always projects this Secret, so the command holds for any identity — if you setCONTROLPLANE_NAME=fooin Step 2, readfoo-keystone-admin-credentialsinstead.
With the default
KIND_HOST_PORT=443usehttps://keystone.127-0-0-1.nip.io/v3and drop thepublicEndpointline from the CR in Step 3.
Teardown
make teardown-infraRelated references
- ControlPlane CRD API Reference — every
spec.*field, the webhooks, and the status conditions. - ControlPlane Reconciler — the sub-reconciler ordering and gating semantics.
- Quick Start — the compact per-service Keystone path.