diff --git a/internal/catalogue/resolve_test.go b/internal/catalogue/resolve_test.go index 80c4e69..24d7144 100644 --- a/internal/catalogue/resolve_test.go +++ b/internal/catalogue/resolve_test.go @@ -224,6 +224,29 @@ func TestAMeshWideClaimIsHeldByOneNode(t *testing.T) { } } +func TestAFoundationModuleCannotBeRaisedOnASecondNode(t *testing.T) { + // novox/hq 04-ISSUES/056. The store and broker are adopted in place on the control-node; each + // foundation module claims a mesh-scoped seat named after its server — the same mechanism that + // keeps one controller — so a second `assign` to another node is refused rather than silently + // raising a second postgres or broker that holds none of the first's data. + for _, tc := range []struct{ module, seat string }{ + {"postgres", "mesh-store"}, + {"lavinmq", "mesh-broker"}, + {"mesh-controller", "mesh-controller"}, + } { + _, err := Resolve( + shelf(mod(tc.module, nil, nil, nil, Claim{Name: tc.seat, Scope: ScopeMesh})), + []string{tc.module}, workstation(), + World{Held: []Held{{Claim: tc.seat, Scope: ScopeMesh, Node: "anchor", Module: tc.module}}}) + if err == nil { + t.Fatalf("%s was raised on a second node though %s is a mesh-wide seat", tc.module, tc.seat) + } + if !strings.Contains(err.Error(), "one per mesh") { + t.Errorf("%s: the refusal does not say it is one per mesh: %v", tc.module, err) + } + } +} + func TestASiteClaimOnlyCollidesWithinThatSite(t *testing.T) { // A DHCP server per segment. Two of them is a fault at one site and perfectly ordinary // across two, and treating site as mesh would forbid the ordinary case. diff --git a/module.json b/module.json index a3b7a9d..7d55ffc 100644 --- a/module.json +++ b/module.json @@ -7,7 +7,7 @@ ], "claims": [ { - "name": "the-controller", + "name": "mesh-controller", "scope": "mesh" } ],