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 ## Sequencing call this roadmap makes
`TerdutServer` does **not** take over Deployment/Service/bootstrap (§4.1, **The operator creates and owns every `TerdutServer` it manages — it never
§10) until Stage 5. Every earlier stage targets a terdut-server deployed by adopts one deployed independently, by hand or by `charts/terdut-server`**
hand, via the existing `charts/terdut-server` chart, into a namespace (`DESIGN.md` §1). An earlier version of this roadmap staged `TerdutServer`'s
dedicated to this work — disposable, no risk to anything else, and Deployment/Service/bootstrap takeover separately (old Stage 5), behind a
explicitly not something this roadmap plans to migrate in place. This is a hand-deployed server the simpler CRDs could be proven against first.
green-field build: there's no existing install this operator is on the hook That staging existed only because a credential-less operator couldn't
to preserve, so the hand-deployed instance from Stage 1 is meant to be `/api/bootstrap` its way into a server something else had already
retired once Stage 5's operator-managed instance is proven, not migrated. 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
`TerdutTeam.spec.serverRef` still needs a real `TerdutServer` object to its full lifecycle — Deployment, Service, database wiring, bootstrap,
resolve against from Stage 1 onward, though (§4.2, §6) — so `TerdutServer` credentials — in one stage, Stage 1, since bootstrap only has something to
shows up early, just with a deliberately narrow first spec covering only bootstrap once the Deployment exists.
what §6's bootstrap/credential flow needs. `image`/`replicas`/`database`
land in Stage 5, not before.
## Stage 0 — Scaffolding & CI ## 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 into `lint`, all fetched on demand into `bin/` — no separate install
step needed beyond what `make test`/`make lint` already do. step needed beyond what `make test`/`make lint` already do.
- `.release.conf` deliberately **not** added yet: it names a `HELM_CHART` - `.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 — 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 - Gitea Actions CI calling `fmt lint test`, mirroring terdut-server's
`ci.yaml` convention (its `CLAUDE.md`: "a green gate here and a green `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 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. code (Stage 1+) respectively to have anything to check.
- Drop kubebuilder's default `.github/workflows/*` scaffold — this org - Drop kubebuilder's default `.github/workflows/*` scaffold — this org
runs on Gitea, not GitHub; `.gitea/workflows/ci.yaml` is the only CI this 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. **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 Supersedes the Stage 1 shipped before this redesign (commit `1be7cf2`)
namespace — manual, out-of-band, nothing operator-managed yet. This chart outright — that `TerdutServerSpec`/`Status`/controller/tests implemented the
run bootstraps the server itself, before the `TerdutServer` CR exists. now-removed bring-your-own path and get replaced wholesale, not extended.
- `TerdutServer` CRD narrowed to `spec.endpoint` + `spec.credentialsSecretRef` New commits build forward over the old ones; no git history rewrite.
+ `spec.allowedTeams`; the rest of §4.1's spec (`image`, `replicas`,
`networking`, `database`) waits for Stage 5. - Full §4.1 spec: `image`, `replicas`, `networking`, `database`, `sweeper`,
- Controller implements §6's bring-your-own path only: `deadman`, `notify`, `oidc`, `passwordLogin`, `allowedTeams`, all together
`spec.credentialsSecretRef` set and the Secret exists → adopt it, — no narrowing, since bootstrap needs the Deployment it's narrowed away
`status.credentialsSecretRef` mirrors it, `Bootstrapped`/`Ready: True`. from in the version this replaces.
Unset (or not found yet) → `Ready: False`, requeue, no API call made. - Controller manages the Deployment + Service, both Postgres paths from §8
**Self-registration (the `/api/bootstrap`-race fallback in §6 point 1) is at once (bring-your-own DSN *and* the Zalando `postgres-operator`
explicitly deferred to Stage 5**, not implemented here: Stage 1's own `postgresClusterRef` integration — not sequenced, per the user's call),
setup never exercises it (the chart always bootstraps first, per above), and bootstrap/credentials per §6's self-registration flow: `/api/bootstrap`
and this narrowed spec has no username/email fields for it to call once the Deployment has a ready replica, checkpoint the admin key, mint
`/api/bootstrap` with in the first place. Adding it later is additive, the instance-scoped service account, generated credentials Secret in the
not a breaking change to this stage's shape. operator's own namespace, `status.credentialsSecretRef`. Finalizer cleans
- No Deployment/Service reconciliation at all in this stage. up that Secret and the server-side rows on delete (now actually needed,
- First `envtest` suite (controller-runtime's fake API server) plus an unlike the superseded version — this stage creates things server-side).
`httptest.Server` fake of terdut-server's bootstrap/service-account - RBAC: read-only watch on `postgresql.acid.zalan.do`, degrading gracefully
endpoints, per §11. 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` ## Stage 2 — `TerdutTeam`
@@ -101,19 +111,7 @@ land in Stage 5, not before.
easier to get right with the other three controllers' patterns already easier to get right with the other three controllers' patterns already
in place to build on. in place to build on.
## Stage 5 — `TerdutServer` absorbs Deployment/Service/bootstrap (§10, executed) ## Stage 5 — Installer chart + real release
- 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 - Package CRDs + the operator's own Deployment/RBAC into the installer
chart §10 describes; wire `.release.conf`/release-vars the same way chart §10 describes; wire `.release.conf`/release-vars the same way