diff --git a/modules/mesh-config/cmd/mesh-config/main.go b/modules/mesh-config/cmd/mesh-config/main.go index 1334b097..63428e01 100644 --- a/modules/mesh-config/cmd/mesh-config/main.go +++ b/modules/mesh-config/cmd/mesh-config/main.go @@ -115,7 +115,7 @@ func resolving(c *Config) { } return nil } - for _, read := range []string{AssignmentsRead, DeclaredConfigRead, PredefinedRead} { + for _, read := range []string{AssignmentsRead, PlacementRead, DeclaredConfigRead, PredefinedRead} { go func(read string) { for wait := 2 * time.Second; ; wait = min(wait*2, time.Minute) { time.Sleep(wait) diff --git a/modules/mesh-config/cmd/mesh-config/mesh-config b/modules/mesh-config/cmd/mesh-config/mesh-config new file mode 100755 index 00000000..1c04a06a Binary files /dev/null and b/modules/mesh-config/cmd/mesh-config/mesh-config differ diff --git a/modules/mesh-config/cmd/mesh-config/pulled_in_test.go b/modules/mesh-config/cmd/mesh-config/pulled_in_test.go index e4120e2c..dbf9ebfb 100644 --- a/modules/mesh-config/cmd/mesh-config/pulled_in_test.go +++ b/modules/mesh-config/cmd/mesh-config/pulled_in_test.go @@ -21,4 +21,16 @@ func TestAModulePulledInHasItsValuesResolvedOnItsNode(t *testing.T) { if r, _ := c.Reconcile(); len(r.Moved) != 4 || !r.Moved[0].Gone { t.Errorf("a module no longer pulled in moved %+v", r.Moved) } + + // 1126 (ADR 0549 §3, §5): a module the catalogue's placement names on a node resolves there as an assigned one does, + // while the two sources stand; it goes when neither names it. + m.mirror(t, PlacementRead, "placement.ace", `{"v":1,"node":"ace","modules":[{"module":"dunst"}]}`) + if r, err := c.Reconcile(); err != nil || len(r.Moved) != 4 { + t.Fatalf("a placed module's values moved %+v (%v)", r.Moved, err) + } + expect(t, m, "ace", "10", SourceDefault) + _ = m.Delete(PlacementRead, "placement.ace") + if r, _ := c.Reconcile(); len(r.Moved) != 4 || !r.Moved[0].Gone { + t.Errorf("a module no longer placed moved %+v", r.Moved) + } } diff --git a/modules/mesh-config/cmd/mesh-config/resolve.go b/modules/mesh-config/cmd/mesh-config/resolve.go index bc21ec61..ccf35335 100644 --- a/modules/mesh-config/cmd/mesh-config/resolve.go +++ b/modules/mesh-config/cmd/mesh-config/resolve.go @@ -20,7 +20,10 @@ const ( PredefinedState = "predefined" ProposalsState = "proposals" - AssignmentsRead = "mesh-controller.assignments" + AssignmentsRead = "mesh-controller.assignments" + // PlacementRead is the indexer's placement, read from the catalogue at each merge (novox/hq ADR 0549 §2, §3): each + // node's key `placement.` with the modules placed there. + PlacementRead = "mesh-delivery-indexer.placement" DeclaredConfigRead = "mesh-controller.declared-config" PredefinedRead = "mesh-controller.predefined" diff --git a/modules/mesh-config/cmd/mesh-config/sources.go b/modules/mesh-config/cmd/mesh-config/sources.go index df6292dc..34f8dddf 100644 --- a/modules/mesh-config/cmd/mesh-config/sources.go +++ b/modules/mesh-config/cmd/mesh-config/sources.go @@ -27,6 +27,38 @@ func (c *Config) sources() (Sources, error) { }); err != nil { return s, fmt.Errorf("the assignments cannot be read: %w", err) } + // And every module the catalogue's placement names on a node (ADR 0549 §3): until ADR 0547's third milestone both + // sources stand (ADR 0549 §5), so a pair either names is resolved, and none loses its values while the two differ. + // Placement that cannot be read yet (the indexer's state not on the bus) leaves the assignments alone, said. + standing := map[string]bool{} + for _, a := range s.Assignments { + standing[a.Node+"."+a.Module] = true + } + if err := c.each(PlacementRead, func(key string, raw []byte) error { + node, ok := strings.CutPrefix(key, "placement.") + if !ok { + return nil + } + var p struct { + Node string `json:"node"` + Modules []struct { + Module string `json:"module"` + } `json:"modules"` + } + if err := json.Unmarshal(raw, &p); err != nil || p.Node != node { + problems = append(problems, fmt.Sprintf("the placement %s is not one", key)) + return nil + } + for _, m := range p.Modules { + if m.Module != "" && !standing[node+"."+m.Module] { + standing[node+"."+m.Module] = true + s.Assignments = append(s.Assignments, Assignment{Node: node, Module: m.Module}) + } + } + return nil + }); err != nil { + problems = append(problems, fmt.Sprintf("the catalogue's placement cannot be read, so the assignments alone are resolved: %v", err)) + } if err := c.each(DeclaredConfigRead, func(key string, raw []byte) error { m, err := ParseModuleDeclarations(raw) if err != nil { diff --git a/modules/mesh-config/module.json b/modules/mesh-config/module.json index f93497e1..d6b5bdd0 100644 --- a/modules/mesh-config/module.json +++ b/modules/mesh-config/module.json @@ -29,6 +29,7 @@ ], "reads": [ "mesh-controller.assignments", + "mesh-delivery-indexer.placement", "mesh-controller.declared-config", "mesh-controller.predefined" ],