From ee39b8e66812b5d142a4ae9396be0565bf29b5e2 Mon Sep 17 00:00:00 2001 From: Niklas Ye Date: Wed, 30 Sep 2026 19:16:21 +0200 Subject: [PATCH] Add build roadmap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stages the operator's implementation: TerdutServer stays bootstrap/credentials-only (no Deployment/Service takeover) until Stage 5, so every earlier stage targets a hand-deployed terdut-server in a disposable dev namespace instead of forcing the chart-migration decision (§10) up front. --- ROADMAP.md | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 ROADMAP.md diff --git a/ROADMAP.md b/ROADMAP.md new file mode 100644 index 0000000..1005dd7 --- /dev/null +++ b/ROADMAP.md @@ -0,0 +1,120 @@ +# terdut-operator build roadmap + +This is the staging plan for implementing the operator against `DESIGN.md`'s +settled decisions. It exists for the same reason `DESIGN.md` and +`SERVICE-ACCOUNTS.md` do: so each stage starts from an agreed sequencing +instead of re-litigating "what do we build first" mid-PR. + +## Sequencing call this roadmap makes + +`TerdutServer` does **not** take over Deployment/Service/bootstrap (§4.1, +§10) until Stage 5. Every earlier stage targets a terdut-server deployed by +hand, via the existing `charts/terdut-server` chart, into a namespace +dedicated to this work — disposable, no risk to anything else, and +explicitly not something this roadmap plans to migrate in place. This is a +green-field build: there's no existing install this operator is on the hook +to preserve, so the hand-deployed instance from Stage 1 is meant to be +retired once Stage 5's operator-managed instance is proven, not migrated. + +`TerdutTeam.spec.serverRef` still needs a real `TerdutServer` object to +resolve against from Stage 1 onward, though (§4.2, §6) — so `TerdutServer` +shows up early, just with a deliberately narrow first spec covering only +what §6's bootstrap/credential flow needs. `image`/`replicas`/`database` +land in Stage 5, not before. + +## Stage 0 — Scaffolding & CI + +- `go.mod` (`git.ryuvia.com/niklas/terdut-operator`) + Kubebuilder scaffold + (`cmd/main.go`, `config/`, `Makefile`, `PROJECT`), matching terdut-server's + Go toolchain and house style (§3). +- `.release.conf` + a release-vars Makefile target, same shape as + terdut-server's — this repo ships a Helm chart (§1, §10), so it follows + the wrapper-chart release path, not terdut-tui's binary-only one. +- Gitea Actions CI calling `fmt lint test helm-lint`, mirroring + terdut-server's `ci.yaml`/`release.yaml` convention (its `CLAUDE.md`: "a + green gate here and a green pipeline are the same code, not two + descriptions of it"). +- Install `setup-envtest`, wire it into `make test` so the `envtest` suite + (§11) runs without needing the full `kubebuilder` CLI at test time. +- Housekeeping: drop the stray `.DESIGN.md.swp` (leftover vim swapfile, + shouldn't be committed); correct `DESIGN.md` §6/§13's "v1-blocking, not + v1-shippable" language — the service-account feature it was blocking on + has since shipped in terdut-server. + +**Done when:** CI is green on an otherwise-empty scaffold. + +## Stage 1 — `TerdutServer`, bootstrap/credentials only + +- Deploy terdut-server via its existing chart into a fresh, disposable + namespace — manual, out-of-band, nothing operator-managed yet. +- `TerdutServer` CRD narrowed to `spec.endpoint` + `spec.allowedTeams`; the + rest of §4.1's spec (`image`, `replicas`, `networking`, `database`) waits + for Stage 5. +- Controller implements §6 exactly: call `/api/bootstrap` only on a + genuinely empty install; otherwise `GET /api/service-accounts?name=terdut-operator` + and `POST` one if it doesn't exist. Writes the instance-scoped key to a + generated Secret in the operator's own namespace; sets + `status.credentialsSecretRef` and the `Bootstrapped`/`Ready` conditions. +- No Deployment/Service reconciliation at all in this stage. +- First `envtest` suite (controller-runtime's fake API server) plus an + `httptest.Server` fake of terdut-server's bootstrap/service-account + endpoints, per §11. + +## Stage 2 — `TerdutTeam` + +- §4.2: `serverRef` resolution, real update-in-place (POST create / PUT + rename / PUT oidc-groups), team-scoped service-account minting once + `Ready` (§6 point 3), finalizer that DELETEs the team server-side and its + credential Secret. +- First place the "every child resolves its own `teamRef` → + `TerdutTeam.status`, never chains up to `TerdutServer`" pattern (§5) gets + proven end to end. + +## Stage 3 — `TerdutEscalationRule` + `TerdutDeadmanSwitch` + +- Built together: both stay same-namespace-as-their-`TerdutTeam` (§1), so + neither exercises cross-namespace complexity, but together they cover the + two different reconciliation shapes §5's table calls out — whole-policy + PUT-on-drift for the escalation policy, delete-and-recreate (no PUT + available) for the dead man's switch — against the same shared + create/finalizer/resync scaffolding Stage 2 already built. + +## Stage 4 — `TerdutAlertSource` + +- Last of the children on purpose: it has the subtlest failure mode of the + four. Covers webhook Secret generation/ownership (§4.5, §7), the + `WebhookSecretLost` fail-closed condition + `Warning` event (§5, added + 2026-09-30), and the kind-change delete-and-recreate rotation path — all + easier to get right with the other three controllers' patterns already + in place to build on. + +## Stage 5 — `TerdutServer` absorbs Deployment/Service/bootstrap (§10, executed) + +- Extend `TerdutServer` to the full §4.1 spec and stand up a *second*, + operator-managed terdut-server in the same dev namespace — not an + in-place takeover of Stage 1's hand-deployed instance. Once this is + solid, that hand-deployed instance is simply retired. +- Postgres integration (§8): bring-your-own DSN path first (no external CRD + dependency); Zalando `postgres-operator` path as a follow-up — split into + its own stage if it turns out bigger than expected once started. +- This is where §10's "the chart becomes an installer chart" recommendation + actually gets executed, rather than just recommended. + +## Stage 6 — Installer chart + real release + +- Package CRDs + the operator's own Deployment/RBAC into the installer + chart §10 describes; wire `.release.conf`/release-vars the same way + terdut-server does; run it through the `release` skill for a real first + cut. +- Full `kind` end-to-end test per §11: create `TerdutServer` → `TerdutTeam` + → one of each child kind → verify via terdut-server's own API that each + object exists with the right shape → delete the CR → verify the + server-side object is gone. + +## Deferred (§13, unchanged by this roadmap) + +Cross-namespace `allowedTeams` exercised against a real second namespace, +CloudNativePG support, narrower-than-namespace Secret RBAC for the webhook +Secret, gitops-managed team membership, automatic Deployment restart on +upstream Postgres credential rotation, admission webhooks/CEL-only +validation limits, OLM packaging.