diff --git a/modules/mesh-delivery-builder/internal/build/buildread.go b/modules/mesh-delivery-builder/internal/build/buildread.go index ec924e57..859e434c 100644 --- a/modules/mesh-delivery-builder/internal/build/buildread.go +++ b/modules/mesh-delivery-builder/internal/build/buildread.go @@ -171,7 +171,7 @@ func (r *BuildReader) stepNode(v builds.BuilderBuild) string { } slices.Sort(languages) for _, l := range languages { - bm, _ := buildstep.BuildModuleOf(l) + bm, _ := buildstep.BuildSeat(l) // the steps are by build seat (StuckReader.Steps) st, ok := r.Steps.Steps[bm] k, found := r.index[bm][v.Asks[l]] if !ok || !found { diff --git a/modules/mesh-delivery-builder/internal/build/run_test.go b/modules/mesh-delivery-builder/internal/build/run_test.go index 7451bff1..37c788f3 100644 --- a/modules/mesh-delivery-builder/internal/build/run_test.go +++ b/modules/mesh-delivery-builder/internal/build/run_test.go @@ -154,7 +154,8 @@ func (r *rig) answer(a buildstep.StepAsk) buildstep.Step { if node == "" { node = "ace" } - module, _ := buildstep.BuildModuleOf(a.Language) + bm, _ := testSeats().OfAskLanguage(a.Language) + module := bm.Module runner := buildrun.Runner{Caller: r.bm, Steps: r.bm.steps, Bound: time.Second} tool := func(name string) stepFunc { return func(ctx context.Context, args map[string]any) (any, error) { diff --git a/modules/mesh-delivery-checker/internal/check/examiner_test.go b/modules/mesh-delivery-checker/internal/check/examiner_test.go index 7b006a3a..32f9b140 100644 --- a/modules/mesh-delivery-checker/internal/check/examiner_test.go +++ b/modules/mesh-delivery-checker/internal/check/examiner_test.go @@ -212,7 +212,8 @@ var toolPrefix = map[string]string{goBuildModule: "build_go_", nodeBuildModule: // answer runs a check ask as the build module that pulls it does (gobuild.RunAsk), with the fake tools on shanks and the // bench's kept verdicts, and answers the step it writes. func (b *bench) answer(a buildstep.StepAsk) buildstep.Step { - module, _ := buildstep.BuildModuleOf(a.Language) + bm, _ := testSeats().OfAskLanguage(a.Language) + module := bm.Module const node = "shanks" tool := func(name string) gobuild.StepFunc { return func(ctx context.Context, args map[string]any) (any, error) { diff --git a/modules/mesh-delivery-watchman/cmd/mesh-delivery-watchman/integration_test.go b/modules/mesh-delivery-watchman/cmd/mesh-delivery-watchman/integration_test.go index ec19015b..97ef4b04 100644 --- a/modules/mesh-delivery-watchman/cmd/mesh-delivery-watchman/integration_test.go +++ b/modules/mesh-delivery-watchman/cmd/mesh-delivery-watchman/integration_test.go @@ -69,6 +69,14 @@ func serveOwners(t *testing.T, ctx context.Context, js jetstream.JetStream, nc * t.Helper() viewstest.Serve(t, ctx, js, nc, watch.AssignerModule, wantsState, watch.AssignerModule+".assigner_read_wants", "n1_"+watch.WatchmanModule) viewstest.Serve(t, ctx, js, nc, watch.IndexerModule, modulesState, watch.IndexerModule+".indexer_read_modules", "n1_"+watch.WatchmanModule) + // The seat that places modules says who holds each build seat (design 47 G A-126): the module whose steps the watchman watches. + held, _ := json.Marshal(map[string]any{"result": map[string]any{"seats": []map[string]any{ + {"seat": "node-build-go", "holders": []map[string]string{{"module": "mesh-build-go"}}}}}, "node": "ctl"}) + for _, subject := range []string{"mesh.seat.mesh-module-assigner.call.assignments", "mesh.seat.mesh-module-assigner.call.assignments.>"} { + if _, err := nc.Subscribe(subject, func(m *nats.Msg) { _ = m.Respond(held) }); err != nil { + t.Fatal(err) + } + } } // findingsStream is the watchman's own stream of its findings' events (design 47 G §383), which its finding writer says into. diff --git a/modules/mesh-delivery-watchman/cmd/mesh-delivery-watchman/main.go b/modules/mesh-delivery-watchman/cmd/mesh-delivery-watchman/main.go index 3398eca1..9e92c521 100644 --- a/modules/mesh-delivery-watchman/cmd/mesh-delivery-watchman/main.go +++ b/modules/mesh-delivery-watchman/cmd/mesh-delivery-watchman/main.go @@ -19,6 +19,7 @@ import ( "time" "git.novox.be/novox/mesh-sdk/go/process" + "git.novox.be/novox/mesh-sdk/go/seatlist" "git.novox.be/novox/mesh-sdk/go/views" "github.com/nats-io/nats.go" @@ -345,7 +346,16 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro // keeps no steps bucket (yet) is said and left; the asks of its stream are mirrored all the same. taken := map[string]jetstream.KeyWatcher{} for _, lang := range buildstep.AskLanguages { - module, _ := buildstep.BuildModuleOf(lang) + seat, err := buildstep.BuildSeat(lang) + if err != nil { + continue + } + // The build module is the holder of the build seat (design 47 G A-82, A-126): asked of the seat that places modules. + module, err := seatlist.HolderOf(calls.Call, seat) + if err != nil { + logger.Printf("the steps of %s cannot be read (%v): its asks are mirrored and never seen taken", seat, err) + continue + } kv, err := js.KeyValue(ctx, watch.StepsBucket(module)) if err != nil { logger.Printf("the steps of %s cannot be read (%v): its asks are mirrored and never seen taken", module, err) diff --git a/modules/mesh-delivery-watchman/internal/watch/names.go b/modules/mesh-delivery-watchman/internal/watch/names.go index 6b2cf0e5..e9bf2353 100644 --- a/modules/mesh-delivery-watchman/internal/watch/names.go +++ b/modules/mesh-delivery-watchman/internal/watch/names.go @@ -39,5 +39,5 @@ const ( FindingNodeAccountFallback = "node-account-fallback" ) -// StepsBucket is a build module's state steps (reads: mesh-build-go.steps, mesh-build-image.steps, mesh-build-node.steps). +// StepsBucket is a build module's state steps (reads: the build seats' steps, `seat:node-build-.steps`, held by the module that holds the seat). func StepsBucket(module string) string { return module + "_steps" } diff --git a/modules/mesh-delivery-watchman/manifest_test.go b/modules/mesh-delivery-watchman/manifest_test.go index 5f6a53dc..70e977ac 100644 --- a/modules/mesh-delivery-watchman/manifest_test.go +++ b/modules/mesh-delivery-watchman/manifest_test.go @@ -13,6 +13,8 @@ import ( "testing" "mesh-delivery-watchman/internal/watch" + + "git.novox.be/novox/mesh-sdk/go/seatlist" ) // The controller's manifest reader is mesh-controller internal/catalogue (ParseManifest), an internal package of another @@ -56,6 +58,7 @@ type manifest struct { Reads []string `json:"reads"` Consumes []string `json:"consumes"` Invokes []string `json:"invokes"` + Requires []any `json:"requires"` State []state `json:"state"` Streams []struct { Name string `json:"name"` @@ -101,8 +104,9 @@ func TestManifestStateAndReads(t *testing.T) { // 1135/AC6 } reads := slices.Clone(m.Reads) slices.Sort(reads) - if !reflect.DeepEqual(reads, []string{"mesh-build-go.steps", "mesh-build-image.steps", "mesh-build-node.steps", - "mesh-delivery-builder.builds", "mesh-host.health", "mesh-provisioner.provisions", "node-engine.have"}) { + // The build modules are no names (design 47 G A-82): the steps are the states of the holders of the build seats. + if !reflect.DeepEqual(reads, []string{"mesh-delivery-builder.builds", "mesh-host.health", "mesh-provisioner.provisions", "node-engine.have", + "seat:node-build-go.steps", "seat:node-build-image.steps", "seat:node-build-node.steps"}) { t.Errorf("reads = %v", reads) } // Design 47 G §368: the version is MAJOR.MINOR and nothing else. @@ -123,7 +127,7 @@ func TestManifestStateAndReads(t *testing.T) { // 1135/AC6 } // The join: its first view asked again by hand is a hand act, recorded through the seat (views.StatusTools). The waits the // indexer holds at the watchman's start are asked of its tool (ADR 0633 §5), a verb edge as the view's. - if want := []string{"mesh-delivery-assigner.assigner_read_wants", "mesh-delivery-indexer.indexer_pending", "mesh-delivery-indexer.indexer_read_modules", "seat:mesh-hand-acts.hand-act"}; !reflect.DeepEqual(m.Invokes, want) { + if want := []string{"mesh-delivery-assigner.assigner_read_wants", "mesh-delivery-indexer.indexer_pending", "mesh-delivery-indexer.indexer_read_modules", "seat:mesh-hand-acts.hand-act", "seat:mesh-module-assigner.assignments"}; !reflect.DeepEqual(m.Invokes, want) { t.Errorf("invokes = %v, want %v", m.Invokes, want) } // DLV-M5 its first views, 1585/T20 the indexer's take-in events, and DLV-W1 (ADR 0595 §4): the accepted verdicts and the bus module's created and refused, durably. @@ -165,14 +169,16 @@ func TestManifestStateAndReads(t *testing.T) { // 1135/AC6 return e } for name, row := range map[string]struct{ got, want string }{ - "module": {watch.WatchmanModule, m.Module}, - "presence": {watch.PresenceBucket, m.Module + "_" + m.State[0].Name}, - "pulse": {watch.PulseBucket, m.Module + "_" + m.State[1].Name}, - "stuck": {watch.StuckBucket, m.Module + "_" + m.State[2].Name}, - "have": {watch.HaveBucket, bucket(m.Reads, "node-engine.have")}, - "health": {watch.NodeHealthBucket, bucket(m.Reads, "mesh-host.health")}, - "builds": {watch.BuilderBuildsBucket, bucket(m.Reads, "mesh-delivery-builder.builds")}, - "steps": {watch.StepsBucket("mesh-build-go"), bucket(m.Reads, "mesh-build-go.steps")}, + "module": {watch.WatchmanModule, m.Module}, + "presence": {watch.PresenceBucket, m.Module + "_" + m.State[0].Name}, + "pulse": {watch.PulseBucket, m.Module + "_" + m.State[1].Name}, + "stuck": {watch.StuckBucket, m.Module + "_" + m.State[2].Name}, + "have": {watch.HaveBucket, bucket(m.Reads, "node-engine.have")}, + "health": {watch.NodeHealthBucket, bucket(m.Reads, "mesh-host.health")}, + "builds": {watch.BuilderBuildsBucket, bucket(m.Reads, "mesh-delivery-builder.builds")}, + // The build modules are no names (design 47 G A-82): the steps are read as the states of the holders of the build seats. + "steps": {"seat:node-build-go.steps", entry(m.Reads, "seat:node-build-go.steps")}, + "holders": {"seat:" + seatlist.HolderSeat + "." + seatlist.HolderVerb, entry(m.Invokes, "seat:"+seatlist.HolderSeat+"."+seatlist.HolderVerb)}, "wants": {watch.AssignerWantsBucket, bucket(m.Consumes, "mesh-delivery-assigner.views.wants")}, "provisions": {watch.ProvisionsBucket, bucket(m.Reads, "mesh-provisioner.provisions")}, "modules": {watch.ModulesBucket, bucket(m.Consumes, "mesh-delivery-indexer.views.modules")}, diff --git a/modules/mesh-delivery-watchman/module.json b/modules/mesh-delivery-watchman/module.json index d51fa8b9..48d7c590 100644 --- a/modules/mesh-delivery-watchman/module.json +++ b/modules/mesh-delivery-watchman/module.json @@ -16,19 +16,20 @@ } ], "reads": [ - "mesh-build-go.steps", - "mesh-build-image.steps", - "mesh-build-node.steps", "mesh-delivery-builder.builds", "mesh-host.health", "mesh-provisioner.provisions", - "node-engine.have" + "node-engine.have", + "seat:node-build-go.steps", + "seat:node-build-image.steps", + "seat:node-build-node.steps" ], "invokes": [ "mesh-delivery-assigner.assigner_read_wants", "mesh-delivery-indexer.indexer_pending", "mesh-delivery-indexer.indexer_read_modules", - "seat:mesh-hand-acts.hand-act" + "seat:mesh-hand-acts.hand-act", + "seat:mesh-module-assigner.assignments" ], "consumes": [ "mesh-delivery-assigner.views.wants",