Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8a434b8c6 |
@@ -278,6 +278,10 @@ func Wants(m Module, n Nodes, wants map[string]delivery.Want, lastHealthy map[st
|
||||
if need, wait := at.mustAsk(node, asked, hadLive, ran, running.FP, running.Composed == "", d); wait {
|
||||
if need != nil {
|
||||
out.Record = append(out.Record, *need)
|
||||
} else if !hadLive && ran && runs(running) {
|
||||
// The bus, which the assigner never moves (§15), runs here with no want: it is on neither list until the operator
|
||||
// writes its want, and the pass says why (issue 1945).
|
||||
out.Neither[node] = Holding{Rule: delivery.RuleComposed, Words: busMoveWords(m.Name, node, target, d)}
|
||||
}
|
||||
continue
|
||||
}
|
||||
@@ -443,6 +447,17 @@ func (m Module) mustAsk(node string, before delivery.Want, had, ran bool, runnin
|
||||
return &RecordNeed{Node: node, Digest: d.Digest, Judgement: d.Judgement, Words: d.Words}, true
|
||||
}
|
||||
|
||||
// busMoveWords say why the bus module's want at fp is not written on node: its composition differs from what the node runs in more
|
||||
// than the bus's user list, so it is a move of the bus, which the operator writes (ADR 0539 §15).
|
||||
func busMoveWords(module, node, fp string, d delivery.ComposedDigest) string {
|
||||
why := string(d.Judgement)
|
||||
if w := strings.TrimSpace(d.Words); w != "" {
|
||||
why += ": " + w
|
||||
}
|
||||
return fmt.Sprintf("the composition of %s for %s at %s changes more than the bus's user list (judged %s), so it is a move of the bus, "+
|
||||
"which the assigner never makes: the operator writes its want (ADR 0539 §15)", module, node, short(fp), why)
|
||||
}
|
||||
|
||||
// recorded says whether the module is of policy record: as the controller holds it beside the composition's digest, the
|
||||
// operator's own choice included (ADR 0539 §14); on a composition made by hand, which carries none, as the manifest derives it.
|
||||
func (m Module) recorded(d delivery.ComposedDigest) bool {
|
||||
@@ -456,11 +471,28 @@ func (m Module) recorded(d delivery.ComposedDigest) bool {
|
||||
// userListRevision is the want that names the bus module's composition at the fingerprint the node runs when that composition
|
||||
// differs from the one the node runs only in the bus's user list (ComposedDigest.UserListOnly), and whether there is one. A node
|
||||
// whose want names another fingerprint than the one it runs, or a module that does not provide the bus, has none.
|
||||
//
|
||||
// A node whose have is of the old form (from the old node-engine: a build and no fingerprint of the new form, no composition) runs
|
||||
// no fingerprint a composition is kept at, so its revision names F, the newest fingerprint, when the controller judged F's
|
||||
// composition to differ from the declaration the node last applied only in the bus's user list (ADR 0539 §7, ADR 0540 §9). Any
|
||||
// other difference is a move of the bus and stays the operator's (§15). Without this the bus never got its first want after the
|
||||
// switch, and its user list was never written again (issue 1945: novox's nats from 11:23 on 2026-10-25).
|
||||
func (m Module) userListRevision(node string, before delivery.Want, had bool, n Nodes) (delivery.Want, bool) {
|
||||
if !catalogue.ProvidesBus(m.Manifest) {
|
||||
return delivery.Want{}, false
|
||||
}
|
||||
running, ran := n.Haves[node][m.Name]
|
||||
if ran && oldForm(running) && m.FP != "" && (!had || before.Removed || before.FP == m.FP) {
|
||||
d, ok := m.composition(node, m.FP)
|
||||
if !ok || !d.UserListOnly {
|
||||
return delivery.Want{}, false
|
||||
}
|
||||
w := delivery.Want{FP: m.FP, Composed: d.Digest}
|
||||
if had && !before.Removed && before.Composed == d.Digest {
|
||||
w.Backup, w.Words = before.Backup, before.Words
|
||||
}
|
||||
return w, true
|
||||
}
|
||||
if !ran || running.FP == "" || (had && (before.Removed || before.FP != running.FP)) {
|
||||
return delivery.Want{}, false
|
||||
}
|
||||
@@ -475,6 +507,13 @@ func (m Module) userListRevision(node string, before delivery.Want, had bool, n
|
||||
return w, true
|
||||
}
|
||||
|
||||
// oldForm says whether a have is of the old form: the old node-engine's, naming a build and no fingerprint of the new form, and no
|
||||
// composition (issue 1945: novox's nats, build-1792674595610176744).
|
||||
func oldForm(h delivery.Have) bool {
|
||||
return h.Phase != delivery.PhaseRemoved && h.Composed == "" && (h.FP != "" || h.Build != "") &&
|
||||
!strings.HasPrefix(h.FP, delivery.SourcePrefix2)
|
||||
}
|
||||
|
||||
// GrantNamesNode says whether a grant of module names its accounts on node: its own `<node>.<module>` or a process's
|
||||
// `<node>.<module>.<process>`. A grant that names no account (a module that does not speak on the bus) names every node.
|
||||
func GrantNamesNode(g delivery.ControllerGrant, node, module string) bool {
|
||||
|
||||
@@ -498,8 +498,53 @@ func TestAUserListOnlyRevisionOfTheBusIsWrittenAtOnceBeforeTheRecordHold(t *test
|
||||
t.Errorf("%s: the user-list revision was asked: %+v", name, out.Record)
|
||||
}
|
||||
}
|
||||
revision := m.Composed
|
||||
// Issue 1945: a have of the old form (a build, no fingerprint of the new form, no composition) runs no fingerprint a composition is
|
||||
// kept at. The revision then names F when the controller judged F's composition to differ from the declaration the node last
|
||||
// applied only in the user list; any other difference is a move of the bus, refused by §15 and said on neither list.
|
||||
for name, c := range map[string]struct {
|
||||
userListOnly bool
|
||||
wanted map[string]delivery.Want
|
||||
want *delivery.Want // nil: no want written
|
||||
neither bool
|
||||
}{
|
||||
"old form, user list only, no want": {userListOnly: true, want: &delivery.Want{FP: fNew, Composed: userList}},
|
||||
"old form, user list only, a removed want": {userListOnly: true, wanted: map[string]delivery.Want{"ace": {Removed: true}}, want: &delivery.Want{FP: fNew, Composed: userList}},
|
||||
"old form, user list only, written before": {userListOnly: true, wanted: map[string]delivery.Want{"ace": {FP: fNew, Composed: userList}}},
|
||||
"old form, a real move of the bus": {neither: true},
|
||||
} {
|
||||
m.Manifest.Provides = []catalogue.Offer{{Name: "mesh-bus"}}
|
||||
m.Composed = func(node, fp string) (delivery.ComposedDigest, bool) {
|
||||
if fp != fNew {
|
||||
return delivery.ComposedDigest{}, false
|
||||
}
|
||||
if c.userListOnly {
|
||||
return delivery.ComposedDigest{Digest: userList, Judgement: delivery.JudgedNeither, UserListOnly: true, Against: dig(fOld),
|
||||
Words: "only the bus's user list changes, against the declaration ace last applied", Policy: delivery.PolicyRecord}, true
|
||||
}
|
||||
return delivery.ComposedDigest{Digest: dig(fNew), Judgement: delivery.JudgedNeither, Words: "the node runs no composition of mail",
|
||||
Policy: delivery.PolicyRecord}, true
|
||||
}
|
||||
n.Haves["ace"]["mail"] = delivery.Have{Build: "build-1792674595610176744", Phase: delivery.PhaseRunning, Health: delivery.HealthOK}
|
||||
out := Wants(m, n, c.wanted, nil)
|
||||
w, ok := out.Wants["ace"]
|
||||
switch {
|
||||
case c.want == nil && ok:
|
||||
t.Errorf("%s: a want was written: %+v", name, w)
|
||||
case c.want != nil && (!ok || w.FP != c.want.FP || w.Composed != c.want.Composed || w.Removed):
|
||||
t.Errorf("%s: ace's want = %+v (written %v), want %+v", name, w, ok, *c.want)
|
||||
}
|
||||
if len(out.Record) != 0 {
|
||||
t.Errorf("%s: asked: %+v", name, out.Record)
|
||||
}
|
||||
h, on := out.Neither["ace"]
|
||||
if on != c.neither || (on && (!strings.Contains(h.Words, "§15") || !strings.Contains(h.Words, "more than the bus's user list"))) {
|
||||
t.Errorf("%s: on neither list %v (%+v), want %v with the words of §15", name, on, h, c.neither)
|
||||
}
|
||||
}
|
||||
// the same flag on a module that is not the bus changes nothing
|
||||
m.Manifest.Provides = nil
|
||||
m.Manifest.Provides, m.Composed = nil, revision
|
||||
n.Haves["ace"]["mail"] = delivery.Have{FP: fOld, Phase: delivery.PhaseRunning, Health: delivery.HealthOK}
|
||||
if out := Wants(m, n, map[string]delivery.Want{"ace": {FP: fOld, Composed: dig(fOld)}}, nil); out.Wants["ace"].Composed == userList {
|
||||
t.Errorf("a module that is not the bus was written at once: %+v", out.Wants["ace"])
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user