From 76c7ddfc52271fe534730d0a473893ab6f1fef1a Mon Sep 17 00:00:00 2001 From: jochen Date: Sat, 31 Oct 2026 16:38:05 +0100 Subject: [PATCH] Keep main's rule for which haves run inside the node's own composition Review round 1: a have naming no build in a phase but running is also written for a module that runs inside the node's own (waiting for a yes), so taking it out would drop its resources before the yes. The fp the held have now keeps already fixes postgres (issue 1289); a test drives an interrupted backup end to end. Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL --- internal/agent/agent_test.go | 21 +++++++++++++++++++++ internal/selfcompose/selfcompose.go | 6 +----- internal/selfcompose/selfcompose_test.go | 4 ---- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/internal/agent/agent_test.go b/internal/agent/agent_test.go index 58fba89..6ed291d 100644 --- a/internal/agent/agent_test.go +++ b/internal/agent/agent_test.go @@ -591,6 +591,27 @@ func TestADataModuleIsBackedUpFirstAndHeldWhenItFails(t *testing.T) { return h.Phase == string(delivery.PhaseRunning) && h.FP == fp2 && h.Composed == dig2 }) + // The node agent stopped while the backup ran, the module known only from its have: the held have names what runs + // and says it was interrupted (postgres on novox, 2026-10-31, held naming nothing). + ib := newBed(t) + ib.comps.before(ck1, craw1) + ib.comps.before(ck2, craw2) + iwk, iwraw := wantFor(t, "db", fp2, dig2, delivery.BackupAgain) + ib.wants.before(iwk, iwraw) + ib.haves.stored = map[string]Have{"db": {Module: "db", FP: fp1, Composed: dig1, Phase: string(delivery.PhaseRunning)}} + began := make(chan struct{}) + ib.env.Backup = func(ctx context.Context, _ string) Backup { + close(began) + <-ctx.Done() + return Backup{Words: "the backup's ask was cancelled"} + } + stopI := ib.run() + <-began + stopI() + if h := ib.haves.last("db"); h.Phase != string(delivery.PhaseHeld) || h.FP != fp1 || h.Composed != dig1 || h.Detail != BackupInterrupted { + t.Errorf("held after an interrupted backup: %+v", h) + } + // At a start: a backup the node agent's own stop interrupted (postgres on novox, 2026-10-31) is no failure, so it is // backed up again and applied while the want asks a backup; a backup that failed stays held for the operator. for _, row := range []struct { diff --git a/internal/selfcompose/selfcompose.go b/internal/selfcompose/selfcompose.go index f299d55..28d4867 100644 --- a/internal/selfcompose/selfcompose.go +++ b/internal/selfcompose/selfcompose.go @@ -618,11 +618,7 @@ func (c *Composer) takeModuleHave(u agent.Update) bool { // A have that names nothing it runs, failed (the node could not compose a module it never ran: NotComposed), is // no module run inside the node's own composition: taken as one, it was composed from its manifest past its // build (issue 1126, review round 1 of the builds). - // Nor is one in any phase but running that names nothing (held by a backup the node agent's restart - // interrupted: postgres on novox, 2026-10-31, refused for its secret reader). Running with nothing named is - // a module of the node's own (mesh-wireguard). - runsNothing := h.FP == "" && h.Composed == "" && - (h.Phase == delivery.PhaseFailed || h.Build == "" && h.Phase != "" && h.Phase != delivery.PhaseRunning) + runsNothing := h.Phase == delivery.PhaseFailed && h.FP == "" && h.Composed == "" in = h.Phase != delivery.PhaseRemoved && !runsNothing && !strings.HasPrefix(h.FP, delivery.SourcePrefix2) } } diff --git a/internal/selfcompose/selfcompose_test.go b/internal/selfcompose/selfcompose_test.go index febcf76..29a4a39 100644 --- a/internal/selfcompose/selfcompose_test.go +++ b/internal/selfcompose/selfcompose_test.go @@ -1760,10 +1760,6 @@ func TestAHaveSaysWhetherItsModuleRunsInsideTheNodesOwn(t *testing.T) { {"running with no fingerprint (mesh-wireguard)", delivery.Have{Phase: delivery.PhaseRunning, Health: delivery.HealthOK}, true, false}, {"held today", delivery.Have{FP: "build-0123abcd", Composed: "sha256:" + strings.Repeat("c", 64), Phase: delivery.PhaseHeld, Health: delivery.HealthWaits, Detail: "held: a required config value is unset"}, true, true}, - // postgres on novox (2026-10-31): held by a backup the node agent's restart interrupted, its have naming nothing - // it runs; counted in, it was composed from its manifest past its build and refused for its secret reader. - {"held, naming nothing it runs", delivery.Have{Phase: delivery.PhaseHeld, Health: delivery.HealthWaits, - Detail: "the node agent was stopped while the backup ran"}, false, true}, } { t.Run(row.name, func(t *testing.T) { c := New(Env{Node: "n1"})