From f77c130dcdab5e1af257779bf31f124a58130680 Mon Sep 17 00:00:00 2001 From: mesh-tools Date: Tue, 20 Oct 2026 11:16:13 +0000 Subject: [PATCH] stage 6 of 8 for issue 1389 --- cmd/mesh-controller/plan.go | 400 ------------------ .../provisioner_account_integration_test.go | 8 +- cmd/mesh-controller/push.go | 6 +- cmd/mesh-controller/push_recreates_test.go | 10 +- cmd/mesh-controller/recorded_kept.go | 10 - .../recorded_kept_integration_test.go | 14 +- cmd/mesh-controller/recorded_moves_test.go | 2 +- cmd/mesh-controller/replay164_test.go | 2 +- cmd/mesh-controller/replay201_test.go | 2 +- cmd/mesh-controller/replay237_test.go | 2 +- cmd/mesh-controller/replay401_test.go | 2 +- .../replays_integration_test.go | 4 +- cmd/mesh-controller/roster_failure_test.go | 4 +- 13 files changed, 23 insertions(+), 443 deletions(-) diff --git a/cmd/mesh-controller/plan.go b/cmd/mesh-controller/plan.go index 0f479621..bd0a70fe 100644 --- a/cmd/mesh-controller/plan.go +++ b/cmd/mesh-controller/plan.go @@ -262,134 +262,6 @@ func certificateFor(ctx context.Context, open *stores, node string) (string, str return issued, authority.Certificate, nil } -// grantsFor is every credential this node must create, because something elsewhere uses it. -// -// The mirror of what a consumer is given, and the half that makes the credential real: a password -// nothing was told to create is a password that authenticates nowhere. Sealed to this node, so -// the mesh hands over something it cannot itself use. -// -// **One consumer's identity never refuses the provider's machine** (novox/hq ADR 0225, issue 263). -// A consumer whose identity overflows the provision's bound is left out of the grants and returned -// beside them, for push, plan and `status` to say; every other consumer is granted and the provider's -// declaration composes. Refusing here once made a whole machine unpushable for one module elsewhere. -// -// **A provider is granted exactly the consumers whose own resolution binds them to it** (novox/hq -// issue 274). The pair credentials on record say only whom this node was ever asked by: after a -// consumer of a provision that keeps its data was moved and pinned back (issue 273, ADR 0232), the -// credential from the provider it left was still on record, so that provider went on being asked for -// five databases nobody used and never retired them. A credential whose consumer is bound elsewhere -// is withdrawn here like one nobody asks for — the provider retires it and keeps its data (ADR 0230) -// — and returned beside the grants, for plan and push to say. It stays on record: it is the key to -// the login the provider keeps until `cleanup delete`, and to that data should a person pin it back. -func grantsFor(ctx context.Context, open *stores, node string) ( - []catalogue.Grant, []catalogue.Overflow, []catalogue.Unbound, error) { - inv := open.inventory - issued, err := inv.SecretsFrom(ctx, node) - if err != nil { - return nil, nil, nil, err - } - - // Where each consumer is, so a provider that must reach back to one does not have to know how - // the mesh names machines. - shelf, err := inv.Catalogue(ctx) - if err != nil { - return nil, nil, nil, err - } - onNetwork, err := whereEveryoneIs(ctx, inv, shelf) - if err != nil { - return nil, nil, nil, err - } - - // What each consumer actually asked for, taken from that machine's own resolution rather than - // from a record beside it. A provider told to create a password and not what to create it for - // can do nothing with it, and the name a consumer wants is the consumer's to say. - out := make([]catalogue.Grant, 0, len(issued)) - var withheld []catalogue.Overflow - var unbound []catalogue.Unbound - for _, s := range issued { - plan, settings, err := planFor(ctx, open, s.Consumer) - switch { - case unresolvable(err): - // Their set does not resolve. Skipped rather than fatal: this node is not the place - // to report another machine's problem, and a grant for something that is not going to - // run would have the provider create a user nothing uses. - continue - case err != nil: - // The mesh could not be asked what they wanted, which is not the same as their wanting - // nothing — and withholding a grant on that reading takes a consumer's access away - // (novox/hq 04-ISSUES/152). - return nil, nil, nil, fmt.Errorf("what %s asked of %s cannot be read: %w", s.Consumer, s.Name, err) - } - values, asks, err := plan.ContributionsFrom(s.Name, s.ConsumerModule, settings) - if err != nil { - return nil, nil, nil, err - } - // A port in there is the consumer's software port until this. The consumer is on another - // machine, so the assignment that moved it is that machine's — fetched here rather than - // looked for in this one's, which is the whole reason the co-located fix could not reach - // this case (novox/hq 04-ISSUES/038, the cross-node half). - published, err := portsOn(ctx, inv, s.Consumer, s.ConsumerModule) - if err != nil { - return nil, nil, nil, err - } - values = catalogue.AtPublishedPort(values, s.ConsumerModule, published) - from := s.ConsumerModule - if !asks { - // That module no longer wants this. Left empty, which is what the declaration reads - // as "nobody asks for it any more" — and is how a login is withdrawn rather than kept - // working for ever after its consumer went away. - from = "" - } - if bound := plan.BindsFrom(s.Name, s.ConsumerModule, s.Local); from != "" && !slices.Contains(bound, node) { - // It still asks, and not of this node: its resolution — the same one read above, so an - // unreadable one is the error above and never an empty answer here (issue 152) — binds - // this credential to another provider, or under this local name to none. Withdrawn - // exactly as a credential nobody asks for, and said. - from = "" - unbound = append(unbound, catalogue.Unbound{Provision: s.Name, Provider: node, - Consumer: s.Consumer, Module: s.ConsumerModule, Local: s.Local, BoundTo: bound}) - } - // The consumer's identity slug, from its own manifest, carried on the grant so the provider - // derives the same login the consumer does (novox/hq ADR 0049). Judged against the bound of - // this provision, as the consumer's resolution states it from the provider's offer (ADR - // 0225): a consumer it would not fit is left out of the grants and said, rather than a login a - // provider silently shortened — and rather than this whole machine refused for it. - slug := "" - for _, mm := range plan.Modules { - if mm.Module == s.ConsumerModule { - slug = mm.Slug - break - } - } - if from != "" { - bound := boundOfGrant(plan, s, node) - source := catalogue.IdentitySource(slug, s.ConsumerModule) - if catalogue.CheckIdentityWithin(s.Consumer, source, bound) != nil { - withheld = append(withheld, catalogue.Overflow{Provision: s.Name, Provider: node, - Consumer: s.Consumer, Module: s.ConsumerModule, - Identity: catalogue.ConsumerIdentity(s.Consumer, source), Bound: bound}) - continue - } - } - out = append(out, catalogue.Grant{ - Provision: s.Name, Consumer: s.Consumer, At: onNetwork[s.Consumer], - From: from, Values: values, Slug: slug, Sealed: s.ForProvider, Local: s.Local}) - } - return out, withheld, unbound, nil -} - -// boundOfGrant is the identity bound the consumer's own resolution states for the requirement this -// grant answers. A requirement not found there is held to the tightest bound the mesh knows rather -// than to none: what the provider keeps of it is not known here. -func boundOfGrant(consumer catalogue.Resolution, s inventory.Secret, provider string) catalogue.IdentityBound { - for _, n := range consumer.Needs { - if n.Name == s.Name && n.For == s.ConsumerModule && n.From == provider { - return n.Identity - } - } - return catalogue.DefaultIdentityBound -} - // listensLines is what a person is told about what this module would open, and why — the same // `why` every listens entry already carries for the firewall it also feeds (novox/hq ADR 0007), so // deciding whether to assign a module can see what it would open before it opens it, not only @@ -596,91 +468,6 @@ func shownAs(r map[string]any) string { return heading } -// licencesFor is what this node can be answered with by record, and what it was put on. -// -// A mesh with no licences at all is the ordinary case and must not be an error: every existing -// mesh is one, and a control plane that refused to plan because nobody had bought an API key -// would be unusable for the thing it already does. -func licencesFor(ctx context.Context, open *stores, node string) ( - map[string][]catalogue.Record, map[string]map[string]catalogue.Record, error) { - - held, err := open.Licences(ctx) - if err != nil { - return nil, nil, err - } - - all, err := held.All(ctx) - if err != nil { - return nil, nil, err - } - if len(all) == 0 { - return nil, nil, nil - } - - offered := map[string][]catalogue.Record{} - byName := map[string]catalogue.Record{} - for _, one := range all { - record := catalogue.Record{Name: one.Name, Serves: one.Serves} - offered[licences.Provision] = append(offered[licences.Provision], record) - byName[one.Name] = record - } - - using := map[string]map[string]catalogue.Record{} - for _, one := range all { - holders, err := held.HoldersOf(ctx, one.Name) - if err != nil { - return nil, nil, err - } - for _, h := range holders { - if h.Node != node { - continue - } - if using[h.Module] == nil { - using[h.Module] = map[string]catalogue.Record{} - } - using[h.Module][licences.Provision] = byName[one.Name] - } - } - return offered, using, nil -} - -// keyFor is the licence key sealed to one machine, for one module. -// -// **Empty is not an error here.** The mesh discarded the plaintext when it was supplied, so a -// holder recorded afterwards genuinely has no key — and the declaration refuses that by name, -// where the module and the path are both in view, rather than here. -func keyFor(ctx context.Context, open *stores, licence, node, module string) (string, error) { - held, err := open.Licences(ctx) - if err != nil { - return "", err - } - return held.KeyFor(ctx, licence, node, module) -} - -// managerPublicKeyFor is the manager node's public sealing key, but only when (node, module) is the -// licence's manager holder — empty otherwise. -// -// It is delivered to the manager module in its bound facts so it can re-seal a rotated refresh token -// to this node (novox/hq ADR 0050). Public, so it travels in the clear like any other bound fact; and -// scoped to the manager holder alone, so a consumer never receives it and nothing invites a consumer -// to seal anything. -func managerPublicKeyFor( - ctx context.Context, open *stores, inv *inventory.Inventory, licence, node, module string, -) (string, error) { - held, err := open.Licences(ctx) - if err != nil { - return "", err - } - managerNode, managerModule, err := held.ManagerOf(ctx, licence) - if err != nil { - return "", err - } - if managerNode == "" || node != managerNode || module != managerModule { - return "", nil - } - return inv.SealingKeyOf(ctx, node) -} - // servedOnNode is what a module on a node tells a consumer of one of its provisions, with THAT // node's ports on it: the port the node was given (novox/hq ADR 0100) over the one the mesh // assigned over the manifest's own, settled with the node's settings layers. @@ -725,76 +512,6 @@ func portsGivenOn(ctx context.Context, inv *inventory.Inventory, node string, return ports, layers, nil } -// seatsOn is where a node put the holder of each mesh-scoped seat, by seat and by the port the -// holder's software uses — what ${seat:…} answers with (novox/hq 04-ISSUES/102). -// -// Read for every module in the catalogue that claims a seat, in this node's set or not: the store -// and the broker are given their ports at genesis, as settings on a module that may be registered -// and not yet assigned (04-ISSUES/085), and the control plane must follow that setting from the -// first declaration it composes for itself. A holder in this node's set wins over one that is not. -func seatsOn(ctx context.Context, inv *inventory.Inventory, shelf map[string]catalogue.Manifest, - node string, inSet []catalogue.Manifest, adopted bool, taken map[string]bool) (map[string]map[int]int, error) { - assigned := map[string]bool{} - for _, m := range inSet { - assigned[m.Module] = true - } - names := make([]string, 0, len(shelf)) - for name := range shelf { - names = append(names, name) - } - sort.Strings(names) - seats := map[string]map[int]int{} - for _, name := range names { - m := shelf[name] - var claims []string - for _, c := range m.Claims { - if c.At() == catalogue.ScopeMesh { - claims = append(claims, c.Name) - } - } - if len(claims) == 0 { - continue - } - // **Only a port the node was given or the mesh assigned — never the manifest's own - // number.** The sealed value the answer sits beside carries the port genesis wrote, which - // on a given-port node is the predecessor's; a manifest's long-form mapping is the - // catalogue's default, and answering with it would override the right number with one - // the mesh never checked (the contract in seat_into.go). And on an adopted node a holder - // assigned but not yet taken is the found container, on the ports it was found with, not - // the declaration's — so its mesh-assigned ports do not count there either; a given port - // does, because a given port is the found one by construction (ADR 0100). - ports, _, err := portsGivenOn(ctx, inv, node, m) - if err != nil { - return nil, err - } - if adopted && !taken[name] { - layers, err := inv.SettingsFor(ctx, node, m.Module) - if err != nil { - return nil, err - } - ports = map[int]int{} - if given, err := catalogue.GivenPorts(m, layers); err == nil { - ports = given - } - } - if len(ports) == 0 { - continue - } - for _, seat := range claims { - if seats[seat] == nil { - seats[seat] = map[int]int{} - } - for wanted, at := range ports { - if _, said := seats[seat][wanted]; said && !assigned[name] { - continue - } - seats[seat][wanted] = at - } - } - } - return seats, nil -} - // portsOn is one module's assignments on one machine, by the port the software uses. func portsOn( ctx context.Context, inv *inventory.Inventory, node, module string, @@ -812,45 +529,6 @@ func portsOn( return out, nil } -// composeBusUsers is the bus's user list, for a push to the machine that runs the bus. -// -// Empty for every other machine, and for every machine while the mesh is on the bus it runs on -// today — where accounts are a management call and there is no file to write. -// -// **Composed on each push, never kept.** The list is a function of the mesh's records (who exists, -// what runs where, what each declares), and a stored copy would be a second account of who may reach -// the bus, able to disagree with the records while both looked internally consistent (ADR 0043). -// -// A user the mesh has never minted a password for is **left out and said**, not written as a user -// without one — the composer refuses that, because a user with no password is a user anybody is. That -// is an ordinary situation with an obvious remedy (`module issue`, or enrolling), so the push carries -// the rest rather than failing: a bus that is missing one module's user is a mesh where that module -// cannot connect, and a bus with no file at all is a mesh where nothing can. -func composeBusUsers(ctx context.Context, inv *inventory.Inventory, - onThisNode []catalogue.Manifest) (string, error) { - - // **Not gated on which bus the controller is on, and that was a bug.** It read "compose this only - // once the mesh is on the new bus" — which cannot work, because the server needs its user list - // *before* anything moves onto it. Step 2 of the change is exactly that: the server stands in the - // mesh carrying nothing, on its own ports, while every node is still on the old bus (novox/hq - // ADR 0116). Under the old gating that step could not happen: the module would come up, find no - // accounts file, and its entrypoint would wait for one the controller had decided not to write. - // - // So the question is only whether this machine runs the module that asked for the file. A mesh - // that never moves has written a user list nothing reads, which costs a few hundred bytes on one - // node; the reverse cost a step that cannot be taken. - // - // Asked of what this push resolves to rather than of the seat's holder mesh-wide: the file is a - // resource of that module, so the question is whether it is here. - list, missing, err := busUserList(ctx, inv, onThisNode) - if len(missing) > 0 { - fmt.Printf("the bus's user list leaves out %d user(s) the mesh has minted no credential "+ - "for: %s. Each is a user that cannot connect until one is issued\n", - len(missing), strings.Join(missing, ", ")) - } - return list, err -} - // busUserList is composeBusUsers without saying anything: the list, and the users left out of it // for want of a credential. Asked on every send to decide whether the machine holding the bus must // go first (novox/hq issue 249), where saying the same missing users each time would bury them. @@ -905,60 +583,6 @@ func issuedOutsideBy(shelf map[string]catalogue.Manifest, n catalogue.Needed) (s return catalogue.OffersIssuedOutside(shelf, n.Name) } -// issuedToHolder is whether the vault keeps a need's credential for its holder alone (issues 749, 757 and 779): its -// providing module is marked moved, so the vault holds the credential in a row of its own for each holder -// (filledTheNewWay), and its offer says either that the provider's software issues the provision's credential, or -// that the provider gives each consumer a login of its own (form pair, ADR 0158 §11). A credential every holder -// shares, or one a person gives, is neither. -func issuedToHolder(shelf map[string]catalogue.Manifest, n catalogue.Needed) bool { - if n.ByRecord || n.Module == "" { - return false - } - provider, known := shelf[n.Module] - if !known || !movedProvider(provider) { - return false - } - if _, issued := provider.IssuedCredentialOf(n.Name); issued { - return true - } - return provider.CredentialFormOf(n.Name) == catalogue.CredentialFormPair -} - -// providerMoved is whether a need is answered by a module marked moved (issue 788), or by one whose offer of the -// provision says the provider helper answers it (issue 792). -func providerMoved(shelf map[string]catalogue.Manifest, n catalogue.Needed) bool { - if n.ByRecord || n.Module == "" { - return false - } - provider, known := shelf[n.Module] - return known && (movedProvider(provider) || provider.AnsweredByHelperFor(n.Name)) -} - -// movedProvider is whether a provider's credentials are the vault's: the module is marked moved, or it holds the -// mesh-vault seat, whose pair rows have been in the vault since V3 (issue 793), so its consumers' credentials -// (gitea's internal token and admin password) are filled from those very rows with the values unchanged. -func movedProvider(m catalogue.Manifest) bool { - return m.Moved || slices.ContainsFunc(m.Claims, func(c catalogue.Claim) bool { return c.Name == "mesh-vault" }) -} - -func providerModuleOf(resolved catalogue.Resolution, open *stores, ctx context.Context, n catalogue.Needed) string { - for _, m := range resolved.Modules { - if _, shared := m.SharedCredentialOf(n.Name); shared { - return m.Module - } - } - shelf, err := open.inventory.Catalogue(ctx) - if err != nil { - return "" - } - for name, m := range shelf { - if _, shared := m.SharedCredentialOf(n.Name); shared { - return name - } - } - return "" -} - // unheldLogger remembers what was last logged about each node's unmet seat dependencies, so the // log says each change once (novox/hq ADR 0207), on stderr so `status --json` stays a document. // A test gives its own writer and memory, and no longer shares the process's (novox/hq issue 486). @@ -1074,27 +698,3 @@ func sortedNames[V any](m map[string]V) []string { sort.Strings(out) return out } - -// wireBuilds is the build of each carried module that goes on the wire (novox/hq issue 1145): the build its assignment -// names when that assignment is of the commit carried, else the build made from that commit; a module with neither -// is not named. -func wireBuilds(carried map[string]string, runs map[string]chain.Ref, lookup func(module, commit string) (string, error)) (map[string]string, error) { - out := map[string]string{} - for module, commit := range carried { - if commit == "" { - continue - } - if ref, ok := runs[module]; ok && ref.Commit == commit && ref.Build != "" { - out[module] = ref.Build - continue - } - build, err := lookup(module, commit) - if err != nil { - return nil, err - } - if build != "" { - out[module] = build - } - } - return out, nil -} diff --git a/cmd/mesh-controller/provisioner_account_integration_test.go b/cmd/mesh-controller/provisioner_account_integration_test.go index 428a9d74..67750cd7 100644 --- a/cmd/mesh-controller/provisioner_account_integration_test.go +++ b/cmd/mesh-controller/provisioner_account_integration_test.go @@ -8,6 +8,7 @@ import ( "testing" "git.novox.be/novox/mesh-sdk/go/catalogue" + "git.novox.be/novox/mesh-sdk/go/compose" "github.com/novox/mesh-controller/internal/broker" "github.com/novox/mesh-controller/internal/inventory" ) @@ -21,8 +22,9 @@ func TestTheProvisionerClientsAccountIsIssuedForItsServiceAndNeverInvented(t *te if _, err := assign(ctx, open, "laptop", m.Module); err != nil { t.Fatal(err) } - if err := busCredentialIssued(ctx, inv, "laptop", m.Module, broker.ProvisionerSecret); err == nil { - t.Fatal("a push would seal a placeholder for the client's account before it was issued") + // The composition refuses it in the send's words (mesh-sdk's go/compose: TestTheSendsRefusalsAreRefusedInTheSendsWords). + if _, _, err := composeQuestion(ctx, open, "laptop", compose.Foreseeing, false); err == nil || !strings.Contains(err.Error(), "has no bus credential") { + t.Fatalf("a push would seal a placeholder for the client's account before it was issued: %v", err) } if err := issueProvisionerClient(ctx, inv, m, "laptop", broker.Broker{Fingerprint: "ab"}, "10.0.0.1:4222"); err != nil { @@ -38,7 +40,7 @@ func TestTheProvisionerClientsAccountIsIssuedForItsServiceAndNeverInvented(t *te if !slices.ContainsFunc(states, func(s inventory.SecretState) bool { return s.Name == broker.ProvisionerSecret }) { t.Fatalf("the client's account is not sealed to the machine for its service: %+v", states) } - if err := busCredentialIssued(ctx, inv, "laptop", m.Module, broker.ProvisionerSecret); err != nil { + if _, _, err := composeQuestion(ctx, open, "laptop", compose.Foreseeing, false); err != nil && strings.Contains(err.Error(), "has no bus credential") { t.Fatalf("an issued client account is refused: %v", err) } if !runsTheProvisionerClient(m) { diff --git a/cmd/mesh-controller/push.go b/cmd/mesh-controller/push.go index 3a29e07d..241a5012 100644 --- a/cmd/mesh-controller/push.go +++ b/cmd/mesh-controller/push.go @@ -522,8 +522,8 @@ func pushCommand(ctx context.Context, args []string) error { return nil } } - gens, err := generatorsOf(ctx, open) - if err != nil { + // The private network every declaration of the push is composed with: not computable refuses the push up front. + if _, err := generatorsOf(ctx, open); err != nil { return err } @@ -724,7 +724,7 @@ func pushCommand(ctx context.Context, args []string) error { keep := keeping(func(held context.Context, sending []readyNode) ([]readyNode, error) { return holdingBack(held, inv, sending, movable, &heldBack) }) - refused, err := flushBehind(ctx, open, nodes, handled, composeForPush(open, gens), bus, holder, keep, os.Stdout) + refused, err := flushBehind(ctx, open, nodes, handled, composeForPush(open), bus, holder, keep, os.Stdout) refusals = append(refusals, refused...) if err != nil { return err diff --git a/cmd/mesh-controller/push_recreates_test.go b/cmd/mesh-controller/push_recreates_test.go index d17cc610..d8361730 100644 --- a/cmd/mesh-controller/push_recreates_test.go +++ b/cmd/mesh-controller/push_recreates_test.go @@ -48,15 +48,7 @@ func TestAPushSaysWhatItRecreates(t *testing.T) { } // Composed as a person's push composes it. - plan, settings, err := planFor(ctx, open, "anchor") - if err != nil { - t.Fatal(err) - } - gens, err := generators(ctx, open) - if err != nil { - t.Fatal(err) - } - declared, err := declarationWith(ctx, open, "anchor", plan, settings, gens, Allocating) + declared, err := composeSend(ctx, open, "anchor") if err != nil { t.Fatal(err) } diff --git a/cmd/mesh-controller/recorded_kept.go b/cmd/mesh-controller/recorded_kept.go index 1ecd9864..7e16c74e 100644 --- a/cmd/mesh-controller/recorded_kept.go +++ b/cmd/mesh-controller/recorded_kept.go @@ -180,16 +180,6 @@ func recordedKept(ctx context.Context, open *stores, node string) (map[string]st return out, nil } -// keepRecorded puts, in a shelf about to be resolved for a machine, the build each recorded module there -// runs in place of the one the mesh holds; it answers what it kept, module → commit. -func keepRecorded(ctx context.Context, open *stores, node string, shelf map[string]catalogue.Manifest) (map[string]string, error) { - kept, err := recordedKept(ctx, open, node) - if err != nil || len(kept) == 0 { - return nil, err - } - return kept, applyKept(ctx, open, node, kept, shelf) -} - // applyKept puts the manifest of each module kept at the build its machine runs into a shelf, and refuses, in the words of // keepRecorded, a build the records no longer hold. The adapter to the composition's input (compose_input.go) calls it with // a scratch shelf, to read the manifests it gives the node as its own Kept. diff --git a/cmd/mesh-controller/recorded_kept_integration_test.go b/cmd/mesh-controller/recorded_kept_integration_test.go index 2eb39ae3..8f96201e 100644 --- a/cmd/mesh-controller/recorded_kept_integration_test.go +++ b/cmd/mesh-controller/recorded_kept_integration_test.go @@ -63,7 +63,7 @@ func TestARecordedBuildIsCarriedOnlyByAPersonsPush(t *testing.T) { // A plan's, a release plan's, a healer's send: the database is kept as it runs, mail moves. kept := keepingRecorded(ctx) - plan, settings, err := planFor(kept, open, "anchor") + plan, _, err := resolveNode(kept, open, "anchor") if err != nil { t.Fatal(err) } @@ -76,11 +76,7 @@ func TestARecordedBuildIsCarriedOnlyByAPersonsPush(t *testing.T) { if imageOf(t, plan, "postgres", "server") != pgImage { t.Fatal("the recorded module's container lost its image") } - gens, err := generators(kept, open) - if err != nil { - t.Fatal(err) - } - declared, err := declarationWith(kept, open, "anchor", plan, settings, gens, Allocating) + declared, err := composeSend(kept, open, "anchor") if err != nil { t.Fatal(err) } @@ -89,7 +85,7 @@ func TestARecordedBuildIsCarriedOnlyByAPersonsPush(t *testing.T) { } // A person's push: the recorded module's new build. - plan, _, err = planFor(ctx, open, "anchor") + plan, _, err = resolveNode(ctx, open, "anchor") if err != nil { t.Fatal(err) } @@ -98,7 +94,7 @@ func TestARecordedBuildIsCarriedOnlyByAPersonsPush(t *testing.T) { } // The bus step moves the bus alone: a recorded module it is told it may move moves, the others stay. - plan, _, err = planFor(keepingRecorded(ctx, "postgres"), open, "anchor") + plan, _, err = resolveNode(keepingRecorded(ctx, "postgres"), open, "anchor") if err != nil { t.Fatal(err) } @@ -128,7 +124,7 @@ func TestARecordedBuildIsCarriedOnlyByAPersonsPush(t *testing.T) { map[string]string{"postgres": "c0000000", "mailu": "c2222222"}); err != nil { t.Fatal(err) } - if _, _, err := planFor(kept, open, "anchor"); err == nil || !strings.Contains(err.Error(), "push anchor") { + if _, _, err := resolveNode(kept, open, "anchor"); err == nil || !strings.Contains(err.Error(), "push anchor") { t.Fatalf("a recorded build that cannot be kept did not refuse the send with its remedy: %v", err) } diff --git a/cmd/mesh-controller/recorded_moves_test.go b/cmd/mesh-controller/recorded_moves_test.go index 18a759b1..64b705e3 100644 --- a/cmd/mesh-controller/recorded_moves_test.go +++ b/cmd/mesh-controller/recorded_moves_test.go @@ -349,7 +349,7 @@ func TestARecordedMoveThatCannotBeJudgedWaits(t *testing.T) { !strings.Contains(v.said, "not in the build records") { t.Fatalf("an unjudgeable move: %+v", v) } - if _, _, err := planFor(keepingRecorded(t.Context()), rm.open, "anchor"); err == nil || !strings.Contains(err.Error(), "push anchor") { + if _, _, err := resolveNode(keepingRecorded(t.Context()), rm.open, "anchor"); err == nil || !strings.Contains(err.Error(), "push anchor") { t.Fatalf("a send that cannot keep the unjudged build did not refuse with its remedy: %v", err) } // laptop, judged from c1, goes; anchor waits. diff --git a/cmd/mesh-controller/replay164_test.go b/cmd/mesh-controller/replay164_test.go index d0da5f70..d05d2362 100644 --- a/cmd/mesh-controller/replay164_test.go +++ b/cmd/mesh-controller/replay164_test.go @@ -34,7 +34,7 @@ func TestReplay164(t *testing.T) { } sent := func() []map[string]any { t.Helper() - plan, settings, err := planFor(ctx, open, "laptop") + plan, settings, err := resolveNode(ctx, open, "laptop") if err != nil { t.Fatalf("the consumer's machine was refused for a credential only a person gives: %v", err) } diff --git a/cmd/mesh-controller/replay201_test.go b/cmd/mesh-controller/replay201_test.go index 274e527c..6e37021b 100644 --- a/cmd/mesh-controller/replay201_test.go +++ b/cmd/mesh-controller/replay201_test.go @@ -71,7 +71,7 @@ func TestReplay201ThePlansSend(t *testing.T) { if keeping { c = keepingRecorded(ctx) } - plan, settings, err := planFor(c, open, "anchor") + plan, settings, err := resolveNode(c, open, "anchor") if err != nil { t.Fatalf("%s to anchor did not compose: %v", sends, err) } diff --git a/cmd/mesh-controller/replay237_test.go b/cmd/mesh-controller/replay237_test.go index 649d901c..4fb913cc 100644 --- a/cmd/mesh-controller/replay237_test.go +++ b/cmd/mesh-controller/replay237_test.go @@ -34,7 +34,7 @@ func TestReplay237(t *testing.T) { said := answered(t, func() error { return assignCommand(ctx, "assign", []string{"laptop", "docker"}) }) - if _, _, err := planFor(ctx, open, "laptop"); err != nil { + if _, _, err := resolveNode(ctx, open, "laptop"); err != nil { t.Fatalf("laptop does not resolve right after the act: %v\nthe answer was:\n%s", err, said) } if !strings.Contains(said, "web on laptop now has "+catalogue.ContainerRuntimeSeat+" held") { diff --git a/cmd/mesh-controller/replay401_test.go b/cmd/mesh-controller/replay401_test.go index b8c3d08c..12a489f3 100644 --- a/cmd/mesh-controller/replay401_test.go +++ b/cmd/mesh-controller/replay401_test.go @@ -33,7 +33,7 @@ func TestReplay401(t *testing.T) { } sent := func() []map[string]any { t.Helper() - plan, settings, err := planFor(ctx, open, "laptop") + plan, settings, err := resolveNode(ctx, open, "laptop") if err != nil { t.Fatal(err) } diff --git a/cmd/mesh-controller/replays_integration_test.go b/cmd/mesh-controller/replays_integration_test.go index ed787715..da467d3d 100644 --- a/cmd/mesh-controller/replays_integration_test.go +++ b/cmd/mesh-controller/replays_integration_test.go @@ -35,7 +35,7 @@ func TestReplay263AnIdentityTooLongNeverRefusesItsProvidersMachine(t *testing.T) } // The consumer's machine composes first, as a push does: what it is sent is what its provider grants. for _, node := range []string{"laptop", "anchor"} { - plan, settings, err := planFor(ctx, open, node) + plan, settings, err := resolveNode(ctx, open, node) if err != nil { t.Fatalf("%s does not resolve: %v", node, err) } @@ -84,7 +84,7 @@ func TestReplay273AConsumerBesideItsStoreStaysBoundToIt(t *testing.T) { assignAll([2]string{"laptop", "store"}, [2]string{"laptop", "resolver"}, [2]string{"laptop", "network"}, [2]string{"laptop", "board"}) - plan, _, err := planFor(ctx, open, "laptop") + plan, _, err := resolveNode(ctx, open, "laptop") if err != nil { t.Fatal(err) } diff --git a/cmd/mesh-controller/roster_failure_test.go b/cmd/mesh-controller/roster_failure_test.go index 2479f38c..97a0cca1 100644 --- a/cmd/mesh-controller/roster_failure_test.go +++ b/cmd/mesh-controller/roster_failure_test.go @@ -22,7 +22,7 @@ func TestASetThatDoesNotComposeIsMarkedAsTheNodesOwnProblem(t *testing.T) { } } - _, _, err := planFor(t.Context(), open, "laptop") + _, _, err := resolveNode(t.Context(), open, "laptop") if err == nil { t.Fatal("two modules claiming one seat composed anyway") } @@ -38,7 +38,7 @@ func TestAStoreThatCannotBeReadIsNotANodeThatDoesNotCompose(t *testing.T) { stopped, cancel := context.WithCancel(t.Context()) cancel() - _, _, err := planFor(stopped, open, "anchor") + _, _, err := resolveNode(stopped, open, "anchor") if err == nil { t.Fatal("a plan composed against a store that could not be read") }