diff --git a/modules/mesh-conditions/cmd/mesh-conditions/hold.go b/modules/mesh-conditions/cmd/mesh-conditions/hold.go index 5ff9d0a24..5f9e17109 100644 --- a/modules/mesh-conditions/cmd/mesh-conditions/hold.go +++ b/modules/mesh-conditions/cmd/mesh-conditions/hold.go @@ -2,7 +2,7 @@ package main // **A module being delivered raises no condition until its delivery ends** (novox/hq ADR 0564 clause 13 and Limits 12; // issue 1952 C7, C8). The keeper reads the assigner's wants and the nodes' haves, and judges a module's delivery on a -// node with mesh-sdk's delivery.MotionOf, as the view does. While it is delivered there, a finding about it there, or +// node with mesh-sdk's motion.MotionOf, as the view does. While it is delivered there, a finding about it there, or // about what it serves, is kept in the state // `held` and raises nothing. When the delivery ends, healthy at the want or failed, what still stands is raised once, // saying it happened during the delivery; what went is dropped with a log line. The hold lasts at most the delivery's @@ -18,8 +18,10 @@ import ( "strings" "time" + engineclient "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" + meshwants "git.novox.be/novox/mesh-contracts/placement/go/seats/meshwants" stdio "git.novox.be/novox/mesh-sdk/go" - "git.novox.be/novox/mesh-sdk/go/delivery" + "git.novox.be/novox/mesh-sdk/go/motion" cc "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient" seat "git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshconditions" @@ -79,10 +81,10 @@ func splitNodeModule(hk string) (string, string) { } // motion is the delivery of module on node, from its want and have. -func (k *Keeper) motion(node, module string) (delivery.Motion, delivery.Want) { +func (k *Keeper) motion(node, module string) (motion.Motion, motion.Want) { w, wanted := k.wants[node+"."+module] h, has := k.haves[node+"."+module] - return delivery.MotionOf(w, wanted, h, has, "", false), w + return motion.MotionOf(w, wanted, h, has, "", false), w } // holdFor says whether key is held now, and as what. Called with mu held. @@ -137,7 +139,7 @@ func (k *Keeper) bound(string) time.Duration { if k.Bound > 0 { return k.Bound } - return delivery.DefaultDeliveryBound + return motion.DefaultDeliveryBound } func boundKey(node, module string) string { return node + "." + module } @@ -162,15 +164,15 @@ func (k *Keeper) dropHeld(key, why string) { // Want takes one change of the assigner's wants; Have one of the nodes' haves. Each may end a delivery, which raises or // drops what it held. func (k *Keeper) Want(key string, value []byte, deleted bool) error { - node, module, err := delivery.ParseWantKey(key) - if err != nil { + node, module, ok := meshwants.ParseWantsByWantNodeModuleKey(key) + if !ok { return nil // not a want of a module on a node (a held fingerprint's key) } k.mu.Lock() k.init() if deleted { delete(k.wants, node+"."+module) - } else if w, err := delivery.DecodeWant(value); err == nil { + } else if w, err := decodeWant(value); err == nil { k.wants[node+"."+module] = w } else { k.Logf("the want %s is not readable: %v", key, err) @@ -181,7 +183,7 @@ func (k *Keeper) Want(key string, value []byte, deleted bool) error { // Have takes one change of a node's have. func (k *Keeper) Have(key string, value []byte, deleted bool) error { - node, module, isNode, err := delivery.ParseHaveKey(key) + node, module, isNode, err := engineclient.ParseHaveKey(key) if err != nil || isNode { return nil } @@ -189,8 +191,8 @@ func (k *Keeper) Have(key string, value []byte, deleted bool) error { k.init() if deleted { delete(k.haves, node+"."+module) - } else if h, err := delivery.DecodeHave(value); err == nil { - k.haves[node+"."+module] = h + } else if h, err := engineclient.DecodeHave(value); err == nil { + k.haves[node+"."+module] = motion.Have{FP: h.Fp, Composed: h.Composed, Phase: h.Phase, Health: h.Health, Detail: h.Detail} } else { k.Logf("the have %s is not readable: %v", key, err) } @@ -320,3 +322,12 @@ func (k *Keeper) restoreHeld(key string, value []byte) { k.held[key] = h k.mu.Unlock() } + +// decodeWant reads a want of a module on a node as far as a delivery's motion needs it. +func decodeWant(value []byte) (motion.Want, error) { + var w meshwants.WantsByWantNodeModuleValue + if err := json.Unmarshal(value, &w); err != nil { + return motion.Want{}, err + } + return motion.Want{FP: w.Fp, Composed: w.Composed, Removed: w.Removed}, nil +} diff --git a/modules/mesh-conditions/cmd/mesh-conditions/hold_test.go b/modules/mesh-conditions/cmd/mesh-conditions/hold_test.go index 4609b8306..e0291abff 100644 --- a/modules/mesh-conditions/cmd/mesh-conditions/hold_test.go +++ b/modules/mesh-conditions/cmd/mesh-conditions/hold_test.go @@ -5,8 +5,13 @@ import ( "strings" "testing" + engineclient "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" + nodeengine "git.novox.be/novox/mesh-contracts/go/seats/nodeengine" cc "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient" - "git.novox.be/novox/mesh-sdk/go/delivery" + assignerclient "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" + mesh "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" + meshwants "git.novox.be/novox/mesh-contracts/placement/go/seats/meshwants" + "git.novox.be/novox/mesh-sdk/go/motion" ) // The fingerprints and composition of mesh-controller on novox, copied from its have (mesh-delivery-view.node_haves, @@ -27,11 +32,11 @@ const ( // held key as held. func TestADeliveryHoldsItsOwnFindingsUntilItEnds(t *testing.T) { want := func(fp, composed string) []byte { - raw, _ := json.Marshal(delivery.Want{V: delivery.Version, FP: fp, Composed: composed}) + raw, _ := json.Marshal(meshwants.WantsByWantNodeModuleValue{V: 1, Fp: fp, Composed: composed}) return raw } - have := func(fp, composed string, phase delivery.Phase, h delivery.Health) []byte { - raw, _ := json.Marshal(delivery.Have{V: delivery.Version, FP: fp, Composed: composed, Phase: phase, Health: h}) + have := func(fp, composed string, phase, h string) []byte { + raw, _ := json.Marshal(nodeengine.HaveByNodeModuleValue{V: 1, Fp: fp, Composed: composed, Phase: phase, Health: h}) return raw } for _, c := range []struct { @@ -42,19 +47,19 @@ func TestADeliveryHoldsItsOwnFindingsUntilItEnds(t *testing.T) { said string line string }{ - {name: "gone by its end", end: have(ctrlNext, ctrlNextComp, delivery.PhaseRunning, delivery.HealthOK)}, - {name: "still standing at its end", end: have(ctrlNext, ctrlNextComp, delivery.PhaseRunning, delivery.HealthOK), + {name: "gone by its end", end: have(ctrlNext, ctrlNextComp, motion.PhaseRunning, engineclient.HealthOK)}, + {name: "still standing at its end", end: have(ctrlNext, ctrlNextComp, motion.PhaseRunning, engineclient.HealthOK), standing: true, said: "raised module.mesh-controller.novox.unhealthy", line: "during the delivery of version 0d1e2f3a4b5c"}, {name: "past its bound", standing: true, said: "raised module.mesh-controller.novox.unhealthy", line: "did not end within"}, - {name: "failed", end: have(ctrlNext, ctrlNextComp, delivery.PhaseFailed, delivery.HealthFailed), standing: true, + {name: "failed", end: have(ctrlNext, ctrlNextComp, motion.PhaseFailed, engineclient.HealthFailed), standing: true, said: "raised module.mesh-controller.novox.unhealthy", line: "as the delivery of version 0d1e2f3a4b5c"}, } { r := newRig() _ = r.k.Want("want.novox.mesh-controller", want(ctrlRunning, ctrlComposed), false) - _ = r.k.Have("novox.mesh-controller", have(ctrlRunning, ctrlComposed, delivery.PhaseRunning, delivery.HealthOK), false) + _ = r.k.Have("novox.mesh-controller", have(ctrlRunning, ctrlComposed, motion.PhaseRunning, engineclient.HealthOK), false) // The delivery starts: the assigner wants the next fingerprint; the node applies it. _ = r.k.Want("want.novox.mesh-controller", want(ctrlNext, ctrlNextComp), false) - _ = r.k.Have("novox.mesh-controller", have(ctrlNext, ctrlNextComp, delivery.PhaseApplying, delivery.HealthUnknown), false) + _ = r.k.Have("novox.mesh-controller", have(ctrlNext, ctrlNextComp, motion.PhaseApplying, engineclient.HealthUnknown), false) own, ownRaw := finding(t, watchman, cc.ScopeModule, "mesh-controller.novox", "unhealthy", "health", cc.Warning, r.now) other, otherRaw := finding(t, watchman, cc.ScopeModule, "postgres.novox", "unhealthy", "health", cc.Warning, r.now) machine, machineRaw := finding(t, watchman, cc.ScopeMachine, "novox", "silent", "S1", cc.Urgent, r.now) @@ -67,7 +72,7 @@ func TestADeliveryHoldsItsOwnFindingsUntilItEnds(t *testing.T) { } } // The delivery's bound is its own key's time to live, the module's bound (the default where it names none). - if r.st.ttls["bounds/novox.mesh-controller"] != delivery.DefaultDeliveryBound { + if r.st.ttls["bounds/novox.mesh-controller"] != motion.DefaultDeliveryBound { t.Errorf("%s: the bound key lives %s", c.name, r.st.ttls["bounds/novox.mesh-controller"]) } if got := r.changes(); got != "raised "+other+", raised "+machine { @@ -80,7 +85,7 @@ func TestADeliveryHoldsItsOwnFindingsUntilItEnds(t *testing.T) { if c.end != nil { _ = r.k.Have("novox.mesh-controller", c.end, false) } else { - r.now = r.now.Add(delivery.DefaultDeliveryBound) + r.now = r.now.Add(motion.DefaultDeliveryBound) _ = r.k.BoundPassed("novox.mesh-controller") } if got := r.changes(); got != c.said { @@ -97,20 +102,20 @@ func TestADeliveryHoldsItsOwnFindingsUntilItEnds(t *testing.T) { // while the assigner is delivered on ace, though the seat is named mesh-module-assigner. r := newRig() // Research 072 row 37: the placement in its keys per module and node; a stale legacy key beside them is not read. - stale, err := delivery.EncodePlacement(delivery.Placement{Node: "ace"}) + stale, err := assignerclient.EncodePlacement(mesh.Placement{Node: "ace"}) if err != nil { t.Fatal(err) } r.k.Placement("placement.ace", stale, false) - placed, err := delivery.PlacementValues(delivery.Placement{Node: "ace", Modules: []delivery.PlacedModule{{Module: "mesh-delivery-assigner"}}, - Holds: []delivery.Holding{{Claim: "mesh-module-assigner", Scope: "mesh", Module: "mesh-delivery-assigner"}}}) + placed, err := assignerclient.PlacementValues(mesh.Placement{Node: "ace", Modules: []mesh.PlacedModule{{Module: "mesh-delivery-assigner"}}, + Holds: []mesh.Holding{{Claim: "mesh-module-assigner", Scope: "mesh", Module: "mesh-delivery-assigner"}}}) if err != nil { t.Fatal(err) } r.k.Placement("ace.mesh-delivery-assigner", placed["ace.mesh-delivery-assigner"], false) r.k.Placement("ace.node", placed["ace.node"], false) _ = r.k.Want("want.ace.mesh-delivery-assigner", want(ctrlNext, ctrlNextComp), false) - _ = r.k.Have("ace.mesh-delivery-assigner", have(ctrlRunning, ctrlComposed, delivery.PhaseRunning, delivery.HealthOK), false) + _ = r.k.Have("ace.mesh-delivery-assigner", have(ctrlRunning, ctrlComposed, motion.PhaseRunning, engineclient.HealthOK), false) seatKey, seatRaw := finding(t, selfCheck, cc.ScopeSeat, "mesh-module-assigner.ace", "holder-silent", "D3", cc.Warning, r.now) _ = r.k.Finding(selfCheck, seatKey, seatRaw, false) if got := r.changes(); got != "" || len(r.k.Held()) != 1 || r.k.Held()[0].Module != "mesh-delivery-assigner" { diff --git a/modules/mesh-conditions/cmd/mesh-conditions/keeper.go b/modules/mesh-conditions/cmd/mesh-conditions/keeper.go index 4cca61208..c3f33b85b 100644 --- a/modules/mesh-conditions/cmd/mesh-conditions/keeper.go +++ b/modules/mesh-conditions/cmd/mesh-conditions/keeper.go @@ -17,8 +17,10 @@ import ( "sync" "time" + assignerclient "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" + mesh "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" stdio "git.novox.be/novox/mesh-sdk/go" - "git.novox.be/novox/mesh-sdk/go/delivery" + "git.novox.be/novox/mesh-sdk/go/motion" cc "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient" seat "git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshconditions" @@ -31,6 +33,10 @@ const ( watchmanModule = "mesh-delivery-watchman" selfCheckModule = "mesh-self-check" healersModule = "mesh-healers" + // The holders of the placement and the catalog, whose views and findings the keeper follows (the manifest test holds + // these equal to the manifest's `consumes` and `invokes`). + assignerModule = "mesh-delivery-assigner" + indexerModule = "mesh-delivery-indexer" ) // The module, its seat, and the states it keeps. @@ -50,7 +56,7 @@ const HistoryKeptFor = 90 * 24 * time.Hour // Observers are the modules whose `findings` the keeper reads (clause 2; ADR 0565 §8 for the assigner's; the lead's // table of 2026-10-29 for the provisioner's and the router's; ADR 0571 clause 2 for the seat artifact-store's holder, // which observes S25, image-missing). -var Observers = []string{watchmanModule, selfCheckModule, healersModule, delivery.AssignerModule, +var Observers = []string{watchmanModule, selfCheckModule, healersModule, assignerModule, "mesh-provisioner", "messenger", artifactStore, configModule, vaultModule, // The readers of the indexer's and the assigner's state say a first view unanswered past its bound in their own findings // (ADR 0615 §6). @@ -64,10 +70,10 @@ var Observers = []string{watchmanModule, selfCheckModule, healersModule, deliver // The other states it reads, as `.`. const ( AttemptsRead = healersModule + "." + meshhealers.StateAttempts - WantsRead = delivery.AssignerModule + ".wants" + WantsRead = assignerModule + ".wants" // PlacementRead is the mesh-module-assigner seat's placement, read for which module holds a seat on a node (hold.go; the // lead, 2026-10-29, for ADR 0564 clause 2's inputs). - PlacementRead = delivery.PlacementRead + PlacementRead = assignerModule + "." + mesh.StatePlacement HavesRead = "node-engine.have" ConditionWrites = 8 ) @@ -112,19 +118,19 @@ type Keeper struct { Logf func(format string, a ...any) // Homes says, by kind, where each kind the controller raised lives now (homes.go); Homes when nil. Homes map[string]Home - // Bound is a delivery's hold bound; delivery.DefaultDeliveryBound when zero (hold.go). + // Bound is a delivery's hold bound; motion.DefaultDeliveryBound when zero (hold.go). Bound time.Duration mu sync.Mutex findings map[string]map[string]seat.FindingsByKeyValue blind map[string]string attempts map[string]meshhealers.AttemptsByAttemptValue - wants map[string]delivery.Want - haves map[string]delivery.Have + wants map[string]motion.Want + haves map[string]motion.Have // holders are, by `.`, the module the placement says holds the seat there. holders map[string]string // placement is the placement's keys as heard, assembled per node. - placement *delivery.PlacementKeys + placement *assignerclient.Keys stuck map[string]string held map[string]held // passed are the deliveries whose bound passed, by node and module, at the fingerprint wanted then; outOfHold the @@ -139,10 +145,10 @@ func (k *Keeper) init() { if k.findings == nil { k.findings, k.blind, k.attempts = map[string]map[string]seat.FindingsByKeyValue{}, map[string]string{}, map[string]meshhealers.AttemptsByAttemptValue{} - k.wants, k.haves, k.stuck, k.held, k.cleared = map[string]delivery.Want{}, map[string]delivery.Have{}, + k.wants, k.haves, k.stuck, k.held, k.cleared = map[string]motion.Want{}, map[string]motion.Have{}, map[string]string{}, map[string]held{}, map[string]clearedMemo{} k.passed, k.outOfHold, k.holders = map[string]string{}, map[string]string{}, map[string]string{} - k.placement = delivery.NewPlacementKeys() + k.placement = assignerclient.NewKeys() } if k.Now == nil { k.Now = time.Now diff --git a/modules/mesh-conditions/cmd/mesh-conditions/keeper_test.go b/modules/mesh-conditions/cmd/mesh-conditions/keeper_test.go index 287395bff..1b70ecd83 100644 --- a/modules/mesh-conditions/cmd/mesh-conditions/keeper_test.go +++ b/modules/mesh-conditions/cmd/mesh-conditions/keeper_test.go @@ -18,7 +18,6 @@ import ( seat "git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshconditions" meshhealers "git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshhealers" stdio "git.novox.be/novox/mesh-sdk/go" - "git.novox.be/novox/mesh-sdk/go/delivery" ) // fakeStore is the bus's states in memory, with revisions, as the runtime answers them. @@ -336,7 +335,7 @@ func TestEveryControllerKindHasItsHome(t *testing.T) { // hearing, is read: the delivery parts and the tool runner by name (other repositories), and every catalogue module // whose code writes a views finding, found in the catalogue's tree, so a new writer cannot go unobserved. readers := []string{watchman, "mesh-delivery-teller", enrolment, builder, "mesh-delivery-checker", "mesh-delivery-view", - indexer, delivery.AssignerModule, configModule, vaultModule, gitea, "node-tools"} + indexer, assignerModule, configModule, vaultModule, gitea, "node-tools"} readers = append(readers, viewFindingWriters(t, "../../..")...) for _, reader := range readers { if !slices.Contains(Observers, reader) { diff --git a/modules/mesh-conditions/cmd/mesh-conditions/main.go b/modules/mesh-conditions/cmd/mesh-conditions/main.go index f2c4c7cf0..b03277cb3 100644 --- a/modules/mesh-conditions/cmd/mesh-conditions/main.go +++ b/modules/mesh-conditions/cmd/mesh-conditions/main.go @@ -15,7 +15,6 @@ import ( "time" stdio "git.novox.be/novox/mesh-sdk/go" - "git.novox.be/novox/mesh-sdk/go/delivery" "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient" "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshhandactsclient" @@ -142,21 +141,21 @@ func Keep(k *Keeper, v *views.Reader) { // The assigner's read tools of the states the keeper follows, which the manifest invokes (ADR 0615). const ( - WantsTool = delivery.AssignerModule + ".assigner_read_wants" - PlacementTool = delivery.AssignerModule + ".assigner_read_placement" - FindingsTool = delivery.AssignerModule + ".assigner_read_findings" + WantsTool = assignerModule + ".assigner_read_wants" + PlacementTool = assignerModule + ".assigner_read_placement" + FindingsTool = assignerModule + ".assigner_read_findings" // IndexerFindingsTool is the indexer's read tool of its findings: the indexer's state, too, is asked of its owner. - IndexerFindingsTool = delivery.IndexerModule + ".indexer_read_findings" + IndexerFindingsTool = indexerModule + ".indexer_read_findings" ) // NewViews is the keeper's pictures of the assigner's wants, placement and findings and of the indexer's findings. A first view it cannot get past its // bound is a finding of its own, taken in as any observer's: the keeper raises the condition itself. func NewViews(k *Keeper) *views.Reader { v := &views.Reader{Module: Module, Logf: logf, Findings: ownFindings{k}, Acts: meshhandactsclient.Recorder{Ask: stdio.Ask}} - v.Follow(delivery.AssignerModule, "wants", WantsTool) - v.Follow(delivery.AssignerModule, "placement", PlacementTool) - v.Follow(delivery.AssignerModule, seat.StateFindings, FindingsTool) - v.Follow(delivery.IndexerModule, seat.StateFindings, IndexerFindingsTool) + v.Follow(assignerModule, "wants", WantsTool) + v.Follow(assignerModule, "placement", PlacementTool) + v.Follow(assignerModule, seat.StateFindings, FindingsTool) + v.Follow(indexerModule, seat.StateFindings, IndexerFindingsTool) return v } diff --git a/modules/mesh-conditions/cmd/mesh-conditions/manifest_test.go b/modules/mesh-conditions/cmd/mesh-conditions/manifest_test.go index abd0f4433..ac1d48ee4 100644 --- a/modules/mesh-conditions/cmd/mesh-conditions/manifest_test.go +++ b/modules/mesh-conditions/cmd/mesh-conditions/manifest_test.go @@ -9,7 +9,6 @@ import ( seat "git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshconditions" meshhealers "git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshhealers" "git.novox.be/novox/mesh-sdk/go/catalogue" - "git.novox.be/novox/mesh-sdk/go/delivery" "git.novox.be/novox/mesh-sdk/go/seatlist/seatlisttest" ) @@ -37,7 +36,7 @@ func TestTheManifestSaysWhatTheCodeDoes(t *testing.T) { // followed by their events, not read (ADR 0615 §1). reads := []string{HavesRead, AttemptsRead} for _, o := range Observers { - if o != delivery.AssignerModule && o != delivery.IndexerModule { + if o != assignerModule && o != indexerModule { reads = append(reads, o+"."+seat.StateFindings) } } diff --git a/modules/mesh-conditions/go.mod b/modules/mesh-conditions/go.mod index 4902fa3a2..ad8f90e67 100644 --- a/modules/mesh-conditions/go.mod +++ b/modules/mesh-conditions/go.mod @@ -3,6 +3,7 @@ module mesh-conditions go 1.26.0 require ( + git.novox.be/novox/mesh-contracts/placement/go/seats/meshwants v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshhandactsclient v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshconditions v0.0.0-00010101000000-000000000000