Add build roadmap
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.
This commit is contained in:
+120
@@ -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.
|
||||
Reference in New Issue
Block a user