ROADMAP.md: merge Stage 1 + old Stage 5, renumber
CI / test (push) Successful in 1m33s

Follows DESIGN.md's redesign (previous commit): with no hand-deployed
server to prove the simpler CRDs against, there's no reason left to defer
TerdutServer's Deployment/Service/database management behind a separate
later stage. Stage 1 now covers TerdutServer's full lifecycle --
Deployment, Service, both Postgres paths from §8 at once (bring-your-own
DSN and Zalando, per the user's call, not sequenced), bootstrap,
credentials -- built together, since bootstrap only has something to
bootstrap once the Deployment exists.

Old Stage 5 (TerdutServer absorbs Deployment/Service/bootstrap) is gone,
folded into Stage 1. Old Stage 6 (installer chart + release) renumbers to
Stage 5. Stages 2-4 (TerdutTeam, EscalationRule+DeadmanSwitch,
AlertSource) are unchanged in content, renumbering only where old Stage 5
disappears from ahead of them.

Explicitly supersedes the Stage 1 shipped before this redesign (commit
1be7cf2): that TerdutServerSpec/Status/controller/tests implemented the
now-removed bring-your-own path and get replaced wholesale when Stage 1
is actually implemented next, not extended.
This commit is contained in:
Niklas Ye
2026-10-01 08:50:24 +02:00
parent f1fd64a567
commit fc68ee7256
+49 -51
View File
@@ -7,20 +7,18 @@ 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.
**The operator creates and owns every `TerdutServer` it manages — it never
adopts one deployed independently, by hand or by `charts/terdut-server`**
(`DESIGN.md` §1). An earlier version of this roadmap staged `TerdutServer`'s
Deployment/Service/bootstrap takeover separately (old Stage 5), behind a
hand-deployed server the simpler CRDs could be proven against first.
That staging existed only because a credential-less operator couldn't
`/api/bootstrap` its way into a server something else had already
bootstrapped (`DESIGN.md` §6's original gap). With no server to adopt at
all, that split has nothing left to justify it: `TerdutServer` now builds
its full lifecycle — Deployment, Service, database wiring, bootstrap,
credentials — in one stage, Stage 1, since bootstrap only has something to
bootstrap once the Deployment exists.
## Stage 0 — Scaffolding & CI
@@ -32,13 +30,13 @@ land in Stage 5, not before.
into `lint`, all fetched on demand into `bin/` — no separate install
step needed beyond what `make test`/`make lint` already do.
- `.release.conf` deliberately **not** added yet: it names a `HELM_CHART`
this repo doesn't have until Stage 6. Adding it now would either be a
this repo doesn't have until Stage 5. Adding it now would either be a
stub that lies about what's releasable or dead config nobody can run —
it lands in Stage 6, alongside the chart it describes.
it lands in Stage 5, alongside the chart it describes.
- Gitea Actions CI calling `fmt lint test`, mirroring terdut-server's
`ci.yaml` convention (its `CLAUDE.md`: "a green gate here and a green
pipeline are the same code, not two descriptions of it") minus the
`chart`/`security` jobs, which need a chart (Stage 6) and real controller
`chart`/`security` jobs, which need a chart (Stage 5) and real controller
code (Stage 1+) respectively to have anything to check.
- Drop kubebuilder's default `.github/workflows/*` scaffold — this org
runs on Gitea, not GitHub; `.gitea/workflows/ci.yaml` is the only CI this
@@ -50,28 +48,40 @@ land in Stage 5, not before.
**Done when:** CI is green on an otherwise-empty scaffold.
## Stage 1 — `TerdutServer`, bootstrap/credentials only
## Stage 1 — `TerdutServer`, full lifecycle
- Deploy terdut-server via its existing chart into a fresh, disposable
namespace — manual, out-of-band, nothing operator-managed yet. This chart
run bootstraps the server itself, before the `TerdutServer` CR exists.
- `TerdutServer` CRD narrowed to `spec.endpoint` + `spec.credentialsSecretRef`
+ `spec.allowedTeams`; the rest of §4.1's spec (`image`, `replicas`,
`networking`, `database`) waits for Stage 5.
- Controller implements §6's bring-your-own path only:
`spec.credentialsSecretRef` set and the Secret exists → adopt it,
`status.credentialsSecretRef` mirrors it, `Bootstrapped`/`Ready: True`.
Unset (or not found yet) → `Ready: False`, requeue, no API call made.
**Self-registration (the `/api/bootstrap`-race fallback in §6 point 1) is
explicitly deferred to Stage 5**, not implemented here: Stage 1's own
setup never exercises it (the chart always bootstraps first, per above),
and this narrowed spec has no username/email fields for it to call
`/api/bootstrap` with in the first place. Adding it later is additive,
not a breaking change to this stage's shape.
- 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.
Supersedes the Stage 1 shipped before this redesign (commit `1be7cf2`)
outright — that `TerdutServerSpec`/`Status`/controller/tests implemented the
now-removed bring-your-own path and get replaced wholesale, not extended.
New commits build forward over the old ones; no git history rewrite.
- Full §4.1 spec: `image`, `replicas`, `networking`, `database`, `sweeper`,
`deadman`, `notify`, `oidc`, `passwordLogin`, `allowedTeams`, all together
— no narrowing, since bootstrap needs the Deployment it's narrowed away
from in the version this replaces.
- Controller manages the Deployment + Service, both Postgres paths from §8
at once (bring-your-own DSN *and* the Zalando `postgres-operator`
`postgresClusterRef` integration — not sequenced, per the user's call),
and bootstrap/credentials per §6's self-registration flow: `/api/bootstrap`
once the Deployment has a ready replica, checkpoint the admin key, mint
the instance-scoped service account, generated credentials Secret in the
operator's own namespace, `status.credentialsSecretRef`. Finalizer cleans
up that Secret and the server-side rows on delete (now actually needed,
unlike the superseded version — this stage creates things server-side).
- RBAC: read-only watch on `postgresql.acid.zalan.do`, degrading gracefully
if that CRD isn't installed (§8, §9).
- `envtest` covering Deployment/Service reconciliation and both database
paths — the Zalando path needs that CRD's schema vendored into the test
environment (there's no real `postgres-operator` controller in `envtest`,
only the CRD shape to create fixture objects against) — plus the
self-registration flow against an `httptest.Server` fake of
`/api/bootstrap` and `/api/service-accounts` (§11), this time exercising
the real flow rather than an adopt-only stand-in.
- A real `kind` end-to-end pass (bring-your-own DSN is simplest there) to
prove a `TerdutServer` CR actually produces a running, bootstrapped
terdut-server pod. The Zalando path can additionally be validated for
real against the org's own cluster later, where `postgres-operator`
already runs, rather than only in a disposable `kind` stand-in.
## Stage 2 — `TerdutTeam`
@@ -101,19 +111,7 @@ land in Stage 5, not before.
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
## Stage 5 — 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