Hold the controller's own lease, bus and health conditions while its delivery runs (ADR 0505 §2)

This commit is contained in:
2026-10-22 14:03:09 +02:00
parent 92225cd528
commit 1d995d2bef
2 changed files with 63 additions and 1 deletions
@@ -42,3 +42,50 @@ func TestTheSettleIsReadFromTheSettingsFileAndIsTwoMinutesByDefault(t *testing.T
t.Fatalf("a setting of 0: settle %s, want none", got)
}
}
// controllerConditions are the controller's own conditions as it raises them: the lease's (mesh-controller
// signals.go watchLease: scope core, id controller.lease, the machine its host names, which need not be a node's
// name) and the bus's (reconnects.go: scope bus, id the bus user).
func controllerConditions(c *clock) []Condition {
lease := cond("core.controller.lease.lost", Urgent, "the controller lost its lease")
lease.ID, lease.Machine, lease.Raised = "controller.lease", "a-host-name", c.now()
bus := cond("bus.controller.bus-reconnects", Urgent, "the controller reconnects to the bus")
bus.Raised = c.now()
health := cond("module.controller.unhealthy", Urgent, "the controller is not healthy")
health.Raised = c.now()
return []Condition{lease, bus, health}
}
// ADR 0505 §2a: the controller's lease, bus and health conditions are held while a walk moving mesh-controller is
// open, whatever name their keys give the controller and whatever host they name.
func TestTheControllersOwnConditionsAreHeldWhileAWalkMovesMeshController(t *testing.T) {
h, tg, dt, c, _ := newHolder(t)
walk(t, h, "plan-c", WalkStarted, "", c.now(), 1200, novox("mesh-controller"))
for _, k := range controllerConditions(c) {
h.Condition(EventRaised, k)
}
runFor(h, c, time.Minute)
if saidOn(tg, dt) != 0 {
t.Fatalf("the controller's own conditions were said during its delivery: %+v %+v", tg.sends, dt.sends)
}
for _, k := range controllerConditions(c) {
if !has(outcomes(h, k.Key), "held") {
t.Errorf("%s was not held: %v", k.Key, outcomes(h, k.Key))
}
}
}
// ADR 0505 §2a: a walk that does not move mesh-controller holds none of them.
func TestTheControllersOwnConditionsAreNotHeldByAWalkOfAnotherModule(t *testing.T) {
h, tg, dt, c, _ := newHolder(t)
walk(t, h, "plan-g", WalkStarted, "", c.now(), 1200, Touch{Machine: "ace", Modules: []string{"gitea"}})
for _, k := range controllerConditions(c) {
h.Condition(EventRaised, k)
}
runFor(h, c, time.Minute)
for _, k := range controllerConditions(c) {
if has(outcomes(h, k.Key), "held") || !heard(headlineOf(k.Key), tg, dt) {
t.Errorf("%s was held by a walk of gitea, or not said: %v", k.Key, outcomes(h, k.Key))
}
}
}
+16 -1
View File
@@ -155,7 +155,7 @@ func (w *Walk) touches(s subject) bool {
}
}
}
if s.machineNamed {
if s.machineNamed && !s.controller {
return false
}
for _, t := range w.Touches {
@@ -719,8 +719,18 @@ type subject struct {
machineNamed bool
// tokens are every name it carries, to tell a message about a walk itself.
tokens []string
// controller: the subject is the controller itself — its lease, its bus connection, its health — matched to the
// module mesh-controller wherever a walk moves it, whatever machine or host the condition names (ADR 0505 §2a).
controller bool
}
// controllerNames are the names the controller's own conditions give it in their keys (`core.controller.lease.lost`,
// `bus.controller.bus-reconnects`, `module.controller...`): each is the module ControllerModule (ADR 0505 §2a).
var controllerNames = map[string]bool{"controller": true, ControllerModule: true}
// ControllerModule is the controller's module, as a walk that moves it names it.
const ControllerModule = "mesh-controller"
// subjectOf is a record's subject: a condition's scope and id (`<scope>.<id>.<kind>` when the record kept no
// subject of its own), a notice's subject words.
func subjectOf(r *Record) subject {
@@ -742,6 +752,11 @@ func subjectOf(r *Record) subject {
if r.Machine != "" {
s.machines, s.machineNamed = append(s.machines, r.Machine), true
}
if (scope == "core" || scope == "bus" || scope == "module" || scope == "seat") && len(tokens) > 0 && controllerNames[tokens[0]] {
// The controller's own subject: the module mesh-controller, wherever it runs (ADR 0505 §2a).
s.modules, s.controller = append(s.modules, ControllerModule), true
return s
}
switch scope {
case "machine", "node", "bus":
s.machines, s.machineNamed = append(s.machines, tokens[0]), true