diff --git a/cmd/mesh-controller/accept_outside.go b/cmd/mesh-controller/accept_outside.go index 3a389f74..2489f542 100644 --- a/cmd/mesh-controller/accept_outside.go +++ b/cmd/mesh-controller/accept_outside.go @@ -55,6 +55,8 @@ func offeredOutsideBy(ctx context.Context, provision string) (string, error) { // its next send would, and the machines that could not be resolved, with why. func consumersFrom(ctx context.Context, open *stores, provider, providerModule, provision string) ( []inventory.PairEnd, []string, error) { + // One reading of the mesh for every machine this looks at. + ctx = withInputCache(ctx) nodes, err := open.inventory.Nodes(ctx) if err != nil { return nil, nil, err @@ -62,7 +64,7 @@ func consumersFrom(ctx context.Context, open *stores, provider, providerModule, var out []inventory.PairEnd var unresolved []string for _, n := range nodes { - plan, _, err := planFor(ctx, open, n.Name) + plan, _, err := resolveNode(ctx, open, n.Name) if err != nil { unresolved = append(unresolved, fmt.Sprintf("%s: %s", n.Name, oneLine(err.Error()))) continue diff --git a/cmd/mesh-controller/acts.go b/cmd/mesh-controller/acts.go index 3b0d11a0..3989f0f8 100644 --- a/cmd/mesh-controller/acts.go +++ b/cmd/mesh-controller/acts.go @@ -314,7 +314,7 @@ type afterAct struct { // after the write, which can only find more unmet than the closure would: a seat said held there is // held in the closure too. func resolvedAfter(ctx context.Context, open *stores, shelf map[string]catalogue.Manifest, node string) afterAct { - plan, _, err := planFor(ctx, open, node) + plan, _, err := resolveNode(ctx, open, node) if err == nil { out := afterAct{plan: plan, unheld: plan.Unheld} for _, m := range plan.Modules { @@ -355,6 +355,8 @@ func splitModules(field string) []string { // Nothing here can fail the act it reports on. A mesh that cannot be read is worth saying and is // not a reason to claim the assignment did not happen — it did. func blockedElsewhere(ctx context.Context, open *stores, except string) string { + // One reading of the mesh for every machine this looks at. + ctx = withInputCache(ctx) nodes, err := open.inventory.Nodes(ctx) if err != nil { return "\n\nThe rest of the mesh could not be checked: " + err.Error() @@ -364,7 +366,7 @@ func blockedElsewhere(ctx context.Context, open *stores, except string) string { if n.Name == except { continue } - if _, _, err := planFor(ctx, open, n.Name); err != nil { + if _, _, err := resolveNode(ctx, open, n.Name); err != nil { blocked[n.Name] = err.Error() } } diff --git a/cmd/mesh-controller/bindings.go b/cmd/mesh-controller/bindings.go index ee0370ad..aab111f1 100644 --- a/cmd/mesh-controller/bindings.go +++ b/cmd/mesh-controller/bindings.go @@ -73,6 +73,8 @@ func reportKept(ctx context.Context, plan catalogue.Resolution) { // sent, on every machine — the resolver kept it there (said, urgent, until a person pins), or a pin // moves it (said, so the data goes first), and never anything else. func probeBindings(ctx context.Context, d *doctor) ([]conditions.Observation, error) { + // One reading of the mesh for every machine this looks at. + ctx = withInputCache(ctx) inv := d.open.inventory nodes, err := inv.Nodes(ctx) if err != nil { @@ -80,7 +82,7 @@ func probeBindings(ctx context.Context, d *doctor) ([]conditions.Observation, er } var out []conditions.Observation for _, n := range nodes { - plan, _, err := planFor(ctx, d.open, n.Name) + plan, _, err := resolveNode(ctx, d.open, n.Name) if err != nil { if unresolvable(err) { // D1 says it, with the binding that refused it when that is why. diff --git a/cmd/mesh-controller/data.go b/cmd/mesh-controller/data.go index 6c41e640..15a808c9 100644 --- a/cmd/mesh-controller/data.go +++ b/cmd/mesh-controller/data.go @@ -152,6 +152,8 @@ type consumerCopy struct { // probeData is D13. func probeData(ctx context.Context, d *doctor) ([]conditions.Observation, error) { + // One reading of the mesh for every machine this looks at. + ctx = withInputCache(ctx) if d.js == nil { return nil, errors.New("no bus to ask the machines over") } @@ -180,7 +182,7 @@ func probeData(ctx context.Context, d *doctor) ([]conditions.Observation, error) } var machines []*machine for _, n := range nodes { - plan, _, err := planFor(ctx, open, n.Name) + plan, _, err := resolveNode(ctx, open, n.Name) if err != nil { if ctx.Err() != nil { return nil, ctx.Err() @@ -849,7 +851,7 @@ func deleteRetiredData(ctx context.Context, conn *nats.Conn, open *stores, r inv return fmt.Errorf("%s of %s on %s was never measured, so where it is was never said; nothing was deleted", r.Item, r.Module, r.Machine) } - if plan, _, err := planFor(ctx, open, r.Machine); err == nil { + if plan, _, err := resolveNode(ctx, open, r.Machine); err == nil { for _, m := range plan.Modules { if _, still := m.DataItem(r.Item); still && m.Module == r.Module { return fmt.Errorf("%s runs on %s again and declares %s: it is not retired any more. Nothing was done", diff --git a/cmd/mesh-controller/facts.go b/cmd/mesh-controller/facts.go index 83cbb7a7..5d9e9781 100644 --- a/cmd/mesh-controller/facts.go +++ b/cmd/mesh-controller/facts.go @@ -17,6 +17,7 @@ import ( "git.novox.be/novox/mesh-sdk/go/validate" "git.novox.be/novox/mesh-sdk/go/catalogue" + "git.novox.be/novox/mesh-sdk/go/compose" "github.com/novox/mesh-controller/internal/artifacts" "github.com/novox/mesh-controller/internal/broker" "github.com/novox/mesh-controller/internal/builder" @@ -263,7 +264,9 @@ func gatherFacts(ctx context.Context, open *stores, busVersion string) (snapshot f.Controller.Commit = c.Commit } - gens, gensErr := generators(ctx, open) + // The Input every machine's declaration below is composed over is read once for the whole snapshot. + ctx = withInputCache(ctx) + _, gensErr := generatorsOf(ctx, open) hostShelf := shelf[hostModule] meshWide := map[string]bool{} engines := map[string]bool{} @@ -361,7 +364,7 @@ func gatherFacts(ctx context.Context, open *stores, busVersion string) (snapshot m.Settings = append(m.Settings, snapshot.Settings{Module: module, Values: scrub.Values(layer.Whole())}) } } - m.Declaration = declarationFacts(ctx, open, n.Name, gens, gensErr, scrub) + m.Declaration = declarationFacts(ctx, open, n.Name, gensErr, scrub) if ctx.Err() != nil { return snapshot.Facts{}, ctx.Err() } @@ -456,14 +459,14 @@ func gatherFacts(ctx context.Context, open *stores, busVersion string) (snapshot // declarationFacts is how one machine's declaration composes now, as the next push would compose it and // without making anything (D1's composition), and whether the node-engine's validator takes it. -func declarationFacts(ctx context.Context, open *stores, node string, gens map[string]catalogue.Generator, - gensErr error, scrub *snapshot.Scrubber) snapshot.Declaration { +func declarationFacts(ctx context.Context, open *stores, node string, gensErr error, + scrub *snapshot.Scrubber) snapshot.Declaration { var d snapshot.Declaration if gensErr != nil { d.Problems = []string{scrub.Text("the private network cannot be computed: " + oneLine(gensErr.Error()))} return d } - declared, problems, err := composedAndValidated(ctx, open, node, gens, Foreseeing) + declared, problems, err := composeQuestion(ctx, open, node, compose.Foreseeing, true) if err != nil { d.Problems = []string{scrub.Text(oneLine(err.Error()))} return d diff --git a/cmd/mesh-controller/nodes.go b/cmd/mesh-controller/nodes.go index 6a30aba3..232bd89e 100644 --- a/cmd/mesh-controller/nodes.go +++ b/cmd/mesh-controller/nodes.go @@ -924,7 +924,7 @@ func showNode(ctx context.Context, stored *stores, name string) error { } // And which of them a push leaves out, and why (novox/hq issue 380): judged as the push judges it, the same // reason its condition says. Not computable is said, never read as "all applied". - plan, settings, err := planFor(ctx, stored, name) + plan, settings, err := resolveNode(ctx, stored, name) switch { case err != nil: fmt.Printf("\n whether a send leaves any assigned module out is NOT known: %s\n", oneLine(err.Error())) diff --git a/cmd/mesh-controller/probes.go b/cmd/mesh-controller/probes.go index c92e3659..b082fbb5 100644 --- a/cmd/mesh-controller/probes.go +++ b/cmd/mesh-controller/probes.go @@ -20,6 +20,7 @@ import ( "golang.org/x/net/dns/dnsmessage" "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/artifacts" "github.com/novox/mesh-controller/internal/broker" @@ -62,7 +63,9 @@ func probeDeclarations(ctx context.Context, d *doctor) ([]conditions.Observation // The private network every declaration is composed with. Not computable is not "could not // look": it is the finding — no machine's declaration composes without it — and the machines that // do not resolve, which are usually why, are named below. - gens, gensErr := generators(ctx, open) + // Every machine below is composed over one reading of the mesh. + ctx = withInputCache(ctx) + _, gensErr := generatorsOf(ctx, open) if gensErr != nil { if ctx.Err() != nil { return nil, ctx.Err() @@ -73,7 +76,7 @@ func probeDeclarations(ctx context.Context, d *doctor) ([]conditions.Observation Said: firstLine(gensErr.Error())}) } for _, n := range nodes { - plan, settings, err := planFor(ctx, open, n.Name) + plan, settings, err := resolveNode(ctx, open, n.Name) if err != nil && !unresolvable(err) { return nil, fmt.Errorf("%s cannot be worked out: %w", n.Name, err) } @@ -85,7 +88,7 @@ func probeDeclarations(ctx context.Context, d *doctor) ([]conditions.Observation } if err == nil && gensErr == nil { var declared sendable - if declared, err = declarationWith(ctx, open, n.Name, plan, settings, gens, Foreseeing); err == nil { + if declared, _, err = composeQuestion(ctx, open, n.Name, compose.Foreseeing, false); err == nil { foreseen = declared.foreseen // With the order it was last sent, so the validator reads the envelope a machine is sent // — its epoch included (novox/hq to-be 45 §6). diff --git a/cmd/mesh-controller/recorded_push.go b/cmd/mesh-controller/recorded_push.go index 676dbd19..c6ca5de3 100644 --- a/cmd/mesh-controller/recorded_push.go +++ b/cmd/mesh-controller/recorded_push.go @@ -93,7 +93,7 @@ func recordedPushOf(ctx context.Context, node string) ([]string, string, error) } defer open.Close() inv := open.inventory - plan, _, err := planFor(ctx, open, node) + plan, _, err := resolveNode(ctx, open, node) if err != nil { return nil, "", fmt.Errorf("its set cannot be worked out: %w", err) } diff --git a/cmd/mesh-controller/registry_verbs.go b/cmd/mesh-controller/registry_verbs.go index 286cb7b0..c89f695d 100644 --- a/cmd/mesh-controller/registry_verbs.go +++ b/cmd/mesh-controller/registry_verbs.go @@ -400,7 +400,7 @@ func imagesCommand(ctx context.Context, args []string) error { } // What the mesh would send now, composed as `plan` composes it. var body []byte - plan, settings, err := planFor(ctx, open, node) + plan, settings, err := resolveNode(ctx, open, node) if err != nil { return err } diff --git a/cmd/mesh-controller/release.go b/cmd/mesh-controller/release.go index 5df0785a..ff063b71 100644 --- a/cmd/mesh-controller/release.go +++ b/cmd/mesh-controller/release.go @@ -247,7 +247,7 @@ func (f moveFacts) walkedBy(module, node, to string) string { // machineMoves is the moves no gate has seen on one machine, read from what it would be sent now. var machineMoves = func(ctx context.Context, open *stores, f moveFacts, node string, all bool) ([]inventory.CarriedMove, error) { - plan, _, err := planFor(ctx, open, node) + plan, _, err := resolveNode(ctx, open, node) if err != nil { return nil, nil // it cannot be worked out: the send says why } diff --git a/cmd/mesh-controller/status.go b/cmd/mesh-controller/status.go index 12f0c92c..5d262e36 100644 --- a/cmd/mesh-controller/status.go +++ b/cmd/mesh-controller/status.go @@ -406,6 +406,8 @@ type builds struct { // anything out of date. The first has consequences now, the second may, the third is a plan for // later — and anything that led with the third would bury the first. func theThreeQuestions(ctx context.Context, open *stores) (answers, error) { + // One reading of the mesh for every machine this looks at. + ctx = withInputCache(ctx) inv := open.inventory var out answers var err error @@ -429,7 +431,7 @@ func theThreeQuestions(ctx context.Context, open *stores) (answers, error) { // // Read through whoResolves, which is what the private network is built from, so this and the // network agree about who could not be resolved rather than deciding it twice. - _, out.refused, err = whoResolves(ctx, open, overlay.Addressing) + _, out.refused, err = whoResolvesOn(ctx, open, overlay.Addressing) if err != nil { return answers{}, err } @@ -453,7 +455,7 @@ func theThreeQuestions(ctx context.Context, open *stores) (answers, error) { // resolve is already in refused, and is passed over here. plans := map[string]planned{} for _, n := range out.nodes { - plan, settings, err := planFor(ctx, open, n.Name) + plan, settings, err := resolveNode(ctx, open, n.Name) if err != nil { if unresolvable(err) { continue