Every caller composes through mesh-sdk's go/compose since issues 1386 to 1388; the controller's second copy of the composition is dead code. The parent of this commit isfb556d6b, whose try (build-1792493691274712799, 17m28s, 2026-10-20 11:12 UTC) ran the equality test green on the live facts: every machine was sent and compared both ways (ADR 0453, issue 1384). Deleted, 40 functions: in plan.go planFor, declarationWith, renderingFor, grantsFor, theRestOfTheMesh and their helpers (boundOfGrant, busCredentialIssued, carriedBuilds, carriedOn, composeBusUsers, engineJudgesRoot, engineReadsHealth, engineTakesHanded, takesHanded, givenUnderAnOldKey, issuedToHolder, keyFor, licencesFor, managerPublicKeyFor, movedProvider, providerMoved, providerModuleOf, sayLeftOut, seatsOn, wireBuilds, zonesInTheMesh); in network.go generators, whoResolves, namesInTheMesh, onThePrivateNetwork; composedAndValidated, adoptionFor, adoptionOf, engineOpensVaultBox, namedModules, localNetworksOf, keepRecorded, passSeatsOn, theMeshPassesSeatsOn, assignmentsOf. Choosing (Reading, Allocating, Foreseeing) is gone: coreBoxesFor and coreBoxesOf take a plain allocating flag. composeForPush no longer takes the private network it ignored. The seat pass of a send is performSeatPasses, over the passes the composition names. Measured: cmd/mesh-controller non-test code 67,643 -> 65,821 lines (-1,822), its tests 57,691 -> 57,286, with the integration part of the equality test removed here because it calls the deleted functions. Not the issue's 3,000: that counts all of plan.go (2,192 lines), of which the callers, the verbs, the perform step and the sends remain (sendable, recorded builds, the held-back and ready-node logic, the plan verb, the gate's trial); and the 37 functions a name walk lists as unreachable are called through interfaces and method sets and are kept. Tests dropped, each with the mesh-sdk go/compose test that holds its cases: - seat_pass_test.go TestACompositionWithTwoClaimantsRecordsNoneAndSaysSo, TestACompositionChangesNothingWhileTheHolderIsAssignedOrNobodyClaims, TestAHolderAssignedButNotOnTheShelfIsNotTakenForGone: rest_test.go TestNoSeatIsPassedForTwoClaimantsAnAssignedHolderOrNoClaimant, TestAHolderAssignedButNotOnTheShelfIsNotTakenForGone, TestASeatWhoseHolderIsAssignedNowhereGoesToItsOnlyClaimant. TestACompositionPassesTheSeatAndWritesOneHandActByTheController and TestALostPassWritesNoActAndAFailedOneLeavesTheRecord stay as tests of the send's step (performSeatPasses); TestAThrowawayMeshPassesTheSeatInMemoryAndWritesNothing stays as TestAThrowawayMeshWritesNoHandActForAPass. - seat_pass_test.go TestNoSeatIsPassedWhenAMachinesAssignmentsCouldNotBeRead: no counterpart, and none is needed: the adapter reads every machine's assignments for the Input and a machine whose read fails fails the Input, so no seat is passed on a partial read. - network_test.go TestOnlyAMachineOnThePrivateNetworkIsNamed: network_test.go TestANodeNotOnThePrivateNetworkGetsNoAddressAndNoNetwork. - held_back_test.go TestASendCarriesTheCurrentBuildsAndALeftOutModuleKeepsItsOwn: node_test.go TestAModuleLeftOutKeepsTheBuildItWasLastSent (and compose_sdk_test.go's table, now literal). - modules_wire_test.go TestTheBuildOfACarriedCommitIsTheAssignmentsOrTheOneBuiltFromIt: node_test.go TestTheBodyCarriesTheOrderTheInputGivesAndTheBuildsOnlyToAnEngineThatReadsThem. - compose_equal_integration_test.go: the old path it compares against is deleted; its last run is the parent's try. Rewritten against mesh-sdk, same cases: issued_test.go (needs now come from compose.Resolve: Needed.IssuedToHolder and ProviderMoved), module_health_test.go and merge_gate_handing tests (the contract in the Input), provisioner_account_integration_test.go (the composition's refusal), one_resolution_integration_test.go (sayFound). Every test of the send, perform, the adapter and the verbs is kept; the tests that used planFor, declarationWith, generators, renderingFor or grantsFor as setup use resolveNode, composeSend, composeQuestion, renderingOf and grantsOf. compose_callers_test.go now fails when any deleted name is declared or called again. Committed by copying the files of a staging branch (stage/r4b-1389 at198be8217c, built fromfb556d6bby patch commits and compared with the author's worktree), so that this commit's parent isfb556d6b.
168 lines
8.6 KiB
Go
168 lines
8.6 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
|
"github.com/novox/mesh-controller/internal/inventory"
|
|
)
|
|
|
|
// sendable is a declaration as a machine is sent it: its resources and, for an adopted node, its
|
|
// mode and which modules were taken on it (novox/hq ADR 0100).
|
|
//
|
|
// **Body is the only place the envelope is marshalled.** It was written by hand at every send site,
|
|
// in the digest the mesh compares, and in `plan --json`; a key added at one and not another would
|
|
// make a machine look out of date for ever, or send something `plan` never showed.
|
|
type sendable struct {
|
|
Resources []map[string]any
|
|
// Sequence orders this send against every other to the same node: one higher each time, taken
|
|
// under the node's hold just before the body is made (novox/hq 04-ISSUES/107). Zero is not sent
|
|
// at all, which a host reads as "no order claimed" — the shape of every declaration before this.
|
|
Sequence int64
|
|
// Epoch is the controller lease's epoch it was composed under (novox/hq to-be 45 §6): a machine that
|
|
// heard a later epoch refuses it. Zero is not sent at all — every machine whose node-engine has not
|
|
// said it reads one is sent none, because an older node-engine refuses a key it does not know, whole
|
|
// (link/order.go, the contract).
|
|
Epoch uint64
|
|
// Generation is the assignment generation it was composed from (novox/hq issue 234): a machine that
|
|
// applied a later one refuses it, so a send composed from a view of the assignments the mesh has moved
|
|
// past cannot undeclare what is still assigned. Zero is not sent at all — every machine whose
|
|
// node-engine has not said it reads one is sent none, for the reason the epoch is not (an older
|
|
// node-engine refuses a key it does not know, whole).
|
|
Generation int64
|
|
// composedFrom is the generation read before this declaration was composed, and actingEpoch the
|
|
// lease epoch its sender acted under — whether or not the machine is sent either — for the record of
|
|
// the send; never on the wire.
|
|
composedFrom int64
|
|
actingEpoch uint64
|
|
// ReadsModules says the machine's node-engine reads a module per resource and a build per module (novox/hq issue
|
|
// 1145): only then are they sent, because an older node-engine refuses a key it does not know, whole.
|
|
ReadsModules bool
|
|
// moduleOf is the composer's owner of each resource id, and buildIDs the build each module carries (its id):
|
|
// kept beside the resources, and on the wire only to a machine that reads them.
|
|
moduleOf map[string]string
|
|
buildIDs map[string]string
|
|
// modules are the modules this declaration names, for the record of the send; never on the wire.
|
|
modules []string
|
|
// localNetwork is every port admitted from the local network and what it resolved to (novox/hq ADR
|
|
// 0340 §8), for plan to say; never on the wire.
|
|
localNetwork []catalogue.LocalNetworkPort
|
|
// Adoption is nil for a converged node, and then the body is byte for byte what it was before
|
|
// adoption existed: an older host parses the envelope strictly and would refuse the key.
|
|
Adoption *adoptionEnvelope
|
|
|
|
// Received and Mesh are not sent in the declaration. They are what this machine's memberships
|
|
// are issued with on the bus (novox/hq ADR 0167): each module's received contributions, from
|
|
// the same composition as its received files, and every machine's private-network address.
|
|
Received map[string]map[string][]catalogue.Contribution
|
|
Mesh []string
|
|
// BusUsers is the bus's user list this declaration carries, empty for every machine but the one
|
|
// holding the bus; not sent apart from the file it is in. Its digest is recorded once sent, so
|
|
// whether that machine must go first is read from the list alone (novox/hq issue 249).
|
|
BusUsers string
|
|
// LeftOut is every module of the machine's set left out of this declaration because a stored
|
|
// setting cannot compose with its definition (novox/hq ADR 0163, rule 6), sorted. The host
|
|
// keeps that module's held things and touches none of its containers; a machine is told
|
|
// everything or nothing about what it IS told, and what it is not told is said. Absent from
|
|
// the body when empty, so a declaration that leaves nothing out is byte for byte what it was.
|
|
LeftOut []string
|
|
// leftOutWhy is why each was, for push and plan to say; never on the wire.
|
|
leftOutWhy map[string]string
|
|
// withheld is every consumer this machine's grants leave out, because its identity overflows the
|
|
// provision's bound (novox/hq ADR 0225); for push and plan to say, never on the wire.
|
|
withheld []catalogue.Overflow
|
|
// unbound is every consumer whose credential from this machine is on record and that is bound
|
|
// elsewhere (novox/hq issue 274); for push and plan to say, never on the wire.
|
|
unbound []catalogue.Unbound
|
|
// foreseen is every own secret composed with a stand-in, as `module/name`: what the next send will
|
|
// make (Foreseeing, novox/hq issue 275). Never on the wire, and a declaration that has any is never
|
|
// sent.
|
|
foreseen []string
|
|
// unplaced is every contribution its holder's template could not render, naming its module and
|
|
// why (novox/hq ADR 0255): left out of this declaration, for push and plan to say, never on the wire.
|
|
unplaced []string
|
|
// handedOnly is each file left out because only the provisioner's client fills it and the module is not handed
|
|
// here (a provider's consumers file, ADR 0373).
|
|
handedOnly []string
|
|
// Builds is the build of each module this declaration carries — module to the commit its build
|
|
// was made from — recorded with the send and never on the wire (novox/hq issue 259, ADR 0221).
|
|
// Composed only on the send path; nil records that it is not known.
|
|
Builds map[string]string
|
|
// Bindings is where each consumer of a provision that keeps its data is bound in this declaration
|
|
// (novox/hq ADR 0232), recorded with the send and never on the wire. Composed only on the send path.
|
|
Bindings []inventory.Binding
|
|
// Filled is the path of every file this declaration hands to the provisioner, and Provisioner the account the
|
|
// client that fills them runs as (novox/hq ADR 0349 §4, §5, design 57 P6): on the wire as `filled_files` and
|
|
// `provisioner_account`, both absent when nothing is handed, so every other declaration is byte for byte what
|
|
// it was. plans is each handed module's plan, kept with the send for the assignments mirror; never on the wire.
|
|
Filled []string
|
|
Provisioner string
|
|
plans map[string]catalogue.Plan
|
|
// requiredBy is, for each handed module only pulled in by another, the module that requires it (issue 765): kept
|
|
// beside its plan, so the assignments mirror gives the plan a value of its own. Never on the wire.
|
|
requiredBy map[string]string
|
|
}
|
|
|
|
// adoptionEnvelope is what an adopted node is told about its mode. Taken is every module taken on
|
|
// it that it runs; Untaken is, for every module it runs that is not taken, the ids of every one of
|
|
// that module's resources — what the host keeps as found until the module is taken (ADR 0103). Ids
|
|
// rather than a rule to split them by, because a module's name may contain a dot.
|
|
type adoptionEnvelope struct {
|
|
Taken []string `json:"taken"`
|
|
Untaken map[string][]string `json:"untaken,omitempty"`
|
|
}
|
|
|
|
// Body is the declaration's bytes, as sent and as digested.
|
|
func (s sendable) Body() ([]byte, error) {
|
|
envelope := map[string]any{"declaration": 1, "resources": s.Resources}
|
|
if s.ReadsModules {
|
|
// The module of each resource the composer owns (what the mesh adds of its own has none), and the build of each
|
|
// module (novox/hq issue 1145): to a machine that said it reads them, and no other.
|
|
named := make([]map[string]any, len(s.Resources))
|
|
for n, r := range s.Resources {
|
|
module, owned := s.moduleOf[fmt.Sprint(r["id"])]
|
|
if !owned {
|
|
named[n] = r
|
|
continue
|
|
}
|
|
with := make(map[string]any, len(r)+1)
|
|
for k, v := range r {
|
|
with[k] = v
|
|
}
|
|
with["module"] = module
|
|
named[n] = with
|
|
}
|
|
envelope["resources"] = named
|
|
if len(s.buildIDs) > 0 {
|
|
envelope["builds"] = s.buildIDs
|
|
}
|
|
}
|
|
if s.Adoption != nil {
|
|
envelope["adoption"] = s.Adoption
|
|
}
|
|
if s.Sequence > 0 {
|
|
envelope["sequence"] = s.Sequence
|
|
}
|
|
if s.Epoch > 0 {
|
|
envelope["epoch"] = s.Epoch
|
|
}
|
|
if len(s.LeftOut) > 0 {
|
|
envelope["left_out"] = s.LeftOut
|
|
}
|
|
if s.Generation > 0 {
|
|
envelope["generation"] = s.Generation
|
|
}
|
|
if len(s.Filled) > 0 {
|
|
envelope["filled_files"] = s.Filled
|
|
envelope["provisioner_account"] = s.Provisioner
|
|
}
|
|
// An empty declaration is deliberate here — the node owns nothing the mesh put there
|
|
// (novox/hq issue 127) — and the host refuses an empty body unless it is told the emptiness
|
|
// is meant, so a truncated or mis-composed body is never mistaken for "own nothing".
|
|
if len(s.Resources) == 0 {
|
|
envelope["owns_nothing"] = true
|
|
}
|
|
return json.Marshal(envelope)
|
|
}
|