Derive the guard from taken modules only: their published private-network ports and their manifests' guards (hq ADR 0103)

This commit is contained in:
2026-09-22 17:58:37 +02:00
parent 28b7fb81ba
commit 8db66e9532
4 changed files with 182 additions and 24 deletions
+14 -1
View File
@@ -539,11 +539,24 @@ func renderingFor(ctx context.Context, open *stores, node string,
if err != nil {
return catalogue.Rendering{}, inventory.Node{}, err
}
// And, on an adopted node, which modules were taken there: the guard is derived from those
// only (novox/hq ADR 0103).
var taken map[string]bool
if record.Adopted {
list, err := inv.Taken(ctx, node)
if err != nil {
return catalogue.Rendering{}, inventory.Node{}, err
}
taken = map[string]bool{}
for _, m := range list {
taken[m] = true
}
}
return catalogue.Rendering{
Settings: settings, Generators: gens, Grants: grants, Needed: needed, Ports: ports,
Certificate: certificate, Authority: authority, Mesh: private, Names: names,
Suffix: overlay.Suffix(), Foundation: foundation, Kept: kept, Adopted: record.Adopted,
Given: given,
Given: given, Taken: taken,
}, record, nil
}
+35
View File
@@ -225,3 +225,38 @@ func TestConsumersAreToldTheGivenPort(t *testing.T) {
t.Fatalf("a port given for the whole mesh was not refused: %v", err)
}
}
// novox/hq ADR 0103: the guard an adopted node is sent follows what was taken there. A store
// assigned but not taken is not guarded — its port may still be the predecessor's — and taking it
// guards it from the next declaration.
func TestTheGuardIsSentForTakenModulesOnly(t *testing.T) {
open := aMesh(t)
ctx := t.Context()
register(t, open, catalogue.Manifest{Module: "store", Version: "1",
Listens: []catalogue.Listening{{Port: 5432, From: catalogue.FromMesh}},
Guards: []int{5432},
Resources: []map[string]any{{"id": "server", "type": "container", "name": "mesh-store",
"ports": []any{"5432:5432"},
"image": "registry.example/pg@sha256:" + strings.Repeat("b", 64)}}})
if err := open.inventory.SetAdopted(ctx, "anchor", true); err != nil {
t.Fatal(err)
}
if _, err := assign(ctx, open, "anchor", "store"); err != nil {
t.Fatal(err)
}
if hasID(composed(t, open, "anchor").Resources, catalogue.GuardID()) {
t.Fatal("an untaken store is guarded")
}
if err := open.inventory.Take(ctx, "anchor", "store"); err != nil {
t.Fatal(err)
}
for _, r := range composed(t, open, "anchor").Resources {
if r["id"] == catalogue.GuardID() {
if r["content"] != catalogue.AsGuard([]int{5432}) {
t.Fatalf("the taken store's guard is:\n%s", r["content"])
}
return
}
}
t.Fatal("a taken store is not guarded")
}