Grants keep the last published membership when the running build cannot be read; holders stay as merged (issue 1966, review round 2)
mesh/delivery merged: source.main moved to novox/mesh-delivery@f9cadbfdbb93
mesh/check failed (2): integration: mesh-delivery-assigner/cmd/mesh-delivery-assigner.TestAProposalIsAnswer…; and 1 more | 5452fa92a731 on f9cadbfdbb93
mesh/delivery merged: source.main moved to novox/mesh-delivery@f9cadbfdbb93
mesh/check failed (2): integration: mesh-delivery-assigner/cmd/mesh-delivery-assigner.TestAProposalIsAnswer…; and 1 more | 5452fa92a731 on f9cadbfdbb93
Claude-Session: https://claude.ai/code/session_01NcbgfFfQ4Z9UE5jK32PmE5
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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
|
||||
// `<node>.<module>`; 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
|
||||
// `<node>.<module>`: 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 `<node>/<module>/<key>`.
|
||||
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 {
|
||||
|
||||
@@ -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]
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user