Stage 4: TerdutAlertSource
CI / test (push) Successful in 1m34s

Covers webhook Secret generation/ownership (DESIGN.md §4.5, §7), the
WebhookSecretLost fail-closed condition, and the kind-change
delete-and-recreate rotation path.

Idempotent-create here is deliberately neither adopt-on-409
(Team/service-account) nor list-and-match-by-name (TerdutDeadmanSwitch):
terdut-server shows the webhook key exactly once, at creation, and never
again, so no server-side lookup could ever recover it after a crash.
Instead the generated webhook Secret itself -- written immediately after
the POST, before status is ever touched -- is this CR's only durable
record that a create already succeeded; found with status.integrationID
still unset on a later reconcile, it's read back directly rather than
POSTing a second, orphaned integration. Found missing with
status.integrationID *set* instead, that's the already-designed
WebhookSecretLost case: fail closed, not self-healed, since the key is
genuinely gone and recreating it would rotate a live webhook URL with no
spec change to explain why.

Renaming (PATCH) never touches the key, so it's applied unconditionally
every reconcile, same as the escalation policy's whole-policy PUT. A
spec.kind change is the one case with no in-place update verb at all:
DELETE the old integration, delete the stale webhook Secret, then run the
same create path fresh -- fires a Warning event since this breaks whatever
still sends to the old URL.

Also: fakeTerdutServer grows POST/PATCH/DELETE .../integrations routes
behind a new handleIntegrationSubPath, split out of handleTeamSubPath to
stay under gocyclo's threshold; three goconst-flagged test literals
("does-not-exist", "unready") and one unparam-flagged test helper
parameter (bootstrapReadyTerdutServer's always-"default" namespace) get
shared/removed now that a fourth same-shaped caller made the repetition
concrete enough for the linter to flag.

DESIGN.md §13 gains one honest gap found while grounding this stage, not
introduced by it: no child CRD specially detects a mid-life teamRef
change; all three always resolve spec.teamRef fresh and trust the
already-stored server-side id remains valid there.

make fmt lint test build all clean; internal/controller envtest coverage
holds at 71.6%.
This commit is contained in:
Niklas Ye
2026-10-01 14:13:19 +02:00
parent b0d50e305a
commit 048f4448c4
23 changed files with 1395 additions and 9 deletions
+17
View File
@@ -153,6 +153,23 @@ New commits build forward over the old ones; no git history rewrite.
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.
- Idempotent-create here is neither adopt-on-409 (Team/service-account) nor
list-and-match-by-name (`TerdutDeadmanSwitch`): terdut-server shows the
webhook key exactly once, at creation, and never again, so no server-side
lookup could ever recover it after a crash. The generated webhook Secret
itself — written immediately after the POST, before `status` is ever
touched — is this CR's only durable record that a create already
succeeded; found on a later reconcile with `status.integrationID` still
unset, it's read back directly rather than POSTing again. Found missing
with `status.integrationID` *set*, that's the already-designed
`WebhookSecretLost` fail-closed case instead.
- **Done, 2026-10-01**: `envtest` coverage for the happy path, rename
(PATCH, no key rotation), a kind change (delete-and-recreate, new id and
key), crash recovery between POST and the Secret write, `WebhookSecretLost`,
`TeamRefNotFound`/`WaitingForTeam`, and deletion. `make fmt lint test
build` all clean; `internal/controller` envtest coverage holds at 71.6%.
No `kind` e2e pass for this stage, same reasoning as Stage 3 (reuses
Stage 1's already-proven real-cluster mechanics unchanged).
## Stage 5 — Installer chart + real release