Checker follows the assignments of the holder of the seat mesh-module-assigner found at run time; FollowAssignments, AssignerModule and AssignmentsBucket go
buildrun gains AssignerHolder, AssignmentsOf and SleepFor (moved from the builder's program, which uses them), so the builder and the checker follow the holder's first-view tool, views family and state alike; the checker's manifest consumes the seat's views assignments and reads its assignments state, its composition reads the picture once followed. The assigner's callers list for the first view gains the checker. Builds: design 47 G A-82, A-119, A-126, A-131 Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
This commit is contained in:
@@ -528,7 +528,7 @@ func TestWantsAreWrittenAndNoDifferenceIs(t *testing.T) {
|
||||
// indexer its runtime of its own answers each first-view tool to the readers its manifest's callers lists name, at
|
||||
// its mark, and refuses every other account, the assigner's own included.
|
||||
firstViewsBeforeAnyNewFormState(t, b, map[string][]string{
|
||||
"assignments": {"n1.mesh-grants", "n1.node-tools", "n1.mesh-delivery-indexer", "n1.mesh-delivery-builder"},
|
||||
"assignments": {"n1.mesh-grants", "n1.node-tools", "n1.mesh-delivery-indexer", "n1.mesh-delivery-builder", "n1.mesh-delivery-checker"},
|
||||
"seat-holders": {"n1.node-tools"},
|
||||
"wants": {"n1.mesh-grants"},
|
||||
})
|
||||
@@ -1811,7 +1811,7 @@ func firstViewsBeforeAnyNewFormState(t *testing.T, b *bed, granted map[string][]
|
||||
return msg.Data, nil
|
||||
}, tool)(b.ctx, "")
|
||||
}
|
||||
readers := []string{"n1.mesh-grants", "n1.node-tools", "n1.mesh-delivery-indexer", "n1.mesh-delivery-assigner", "n1.mesh-delivery-builder"}
|
||||
readers := []string{"n1.mesh-grants", "n1.node-tools", "n1.mesh-delivery-indexer", "n1.mesh-delivery-assigner", "n1.mesh-delivery-builder", "n1.mesh-delivery-checker"}
|
||||
for tool, allowed := range granted {
|
||||
for _, user := range readers {
|
||||
a, err := ask(user, tool)
|
||||
|
||||
@@ -109,7 +109,7 @@ func TestManifest(t *testing.T) {
|
||||
t.Errorf("runs-as = %q: the bundle runs on the assigner's own account (ADR 0621 §1)", m.RunsAs)
|
||||
}
|
||||
// ADR 0621 §2: each first-view tool answers exactly its readers, never the assigner.
|
||||
callers := map[string]string{"assignments": "mesh-grants,node-tools,mesh-delivery-indexer,mesh-delivery-builder", "seat-holders": "node-tools", "wants": "mesh-grants"}
|
||||
callers := map[string]string{"assignments": "mesh-grants,node-tools,mesh-delivery-indexer,mesh-delivery-builder,mesh-delivery-checker", "seat-holders": "node-tools", "wants": "mesh-grants"}
|
||||
env := m.Build.Artifacts[0].Env
|
||||
for tool, list := range callers {
|
||||
if env[views.CallersEnv(tool)] != list || views.ParseCallers(env[views.CallersEnv(tool)]).Allows("n1."+m.Module) {
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
"mesh-delivery-assigner"
|
||||
],
|
||||
"env": {
|
||||
"MESH_VIEW_CALLERS_ASSIGNMENTS": "mesh-grants,node-tools,mesh-delivery-indexer,mesh-delivery-builder",
|
||||
"MESH_VIEW_CALLERS_ASSIGNMENTS": "mesh-grants,node-tools,mesh-delivery-indexer,mesh-delivery-builder,mesh-delivery-checker",
|
||||
"MESH_VIEW_CALLERS_SEAT_HOLDERS": "node-tools",
|
||||
"MESH_VIEW_CALLERS_WANTS": "mesh-grants"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package buildrun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
mma "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner"
|
||||
"git.novox.be/novox/mesh-sdk/go/seatlist"
|
||||
)
|
||||
|
||||
// **No part names the assigner** (design 47 G A-119, A-126, A-131): the module holding the seat mesh-module-assigner is asked of
|
||||
// the seat that places modules through mesh-sdk's seatlist.HolderOf, and the names of the view of its state assignments are made
|
||||
// from it, so a new holder is followed with no change of code. The builder and the checker follow the assignments so.
|
||||
|
||||
// AssignerHolder is the module holding the seat mesh-module-assigner now, asked until it answers or ctx ends. wait is the pause
|
||||
// between two asks that failed; sleep says false when ctx ended during it.
|
||||
func AssignerHolder(ctx context.Context, call seatlist.HolderCaller, wait time.Duration, sleep func(context.Context, time.Duration) bool, logf func(string, ...any)) (string, bool) {
|
||||
for {
|
||||
holder, err := seatlist.HolderOf(call, mma.Seat)
|
||||
if err == nil {
|
||||
return holder, true
|
||||
}
|
||||
logf("who holds the seat %s is not known yet, so no node is counted by the assignments: %v", mma.Seat, err)
|
||||
if !sleep(ctx, wait) {
|
||||
return "", false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AssignmentsNames are the names made from the holder: the view's first-view tool `<holder>.assignments` and the bucket
|
||||
// `<holder>_assignments` of the state.
|
||||
type AssignmentsNames struct{ Holder, Tool, Bucket string }
|
||||
|
||||
// AssignmentsOf is the names of the holder's state assignments.
|
||||
func AssignmentsOf(holder string) AssignmentsNames {
|
||||
return AssignmentsNames{Holder: holder, Tool: holder + "." + mma.StateAssignments, Bucket: holder + "_" + mma.StateAssignments}
|
||||
}
|
||||
|
||||
// SleepFor waits d, a context's bound, and says whether ctx is still alive.
|
||||
func SleepFor(ctx context.Context, d time.Duration) bool {
|
||||
w, cancel := context.WithTimeout(ctx, d)
|
||||
defer cancel()
|
||||
<-w.Done()
|
||||
return ctx.Err() == nil
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package buildrun
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"git.novox.be/novox/mesh-sdk/go/seatlist"
|
||||
)
|
||||
|
||||
// The builder finds the assigner's holder at run time: asked again while the seat list does not answer, and the names of the view
|
||||
// The builder and the checker find the assigner's holder at run time: asked again while the seat list does not answer, and the names of the view
|
||||
// are made from the holder (design 47 G A-119), not fixed in code.
|
||||
func TestTheAssignerHolderIsFoundAtRunTime(t *testing.T) {
|
||||
asks, slept := 0, 0
|
||||
@@ -23,14 +23,14 @@ func TestTheAssignerHolderIsFoundAtRunTime(t *testing.T) {
|
||||
}
|
||||
return json.RawMessage(`{"seats":[{"seat":"mesh-module-assigner","holders":[{"module":"acme-assigner"}]}]}`), nil
|
||||
}
|
||||
holder, ok := assignerHolder(context.Background(), call, time.Second, func(context.Context, time.Duration) bool { slept++; return true }, func(string, ...any) {})
|
||||
holder, ok := AssignerHolder(context.Background(), call, time.Second, func(context.Context, time.Duration) bool { slept++; return true }, func(string, ...any) {})
|
||||
if !ok || holder != "acme-assigner" || slept != 1 {
|
||||
t.Fatalf("holder = %q, %v after %d pauses, want acme-assigner after one", holder, ok, slept)
|
||||
}
|
||||
if n := assignmentsOf(holder); n.Tool != "acme-assigner.assignments" || n.Bucket != "acme-assigner_assignments" {
|
||||
if n := AssignmentsOf(holder); n.Tool != "acme-assigner.assignments" || n.Bucket != "acme-assigner_assignments" {
|
||||
t.Errorf("names = %+v", n)
|
||||
}
|
||||
if _, ok := assignerHolder(context.Background(), func(string, any) (json.RawMessage, error) { return nil, errors.New("down") }, time.Second,
|
||||
if _, ok := AssignerHolder(context.Background(), func(string, any) (json.RawMessage, error) { return nil, errors.New("down") }, time.Second,
|
||||
func(context.Context, time.Duration) bool { return false }, func(string, ...any) {}); ok {
|
||||
t.Error("a holder was found though ctx ended")
|
||||
}
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
package buildrun
|
||||
|
||||
// The holders' states the runner reads, named from the builder's own manifest (module.json `reads`; a unit row of the
|
||||
// builder checks them against it) and from the build seats' states: a bucket is `<holder module>_<state>`, and no contract
|
||||
// names a holder (novox/hq ADR 0625 §4, ADR 0634 §1).
|
||||
const (
|
||||
// AssignmentsBucket is the assigner's state assignments, which the toolchain gate reads for the placing of a build module.
|
||||
AssignerModule = "mesh-delivery-assigner"
|
||||
AssignmentsBucket = AssignerModule + "_assignments"
|
||||
)
|
||||
// The holders' states the runner reads are named from the build seats' and the assigner's holders found at run time: a bucket is
|
||||
// `<holder module>_<state>`, and no contract names a holder (novox/hq ADR 0625 §4, ADR 0634 §1).
|
||||
|
||||
// StepsBucket is the bucket of a build module's state steps.
|
||||
func StepsBucket(module string) string { return module + "_steps" }
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
)
|
||||
|
||||
// PlacedNodes holds which modules the catalogue places on which node: the seat mesh-module-assigner's state `assignments`,
|
||||
// held by the assigner (AssignmentsBucket, read by meshmoduleassignerclient.Placed; ADR 0567), or the assignment half
|
||||
// held by the assigner (the holder's `<holder>_assignments`, read by meshmoduleassignerclient.Placed; ADR 0567), or the assignment half
|
||||
// (meshwantsclient.Placed, fed by Revision). The builder counts the nodes it names (Nodes; ADR 0624 §1). It holds no
|
||||
// toolchain: the builder keeps the toolchains it heard in its state toolchains-heard and forgets one when the node-engine
|
||||
// seat says the build module stopped on the node, never on a placement (design 47 G §546).
|
||||
@@ -26,7 +26,7 @@ type PlacedNodes struct {
|
||||
placed placements
|
||||
known bool // the assignments is settled
|
||||
// OnPlaced is told, outside the lock, once the assignments's history is read and after each revision of it
|
||||
// FollowAssignments hears from then on (the counted nodes, Nodes, may have changed). Nil tells nobody.
|
||||
// FollowAssignmentsIn hears from then on (the counted nodes, Nodes, may have changed). Nil tells nobody.
|
||||
OnPlaced func()
|
||||
}
|
||||
|
||||
@@ -48,13 +48,13 @@ func (g *PlacedNodes) Assignments(key string, value []byte, deleted bool, at tim
|
||||
defer g.mu.Unlock()
|
||||
placed, ok := g.placed.(*meshmoduleassignerclient.Placed)
|
||||
if !ok {
|
||||
g.logf("%s: %s is not read, the placements take the assignment half (design 47)", AssignmentsBucket, key)
|
||||
g.logf("the assignments: %s is not read, the placements take the assignment half (design 47)", key)
|
||||
return false
|
||||
}
|
||||
was := placed.Seeded()
|
||||
_, changed, err := placed.Put(key, value, deleted, at)
|
||||
if err != nil {
|
||||
g.logf("%s: %v (issue 1940)", AssignmentsBucket, err)
|
||||
g.logf("the assignments: %v (issue 1940)", err)
|
||||
}
|
||||
if !g.known {
|
||||
return false
|
||||
@@ -103,7 +103,7 @@ func (g *PlacedNodes) Settled() {
|
||||
g.known = true
|
||||
}
|
||||
|
||||
// assignmentsFollowed is what FollowAssignments feeds: the builder's PlacedNodes, or the ToolchainGate of the checker and the
|
||||
// assignmentsFollowed is what FollowAssignmentsIn feeds: the builder's PlacedNodes, or the ToolchainGate of the checker and the
|
||||
// indexer. settle says the history is read and answers whether what the part passes on changed; placedTold tells its OnPlaced.
|
||||
type assignmentsFollowed interface {
|
||||
Assignments(key string, value []byte, deleted bool, at time.Time) bool
|
||||
@@ -125,17 +125,11 @@ func (g *ToolchainGate) placedTold() {
|
||||
}
|
||||
}
|
||||
|
||||
// FollowAssignments reads the seat mesh-module-assigner's assignments with its history into gate, says it settled, and follows it; changed
|
||||
// is told whenever what the gate passes on changed. Where the state cannot be read or watched (not there, or this part
|
||||
// may not read it) it says so and the gate passes every report on: not fatal, since an indexer, builder or checker that
|
||||
// stops for it stops every delivery (issue 1945). An error of the watch after that ends on the channel.
|
||||
func FollowAssignments(ctx context.Context, js process.BucketOpener, gate assignmentsFollowed, changed func(context.Context), logf func(string, ...any)) (<-chan error, error) {
|
||||
return FollowAssignmentsIn(ctx, js, AssignmentsBucket, gate, changed, logf)
|
||||
}
|
||||
|
||||
// FollowAssignmentsIn is FollowAssignments for the state in bucket, the holder's `<holder>_assignments` where the holder is found at
|
||||
// run time (design 47 G A-119): the builder follows it so; FollowAssignments, with the fixed bucket, is the checker's until its own
|
||||
// change finds the holder.
|
||||
// FollowAssignmentsIn reads the seat mesh-module-assigner's assignments, the state `bucket` of its holder (found at run time, design 47
|
||||
// G A-119, A-126: AssignmentsOf), with its history into gate, says it settled, and follows it; changed is told whenever what the
|
||||
// gate passes on changed. Where the state cannot be read or watched (not there, or this part may not read it) it says so and the gate
|
||||
// passes every report on: not fatal, since an indexer, builder or checker that stops for it stops every delivery (issue 1945). An
|
||||
// error of the watch after that ends on the channel.
|
||||
func FollowAssignmentsIn(ctx context.Context, js process.BucketOpener, bucket string, gate assignmentsFollowed, changed func(context.Context), logf func(string, ...any)) (<-chan error, error) {
|
||||
errs := make(chan error, 1)
|
||||
kv, err := js.KeyValue(ctx, bucket)
|
||||
|
||||
@@ -86,7 +86,7 @@ func TestPlacedNodes(t *testing.T) {
|
||||
refused := errors.New("nats: permissions violation for publish to $JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_assignments")
|
||||
gate := NewPlacedNodes(logger.Printf)
|
||||
for _, bus := range []refusingBus{{err: refused}, {refuseOpen: true, err: refused}} {
|
||||
if _, err := FollowAssignments(context.Background(), bus, gate, func(context.Context) {}, logger.Printf); err != nil {
|
||||
if _, err := FollowAssignmentsIn(context.Background(), bus, "acme-assigner_assignments", gate, func(context.Context) {}, logger.Printf); err != nil {
|
||||
t.Errorf("a refused assignments stopped the start: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
//
|
||||
// ToolchainGate stands between the toolchains buckets and what takes their entries in (the indexer's PutToolchain, a
|
||||
// ToolchainSet): an entry is passed on only while the assignments (the seat mesh-module-assigner's state `assignments`, held by
|
||||
// the assigner, AssignmentsBucket, read by meshmoduleassignerclient.Placed; ADR 0567, ADR 0549 §3) places its build module on
|
||||
// the assigner, its holder's state assignments, read by meshmoduleassignerclient.Placed; ADR 0567, ADR 0549 §3) places its build module on
|
||||
// its node, and only once it was written since that assignments; it is passed on as deleted otherwise, said once, and
|
||||
// passed on when its node writes it again. A report written before the assignments is one an earlier run of the module
|
||||
// left (issue 1940, 2026-10-25 20:56: mesh-build-node put on ace again brought ace.typescript v26.12.0 of 2026-10-23
|
||||
@@ -42,7 +42,7 @@ type ToolchainGate struct {
|
||||
raw map[string]map[string][]byte // module -> toolchains key -> value, as the bucket holds it
|
||||
left map[string]string // <module>/<key> -> why it was said left out, so each reason is said once
|
||||
// OnPlaced is told, outside the gate's lock, once the assignments's history is read and after each revision of it
|
||||
// FollowAssignments hears from then on (the counted nodes, Nodes, may have changed). Nil tells nobody.
|
||||
// FollowAssignmentsIn hears from then on (the counted nodes, Nodes, may have changed). Nil tells nobody.
|
||||
OnPlaced func()
|
||||
}
|
||||
|
||||
@@ -100,13 +100,13 @@ func (g *ToolchainGate) Assignments(key string, value []byte, deleted bool, at t
|
||||
defer g.mu.Unlock()
|
||||
placed, ok := g.placed.(*meshmoduleassignerclient.Placed)
|
||||
if !ok {
|
||||
g.logf("%s: %s is not read, the gate takes the assignment half (design 47)", AssignmentsBucket, key)
|
||||
g.logf("the assignments: %s is not read, the gate takes the assignment half (design 47)", key)
|
||||
return false
|
||||
}
|
||||
was := placed.Seeded()
|
||||
node, changed, err := placed.Put(key, value, deleted, at)
|
||||
if err != nil {
|
||||
g.logf("%s: %v (issue 1940)", AssignmentsBucket, err)
|
||||
g.logf("the assignments: %v (issue 1940)", err)
|
||||
}
|
||||
if !g.known {
|
||||
return false
|
||||
|
||||
@@ -249,7 +249,7 @@ func TestToolchainGate(t *testing.T) {
|
||||
refused := errors.New("nats: permissions violation for publish to $JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_assignments")
|
||||
gate, _, _ := start(t, false)
|
||||
for _, bus := range []refusingBus{{err: refused}, {refuseOpen: true, err: refused}} {
|
||||
if _, err := FollowAssignments(context.Background(), bus, gate, func(context.Context) {}, logger.Printf); err != nil {
|
||||
if _, err := FollowAssignmentsIn(context.Background(), bus, "acme-assigner_assignments", gate, func(context.Context) {}, logger.Printf); err != nil {
|
||||
t.Errorf("a refused assignments stopped the start: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,11 +347,11 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro
|
||||
// the view is the holder's first-view tool, followed by its views family (A-119, A-131); no assigner module is named.
|
||||
assignmentsErrs := make(chan error, 1)
|
||||
go func() {
|
||||
holder, ok := assignerHolder(ctx, calls.Call, 5*time.Second, sleepFor, logger.Printf)
|
||||
holder, ok := buildrun.AssignerHolder(ctx, calls.Call, 5*time.Second, buildrun.SleepFor, logger.Printf)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
names := assignmentsOf(holder)
|
||||
names := buildrun.AssignmentsOf(holder)
|
||||
assignments, err := reader.Follow(names.Holder, mma.StateAssignments, names.Tool)
|
||||
if err != nil {
|
||||
assignmentsErrs <- err
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
nodeengineseat "git.novox.be/novox/mesh-contracts/go/seats/nodeengine"
|
||||
@@ -23,6 +24,7 @@ import (
|
||||
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/buildrun"
|
||||
"git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"git.novox.be/novox/mesh-delivery/sdk/composer"
|
||||
"git.novox.be/novox/mesh-sdk/go/views"
|
||||
)
|
||||
|
||||
// The checker's composition of a check on the bus (novox/hq ADR 0593 §7, §9; check.EngineCompositions): each placed
|
||||
@@ -58,6 +60,18 @@ func (e engineAsks) ComposeCandidate(ctx context.Context, node string, ask ne.Co
|
||||
}
|
||||
}
|
||||
|
||||
// assignedNodes is the placedNodes' read of the assignments' picture, which is followed once the holder of the seat mesh-module-assigner
|
||||
// is known: an error says so until then, so a check never passes with no node asked.
|
||||
func assignedNodes(p *atomic.Pointer[views.Picture]) func(context.Context) (jetstream.KeyValue, error) {
|
||||
return func(context.Context) (jetstream.KeyValue, error) {
|
||||
pic := p.Load()
|
||||
if pic == nil {
|
||||
return nil, errors.New("the seat that places modules has no holder known yet")
|
||||
}
|
||||
return pic, nil
|
||||
}
|
||||
}
|
||||
|
||||
// placedNodes names every node the seat mesh-module-assigner places now, read from its state when a check composes (no
|
||||
// watch: a check reads it once). A state that cannot be read is an error, so the check says its composition was not
|
||||
// made rather than passing with no node asked (review finding 4).
|
||||
@@ -70,14 +84,14 @@ func (p placedNodes) Placed() ([]string, error) {
|
||||
defer cancel()
|
||||
kv, err := p.kv(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("the state %s cannot be read: %w", buildrun.AssignmentsBucket, err)
|
||||
return nil, fmt.Errorf("the assignments cannot be read: %w", err)
|
||||
}
|
||||
lister, err := kv.ListKeys(ctx)
|
||||
if errors.Is(err, jetstream.ErrNoKeysFound) {
|
||||
return nil, nil // assignments that places nothing
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("the state %s cannot be listed: %w", buildrun.AssignmentsBucket, err)
|
||||
return nil, fmt.Errorf("the assignments cannot be listed: %w", err)
|
||||
}
|
||||
seen := map[string]bool{}
|
||||
for key := range lister.Keys() {
|
||||
|
||||
@@ -446,7 +446,7 @@ func (b *bed) said(t *testing.T, words string) {
|
||||
// the seat verb's caller-named and plain subjects); the others never answer. It answers what it was asked.
|
||||
func (b *bed) engine(t *testing.T, nodes ...string) func() []ne.ComposeCandidateInput {
|
||||
t.Helper()
|
||||
kv, err := b.js.CreateKeyValue(b.ctx, jetstream.KeyValueConfig{Bucket: buildrun.AssignmentsBucket, History: 1})
|
||||
kv, err := b.js.CreateKeyValue(b.ctx, jetstream.KeyValueConfig{Bucket: testAssigner + "_assignments", History: 1})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -615,7 +615,16 @@ func startBed(t *testing.T) *bed {
|
||||
for _, st := range []string{candidatesState, modulesState, manifestsState, meshcatalog.StatePackages, check.IndexerPredefinedState, seatDefinitionsState} {
|
||||
viewstest.Serve(t, ctx, js, ctl, check.IndexerModule, st, check.IndexerModule+".indexer_read_"+strings.ReplaceAll(st, "-", "_"), "n1_"+check.CheckerModule)
|
||||
}
|
||||
viewstest.Serve(t, ctx, js, ctl, buildrun.AssignerModule, meshmoduleassigner.StateAssignments, buildrun.AssignerModule+".assigner_read_assignments", "n1_"+check.CheckerModule)
|
||||
// The seat mesh-module-assigner's holder is read from the seat's verb assignments, as the checker finds it at run time (A-119, A-126).
|
||||
for _, subject := range []string{"mesh.seat.mesh-module-assigner.call.assignments", "mesh.seat.mesh-module-assigner.call.assignments.>"} {
|
||||
if _, err := ctl.Subscribe(subject, func(m *nats.Msg) {
|
||||
reply, _ := json.Marshal(map[string]any{"result": map[string]any{"seats": []map[string]any{{"seat": "mesh-module-assigner", "holders": []map[string]string{{"module": testAssigner}}}}}, "node": "ctl"})
|
||||
_ = m.Respond(reply)
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
viewstest.Serve(t, ctx, js, ctl, testAssigner, meshmoduleassigner.StateAssignments, testAssigner+"."+meshmoduleassigner.StateAssignments, "n1_"+check.CheckerModule)
|
||||
// 1929/AC1: the indexer's value of the repository's module, with no TypeScript manifest, so the Go build module alone
|
||||
// judges the repository (an unknown repository is judged by both, issue 1887 A12).
|
||||
known, err := meshcatalogclient.EncodeModule(meshcatalog.ModulesByModuleValue{Repository: repo, Commit: trunk, Path: "modules/m", None2: "the fingerprint is computed with its chain"})
|
||||
@@ -1274,6 +1283,8 @@ func TestTheCheckerSaysOwedReadableOnTheBus(t *testing.T) { // R2-1
|
||||
}
|
||||
}
|
||||
|
||||
const testAssigner = "acme-assigner" // a holder of the seat mesh-module-assigner no code names
|
||||
|
||||
// buildSeatDefinitions are the seat definitions the bed's seat mesh-catalog lists: the build seats, as the mesh's own definitions of
|
||||
// them, whose acting rules say which language each builds.
|
||||
var buildSeatDefinitions = func() map[string]seatdef.Definition {
|
||||
|
||||
@@ -86,7 +86,6 @@ const (
|
||||
seatDefinitionsState = "seat-definitions"
|
||||
// The owners' views families, as their manifests' `streams` name them.
|
||||
indexerViews = "indexer-views"
|
||||
assignerViews = "assigner-views"
|
||||
modulesState = "modules"
|
||||
manifestsState = "manifests"
|
||||
)
|
||||
@@ -184,7 +183,8 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro
|
||||
manifests := followOwner(check.IndexerModule, manifestsState, "indexer_read_manifests", indexerViews)
|
||||
packages := followOwner(check.IndexerModule, meshcatalog.StatePackages, "indexer_read_packages", indexerViews)
|
||||
predefined := followOwner(check.IndexerModule, check.IndexerPredefinedState, "indexer_read_predefined", indexerViews)
|
||||
assignments := followOwner(buildrun.AssignerModule, meshmoduleassigner.StateAssignments, "assigner_read_assignments", assignerViews)
|
||||
// The assignments is followed once the seat list names the holder of the seat mesh-module-assigner (below).
|
||||
var assignments atomic.Pointer[views.Picture]
|
||||
// The seventh: the indexer's seat definitions, so a pull request whose head's definition does not parse fails.
|
||||
seatDefs := followOwner(check.IndexerModule, seatDefinitionsState, "indexer_read_seat_definitions", indexerViews)
|
||||
if followed != nil {
|
||||
@@ -290,7 +290,7 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro
|
||||
compositions := check.EngineCompositions{
|
||||
Requests: check.RequestKeeper{Store: requestsKV},
|
||||
Asker: engineAsks{call: caller.CallOn},
|
||||
Placed: placedNodes{kv: func(context.Context) (jetstream.KeyValue, error) { return assignments, nil }}.Placed,
|
||||
Placed: placedNodes{kv: assignedNodes(&assignments)}.Placed,
|
||||
Bound: answerBound,
|
||||
Sandbox: seatSandbox{asker: buildrun.Asker{Seats: buildSeats, Publisher: asks, Waiter: waiter, Bound: stepBound, Dropper: buildrun.SeatDrops{Caller: caller}}},
|
||||
Verdicts: verdicts.all,
|
||||
@@ -343,12 +343,25 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro
|
||||
// Until the assignments's first view is answered the gate passes every report on, as before the assignments is settled.
|
||||
assignmentsErrs := make(chan error, 1)
|
||||
go func() {
|
||||
// The module holding the seat mesh-module-assigner is found at run time (design 47 G A-119, A-126, A-131): the view is its
|
||||
// first-view tool, followed by its views family; no assigner module is named.
|
||||
holder, ok := buildrun.AssignerHolder(ctx, caller.Call, 5*time.Second, buildrun.SleepFor, logger.Printf)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
names := buildrun.AssignmentsOf(holder)
|
||||
pic, err := reader.Follow(names.Holder, meshmoduleassigner.StateAssignments, names.Tool)
|
||||
if err != nil {
|
||||
assignmentsErrs <- err
|
||||
return
|
||||
}
|
||||
assignments.Store(pic)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-assignments.Follower().Ready():
|
||||
case <-pic.Follower().Ready():
|
||||
}
|
||||
pe, err := buildrun.FollowAssignments(ctx, reader, gate, ch.NodesChanged, logger.Printf)
|
||||
pe, err := buildrun.FollowAssignmentsIn(ctx, reader, names.Bucket, gate, ch.NodesChanged, logger.Printf)
|
||||
if err != nil {
|
||||
assignmentsErrs <- err
|
||||
return
|
||||
|
||||
@@ -114,7 +114,7 @@ func TestManifestDeclarations(t *testing.T) { // 1678/AC1
|
||||
}
|
||||
// The build modules are no names (design 47 G A-82): the steps and toolchains of the build seats are read as the seat's holder's states.
|
||||
if want := []string{"mesh-delivery-builder.builds", "mesh-delivery-watchman.presence", "node-engine.have",
|
||||
"seat:node-build-go.steps", "seat:node-build-go.toolchains", "seat:node-build-node.steps", "seat:node-build-node.toolchains"}; !reflect.DeepEqual(sorted(m.Reads), want) {
|
||||
"seat:mesh-module-assigner.assignments", "seat:node-build-go.steps", "seat:node-build-go.toolchains", "seat:node-build-node.steps", "seat:node-build-node.toolchains"}; !reflect.DeepEqual(sorted(m.Reads), want) {
|
||||
t.Errorf("reads = %v, want %v", m.Reads, want)
|
||||
}
|
||||
// DLV-M2 (ADR 0615 §1, ADR 0596): the indexer's candidates, modules, manifests, packages and predefined keys and the
|
||||
@@ -128,7 +128,7 @@ func TestManifestDeclarations(t *testing.T) { // 1678/AC1
|
||||
// build module's tool and holds no capacity: it puts check asks on the Go and Node build seats (ADR 0525 §1, §4).
|
||||
// The forge's file read through the seat git, never a module's tool (milestone 47/G W6.14), the build seats' drop, and of
|
||||
// the node seat node-engine its one verb compose-candidate, asked of every placed node (ADR 0593 §7), never its other verbs.
|
||||
if want := []string{"mesh-delivery-assigner.assigner_read_assignments", "mesh-delivery-indexer.indexer_read_candidates", "mesh-delivery-indexer.indexer_read_manifests",
|
||||
if want := []string{"mesh-delivery-indexer.indexer_read_candidates", "mesh-delivery-indexer.indexer_read_manifests",
|
||||
"mesh-delivery-indexer.indexer_read_modules", "mesh-delivery-indexer.indexer_read_packages", "mesh-delivery-indexer.indexer_read_predefined",
|
||||
"mesh-delivery-indexer.indexer_read_seat_definitions",
|
||||
"seat:git.file", "seat:mesh-catalog.revision", "seat:mesh-catalog.seat-definitions", "seat:mesh-config.renames", "seat:mesh-hand-acts.hand-act", "seat:mesh-module-assigner.assignments", "seat:node-build-go.drop", "seat:node-build-node.drop", "seat:node-engine.compose-candidate"}; !reflect.DeepEqual(sorted(m.Invokes), want) {
|
||||
@@ -138,8 +138,8 @@ func TestManifestDeclarations(t *testing.T) { // 1678/AC1
|
||||
t.Errorf("uses = %v, want the Go and Node build seats", m.Uses)
|
||||
}
|
||||
// The checker is woken by a build seat's report of a toolchain, as the builder is: the bucket may not exist when it starts.
|
||||
if want := []string{"mesh-catalog.catalog-revision", "mesh-catalog.seat-definition-changed", "mesh-catalog.seat-definition-removed", "mesh-conditions.findings-asked", "mesh-delivery-assigner.views.assignments", "mesh-delivery-indexer.views.candidates", "mesh-delivery-indexer.views.manifests", "mesh-delivery-indexer.views.modules",
|
||||
"mesh-delivery-indexer.views.packages", "mesh-delivery-indexer.views.predefined", "mesh-delivery-indexer.views.seat-definitions", "node-build-go.toolchain-reported", "node-build-node.toolchain-reported", "node-engine.running"}; !reflect.DeepEqual(sorted(m.Consumes), want) {
|
||||
if want := []string{"mesh-catalog.catalog-revision", "mesh-catalog.seat-definition-changed", "mesh-catalog.seat-definition-removed", "mesh-conditions.findings-asked", "mesh-delivery-indexer.views.candidates", "mesh-delivery-indexer.views.manifests", "mesh-delivery-indexer.views.modules",
|
||||
"mesh-delivery-indexer.views.packages", "mesh-delivery-indexer.views.predefined", "mesh-delivery-indexer.views.seat-definitions", "node-build-go.toolchain-reported", "node-build-node.toolchain-reported", "node-engine.running", "seat:mesh-module-assigner.views.assignments"}; !reflect.DeepEqual(sorted(m.Consumes), want) {
|
||||
t.Errorf("consumes = %v, want %v", m.Consumes, want)
|
||||
}
|
||||
if b, ok := m.OwnSecrets["broker"]; !ok || len(m.OwnSecrets) != 1 || b != "${dir:state}/broker" {
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"mesh-catalog.catalog-revision",
|
||||
"mesh-catalog.seat-definition-changed",
|
||||
"mesh-catalog.seat-definition-removed",
|
||||
"mesh-delivery-assigner.views.assignments",
|
||||
"mesh-delivery-indexer.views.candidates",
|
||||
"mesh-delivery-indexer.views.manifests",
|
||||
"mesh-delivery-indexer.views.modules",
|
||||
@@ -26,6 +25,7 @@
|
||||
"mesh-delivery-indexer.views.predefined",
|
||||
"mesh-delivery-indexer.views.seat-definitions",
|
||||
"mesh-conditions.findings-asked",
|
||||
"seat:mesh-module-assigner.views.assignments",
|
||||
"node-build-go.toolchain-reported",
|
||||
"node-build-node.toolchain-reported",
|
||||
"node-engine.running"
|
||||
@@ -53,6 +53,7 @@
|
||||
"mesh-delivery-builder.builds",
|
||||
"mesh-delivery-watchman.presence",
|
||||
"node-engine.have",
|
||||
"seat:mesh-module-assigner.assignments",
|
||||
"seat:node-build-go.steps",
|
||||
"seat:node-build-go.toolchains",
|
||||
"seat:node-build-node.steps",
|
||||
@@ -79,7 +80,6 @@
|
||||
"checker_verdicts"
|
||||
],
|
||||
"invokes": [
|
||||
"mesh-delivery-assigner.assigner_read_assignments",
|
||||
"mesh-delivery-indexer.indexer_read_candidates",
|
||||
"mesh-delivery-indexer.indexer_read_manifests",
|
||||
"mesh-delivery-indexer.indexer_read_modules",
|
||||
|
||||
@@ -58,7 +58,6 @@ 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"`
|
||||
|
||||
Reference in New Issue
Block a user