Refuse mesh-watcher and mesh-self-check on one node, so the watcher of the self-check never falls silent with it (issue 1952 S8, ADR 0564 clause 6, Limits 5)

This commit is contained in:
2026-10-29 21:37:59 +01:00
parent 544720b00a
commit 83d928fe50
2 changed files with 22 additions and 2 deletions
+18 -1
View File
@@ -364,12 +364,20 @@ func (p Placement) Held() []catalogue.Held {
return out
}
// KeptApart are the modules never placed on one node, each row naming the record that asks it (novox/hq ADR 0564 clause 6
// and Limits 5; issue 1952 S8): no manifest word says it, so the check below holds the table. mesh-watcher hears the
// self-check's `checked` from a second machine (ADR 0227 §6), which on the self-check's own node would fall silent with
// it.
var KeptApart = []struct{ A, B, Record string }{
{A: "mesh-watcher", B: "mesh-self-check", Record: "ADR 0227 §6, ADR 0564 clause 6"},
}
// CheckPlacements is the holder's check of a proposal laid over every placement (ADR 0567 §2), over the placements by
// node, the modules the indexer holds and the nodes with a record. It returns every refusal, in node order, never only the first: a
// placement naming a node not enrolled or a module the catalogue does not hold; two modules of one node on the same host
// port; a pin, binding or holding naming a provider that is not placed where it says; a seat of scope mesh held twice, unless the seat is replicated (catalogue.Seat's Replicated, ADR 0223), which
// is held once per node;
// two nodes with one address on the private network; more than one hub.
// two nodes with one address on the private network; more than one hub; two modules KeptApart on one node.
// A host port a manifest declares without a number and a requirement left unbound are the composition's to find (its
// ForeseenPorts and Unbound), which the catalogue's composition check reads.
func CheckPlacements(placements map[string]Placement, held func(module string) bool, enrolled func(node string) bool) []string {
@@ -411,6 +419,15 @@ func CheckPlacements(placements map[string]Placement, held func(module string) b
if !enrolled(node) {
refusals = append(refusals, fmt.Sprintf("%s is a node with no record", node))
}
placedHere := map[string]bool{}
for _, m := range p.Modules {
placedHere[m.Module] = true
}
for _, apart := range KeptApart {
if placedHere[apart.A] && placedHere[apart.B] {
refusals = append(refusals, fmt.Sprintf("%s: %s and %s are never placed on one node (%s)", node, apart.A, apart.B, apart.Record))
}
}
hostPorts := map[int]string{}
for _, m := range p.Modules {
if !held(m.Module) && !ProvidedByMesh(m.Module) { // the mesh provides the private network itself
+4 -1
View File
@@ -179,6 +179,8 @@ func TestPlacement(t *testing.T) {
clash.Modules = append([]PlacedModule{{Module: "ghost", Ports: map[string]int{"80": 18080}}}, g14.Modules...)
clash.Modules[2] = PlacedModule{Module: "app", Ports: map[string]int{"8080": 18080}, Bindings: map[string]Provider{"postgres": {Node: "ace", Module: "postgres"}}}
clash.Pins = map[string]Provider{"shell": {Node: "g14", Module: "bash"}}
// ADR 0564 clause 6, Limits 5 (issue 1952 S8): mesh-watcher and mesh-self-check are never placed on one node.
clash.Modules = append(clash.Modules, PlacedModule{Module: "mesh-watcher"}, PlacedModule{Module: "mesh-self-check"})
twice := novox
twice.Node = "stranger"
twice.Decided.Address = "10.77.0.2"
@@ -186,7 +188,8 @@ func TestPlacement(t *testing.T) {
for _, want := range []string{"holds no module ghost", "ghost and app are both published on host port 18080",
"bound to ace/postgres", "pin of shell names g14/bash", "stranger is a node with no record",
"mesh-store, held once for the mesh, is held on novox and stranger",
"g14 and stranger both have the address 10.77.0.2", "novox, stranger are each placed as it"} {
"g14 and stranger both have the address 10.77.0.2", "novox, stranger are each placed as it",
"g14: mesh-watcher and mesh-self-check are never placed on one node (ADR 0227 §6, ADR 0564 clause 6)"} {
found := false
for _, line := range r {
found = found || strings.Contains(line, want)