DLV-M2: the checker follows the indexer's candidates, modules, manifests, packages and predefined keys and the assigner's placement by first views
ADR 0615 §1 and ADR 0596: the checker invokes the six read tools, consumes their views families and reads none of the six buckets; this ends its direct read of the packages state (the lead's ruling 2). It keeps first-views and findings and announces both as its own. The lead's ruling: an owner that is down never holds the start; the checks, the tries and the hand asks start once every first view is answered, the placement is followed into the toolchain gate once its first view is. firstview.Reader opens a followed state's picture as a process.BucketOpener (the builder uses it too). Tools checker_first_views and checker_first_view_again. Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
This commit is contained in:
@@ -178,7 +178,7 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro
|
||||
return
|
||||
case <-placement.Follower().Ready():
|
||||
}
|
||||
pe, err := buildrun.FollowPlacement(ctx, pictures{placement}, gate, b.NodesChanged, logger.Printf)
|
||||
pe, err := buildrun.FollowPlacement(ctx, reader, gate, b.NodesChanged, logger.Printf)
|
||||
if err != nil {
|
||||
placementErrs <- err
|
||||
return
|
||||
@@ -270,16 +270,6 @@ const (
|
||||
candidatesState = "candidates"
|
||||
)
|
||||
|
||||
// pictures opens the placement's picture where the gate's follower opens its bucket.
|
||||
type pictures struct{ placement *firstview.Picture }
|
||||
|
||||
func (p pictures) KeyValue(ctx context.Context, bucket string) (jetstream.KeyValue, error) {
|
||||
if bucket != p.placement.Bucket() {
|
||||
return nil, fmt.Errorf("the builder reads %s by its owner's first view alone", bucket)
|
||||
}
|
||||
return p.placement, nil
|
||||
}
|
||||
|
||||
// buildFromModules builds from the indexer's modules and candidates once the first views of the modules, the manifests and
|
||||
// the candidates are answered (never at the start: an indexer that is down holds no other part of it), and follows them.
|
||||
func buildFromModules(ctx context.Context, b *build.Builder, modules, manifests, candidates *firstview.Picture) error {
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/buildrun"
|
||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"git.novox.be/novox/mesh-sdk/go/views"
|
||||
|
||||
"mesh-delivery-checker/internal/check"
|
||||
)
|
||||
@@ -326,6 +327,9 @@ func main() {
|
||||
return err
|
||||
}}
|
||||
tools := append(append(append(Tools(r), TryTools(trying)...), SeatTryTools(trying)...), RecoveryTools(r, written)...)
|
||||
// checker_first_views and checker_first_view_again: where the checker's first views of the indexer's and the assigner's
|
||||
// states stand, and asking one again by hand (ADR 0615, DLV-M2).
|
||||
tools = append(tools, views.StatusTools(delivery.CheckerModule, "checker_first_views", "checker_first_view_again", stdio.State(views.StatusState), time.Now)...)
|
||||
if err := stdio.Serve("", logged(tools)); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "[mesh-delivery-checker-tools] %v\n", err)
|
||||
os.Exit(1)
|
||||
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
"git.novox.be/novox/mesh-contracts/go/seats/npmpackageregistry"
|
||||
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/buildrun"
|
||||
"git.novox.be/novox/mesh-sdk/go/announce"
|
||||
"git.novox.be/novox/mesh-delivery/sdk/firstview/firstviewtest"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"git.novox.be/novox/mesh-sdk/go/process"
|
||||
|
||||
@@ -440,6 +441,12 @@ func startBed(t *testing.T) *bed {
|
||||
}
|
||||
b.buckets[name] = kv
|
||||
}
|
||||
// The indexer and the assigner as the owners of the states the checker follows: their read tools and views families
|
||||
// (ADR 0615, DLV-M2).
|
||||
for _, st := range []string{candidatesState, modulesState, manifestsState, delivery.PackagesState, delivery.IndexerPredefinedState} {
|
||||
firstviewtest.Serve(t, ctx, js, ctl, delivery.IndexerModule, st, "n1_"+delivery.CheckerModule)
|
||||
}
|
||||
firstviewtest.Serve(t, ctx, js, ctl, delivery.AssignerModule, delivery.PlacementState, "n1_"+delivery.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 := delivery.EncodeModule(delivery.Module{Repository: repo, Commit: trunk, Path: "modules/m", None2: "the fingerprint is computed with its chain"})
|
||||
@@ -1071,7 +1078,7 @@ func TestTheCheckerSaysOwedReadableOnTheBus(t *testing.T) { // R2-1
|
||||
}
|
||||
if said.Module != "mesh-delivery-checker" || said.Node != "n1" || !said.Keeps("owed") || !said.Readable("owed") ||
|
||||
!said.Readable("previews") || !said.Readable("verdicts") || !said.Readable("kept") || !said.Readable("tries") || said.Readable("hearing") ||
|
||||
!said.Keeps("asks") || said.Readable("asks") || len(said.States) != 7 {
|
||||
t.Fatalf("the checker said %+v, want its seven states, asks and hearing its own, with kept, owed, previews, tries and verdicts readable, tries by the delivery view (1929/AC4; 1887 A5b; build_usage)", said)
|
||||
!said.Keeps("asks") || said.Readable("asks") || !said.Keeps("first-views") || said.Readable("first-views") || !said.Keeps("findings") || len(said.States) != 9 {
|
||||
t.Fatalf("the checker said %+v, want its nine states, asks, hearing and first-views its own (DLV-M2), with kept, owed, previews, tries and verdicts readable, tries by the delivery view (1929/AC4; 1887 A5b; build_usage)", said)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,14 +51,10 @@ type judgeDeps struct {
|
||||
}
|
||||
|
||||
// keptPredefined reads the indexer's key predefined.list (delivery.PredefinedKept), bound once its state appears.
|
||||
type keptPredefined struct{ b *process.AwaitedBucket }
|
||||
type keptPredefined struct{ kv jetstream.KeyValue }
|
||||
|
||||
func (k keptPredefined) PredefinedList(ctx context.Context) (json.RawMessage, error) {
|
||||
kv := k.b.KV()
|
||||
if kv == nil {
|
||||
return nil, process.ErrNotYet
|
||||
}
|
||||
e, err := kv.Get(ctx, delivery.PredefinedListKey)
|
||||
e, err := k.kv.Get(ctx, delivery.PredefinedListKey)
|
||||
if errors.Is(err, jetstream.ErrKeyNotFound) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -24,6 +24,9 @@ import (
|
||||
"git.novox.be/novox/mesh-sdk/go/announce"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"git.novox.be/novox/mesh-sdk/go/process"
|
||||
"git.novox.be/novox/mesh-sdk/go/views"
|
||||
|
||||
"git.novox.be/novox/mesh-delivery/sdk/firstview"
|
||||
|
||||
"mesh-delivery-checker/internal/check"
|
||||
)
|
||||
@@ -61,6 +64,20 @@ func durationEnv(getenv func(string) string, name string, def time.Duration) (ti
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// The checker's own states of its first views (views.StatusState) and of its findings, where the condition keeper raises a
|
||||
// first view unanswered past its bound (ADR 0615 §6); and the indexer's states it follows (delivery.CandidatesBucket,
|
||||
// ModulesBucket, ManifestsBucket).
|
||||
var (
|
||||
firstViewsBucket = delivery.CheckerModule + "_" + views.StatusState
|
||||
findingsBucket = delivery.CheckerModule + "_findings"
|
||||
)
|
||||
|
||||
const (
|
||||
candidatesState = "candidates"
|
||||
modulesState = "modules"
|
||||
manifestsState = "manifests"
|
||||
)
|
||||
|
||||
// run is the program: it returns when ctx ends or the bus cannot be used.
|
||||
// keptStates is every state the checker keeps and which others may read (ADR 0495 §2-§3): owed by the forge module,
|
||||
// which writes mesh/check from it (ADR 0521), and by the forge bridge; previews by the indexer; verdicts by the gate; tries by the delivery view.
|
||||
@@ -77,6 +94,10 @@ var keptStates = []announce.State{
|
||||
// The tries are read by the delivery view, which tells a try's asks from a candidate's by the asks each names (build_usage).
|
||||
{Name: "tries", Readable: true},
|
||||
{Name: "verdicts", Readable: true},
|
||||
// Where its first views of the indexer's and the assigner's states stand (read by checker_first_views alone), and its
|
||||
// findings of one unanswered past its bound, which the condition keeper observes as it does every reader's (ADR 0615 §6).
|
||||
{Name: views.StatusState},
|
||||
{Name: "findings"},
|
||||
}
|
||||
|
||||
func run(ctx context.Context, getenv func(string) string, logOut io.Writer) error {
|
||||
@@ -90,11 +111,10 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
// Its own states, the candidates it checks, and the modules and manifests that say which build modules judge a repository:
|
||||
// a check without one of them would be wrong or unwritten, so each stops the start, naming its grant (issue 1945).
|
||||
// Its own states: a check without one of them would be unwritten, so each stops the start, naming its grant (issue 1945).
|
||||
buckets := map[string]jetstream.KeyValue{}
|
||||
for _, name := range []string{delivery.CheckerVerdictsBucket, delivery.CheckerKeptBucket, delivery.CheckerOwedBucket, delivery.PreviewsBucket,
|
||||
delivery.CandidatesBucket, delivery.ModulesBucket, delivery.ManifestsBucket, check.TriesBucket} {
|
||||
check.TriesBucket} {
|
||||
if buckets[name], err = process.EssentialBucket(ctx, js, "checker", name, "judge or keep a check"); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -111,6 +131,19 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro
|
||||
if err := caller.Keeps(keptStates...); err != nil {
|
||||
logger.Printf("what the checker keeps could not be said on the bus, so readers of owed, previews and verdicts are not granted until it is: %v", err)
|
||||
}
|
||||
// The candidates it checks, the modules and manifests that say which build modules judge a repository, the published
|
||||
// packages, the predefined keys and the placement are their owners' states (the indexer's, the assigner's), read by the
|
||||
// first view of their read tools and then their views families, never their buckets (ADR 0615 §1, ADR 0596, DLV-M2).
|
||||
// An owner that is down never holds the start: the checks start once every first view is answered, and until then the
|
||||
// unanswered first view is the checker's finding.
|
||||
reader := &firstview.Reader{Module: delivery.CheckerModule, Call: caller.Call, Logf: logger.Printf}
|
||||
reader.Findings = process.AwaitBucket(ctx, js, conn, findingsBucket, "a first view unanswered past its bound is said in the log alone", logger.Printf).KV
|
||||
candidates := reader.Follow(delivery.IndexerModule, candidatesState)
|
||||
modules := reader.Follow(delivery.IndexerModule, modulesState)
|
||||
manifests := reader.Follow(delivery.IndexerModule, manifestsState)
|
||||
packages := reader.Follow(delivery.IndexerModule, delivery.PackagesState)
|
||||
predefined := reader.Follow(delivery.IndexerModule, delivery.IndexerPredefinedState)
|
||||
placement := reader.Follow(delivery.AssignerModule, delivery.PlacementState)
|
||||
|
||||
stepBound, err := durationEnv(getenv, "MESH_CHECKER_STEP_BOUND", check.DefaultStepBound)
|
||||
if err != nil {
|
||||
@@ -183,7 +216,7 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro
|
||||
verdicts := &manifestVerdicts{}
|
||||
compositions := check.EngineCompositions{
|
||||
Asker: engineAsks{call: caller.CallOn},
|
||||
Placed: placedNodes{kv: func(at context.Context) (jetstream.KeyValue, error) { return js.KeyValue(at, delivery.PlacementBucket) }}.Placed,
|
||||
Placed: placedNodes{kv: func(context.Context) (jetstream.KeyValue, error) { return placement, nil }}.Placed,
|
||||
Bound: answerBound,
|
||||
Sandbox: seatSandbox{asker: buildrun.Asker{Publisher: asks, Waiter: waiter, Bound: stepBound}},
|
||||
Verdicts: verdicts.all,
|
||||
@@ -196,23 +229,12 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro
|
||||
logger.Printf("%s of %s was written and the part that reads it could not be told: %v", event, key, err)
|
||||
}
|
||||
}
|
||||
repos := &repoModules{manifests: kvStore{buckets[delivery.ManifestsBucket]}, logf: logger.Printf, verdicts: verdicts}
|
||||
repos := &repoModules{manifests: kvStore{manifests}, logf: logger.Printf, verdicts: verdicts}
|
||||
compositions.Composer = func() string { return repos.CommitOf(check.NodeEngineModule) }
|
||||
compositions.Manifests = repos
|
||||
// The indexer's packages state (ADR 0593 §2): followed once the bus has it, so a Go check builds every mesh package at
|
||||
// its published version; until then a check builds each at the version its go.mod names.
|
||||
// The indexer's packages state (ADR 0593 §2), so a Go check builds every mesh package at its published version: read
|
||||
// once the first views are answered, before any check starts (afterFirstViews).
|
||||
published := &publishedPackages{}
|
||||
process.AwaitBucket(ctx, js, conn, delivery.PackagesBucket, "a Go check builds each mesh package at the version its go.mod names",
|
||||
logger.Printf).Ready(func(kv jetstream.KeyValue) {
|
||||
w, err := settle(ctx, kv, func(e jetstream.KeyValueEntry) { published.Put(e.Key(), e.Value(), !opOf(e)) })
|
||||
if err != nil {
|
||||
logger.Printf("%s cannot be watched, so a Go check builds each mesh package at the version its go.mod names: %v", delivery.PackagesBucket, err)
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
_ = follow(ctx, w, func(e jetstream.KeyValueEntry) { published.Put(e.Key(), e.Value(), !opOf(e)) })
|
||||
}()
|
||||
})
|
||||
ch.Runs = repos // the node-engine a check reads beside at its running commit (ADR 0548 §6)
|
||||
// A group's tagged versions (design 51 *Tagged versions*): tag-wanted on the checker's stream asks, and the package
|
||||
// registry seats' published heard on the checker's durable consumer of the shared events stream. Where that consumer is
|
||||
@@ -239,9 +261,8 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro
|
||||
kept: kvStore{buckets[delivery.CheckerKeptBucket]}, previews: kvStore{buckets[delivery.PreviewsBucket]},
|
||||
repos: repos, published: published, builds: builds, ends: ends, buildWait: set.buildWait, forge: forgeReads{call: caller.Call}, logf: logger.Printf,
|
||||
announce: announce, compositions: compositions, tags: tags,
|
||||
nodes: openNodeNames(ctx, js, logger.Printf),
|
||||
predefined: keptPredefined{process.AwaitBucket(ctx, js, conn, delivery.IndexerModule+"_"+delivery.IndexerPredefinedState,
|
||||
"a change's manifests are judged with no predefined keys, each fallback as its value declares it", logger.Printf)},
|
||||
nodes: openNodeNames(ctx, js, logger.Printf),
|
||||
predefined: keptPredefined{predefined},
|
||||
})
|
||||
ch.Announce = announce
|
||||
tryer.Judge = ch.Judge
|
||||
@@ -252,10 +273,21 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro
|
||||
// placement is settled first, so the toolchains settle through the gate already knowing it.
|
||||
gate := buildrun.NewToolchainGate([]string{buildrun.GoBuildModule, buildrun.NodeBuildModule}, tools.Put, logger.Printf)
|
||||
watch.put = gate.Toolchain
|
||||
placementErrs, err := buildrun.FollowPlacement(ctx, js, gate, ch.NodesChanged, logger.Printf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Until the placement's first view is answered the gate passes every report on, as before the placement is settled.
|
||||
placementErrs := make(chan error, 1)
|
||||
go func() {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-placement.Follower().Ready():
|
||||
}
|
||||
pe, err := buildrun.FollowPlacement(ctx, reader, gate, ch.NodesChanged, logger.Printf)
|
||||
if err != nil {
|
||||
placementErrs <- err
|
||||
return
|
||||
}
|
||||
placementErrs <- <-pe
|
||||
}()
|
||||
if err := watch.start(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -338,65 +370,89 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro
|
||||
return nil
|
||||
}
|
||||
lazy.ensure()
|
||||
// The modules say which repository holds a TypeScript module, so the Node build module judges it too.
|
||||
mw, err := settle(ctx, buckets[delivery.ModulesBucket], func(e jetstream.KeyValueEntry) { repos.Put(ctx, e.Key(), e.Value(), !opOf(e)) })
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
errs <- follow(ctx, mw, func(e jetstream.KeyValueEntry) {
|
||||
before := repos.CommitOf(check.NodeEngineModule)
|
||||
repos.Put(ctx, e.Key(), e.Value(), !opOf(e))
|
||||
if after := repos.CommitOf(check.NodeEngineModule); after != before {
|
||||
ch.TrunkMoved(ctx) // the node-engine the mesh runs moved: a verdict that read it beside is carried (ADR 0548 §6)
|
||||
}
|
||||
})
|
||||
}()
|
||||
each := func(e jetstream.KeyValueEntry) {
|
||||
if opOf(e) {
|
||||
ch.Candidate(ctx, e.Key(), e.Value())
|
||||
} else {
|
||||
ch.Forget(e.Key())
|
||||
afterFirstViews := func() error {
|
||||
// The modules say which repository holds a TypeScript module, so the Node build module judges it too.
|
||||
pw, err := settle(ctx, packages, func(e jetstream.KeyValueEntry) { published.Put(e.Key(), e.Value(), !opOf(e)) })
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
w, err := settle(ctx, buckets[delivery.CandidatesBucket], each)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// A key left running for heads no candidate holds is closed; the candidates are all known now.
|
||||
if err := ch.Sweep(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
// The tries: a queued one starts, one left running is taken over, an ended one past its keeping is removed.
|
||||
tw, err := settle(ctx, buckets[check.TriesBucket], func(jetstream.KeyValueEntry) {})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tryer.Reconsider(ctx)
|
||||
go func() {
|
||||
errs <- follow(ctx, tw, func(e jetstream.KeyValueEntry) {
|
||||
go func() {
|
||||
errs <- follow(ctx, pw, func(e jetstream.KeyValueEntry) { published.Put(e.Key(), e.Value(), !opOf(e)) })
|
||||
}()
|
||||
mw, err := settle(ctx, modules, func(e jetstream.KeyValueEntry) { repos.Put(ctx, e.Key(), e.Value(), !opOf(e)) })
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
errs <- follow(ctx, mw, func(e jetstream.KeyValueEntry) {
|
||||
before := repos.CommitOf(check.NodeEngineModule)
|
||||
repos.Put(ctx, e.Key(), e.Value(), !opOf(e))
|
||||
if after := repos.CommitOf(check.NodeEngineModule); after != before {
|
||||
ch.TrunkMoved(ctx) // the node-engine the mesh runs moved: a verdict that read it beside is carried (ADR 0548 §6)
|
||||
}
|
||||
})
|
||||
}()
|
||||
each := func(e jetstream.KeyValueEntry) {
|
||||
if opOf(e) {
|
||||
tryer.Put(ctx, e.Key(), e.Value())
|
||||
}
|
||||
})
|
||||
}()
|
||||
// The checker's own verdicts, updates only, in one watch: one checker_ask_again wrote asks its check again
|
||||
// (Checker.VerdictWritten); a deleted key of heads a candidate holds is judged again (Checker.VerdictDeleted, issue 1733).
|
||||
vw, err := buckets[delivery.CheckerVerdictsBucket].WatchAll(ctx, jetstream.UpdatesOnly())
|
||||
if err != nil {
|
||||
return fmt.Errorf("the checker's verdicts cannot be watched, so no check can be asked again by hand and a deleted verdict would never be written again: %w", err)
|
||||
}
|
||||
go func() {
|
||||
errs <- follow(ctx, vw, func(e jetstream.KeyValueEntry) {
|
||||
if opOf(e) {
|
||||
ch.VerdictWritten(ctx, e.Key(), e.Value())
|
||||
ch.Candidate(ctx, e.Key(), e.Value())
|
||||
} else {
|
||||
ch.VerdictDeleted(ctx, e.Key())
|
||||
ch.Forget(e.Key())
|
||||
}
|
||||
})
|
||||
}
|
||||
w, err := settle(ctx, candidates, each)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// A key left running for heads no candidate holds is closed; the candidates are all known now.
|
||||
if err := ch.Sweep(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
// The tries: a queued one starts, one left running is taken over, an ended one past its keeping is removed.
|
||||
tw, err := settle(ctx, buckets[check.TriesBucket], func(jetstream.KeyValueEntry) {})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tryer.Reconsider(ctx)
|
||||
go func() {
|
||||
errs <- follow(ctx, tw, func(e jetstream.KeyValueEntry) {
|
||||
if opOf(e) {
|
||||
tryer.Put(ctx, e.Key(), e.Value())
|
||||
}
|
||||
})
|
||||
}()
|
||||
// The checker's own verdicts, updates only, in one watch: one checker_ask_again wrote asks its check again
|
||||
// (Checker.VerdictWritten); a deleted key of heads a candidate holds is judged again (Checker.VerdictDeleted, issue 1733).
|
||||
vw, err := buckets[delivery.CheckerVerdictsBucket].WatchAll(ctx, jetstream.UpdatesOnly())
|
||||
if err != nil {
|
||||
return fmt.Errorf("the checker's verdicts cannot be watched, so no check can be asked again by hand and a deleted verdict would never be written again: %w", err)
|
||||
}
|
||||
go func() {
|
||||
errs <- follow(ctx, vw, func(e jetstream.KeyValueEntry) {
|
||||
if opOf(e) {
|
||||
ch.VerdictWritten(ctx, e.Key(), e.Value())
|
||||
} else {
|
||||
ch.VerdictDeleted(ctx, e.Key())
|
||||
}
|
||||
})
|
||||
}()
|
||||
go func() { errs <- follow(ctx, w, each) }()
|
||||
return nil
|
||||
}
|
||||
reader.Start(ctx, js, cred, process.AwaitBucket(ctx, js, conn, firstViewsBucket, "checker_first_views reads nothing and checker_first_view_again is not heard", logger.Printf))
|
||||
// The checks, the tries and the hand asks, once every first view is answered (the lead's ruling for DLV-M2): a check
|
||||
// judged without the candidates, the modules, the manifests, the published packages, the predefined keys or the
|
||||
// placement would be another check.
|
||||
go func() {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-reader.Ready():
|
||||
}
|
||||
if err := afterFirstViews(); err != nil {
|
||||
errs <- err
|
||||
}
|
||||
}()
|
||||
logger.Printf("started on %s", cred.Node)
|
||||
go func() { errs <- follow(ctx, w, each) }()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
ch.Wait()
|
||||
|
||||
@@ -102,19 +102,27 @@ func TestManifestDeclarations(t *testing.T) { // 1678/AC1
|
||||
t.Errorf("module = %q", m.Module)
|
||||
}
|
||||
// delivery.CheckerKeptHistory, CheckerOwedHistory, PreviewsHistory, CheckerVerdictsHistory; the names follow the SDK's buckets.
|
||||
if want := []state{{Name: "asks", History: 1}, {Name: "hearing", History: 1}, {Name: "kept", History: 1}, {Name: "owed", History: 5}, {Name: "previews", History: 5}, {Name: "tries", History: 1}, {Name: "verdicts", History: 5}}; !reflect.DeepEqual(m.State, want) {
|
||||
if want := []state{{Name: "asks", History: 1}, {Name: "hearing", History: 1}, {Name: "kept", History: 1}, {Name: "owed", History: 5}, {Name: "previews", History: 5}, {Name: "tries", History: 1}, {Name: "verdicts", History: 5}, {Name: "first-views", History: 1}, {Name: "findings", History: 1}}; !reflect.DeepEqual(m.State, want) {
|
||||
t.Errorf("state = %+v, want %+v", m.State, want)
|
||||
}
|
||||
if want := []string{"mesh-build-go.steps", "mesh-build-go.toolchains", "mesh-build-node.steps", "mesh-build-node.toolchains",
|
||||
"mesh-delivery-assigner.placement", "mesh-delivery-builder.builds", "mesh-delivery-gate.source", "mesh-delivery-gate.turns", "mesh-delivery-indexer.candidates",
|
||||
"mesh-delivery-indexer.manifests", "mesh-delivery-indexer.modules", "mesh-delivery-indexer.packages", "mesh-delivery-indexer.predefined", "mesh-delivery-watchman.presence", "node-engine.have"}; !reflect.DeepEqual(sorted(m.Reads), want) {
|
||||
"mesh-delivery-builder.builds", "mesh-delivery-gate.source", "mesh-delivery-gate.turns", "mesh-delivery-watchman.presence", "node-engine.have"}; !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
|
||||
// assigner's placement are asked by their read tools and followed by their views families, never read.
|
||||
for _, r := range m.Reads {
|
||||
if strings.HasPrefix(r, "mesh-delivery-indexer.") || strings.HasPrefix(r, "mesh-delivery-assigner.") {
|
||||
t.Errorf("reads %s: the indexer's and the assigner's state is read by their tools and views families alone", r)
|
||||
}
|
||||
}
|
||||
// 1887/A5b: the checker reads nothing of the controller's plans-building (ADR 0483 superseded by ADR 0525 §7), calls no
|
||||
// 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 reads for a mesh-sdk pin (ADR 0484 clause 9), and of the node seat node-engine its one verb
|
||||
// compose-candidate, asked of every placed node (ADR 0593 §7), never the seat's other verbs.
|
||||
if want := []string{"gitea.gitea_commits", "gitea.gitea_compare", "gitea.gitea_get_file", "gitea.gitea_list_branches",
|
||||
"mesh-delivery-assigner.assigner_read_placement", "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",
|
||||
"seat:node-engine.compose-candidate"}; !reflect.DeepEqual(sorted(m.Invokes), want) {
|
||||
t.Errorf("invokes = %v, want the forge's reads and compose-candidate", m.Invokes)
|
||||
}
|
||||
@@ -123,7 +131,9 @@ func TestManifestDeclarations(t *testing.T) { // 1678/AC1
|
||||
}
|
||||
// The checker is woken by a build module's report of a toolchain, as the indexer is: the bucket may not exist when it starts.
|
||||
// And a group's check by the package registry seats' published of its tagged versions (design 51 *Tagged versions*).
|
||||
if want := []string{"go-package-registry.published", "mesh-build-go.toolchain.reported", "mesh-build-node.toolchain.reported", "npm-package-registry.published"}; !reflect.DeepEqual(sorted(m.Consumes), want) {
|
||||
if want := []string{"go-package-registry.published", "mesh-build-go.toolchain.reported", "mesh-build-node.toolchain.reported",
|
||||
"mesh-delivery-assigner.views.placement", "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", "npm-package-registry.published"}; !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" {
|
||||
@@ -141,7 +151,7 @@ func TestManifestDeclarations(t *testing.T) { // 1678/AC1
|
||||
// The read tools (issue 1686) are served by the tools bundle, not by the checker's process.
|
||||
// The try's tools (ADR 0501 §5; the end by hand, issue 734) beside them: mesh-controller's try calls them. A lost check
|
||||
// is asked again by hand (checker_ask_again).
|
||||
if want := []string{"checker_ask_again", "checker_kept", "checker_previews", "checker_pull", "checker_status", "checker_try", "checker_try_end", "checker_try_read", "checker_verdicts"}; !reflect.DeepEqual(sorted(m.Tools), want) {
|
||||
if want := []string{"checker_ask_again", "checker_first_view_again", "checker_first_views", "checker_kept", "checker_previews", "checker_pull", "checker_status", "checker_try", "checker_try_end", "checker_try_read", "checker_verdicts"}; !reflect.DeepEqual(sorted(m.Tools), want) {
|
||||
t.Errorf("tools = %v, want %v", m.Tools, want)
|
||||
}
|
||||
// The try is the checker's own seat's verbs (ADR 0547 §10, issue 774), served beside the tools by the same code.
|
||||
|
||||
@@ -17,6 +17,12 @@
|
||||
"go-package-registry.published",
|
||||
"mesh-build-go.toolchain.reported",
|
||||
"mesh-build-node.toolchain.reported",
|
||||
"mesh-delivery-assigner.views.placement",
|
||||
"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",
|
||||
"npm-package-registry.published"
|
||||
],
|
||||
"streams": [
|
||||
@@ -30,22 +36,18 @@
|
||||
{ "name": "owed", "history": 5 },
|
||||
{ "name": "previews", "history": 5 },
|
||||
{ "name": "tries", "history": 1 },
|
||||
{ "name": "verdicts", "history": 5 }
|
||||
{ "name": "verdicts", "history": 5 },
|
||||
{ "name": "first-views", "history": 1 },
|
||||
{ "name": "findings", "history": 1 }
|
||||
],
|
||||
"reads": [
|
||||
"mesh-build-go.steps",
|
||||
"mesh-build-go.toolchains",
|
||||
"mesh-build-node.steps",
|
||||
"mesh-build-node.toolchains",
|
||||
"mesh-delivery-assigner.placement",
|
||||
"mesh-delivery-builder.builds",
|
||||
"mesh-delivery-gate.source",
|
||||
"mesh-delivery-gate.turns",
|
||||
"mesh-delivery-indexer.candidates",
|
||||
"mesh-delivery-indexer.manifests",
|
||||
"mesh-delivery-indexer.modules",
|
||||
"mesh-delivery-indexer.packages",
|
||||
"mesh-delivery-indexer.predefined",
|
||||
"mesh-delivery-watchman.presence",
|
||||
"node-engine.have"
|
||||
],
|
||||
@@ -59,6 +61,8 @@
|
||||
"checker_previews",
|
||||
"checker_pull",
|
||||
"checker_status",
|
||||
"checker_first_views",
|
||||
"checker_first_view_again",
|
||||
"checker_try",
|
||||
"checker_try_end",
|
||||
"checker_try_read",
|
||||
@@ -69,6 +73,12 @@
|
||||
"gitea.gitea_compare",
|
||||
"gitea.gitea_get_file",
|
||||
"gitea.gitea_list_branches",
|
||||
"mesh-delivery-assigner.assigner_read_placement",
|
||||
"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",
|
||||
"seat:node-engine.compose-candidate"
|
||||
],
|
||||
|
||||
|
||||
@@ -105,6 +105,16 @@ func (r *Reader) Picture(bucket string) *Picture {
|
||||
return r.pics[bucket]
|
||||
}
|
||||
|
||||
// KeyValue opens a followed state's picture by its bus name, where a process opened the bucket (process.BucketOpener,
|
||||
// as buildrun.FollowPlacement takes it); any other bucket is refused, for the reader reads its owners' states by their
|
||||
// first views alone.
|
||||
func (r *Reader) KeyValue(_ context.Context, bucket string) (jetstream.KeyValue, error) {
|
||||
if p := r.Picture(bucket); p != nil {
|
||||
return p, nil
|
||||
}
|
||||
return nil, fmt.Errorf("%s reads %s by its owner's first view alone, and does not follow it", r.Module, bucket)
|
||||
}
|
||||
|
||||
// Followers are the followers, in the order they were added.
|
||||
func (r *Reader) Followers() []*views.Follower {
|
||||
r.mu.Lock()
|
||||
|
||||
Reference in New Issue
Block a user