From 9da14440e8ce770aa9e303ced44e257425ace06d Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 3 Nov 2026 17:26:30 +0100 Subject: [PATCH] =?UTF-8?q?The=20assigner=20waits=20only=20on=20the=20keys?= =?UTF-8?q?=20of=20provisions=20the=20newest=20manifest=20still=20requires?= =?UTF-8?q?:=20an=20older=20edge's=20asked=20or=20retiring=20key=20holds?= =?UTF-8?q?=20nothing=20(ADR=200603=20=C2=A79)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mesh-delivery-assigner/internal/assign/wants.go | 8 +++++++- .../internal/assign/wants_test.go | 13 ++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/modules/mesh-delivery-assigner/internal/assign/wants.go b/modules/mesh-delivery-assigner/internal/assign/wants.go index ab020fca..a7f19d62 100644 --- a/modules/mesh-delivery-assigner/internal/assign/wants.go +++ b/modules/mesh-delivery-assigner/internal/assign/wants.go @@ -153,8 +153,14 @@ func (m Module) provisionWaiting(node string) (string, bool) { return fmt.Sprintf("provision %s has no key yet for %s on %s", missing[0], m.Name, node), true } keyed := m.Provisions(node) + // Only a provision the newest manifest still requires is waited on (ADR 0603 §9): a key of an older revision's edge, + // asked or retiring for the operator's answer (ADR 0570 §5), holds nothing. + needed := m.Manifest.Requires + if m.Answering != nil { + needed = catalogue.Awaited(m.Manifest, m.Answering) + } for _, key := range slices.Sorted(maps.Keys(keyed)) { - if p := keyed[key]; p.State != provisions.Provided { + if p := keyed[key]; p.State != provisions.Provided && slices.Contains(needed, p.Provision) { words := fmt.Sprintf("the provision %s of %s on %s is %s, not provided", key, m.Name, node, p.State) if p.Why != "" { words += ": " + p.Why diff --git a/modules/mesh-delivery-assigner/internal/assign/wants_test.go b/modules/mesh-delivery-assigner/internal/assign/wants_test.go index ba188a94..00c91352 100644 --- a/modules/mesh-delivery-assigner/internal/assign/wants_test.go +++ b/modules/mesh-delivery-assigner/internal/assign/wants_test.go @@ -108,14 +108,17 @@ func TestEachRuleHoldsTheWantBack(t *testing.T) { // asked for and requires oidc instead; the seat's keys follow the newest manifest, so the older edge is no wait: a provided // oidc moves the want, an asked one holds it and names oidc and never database. for _, c := range []struct { - name string - state string - moves bool - }{{"the replaced edge provided", provisions.Provided, true}, {"the replaced edge asked", provisions.Asked, false}} { + name string + state, older string + moves bool + }{{"the replaced edge provided, the older edge retiring", provisions.Provided, provisions.Retiring, true}, + {"the replaced edge provided, the older edge asked", provisions.Provided, provisions.Asked, true}, + {"the replaced edge asked, the older edge retiring", provisions.Asked, provisions.Retiring, false}} { m, n := ready() m.Manifest.Requires = []string{"oidc"} m.Provisions = func(node string) map[string]provisions.Provision { - return map[string]provisions.Provision{node + ".mail.oidc": {Provision: "oidc", State: c.state}} + return map[string]provisions.Provision{node + ".mail.oidc": {Provision: "oidc", State: c.state}, + node + ".mail.database": {Provision: "database", State: c.older, Why: "the operator's answer"}} } out := Wants(m, n, wantedOld(), nil) if got := out.Wants["g14"].FP == fNew; got != c.moves || (!c.moves && (!strings.Contains(out.Rules["g14"].Words, "oidc") || strings.Contains(out.Rules["g14"].Words, "database"))) {