diff --git a/modules/mesh-grants/grants_test.go b/modules/mesh-grants/grants_test.go index 01c04158..117f1ee0 100644 --- a/modules/mesh-grants/grants_test.go +++ b/modules/mesh-grants/grants_test.go @@ -4064,6 +4064,17 @@ func TestPassComposesMembershipFromTheHaveBuild(t *testing.T) { t.Errorf("b.mod-y's membership lacks the running build's read: %s", got) } }) + t.Run("a pass that cannot read the build a have names keeps the membership last published", func(t *testing.T) { + b := fixture() + runs(b) + part := partOn(b) + part.Pass() + b.refused[grants.ReadBuilds] = true + part.Pass() + if got := string(b.own[grants.StateMemberships]["b.mod-y"]); !strings.Contains(got, "mod-x.old") { + t.Errorf("b.mod-y's membership lost the running build's read when its build could not be read: %s", got) + } + }) t.Run("a build that cannot be read is a failed read", func(t *testing.T) { b := fixture() runs(b) @@ -4072,22 +4083,4 @@ func TestPassComposesMembershipFromTheHaveBuild(t *testing.T) { t.Errorf("a build read refused is not a failed read: %v", f) } }) - t.Run("the seats held are the have build's claims, then the want's, then the trunk's", func(t *testing.T) { - claims := catalogue.Manifest{Module: "m", Version: "1", Claims: []catalogue.Claim{{Name: "mesh-catalog", Scope: catalogue.ScopeMesh}}} - bare := catalogue.Manifest{Module: "m", Version: "1"} - for name, row := range map[string]struct { - had, running map[string]catalogue.Manifest - held bool - }{ - "trunk alone": {nil, nil, false}, - "have claims": {map[string]catalogue.Manifest{"a.m": claims}, map[string]catalogue.Manifest{"a.m": bare}, true}, - "want claims": {nil, map[string]catalogue.Manifest{"a.m": claims}, true}, - "have is bare": {map[string]catalogue.Manifest{"a.m": bare}, map[string]catalogue.Manifest{"a.m": claims}, false}, - } { - in := grants.Inputs{Nodes: []string{"a"}, Assigned: map[string][]string{"a": {"m"}}, Manifests: map[string]catalogue.Manifest{"m": bare}, Had: row.had, Running: row.running} - if got := len(grants.HoldingsOf(in).Claimed) > 0; got != row.held { - t.Errorf("%s: claimed %v, want %v", name, got, row.held) - } - } - }) } diff --git a/modules/mesh-grants/internal/grants/inputs.go b/modules/mesh-grants/internal/grants/inputs.go index ae603936..4b63aa60 100644 --- a/modules/mesh-grants/internal/grants/inputs.go +++ b/modules/mesh-grants/internal/grants/inputs.go @@ -154,6 +154,9 @@ type Inputs struct { // Had is the manifest of the build each machine runs now (its have's fingerprint, the builder's build of it), by // `.`; the want's Running may already be a newer build. A membership is composed from both and the trunk's. Had map[string]catalogue.Manifest + // HadFailed is why the build a machine runs could not be read this pass (a have or a build read that failed), by + // `.`: its membership is then composed without that build's reads, and the part keeps the last published. + HadFailed map[string]string // Values is each resolved config value, by `//`. Values map[string]any // BusNode is the machine running the bus's module, and Loaded what its node agent says it loaded. @@ -325,7 +328,7 @@ func ReadInputs(r Reader, cache *Manifests) Inputs { gate := cache.gate cache.mu.Unlock() in := Inputs{Assigned: map[string][]string{}, Manifests: map[string]catalogue.Manifest{}, Digests: map[string]string{}, Fingerprints: map[string]string{}, - NoFingerprint: map[string]string{}, Running: map[string]catalogue.Manifest{}, Had: map[string]catalogue.Manifest{}, + NoFingerprint: map[string]string{}, Running: map[string]catalogue.Manifest{}, Had: map[string]catalogue.Manifest{}, HadFailed: map[string]string{}, Values: map[string]any{}, Gaps: append([]Gap(nil), standingGaps...)} in.SeatListUnread = gate != nil && !gate.MayAct() nodes := map[string]bool{} @@ -756,6 +759,8 @@ func runningByHaves(r Reader, in *Inputs, cache *Manifests, into map[string]cata raw, found, err := r.Get(ReadHave, node+"."+module) if err != nil { in.Gaps = append(in.Gaps, Gap{Input: ReadHave + " " + node + "." + module, Why: "cannot be read, so the build it runs is unknown: " + err.Error(), Failed: true}) + in.HadFailed[node+"."+module] = ReadHave + " cannot be read" + } if err != nil || !found { fromTrunk = append(fromTrunk, node+"."+module) @@ -773,6 +778,7 @@ func runningByHaves(r Reader, in *Inputs, cache *Manifests, into map[string]cata } braw, found, err := r.Get(ReadBuilds, bk) if err != nil { + in.HadFailed[node+"."+module] = ReadBuilds + " cannot be read" in.Gaps = append(in.Gaps, Gap{Input: ReadBuilds + " " + bk, Why: "cannot be read, so the manifest of the build " + node + "." + module + " runs is unknown: " + err.Error(), Failed: true}) } if err != nil || !found { diff --git a/modules/mesh-grants/internal/grants/memberships.go b/modules/mesh-grants/internal/grants/memberships.go index 165c12b6..55946fc1 100644 --- a/modules/mesh-grants/internal/grants/memberships.go +++ b/modules/mesh-grants/internal/grants/memberships.go @@ -206,10 +206,7 @@ func HoldingsOf(in Inputs) Holdings { } for node, modules := range in.Assigned { for _, module := range modules { - m, ok := in.Had[node+"."+module] - if !ok { - m, ok = in.Running[node+"."+module] - } + m, ok := in.Running[node+"."+module] if !ok { m, ok = in.Manifests[module] } diff --git a/modules/mesh-grants/internal/grants/part.go b/modules/mesh-grants/internal/grants/part.go index 82c59b34..6590b9ec 100644 --- a/modules/mesh-grants/internal/grants/part.go +++ b/modules/mesh-grants/internal/grants/part.go @@ -436,7 +436,11 @@ func (p *Part) Compose() Result { // A pass that composes part of the mesh only grows a published membership: it publishes the union of the one last // published and the one composed (keeploaded.go, issue 1966), so a tool runner or a seat holder goes on serving what it // serves and still receives what the pass that ends the hold needs. - if why := in.keepsLoaded(); found && why != "" { + why := in.keepsLoaded() + if why == "" && in.HadFailed[key] != "" { + why = "the build it runs is unread (" + in.HadFailed[key] + ")" + } + if found && why != "" { if bare, err := membership.BareGrantsMembership(was); err == nil { if body, err := json.Marshal(ms[key].Membership); err == nil { if grown, kept, err := UnionMembership(bare, body); err == nil && len(kept) > 0 { diff --git a/modules/mesh-grants/internal/grants/records.go b/modules/mesh-grants/internal/grants/records.go index 06e674f9..b82f4dd1 100644 --- a/modules/mesh-grants/internal/grants/records.go +++ b/modules/mesh-grants/internal/grants/records.go @@ -448,10 +448,7 @@ func holdersAfterPass(in Inputs) ([]catalogue.Held, []Gap) { var assignments []catalogue.Assignment for _, node := range slices.Sorted(maps.Keys(in.Assigned)) { for _, module := range in.Assigned[node] { - m, ok := in.Had[node+"."+module] - if !ok { - m, ok = in.Running[node+"."+module] - } + m, ok := in.Running[node+"."+module] if !ok { m, ok = in.Manifests[module] }