diff --git a/modules/mesh-provisioner/cmd/mesh-provisioner/composed_test.go b/modules/mesh-provisioner/cmd/mesh-provisioner/composed_test.go index 88eeaafdb..62a873bf0 100644 --- a/modules/mesh-provisioner/cmd/mesh-provisioner/composed_test.go +++ b/modules/mesh-provisioner/cmd/mesh-provisioner/composed_test.go @@ -12,13 +12,15 @@ package main import ( "context" "encoding/json" + engineclient "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" + catalogclient "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" + catalog "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" "os" "strings" "testing" "time" "git.novox.be/novox/mesh-sdk/go/catalogue" - "git.novox.be/novox/mesh-sdk/go/delivery" ) // today is a captured composition: the node, the builds its wants named, and the plans at them and at the newest. @@ -48,7 +50,7 @@ func published(t *testing.T, s *memStore, c today) { v["plan"] = plan } raw, _ := json.Marshal(v) - s.write(t, ComposedRead, mustKey(delivery.ComposedKey(c.Node, module, fp)), string(raw)) + s.write(t, ComposedRead, mustKey(composedKey(c.Node, module, fp)), string(raw)) } for module, fp := range fpsOf { plan, has := c.Newest.Plans[module] @@ -58,7 +60,7 @@ func published(t *testing.T, s *memStore, c today) { plan, has := c.AtWants.Plans[module] put(module, fp, plan, has) } - s.write(t, ComposedRead, mustKey(delivery.ComposedNodeKey(c.Node)), `{"v":1,"node":"`+c.Node+`","resources":[]}`) + s.write(t, ComposedRead, mustKey(engineclient.HaveNodeKey(c.Node)), `{"v":1,"node":"`+c.Node+`","resources":[]}`) } // goldenMesh is the store as the provisioner reads it for frombustest's mesh: the indexer's modules, n1's placement, the @@ -73,9 +75,9 @@ func goldenMesh(t *testing.T, c today) *memStore { raw = `{"module":"postgres","version":"1","provides":[{"name":"postgres-database","scope":"mesh"}],` + `"serves":{"postgres-database":{"port":5432}}}` } - digest := delivery.ManifestDigest([]byte(raw)) - value, _ := delivery.EncodeManifest(delivery.Manifest{Module: module, JSON: raw}) - s.write(t, ManifestsRead, mustKey(delivery.ManifestsKey(digest)), string(value)) + digest := catalogclient.ManifestDigest([]byte(raw)) + value, _ := catalogclient.EncodeManifest(catalog.ManifestsByDigestValue{Module: module, Manifest: raw}) + s.write(t, ManifestsRead, mustKey(catalogclient.ManifestsKey(digest)), string(value)) s.write(t, ModulesRead, module, `{"v":1,"fp2":"`+fp+`","manifest":"`+digest+`"}`) } s.write(t, PlacementRead, "placement."+c.Node, `{"v":1,"node":"`+c.Node+`","decided":{},"modules":[{"module":"store"},`+ @@ -140,7 +142,7 @@ func TestARefusedOrMissingCompositionHoldsItsModule(t *testing.T) { t.Fatal(err) } plan, _ := s.Get(PlansState, "n1.restic") - key := mustKey(delivery.ComposedKey("n1", "restic", fpsOf["restic"])) + key := mustKey(composedKey("n1", "restic", fpsOf["restic"])) s.write(t, ComposedRead, key, `{"v":1,"node":"n1","module":"restic","fp":"`+fpsOf["restic"]+`","resources":[],`+ `"refused":"restic requires postgres-database and no module offers it"}`) out, err := h.ComposeNode(context.Background(), c.Node) @@ -202,3 +204,12 @@ func valueOf(e *Entry) string { } return string(e.Value) } + +// composedKey is a module's key in the node-engine's composed state, from the fingerprint the test names it by. +func composedKey(node, module, fp string) (string, error) { + part, err := catalogclient.KeyPart(fp) + if err != nil { + return "", err + } + return engineclient.ComposedKey(node, module, part) +} diff --git a/modules/mesh-provisioner/cmd/mesh-provisioner/holder.go b/modules/mesh-provisioner/cmd/mesh-provisioner/holder.go index 1bf227552..01e212089 100644 --- a/modules/mesh-provisioner/cmd/mesh-provisioner/holder.go +++ b/modules/mesh-provisioner/cmd/mesh-provisioner/holder.go @@ -31,6 +31,12 @@ import ( "encoding/json" "errors" "fmt" + engineclient "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" + assignerclient "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" + meshseat "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" + catalogclient "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" + catalog "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" + builds "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds" "slices" "sort" "strings" @@ -42,7 +48,6 @@ import ( "git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient" stdio "git.novox.be/novox/mesh-sdk/go" "git.novox.be/novox/mesh-sdk/go/catalogue" - "git.novox.be/novox/mesh-sdk/go/delivery" ) // The states it keeps, beside Registry, Needs, Applied and Collect. RefusedState holds, by `.`, why a @@ -64,14 +69,14 @@ type Refusal struct { // The reads it makes the plans from. ComposedRead is every node's published compositions, `composed-` (ADR 0593 // §6), read as the node-engine's named state: its keys are `..` and `.node`. const ( - PlacementRead = delivery.PlacementRead - WantsRead = delivery.AssignerModule + ".wants" - ModulesRead = delivery.IndexerModule + ".modules" - ManifestsRead = delivery.IndexerModule + ".manifests" + PlacementRead = (assignerModule + "." + meshseat.StatePlacement) + WantsRead = assignerModule + ".wants" + ModulesRead = indexerModule + "." + catalog.StateModules + ManifestsRead = indexerModule + "." + catalog.StateManifests HaveRead = "node-engine.have" // BuildsRead is the builder's builds, `.`: the key of the build a provider runs names the // digest of the manifest it was built from (novox/hq ADR 0603 clause 5). - BuildsRead = delivery.BuilderModule + ".builds" + BuildsRead = builds.BuilderModule + ".builds" ComposedRead = "mesh-host.composed" ) @@ -461,7 +466,7 @@ func (h *Holder) wantedOn(node string) (map[string]string, error) { return nil, fmt.Errorf("the wants cannot be read: %w", err) } for _, k := range keys { - n, module, err := delivery.ParseWantKey(k) + n, module, err := parseWantKey(k) if err != nil || n != node { continue } @@ -469,8 +474,8 @@ func (h *Holder) wantedOn(node string) (map[string]string, error) { if err != nil || e == nil { continue } - if w, err := delivery.DecodeWant(e.Value); err == nil && !w.Removed && w.FP != "" { - out[module] = w.FP + if w, err := decodeWant(e.Value); err == nil && !w.Removed && w.Fp != "" { + out[module] = w.Fp } } return out, nil @@ -485,7 +490,7 @@ func (h *Holder) runningOn(node string) (map[string]string, error) { return nil, fmt.Errorf("the haves cannot be read: %w", err) } for _, k := range keys { - n, module, isNode, err := delivery.ParseHaveKey(k) + n, module, isNode, err := engineclient.ParseHaveKey(k) if err != nil || isNode || n != node { continue } @@ -493,8 +498,8 @@ func (h *Holder) runningOn(node string) (map[string]string, error) { if err != nil || e == nil { continue } - if have, err := delivery.DecodeHave(e.Value); err == nil && have.FP != "" { - out[module] = have.FP + if have, err := engineclient.DecodeHave(e.Value); err == nil && have.Fp != "" { + out[module] = have.Fp } } return out, nil @@ -808,27 +813,27 @@ func (h *Holder) haveOutcome(p *meshprovisionerclient.Provision) { p.State, p.Why = meshprovisionerclient.Asked, "waits for "+where+" to run: it says nothing yet" return } - have, err := delivery.DecodeHave(e.Value) + have, err := engineclient.DecodeHave(e.Value) if err != nil { p.State, p.Why = meshprovisionerclient.Asked, "the have of "+where+" is not readable" return } wantFP := "" if we, err := h.Store.Get(WantsRead, "want."+key); err == nil && we != nil { - if w, err := delivery.DecodeWant(we.Value); err == nil && !w.Removed { - wantFP = w.FP + if w, err := decodeWant(we.Value); err == nil && !w.Removed { + wantFP = w.Fp } } switch { - case have.Phase == delivery.PhaseFailed || have.Health == delivery.HealthFailed: + case have.Phase == engineclient.PhaseFailed || have.Health == engineclient.HealthFailed: p.State, p.Why = meshprovisionerclient.Failed, orSaid(have.Detail, where+" failed") - case have.Phase == delivery.PhaseRunning && have.Health == delivery.HealthOK && (wantFP == "" || have.FP == wantFP): + case have.Phase == engineclient.PhaseRunning && have.Health == engineclient.HealthOK && (wantFP == "" || have.Fp == wantFP): // A provider's have counts as its outcome only from a build whose manifest offers the provision (ADR 0603 clause 5). // Refused only when that manifest is known and does not offer it: a have with no builder key (an old-form build, an // adopted module) or an unreadable manifest keeps the outcome it had. - if offers, known := h.buildOffers(p.Provider.Module, have.FP, p.Provision); known && !offers { + if offers, known := h.buildOffers(p.Provider.Module, have.Fp, p.Provision); known && !offers { p.State, p.Why = meshprovisionerclient.Asked, "waits for "+where+" to run a build that offers "+p.Provision+": the build it runs ("+ - have.FP+") does not" + have.Fp+") does not" return } p.State, p.Why = meshprovisionerclient.Provided, "" @@ -842,7 +847,7 @@ func (h *Holder) haveOutcome(p *meshprovisionerclient.Provision) { // offers the provision; known is false when the builder holds no such key, the key names no manifest, or the indexer holds // no such manifest (the caller then does not refuse). func (h *Holder) buildOffers(module, fingerprint, provision string) (offers, known bool) { - key, err := delivery.BuilderBuildKey(module, fingerprint) + key, err := builds.BuilderBuildKey(module, fingerprint) if err != nil { return false, false } @@ -850,11 +855,11 @@ func (h *Holder) buildOffers(module, fingerprint, provision string) (offers, kno if err != nil || be == nil { return false, false } - b, err := delivery.DecodeBuilderBuild(be.Value) + b, err := builds.DecodeBuilderBuild(be.Value) if err != nil || b.Manifest == "" { return false, false } - mk, err := delivery.ManifestsKey(b.Manifest) + mk, err := catalogclient.ManifestsKey(b.Manifest) if err != nil { return false, false } @@ -862,14 +867,14 @@ func (h *Holder) buildOffers(module, fingerprint, provision string) (offers, kno if err != nil || me == nil { return false, false } - man, err := delivery.DecodeManifest(me.Value) + man, err := catalogclient.DecodeManifest(me.Value) if err != nil { return false, false } var decl struct { Provides []json.RawMessage `json:"provides"` } - if json.Unmarshal([]byte(man.JSON), &decl) != nil { + if json.Unmarshal([]byte(man.Manifest), &decl) != nil { return false, false } for _, raw := range decl.Provides { @@ -947,7 +952,7 @@ func keepsEntriesFor(manifest, provision string) (bool, error) { // providerManifest is a provider's manifest JSON at the trunk, as the indexer holds it. func (h *Holder) providerManifest(module string) (string, error) { - mk, err := delivery.ModulesKey(module) + mk, err := catalogclient.ModulesKey(module) if err != nil { return "", err } @@ -955,11 +960,11 @@ func (h *Holder) providerManifest(module string) (string, error) { if err != nil || e == nil { return "", fmt.Errorf("the indexer holds no modules.%s (%v)", module, err) } - m, err := delivery.DecodeModule(e.Value) + m, err := catalogclient.DecodeModule(e.Value) if err != nil || m.Manifest == "" { return "", fmt.Errorf("the indexer's modules.%s names no manifest", module) } - key, err := delivery.ManifestsKey(m.Manifest) + key, err := catalogclient.ManifestsKey(m.Manifest) if err != nil { return "", err } @@ -967,11 +972,11 @@ func (h *Holder) providerManifest(module string) (string, error) { if err != nil || me == nil { return "", fmt.Errorf("the indexer holds no manifest of %s (%v)", module, err) } - man, err := delivery.DecodeManifest(me.Value) + man, err := catalogclient.DecodeManifest(me.Value) if err != nil { return "", err } - return man.JSON, nil + return man.Manifest, nil } // retired says a retiring key the operator approved is retired: its provider's entry is gone. @@ -1310,7 +1315,7 @@ func (h *Holder) put(key string, p meshprovisionerclient.Provision) { // NodesOfPlacement is the node a placement key names, in either form (research 072 row 37). func NodesOfPlacement(key string) []string { - if n, _, _, err := delivery.ParsePlacementKey(key); err == nil { + if n, _, _, err := assignerclient.ParseKey(key); err == nil { return []string{n} } return nil @@ -1318,7 +1323,7 @@ func NodesOfPlacement(key string) []string { // NodesOfWant is the node a want key names. func NodesOfWant(key string) []string { - if n, _, err := delivery.ParseWantKey(key); err == nil { + if n, _, err := parseWantKey(key); err == nil { return []string{n} } return nil @@ -1344,7 +1349,7 @@ func (h *Holder) NodesOfModule(module string) []string { } // placedOn is every node a module is placed on, sorted, from the placement's keys in either form -// (delivery.PlacementsOf; research 072 row 37). A node whose keys do not read whole is read as they stand. +// (assignerclient.PlacementsOf; research 072 row 37). A node whose keys do not read whole is read as they stand. func (h *Holder) placedOn(module string) []string { nodes := map[string]bool{} raw := map[string][]byte{} @@ -1355,7 +1360,7 @@ func (h *Holder) placedOn(module string) []string { } } } - placements, _ := delivery.PlacementsOf(raw) + placements, _ := assignerclient.PlacementsOf(raw) for n, p := range placements { for _, m := range p.Modules { if m.Module == module { @@ -1517,7 +1522,7 @@ func FromCompositions(s Store) Composer { } } -// publishedPlan is what the holder reads of a published composition (delivery.Composed): its plan, or the engine's +// publishedPlan is what the holder reads of a published composition (the node-engine's composed state): its plan, or the engine's // refusal. Read leniently, so a field the node-engine adds later is no failure here. type publishedPlan struct { Plan *catalogue.Plan `json:"plan"` @@ -1526,7 +1531,11 @@ type publishedPlan struct { // composedAt is a module's key in its node's compositions, or "" for a fingerprint no key can name. func composedAt(node, module, fp string) string { - k, err := delivery.ComposedKey(node, module, fp) + part, err := catalogclient.KeyPart(fp) + if err != nil { + return "" + } + k, err := engineclient.ComposedKey(node, module, part) if err != nil { return "" } @@ -1541,7 +1550,7 @@ func publishedOn(s Store, node string) (map[string]publishedPlan, error) { } out := map[string]publishedPlan{} for _, k := range keys { - n, _, _, isNode, err := delivery.ParseComposedKey(k) + n, _, _, isNode, err := engineclient.ParseComposedKey(k) if err != nil || isNode || n != node { continue } @@ -1566,25 +1575,25 @@ func placedModules(s Store, node string) ([]string, map[string]string, error) { return nil, nil, fmt.Errorf("the placement cannot be read: %w", err) } for _, k := range keys { - if n, _, _, err := delivery.ParsePlacementKey(k); err != nil || n != node { + if n, _, _, err := assignerclient.ParseKey(k); err != nil || n != node { continue } if e, err := s.Get(PlacementRead, k); err == nil && e != nil { raw[k] = e.Value } } - placements, _ := delivery.PlacementsOf(raw) + placements, _ := assignerclient.PlacementsOf(raw) var placed []string fp2 := map[string]string{} for _, m := range placements[node].Modules { placed = append(placed, m.Module) - mk, err := delivery.ModulesKey(m.Module) + mk, err := catalogclient.ModulesKey(m.Module) if err != nil { continue } if e, err := s.Get(ModulesRead, mk); err == nil && e != nil { - if mod, err := delivery.DecodeModule(e.Value); err == nil { - fp2[m.Module] = mod.FP2 + if mod, err := catalogclient.DecodeModule(e.Value); err == nil { + fp2[m.Module] = mod.Fp2 } } } diff --git a/modules/mesh-provisioner/cmd/mesh-provisioner/holder_integration_test.go b/modules/mesh-provisioner/cmd/mesh-provisioner/holder_integration_test.go index d4c4706d1..4938b8ea6 100644 --- a/modules/mesh-provisioner/cmd/mesh-provisioner/holder_integration_test.go +++ b/modules/mesh-provisioner/cmd/mesh-provisioner/holder_integration_test.go @@ -14,6 +14,9 @@ import ( "context" "encoding/json" "errors" + catalogclient "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" + catalog "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" + builds "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds" "strings" "testing" "time" @@ -24,7 +27,6 @@ import ( oc "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel" "git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient" stdio "git.novox.be/novox/mesh-sdk/go" - "git.novox.be/novox/mesh-sdk/go/delivery" "git.novox.be/novox/mesh-sdk/go/testbus" ) @@ -36,7 +38,7 @@ type kvStore struct { func bucketOf(state string) string { if state == ComposedRead { // the node-engine's named state: here n1's bucket, the one node that composes - return delivery.ComposedBucketPrefix + "n1" + return "composed-n1" } if owner, name, ok := strings.Cut(state, "."); ok { return owner + "_" + name @@ -131,9 +133,9 @@ func TestTheHolderPlansProvidesAndAsksOnTheBus(t *testing.T) { mem := goldenMesh(t, c) published(t, mem, c) vaultManifest := `{"module":"mesh-vault","version":"1","state":["bound"]}` // the vault keeps its outcomes as entries - vaultDigest := delivery.ManifestDigest([]byte(vaultManifest)) - vaultValue, _ := delivery.EncodeManifest(delivery.Manifest{Module: meshprovisionerclient.VaultModule, JSON: vaultManifest}) - mem.write(t, ManifestsRead, mustKey(delivery.ManifestsKey(vaultDigest)), string(vaultValue)) + vaultDigest := catalogclient.ManifestDigest([]byte(vaultManifest)) + vaultValue, _ := catalogclient.EncodeManifest(catalog.ManifestsByDigestValue{Module: meshprovisionerclient.VaultModule, Manifest: vaultManifest}) + mem.write(t, ManifestsRead, mustKey(catalogclient.ManifestsKey(vaultDigest)), string(vaultValue)) mem.write(t, ModulesRead, meshprovisionerclient.VaultModule, `{"v":1,"none2":"the test's","manifest":"`+vaultDigest+`"}`) for state, keys := range mem.states { for key, raw := range keys { @@ -226,15 +228,15 @@ func TestTheHolderPlansProvidesAndAsksOnTheBus(t *testing.T) { settled(HaveRead, node+".postgres") // The running build's manifest, as the builder keyed it: one that does not offer the provision keeps the key asked // (ADR 0603 clause 5), one that does lets the have count. - buildsKey := mustKey(delivery.BuilderBuildKey("postgres", fpsOf["postgres"])) + buildsKey := mustKey(builds.BuilderBuildKey("postgres", fpsOf["postgres"])) offered := func(provides string) { raw := `{"module":"postgres","version":"1","provides":` + provides + `}` - digest := delivery.ManifestDigest([]byte(raw)) - value, _ := delivery.EncodeManifest(delivery.Manifest{Module: "postgres", JSON: raw}) - if err := s.Put(ManifestsRead, mustKey(delivery.ManifestsKey(digest)), json.RawMessage(value)); err != nil { + digest := catalogclient.ManifestDigest([]byte(raw)) + value, _ := catalogclient.EncodeManifest(catalog.ManifestsByDigestValue{Module: "postgres", Manifest: raw}) + if err := s.Put(ManifestsRead, mustKey(catalogclient.ManifestsKey(digest)), json.RawMessage(value)); err != nil { t.Fatal(err) } - build, err := delivery.EncodeBuilderBuild(delivery.BuilderBuild{State: delivery.BuildDone, Manifest: digest}) + build, err := builds.EncodeBuilderBuild(builds.BuilderBuild{State: builds.BuildDone, Manifest: digest}) if err != nil { t.Fatal(err) } diff --git a/modules/mesh-provisioner/cmd/mesh-provisioner/holder_test.go b/modules/mesh-provisioner/cmd/mesh-provisioner/holder_test.go index 183bb333c..00bb0a0bd 100644 --- a/modules/mesh-provisioner/cmd/mesh-provisioner/holder_test.go +++ b/modules/mesh-provisioner/cmd/mesh-provisioner/holder_test.go @@ -4,6 +4,11 @@ import ( "context" "encoding/json" "fmt" + assignerclient "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" + meshseat "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" + catalogclient "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" + catalog "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" + builds "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds" "go/ast" "go/parser" "go/token" @@ -18,7 +23,6 @@ import ( "git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient" stdio "git.novox.be/novox/mesh-sdk/go" "git.novox.be/novox/mesh-sdk/go/catalogue" - "git.novox.be/novox/mesh-sdk/go/delivery" ) // router is the operator's channel as the holder asks on it: it holds an ask open, or, like the router past its open @@ -87,10 +91,10 @@ func newMesh() *mesh { for module, states := range map[string]string{"postgres": `["bound"]`, "keycloak": `["bound"]`, "mesh-vault": `["bound"]`, "systemd-resolved": `[]`, "minio": `[]`} { raw := `{"module":"` + module + `","version":"1","state":` + states + `}` - digest := delivery.ManifestDigest([]byte(raw)) - value, _ := delivery.EncodeManifest(delivery.Manifest{Module: module, JSON: raw}) - _ = m.s.Put(ManifestsRead, mustKey(delivery.ManifestsKey(digest)), json.RawMessage(value)) - _ = m.s.Put(ModulesRead, mustKey(delivery.ModulesKey(module)), + digest := catalogclient.ManifestDigest([]byte(raw)) + value, _ := catalogclient.EncodeManifest(catalog.ManifestsByDigestValue{Module: module, Manifest: raw}) + _ = m.s.Put(ManifestsRead, mustKey(catalogclient.ManifestsKey(digest)), json.RawMessage(value)) + _ = m.s.Put(ModulesRead, mustKey(catalogclient.ModulesKey(module)), json.RawMessage(`{"v":1,"none2":"a test","manifest":"`+digest+`","repository":"novox/mesh-catalog","commit":"c1"}`)) } return m @@ -193,16 +197,16 @@ func TestAKeysStateFollowsItsProvidersOutcome(t *testing.T) { // A have counts as the provider's outcome only from a build whose manifest offers the provision (ADR 0603 clause 5): // refused when the build's manifest is known and does not offer it, provided when it does. bfp := "src2:" + strings.Repeat("bb", 32) - buildKey := mustKey(delivery.BuilderBuildKey("systemd-resolved", bfp)) + buildKey := mustKey(builds.BuilderBuildKey("systemd-resolved", bfp)) for _, row := range []struct{ name, provides, state, why string }{ {"a build whose manifest does not offer it", `["other"]`, meshprovisionerclient.Asked, "offers split-dns"}, {"a build whose manifest offers it", `[{"name":"split-dns"}]`, meshprovisionerclient.Provided, ""}, } { raw := `{"module":"systemd-resolved","version":"1","provides":` + row.provides + `}` - digest := delivery.ManifestDigest([]byte(raw)) - value, _ := delivery.EncodeManifest(delivery.Manifest{Module: "systemd-resolved", JSON: raw}) - _ = m.s.Put(ManifestsRead, mustKey(delivery.ManifestsKey(digest)), json.RawMessage(value)) - build, err := delivery.EncodeBuilderBuild(delivery.BuilderBuild{State: delivery.BuildDone, Manifest: digest}) + digest := catalogclient.ManifestDigest([]byte(raw)) + value, _ := catalogclient.EncodeManifest(catalog.ManifestsByDigestValue{Module: "systemd-resolved", Manifest: raw}) + _ = m.s.Put(ManifestsRead, mustKey(catalogclient.ManifestsKey(digest)), json.RawMessage(value)) + build, err := builds.EncodeBuilderBuild(builds.BuilderBuild{State: builds.BuildDone, Manifest: digest}) if err != nil { t.Fatal(err) } @@ -222,7 +226,7 @@ func TestAKeysStateFollowsItsProvidersOutcome(t *testing.T) { // provision depends on it (clauses 3, 6). c := newMesh() // The placement in its keys per module and node (research 072 row 37). - novox, err := delivery.PlacementValues(delivery.Placement{Node: "novox", Modules: []delivery.PlacedModule{{Module: "mesh-vault"}}}) + novox, err := assignerclient.PlacementValues(meshseat.Placement{Node: "novox", Modules: []meshseat.PlacedModule{{Module: "mesh-vault"}}}) if err != nil { t.Fatal(err) } @@ -507,10 +511,10 @@ func TestTheHolderHoldsNoTimer(t *testing.T) { // manifestOf puts a provider's manifest into the indexer's reads as the holder finds it. func (m *mesh) manifestOf(module, raw string) { - digest := delivery.ManifestDigest([]byte(raw)) - value, _ := delivery.EncodeManifest(delivery.Manifest{Module: module, JSON: raw}) - _ = m.s.Put(ManifestsRead, mustKey(delivery.ManifestsKey(digest)), json.RawMessage(value)) - _ = m.s.Put(ModulesRead, mustKey(delivery.ModulesKey(module)), + digest := catalogclient.ManifestDigest([]byte(raw)) + value, _ := catalogclient.EncodeManifest(catalog.ManifestsByDigestValue{Module: module, Manifest: raw}) + _ = m.s.Put(ManifestsRead, mustKey(catalogclient.ManifestsKey(digest)), json.RawMessage(value)) + _ = m.s.Put(ModulesRead, mustKey(catalogclient.ModulesKey(module)), json.RawMessage(`{"v":1,"none2":"a test","manifest":"`+digest+`","repository":"novox/mesh-catalog","commit":"c1"}`)) } diff --git a/modules/mesh-provisioner/cmd/mesh-provisioner/main.go b/modules/mesh-provisioner/cmd/mesh-provisioner/main.go index 27b50ad19..cd44b0eaa 100644 --- a/modules/mesh-provisioner/cmd/mesh-provisioner/main.go +++ b/modules/mesh-provisioner/cmd/mesh-provisioner/main.go @@ -9,6 +9,7 @@ package main import ( "context" "fmt" + catalogclient "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" "os" "strings" "sync" @@ -18,7 +19,6 @@ import ( "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient" oc "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel" 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/meshhandactsclient" "git.novox.be/novox/mesh-sdk/go/views" @@ -176,20 +176,20 @@ func slowest(took []Took) time.Duration { // The owners' read tools of the states the provisioner follows, which the manifest invokes (ADR 0615). const ( - PlacementTool = delivery.AssignerModule + ".assigner_read_placement" - WantsTool = delivery.AssignerModule + ".assigner_read_wants" - ModulesTool = delivery.IndexerModule + ".indexer_read_modules" - ManifestsTool = delivery.IndexerModule + ".indexer_read_manifests" + PlacementTool = assignerModule + ".assigner_read_placement" + WantsTool = assignerModule + ".assigner_read_wants" + ModulesTool = indexerModule + ".indexer_read_modules" + ManifestsTool = indexerModule + ".indexer_read_manifests" ) // newViews is the provisioner's pictures of the assigner's placement and wants and the indexer's modules and manifests: // each asked once of its owner's read tool, then followed by the owner's events (ADR 0615). func newViews() *views.Reader { v := &views.Reader{Module: Module, Logf: logf, Findings: meshconditionsclient.FindingWriter{State: stdio.State("findings")}, Acts: meshhandactsclient.Recorder{Ask: stdio.Ask}} - v.Follow(delivery.AssignerModule, "placement", PlacementTool) - v.Follow(delivery.AssignerModule, "wants", WantsTool) - v.Follow(delivery.IndexerModule, "modules", ModulesTool) - v.Follow(delivery.IndexerModule, "manifests", ManifestsTool) + v.Follow(assignerModule, "placement", PlacementTool) + v.Follow(assignerModule, "wants", WantsTool) + v.Follow(indexerModule, "modules", ModulesTool) + v.Follow(indexerModule, "manifests", ManifestsTool) return v } @@ -253,7 +253,7 @@ func main() { follow(ComposedRead, func(c stdio.StateChange) error { w.add(NodesOfNodeKey(c.Key)...); return nil }) follow(HaveRead, func(c stdio.StateChange) error { w.add(NodesOfNodeKey(c.Key)...); return nil }) follow(ModulesRead, func(c stdio.StateChange) error { - if module, err := delivery.ParseModulesKey(c.Key); err == nil { + if module, err := catalogclient.ParseModulesKey(c.Key); err == nil { w.add(h.NodesOfModule(module)...) } return nil diff --git a/modules/mesh-provisioner/cmd/mesh-provisioner/provisioner_test.go b/modules/mesh-provisioner/cmd/mesh-provisioner/provisioner_test.go index 4bccbcebe..9fb54ccb2 100644 --- a/modules/mesh-provisioner/cmd/mesh-provisioner/provisioner_test.go +++ b/modules/mesh-provisioner/cmd/mesh-provisioner/provisioner_test.go @@ -296,10 +296,15 @@ func TestTheManifestDeclaresWhatTheCodeKeeps(t *testing.T) { Consumes []string `json:"consumes"` Tools []string `json:"tools"` OwnSecrets map[string]string `json:"own-secrets"` + Packages map[string]string `json:"packages"` } if err := json.Unmarshal(raw, &m); err != nil { t.Fatal(err) } + // The builder's keys are read through its published package, taken by its series (ADR 0634 §1). + if m.Packages["builds"] != "0.1" { + t.Errorf("packages %v: want builds of series 0.1", m.Packages) + } if m.Module != "mesh-provisioner" || m.RunsAs != "mesh-provisioner" || m.OwnSecrets["broker"] == "" { t.Errorf("an account of its own: module %q, runs-as %q, own secrets %v", m.Module, m.RunsAs, m.OwnSecrets) } diff --git a/modules/mesh-provisioner/cmd/mesh-provisioner/readers.go b/modules/mesh-provisioner/cmd/mesh-provisioner/readers.go new file mode 100644 index 000000000..a2515b025 --- /dev/null +++ b/modules/mesh-provisioner/cmd/mesh-provisioner/readers.go @@ -0,0 +1,31 @@ +package main + +import ( + "encoding/json" + "fmt" + + meshwants "git.novox.be/novox/mesh-contracts/placement/go/seats/meshwants" +) + +// The holders whose states this module reads, one constant each (a reader names its holder once; ADR 0634 §4). +const ( + assignerModule = "mesh-delivery-assigner" + indexerModule = "mesh-delivery-indexer" +) + +// parseWantKey reads a want's key, `want..`; the want of a node's own and a held fingerprint's key are +// not one. +func parseWantKey(key string) (node, module string, err error) { + node, module, ok := meshwants.ParseWantsByWantNodeModuleKey(key) + if !ok { + return "", "", fmt.Errorf("%q is not the key of a module's want on a node", key) + } + return node, module, nil +} + +// decodeWant reads a want of a module on a node, as the wants seat types it. +func decodeWant(value []byte) (meshwants.WantsByWantNodeModuleValue, error) { + var w meshwants.WantsByWantNodeModuleValue + err := json.Unmarshal(value, &w) + return w, err +} diff --git a/modules/mesh-provisioner/go.mod b/modules/mesh-provisioner/go.mod index 9c368615f..c4cdc3cba 100644 --- a/modules/mesh-provisioner/go.mod +++ b/modules/mesh-provisioner/go.mod @@ -3,6 +3,13 @@ module mesh-provisioner 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/go/clients/nodeengineclient v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient 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 diff --git a/modules/mesh-provisioner/module.json b/modules/mesh-provisioner/module.json index db0a66b75..8cdbc5319 100644 --- a/modules/mesh-provisioner/module.json +++ b/modules/mesh-provisioner/module.json @@ -1,6 +1,9 @@ { "module": "mesh-provisioner", "version": "2", + "packages": { + "builds": "0.1" + }, "runs-as": "mesh-provisioner", "claims": [ {