Hold what a walk's merges change on a machine it sends nothing to, and name only where it sent (ADR 0505 §2)

This commit is contained in:
2026-10-22 14:04:06 +02:00
parent 1d995d2bef
commit 426db57d3f
2 changed files with 49 additions and 6 deletions
@@ -89,3 +89,33 @@ func TestTheControllersOwnConditionsAreNotHeldByAWalkOfAnotherModule(t *testing.
}
}
}
// shanksDeclared is a touch as the controller says a machine whose declaration the walk's merges change and to which
// it sends nothing (mesh-controller walk_said.go walkTouch, `declared`, ADR 0505 §2b).
var shanksDeclared = Touch{Machine: "shanks", Modules: []string{"mesh-build-image"}, Declared: true}
// ADR 0505 §2b: a machine whose declaration a walk's merges change is held like a machine it sends to, and is not
// named among the machines the walk reached.
func TestAMachineWhoseDeclarationTheWalkChangesIsHeldAndNotNamedAsReached(t *testing.T) {
ev, ok, err := DecodeWalk([]byte(`{"id":"plan-d","walk":"plan-d","phase":"started","budget_class":"leaf",` +
`"bound_seconds":600,"touches":[{"machine":"novox","modules":["gitea"]},` +
`{"machine":"shanks","modules":["mesh-build-image"],"declared":true}]}`))
if err != nil || !ok || len(ev.Touches) != 2 || !ev.Touches[1].Declared || ev.Touches[0].Declared {
t.Fatalf("a declared touch was not read: %+v %v %v", ev.Touches, ok, err)
}
h, tg, dt, c, _ := newHolder(t)
walk(t, h, "plan-d", WalkStarted, "", c.now(), 600, novox("gitea"), shanksDeclared)
k := cond("machine.shanks.uncomposable", Urgent, "shanks cannot get new instructions")
k.Machine, k.Raised = "shanks", c.now()
h.Condition(EventRaised, k)
runFor(h, c, time.Minute)
if !has(outcomes(h, k.Key), "held") || heard(headlineOf(k.Key), tg, dt) {
t.Fatalf("a condition on the declared machine was not held: %v", outcomes(h, k.Key))
}
if got := touchWords(mergeTouches([]Touch{novox("gitea"), shanksDeclared}, nil)); got != "gitea on novox" {
t.Fatalf("what the walk reached reads %q, want only novox", got)
}
if got := mergeTouches([]Touch{shanksDeclared}, []Touch{{Machine: "shanks", Modules: []string{"buildah"}}}); got[0].Declared {
t.Fatalf("a machine the walk also sends to stayed declared: %+v", got)
}
}
+19 -6
View File
@@ -94,6 +94,10 @@ func (h *Holder) settle() time.Duration {
type Touch struct {
Machine string `json:"machine"`
Modules []string `json:"modules"`
// Declared: the walk sends nothing to this machine, but its merges change the machine's declaration (an
// assignment, a provision or a requirement added or removed, ADR 0505 §2b). Held like any touch; never named
// among the machines the walk reached.
Declared bool `json:"declared,omitempty"`
}
// WalkEvent is a walk's start or end as the controller says it.
@@ -229,9 +233,10 @@ func DecodeWalk(body []byte) (WalkEvent, bool, error) {
}
if v, ok := raw["touches"]; ok && string(v) != "null" {
var list []struct {
Machine string `json:"machine"`
Node string `json:"node"`
Modules []string `json:"modules"`
Machine string `json:"machine"`
Node string `json:"node"`
Modules []string `json:"modules"`
Declared bool `json:"declared"`
}
if json.Unmarshal(v, &list) != nil {
return WalkEvent{}, false, fmt.Errorf("walk %s: touches is not a list of {machine, modules}", ev.Walk)
@@ -242,7 +247,7 @@ func DecodeWalk(body []byte) (WalkEvent, bool, error) {
m = t.Node
}
if m != "" {
ev.Touches = append(ev.Touches, Touch{Machine: m, Modules: t.Modules})
ev.Touches = append(ev.Touches, Touch{Machine: m, Modules: t.Modules, Declared: t.Declared})
}
}
}
@@ -266,9 +271,11 @@ func DecodeWalk(body []byte) (WalkEvent, bool, error) {
return ev, true, nil
}
// mergeTouches is what two sayings of a walk's touches name together.
// mergeTouches is what two sayings of a walk's touches name together. A machine is declared only while no saying
// sends to it (ADR 0505 §2b).
func mergeTouches(a, b []Touch) []Touch {
by := map[string]map[string]bool{}
sent := map[string]bool{}
var order []string
for _, list := range [][]Touch{a, b} {
for _, t := range list {
@@ -279,6 +286,9 @@ func mergeTouches(a, b []Touch) []Touch {
for _, m := range t.Modules {
by[t.Machine][m] = true
}
if !t.Declared {
sent[t.Machine] = true
}
}
}
sort.Strings(order)
@@ -289,7 +299,7 @@ func mergeTouches(a, b []Touch) []Touch {
mods = append(mods, m)
}
sort.Strings(mods)
out = append(out, Touch{Machine: machine, Modules: mods})
out = append(out, Touch{Machine: machine, Modules: mods, Declared: !sent[machine]})
}
return out
}
@@ -684,6 +694,9 @@ func touchWords(touches []Touch) string {
mods := map[string]bool{}
var machines []string
for _, t := range touches {
if t.Declared {
continue // its declaration changed; nothing was sent to it (ADR 0505 §2b)
}
machines = append(machines, t.Machine)
for _, m := range t.Modules {
mods[m] = true