Read the rest of the mesh and the network over mesh-sdk, and fail when a caller reaches for the old composition (issue 1388)

The seats, holdings, source, build-source, delivery group check, check request, backup plan, adoption (take and converge) and the network graph now read the rest of the mesh through restOfTheMesh and compose.Graph. compose_callers_test.go scans the package and names any function other than the old composition's own that still calls planFor, declarationWith, composedAndValidated, generators, renderingFor, theRestOfTheMesh or whoResolves; issue 1389 deletes those.
This commit is contained in:
2026-10-20 09:15:34 +00:00
parent 7855ea126c
commit bd17a041de
10 changed files with 85 additions and 20 deletions
+11 -5
View File
@@ -13,6 +13,7 @@ import (
"time"
"git.novox.be/novox/mesh-sdk/go/catalogue"
"git.novox.be/novox/mesh-sdk/go/compose"
"git.novox.be/novox/mesh-sdk/go/overlay"
"github.com/novox/mesh-controller/internal/inventory"
)
@@ -243,7 +244,7 @@ func take(ctx context.Context, open *stores, node, module string, opts takeOptio
return "", err
}
if !slices.Contains(assigned, module) {
if plan, _, err := planFor(ctx, open, node); err == nil {
if plan, _, err := resolveNode(ctx, open, node); err == nil {
if why, runs := plan.Because[module]; runs {
return "", fmt.Errorf("%w: %s runs on %s because %s — assign it to %s to take it",
inventory.ErrNotAssigned, module, node, why, node)
@@ -649,7 +650,7 @@ func converge(ctx context.Context, open *stores, node string, yes bool, digest s
if err != nil {
return "", err
}
plan, settings, err := planFor(ctx, open, node)
plan, _, err := resolveNode(ctx, open, node)
if err != nil {
return "", err
}
@@ -706,14 +707,19 @@ func converge(ctx context.Context, open *stores, node string, yes bool, digest s
filter)
}
gens, err := generators(ctx, open)
// What the filter is derived from: the rendering of this machine as a question reads it, with the private network.
input, err := inputFor(ctx, open)
if err != nil {
return "", err
}
with, _, err := renderingFor(ctx, open, node, plan, settings, gens, Reading)
rendered, err := compose.Render(input.Input, node, compose.Reading)
if err != nil {
return "", err
}
with := rendered.Rendering
if with.Generators, err = compose.Generators(input.Input); err != nil {
return "", err
}
rules, err := plan.Rules(with)
if err != nil {
return "", err
@@ -776,7 +782,7 @@ func converge(ctx context.Context, open *stores, node string, yes bool, digest s
if _, err := inv.Assign(ctx, node, filter); err != nil {
return "", err
}
if _, _, err := planFor(ctx, open, node); err != nil {
if _, _, err := resolveNode(ctx, open, node); err != nil {
_ = inv.Unassign(ctx, node, filter)
return "", fmt.Errorf("%s cannot run %s, so it was not converged: %w", node, filter, err)
}
+1 -1
View File
@@ -88,7 +88,7 @@ func newSourceForms(ctx context.Context, inv *inventory.Inventory) *sourceForms
if world == nil {
w := catalogue.World{}
if shelf, err := inv.Catalogue(ctx); err == nil {
if read, err := theRestOfTheMesh(ctx, inv, shelf, ""); err == nil {
if read, err := restOfTheMeshOf(ctx, inv, shelf, ""); err == nil {
w = read
}
}
+1 -1
View File
@@ -281,7 +281,7 @@ func checkRequestFor(ctx context.Context, open *stores, p link.PullUpdated, scop
for _, e := range entries {
shelf[e.Manifest.Module] = e.Manifest
}
world, err := theRestOfTheMesh(ctx, open.inventory, shelf, "")
world, err := restOfTheMesh(ctx, open, shelf, "")
if err != nil {
return link.BuildRequest{}, err
}
@@ -0,0 +1,63 @@
package main
import (
"go/ast"
"go/parser"
"go/token"
"path/filepath"
"sort"
"strings"
"testing"
)
// 1388: no caller composes through the controller's own composition any more. The only functions left that call it are the
// composition's own, which issue 1389 deletes with it; this fails the moment any other function reaches for it again.
func TestNoCallerComposesThroughTheControllersOwnComposition(t *testing.T) {
legacy := map[string]bool{"planFor": true, "declarationWith": true, "composedAndValidated": true, "generators": true,
"renderingFor": true, "theRestOfTheMesh": true, "whoResolves": true}
// The composition's own pairs, until 1389: "file: caller calls callee".
allowed := map[string]bool{
"facts.go: composedAndValidated calls declarationWith": true,
"facts.go: composedAndValidated calls planFor": true,
"network.go: generators calls whoResolves": true,
"network.go: whoResolves calls planFor": true,
"plan.go: declarationWith calls renderingFor": true,
"plan.go: grantsFor calls planFor": true,
"plan.go: planFor calls theRestOfTheMesh": true,
}
files, err := filepath.Glob("*.go")
if err != nil {
t.Fatal(err)
}
fset := token.NewFileSet()
var found []string
for _, f := range files {
if strings.HasSuffix(f, "_test.go") {
continue
}
file, err := parser.ParseFile(fset, f, nil, 0)
if err != nil {
t.Fatal(err)
}
for _, d := range file.Decls {
fn, ok := d.(*ast.FuncDecl)
if !ok || fn.Body == nil {
continue
}
ast.Inspect(fn.Body, func(n ast.Node) bool {
if c, ok := n.(*ast.CallExpr); ok {
if id, ok := c.Fun.(*ast.Ident); ok && legacy[id.Name] {
found = append(found, f+": "+fn.Name.Name+" calls "+id.Name)
}
}
return true
})
}
}
sort.Strings(found)
for _, pair := range found {
if !allowed[pair] {
t.Errorf("%s: it composes through the controller's own composition, not mesh-sdk's", pair)
}
}
}
+1 -1
View File
@@ -1179,7 +1179,7 @@ func askGroupCheck(ctx context.Context, open *stores, group string, members []or
request.Check.Group = group
// A group's check is queued as ready when any member's pull request is marked ready.
request.Check.Ready = slices.ContainsFunc(members, func(m orderMember) bool { return m.Ready })
world, err := theRestOfTheMesh(ctx, open.inventory, shelfOf(entries), "")
world, err := restOfTheMesh(ctx, open, shelfOf(entries), "")
if err != nil {
return "", err
}
+1 -1
View File
@@ -36,7 +36,7 @@ func recordDerivedHolders(ctx context.Context, open *stores) ([]string, error) {
return nil, err
}
// exclude nobody: every node's claims, resolved with the holdings on record.
world, err := theRestOfTheMesh(ctx, inv, shelf, "")
world, err := restOfTheMesh(ctx, open, shelf, "")
if err != nil {
return nil, err
}
+3 -7
View File
@@ -12,6 +12,7 @@ import (
"time"
"git.novox.be/novox/mesh-sdk/go/catalogue"
"git.novox.be/novox/mesh-sdk/go/compose"
"git.novox.be/novox/mesh-sdk/go/overlay"
"github.com/novox/mesh-controller/internal/inventory"
)
@@ -308,16 +309,11 @@ func network(ctx context.Context, inv *inventory.Inventory, on map[string]bool,
// graph is the whole mesh's network, for showing it.
func graph(ctx context.Context, open *stores) ([]overlay.Node, overlay.Graph, error) {
inv := open.inventory
on, refused, err := whoResolves(ctx, open, overlay.Requirement)
in, err := inputFor(ctx, open)
if err != nil {
return nil, nil, err
}
g, err := network(ctx, inv, on, refused)
if err != nil {
return nil, nil, err
}
return g.Nodes(), g.Graph(), nil
return compose.Graph(in.Input)
}
// whoResolves is the machines whose resolution answers a requirement, and why the others did not.
+1 -1
View File
@@ -124,7 +124,7 @@ func loadBackupFacts(ctx context.Context, inv *inventory.Inventory) (backupFacts
if err != nil {
return nil, err
}
world, err := theRestOfTheMesh(ctx, inv, shelf, "")
world, err := restOfTheMeshOf(ctx, inv, shelf, "")
if err != nil {
return nil, err
}
+1 -1
View File
@@ -252,7 +252,7 @@ func seatsCommand(ctx context.Context, args []string) error {
return err
}
// Every node, none excluded: the same view of what each machine holds that planning uses.
world, err := theRestOfTheMesh(ctx, inv, shelf, "")
world, err := restOfTheMesh(ctx, open, shelf, "")
if err != nil {
return err
}
+2 -2
View File
@@ -68,7 +68,7 @@ func cloneFrom(ctx context.Context, source buildSource) (string, error) {
if err != nil {
return "", err
}
world, err := theRestOfTheMesh(ctx, open.inventory, shelf, "")
world, err := restOfTheMesh(ctx, open, shelf, "")
if err != nil {
return "", err
}
@@ -154,7 +154,7 @@ func seatBases(ctx context.Context) map[string]string {
if err != nil {
return unread("the catalogue", err)
}
world, err := theRestOfTheMesh(ctx, open.inventory, shelf, "")
world, err := restOfTheMesh(ctx, open, shelf, "")
if err != nil {
return unread("who holds which seat", err)
}