G start: the indexer serves modules again, the watchman builds, the graph's order comes from the verbs a package calls #216
@@ -466,7 +466,8 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro
|
||||
SeatDefinitions: awaitedStore{b: seatDefs}, ReadFile: readFileVia(calls.Call), SaySeatDefinition: saySeats,
|
||||
Revisions: revisions, EntryIndex: awaitedStore{b: process.AwaitBucket(ctx, js, conn, index.Bucket(cred.Module, index.EntryIndexState), "no entry is found by its fingerprint", logger.Printf)}, // Message a kept call: a read, called again through a transport fault (ADR 0668 §1)
|
||||
// Each catalog revision may bring another holder of a build seat: the build modules are found again (buildmodules.go).
|
||||
SayCatalogRevision: afterRevision(refreshingBuildModules(sayCatalogRevision(conn, cred), &buildFollow), &fillFollow),
|
||||
// A new trunk revision that changes the modules is said on the views family of modules too (design 47 G A-360).
|
||||
SayCatalogRevision: afterRevision(refreshingBuildModules(sayingModulesView(sayCatalogRevision(conn, cred), cred, views.JetStreamPublish(js)), &buildFollow), &fillFollow),
|
||||
SayFill: sayFill(conn, cred),
|
||||
// The catalog's released versions (design 47 G §349): the state versions, bound when it appears, and the event version-released.
|
||||
Versions: awaitedStore{b: process.AwaitBucket(ctx, js, conn, index.Bucket(cred.Module, index.VersionsState), "no released version is kept, so no package-published makes a version", logger.Printf)},
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/nats-io/nats.go"
|
||||
|
||||
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-contracts/go/seats/meshcatalog"
|
||||
"git.novox.be/novox/mesh-sdk/go/keys"
|
||||
"git.novox.be/novox/mesh-sdk/go/views"
|
||||
|
||||
"mesh-delivery-indexer/internal/index"
|
||||
)
|
||||
|
||||
// Design 47 G A-360 (completing A-354): a new trunk revision that changes the modules says one views event of modules,
|
||||
// the mark at the revision's number, on the indexer's own event subject; a revision of another watched branch, one that
|
||||
// changes nothing, and a publish that fails say so.
|
||||
func TestANewTrunkRevisionChangingTheModulesSaysOneViewsEventOfModules(t *testing.T) {
|
||||
at := time.Date(2026, 11, 13, 18, 0, 0, 0, time.UTC)
|
||||
var sent []*nats.Msg
|
||||
var ids []string
|
||||
var fail error
|
||||
said := 0
|
||||
say := sayingModulesView(func(context.Context, meshcatalog.CatalogRevisionEvent) error { said++; return nil }, testAccount,
|
||||
func(_ context.Context, m *nats.Msg, id string) error {
|
||||
sent, ids = append(sent, m), append(ids, id)
|
||||
return fail
|
||||
})
|
||||
trunk := keys.EscapeName(index.TrunkBranch)
|
||||
for _, e := range []meshcatalog.CatalogRevisionEvent{
|
||||
{Revision: 7, Kind: "trunk", Branch: trunk, At: &at, Changed: []string{"avahi", "nats"}},
|
||||
{Revision: 8, Kind: "trunk", Branch: keys.EscapeName("feat/x"), At: &at, Changed: []string{"avahi"}},
|
||||
{Revision: 9, Kind: "candidate", Branch: trunk, At: &at, Changed: []string{"avahi"}},
|
||||
{Revision: 10, Kind: "trunk", Branch: trunk, At: &at},
|
||||
} {
|
||||
if err := say(context.Background(), e); err != nil {
|
||||
t.Fatalf("revision %d: %v", e.Revision, err)
|
||||
}
|
||||
}
|
||||
if said != 4 || len(sent) != 1 || sent[0].Subject != testAccount.EventSubject(views.EventName("modules")) {
|
||||
t.Fatalf("catalog-revision said %d times, views events %d (%v), want 4 and one on views.modules", said, len(sent), sent)
|
||||
}
|
||||
ev, err := views.DecodeEvent(sent[0].Data)
|
||||
if err != nil || !ev.Mark || ev.Owner != testIndexer || ev.State != "modules" || ev.Revision != 7 || !ev.At.Equal(at) || ids[0] != ev.ID() {
|
||||
t.Errorf("the views event of modules is %+v (%v), id %q, want the mark of modules at revision 7 under its own id", ev, err, ids[0])
|
||||
}
|
||||
fail = errors.New("the bus refuses")
|
||||
if err := say(context.Background(), meshcatalog.CatalogRevisionEvent{Revision: 11, Kind: "trunk", Branch: trunk, At: &at, Changed: []string{"nats"}}); err == nil ||
|
||||
!strings.Contains(err.Error(), "views event of modules at revision 11") {
|
||||
t.Errorf("a views event not said answered %v, want it said", err)
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
||||
"git.novox.be/novox/mesh-sdk/go/process"
|
||||
"git.novox.be/novox/mesh-sdk/go/views"
|
||||
|
||||
"mesh-delivery-indexer/internal/index"
|
||||
)
|
||||
@@ -145,6 +146,35 @@ func sayCatalogRevision(conn *nats.Conn, cred process.Account) func(context.Cont
|
||||
}
|
||||
}
|
||||
|
||||
// sayingModulesView is say, and then, for a new trunk revision that changes the modules, the indexer's views event of
|
||||
// modules (index.ModulesViewsEvent; design 47 G A-360) on its own event subject under the event's duplicate id, so the
|
||||
// readers of the first view modules hear a newly merged module without asking again. One not said is returned with
|
||||
// say's error, which the take-in logs.
|
||||
func sayingModulesView(say func(context.Context, meshcatalog.CatalogRevisionEvent) error, cred process.Account,
|
||||
publish func(context.Context, *nats.Msg, string) error) func(context.Context, meshcatalog.CatalogRevisionEvent) error {
|
||||
return func(ctx context.Context, e meshcatalog.CatalogRevisionEvent) error {
|
||||
err := say(ctx, e)
|
||||
ev, ok := index.ModulesViewsEvent(cred.Module, e)
|
||||
if !ok {
|
||||
return err
|
||||
}
|
||||
data, encErr := ev.Encode()
|
||||
if encErr == nil {
|
||||
msg := nats.NewMsg(cred.EventSubject(views.EventName(index.ModulesState)))
|
||||
msg.Header.Set("x-event-id", ev.ID())
|
||||
msg.Header.Set("x-source", cred.Module)
|
||||
msg.Header.Set("x-time", time.Now().UTC().Format(time.RFC3339Nano))
|
||||
msg.Header.Set("content-type", "application/json")
|
||||
msg.Data = data
|
||||
encErr = publish(ctx, msg, ev.ID())
|
||||
}
|
||||
if encErr != nil {
|
||||
return errors.Join(err, fmt.Errorf("the views event of modules at revision %d is not said, its readers hold the revision before until the next: %w", e.Revision, encErr))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// sayFill publishes the seat mesh-catalog's event filling, full or room of the catalog graph revisions under the duplicate
|
||||
// id given (milestone 47/G W4.6), so one said twice is heard once.
|
||||
func sayFill(conn *nats.Conn, cred process.Account) func(context.Context, string, string, meshcatalog.Fill) error {
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
// WithEdges gives each entry of a revision its edges. The entries given are not changed. The edges of kinds 1 to 4 and 6 are
|
||||
// each entry's own; those of kind 5 (§711) need the others' edges of kinds 1 to 4, so they come after.
|
||||
func WithEdges(entries map[string]meshcatalog.CatalogEntry) map[string]meshcatalog.CatalogEntry {
|
||||
entries = withCallsLeftOut(entries)
|
||||
out := make(map[string]meshcatalog.CatalogEntry, len(entries))
|
||||
for module, e := range entries {
|
||||
e.Edges = edgesOf(e, entries)
|
||||
@@ -42,6 +43,62 @@ func WithEdges(entries map[string]meshcatalog.CatalogEntry) map[string]meshcatal
|
||||
return out
|
||||
}
|
||||
|
||||
// withCallsLeftOut gives each entry the seat call words left out on a cycle (design 47 G A-361, A-357; ADR 0614 §3): the
|
||||
// module graph's LeaveOut is run over the entries with a package's seat call words (seatCallsOf) counted among the invokes of
|
||||
// every module built against it, and what it leaves out joins each entry's LeftOut, so a call word's edge on a cycle is
|
||||
// left out like an invoked verb's, and so is an invoked verb's edge that a call word's edge closes a cycle with. The entries
|
||||
// given are not changed; with no seat call word anywhere they are returned as they are.
|
||||
func withCallsLeftOut(entries map[string]meshcatalog.CatalogEntry) map[string]meshcatalog.CatalogEntry {
|
||||
g, any := graph.Graph{}, false
|
||||
for module, e := range entries {
|
||||
calls := seatCallsOf(e, entries)
|
||||
any = any || len(calls) > 0
|
||||
invokes := slices.Clone(e.Invokes)
|
||||
for _, c := range calls {
|
||||
if !slices.Contains(invokes, c) {
|
||||
invokes = append(invokes, c)
|
||||
}
|
||||
}
|
||||
g[module] = graph.Node{Bases: e.Bases, Awaits: e.Awaits, Provides: e.Provides, Invokes: invokes, Serves: e.Serves}
|
||||
}
|
||||
if !any {
|
||||
return entries
|
||||
}
|
||||
g.LeaveOut()
|
||||
out := make(map[string]meshcatalog.CatalogEntry, len(entries))
|
||||
for module, e := range entries {
|
||||
for _, v := range g[module].LeftOut {
|
||||
if !slices.Contains(e.LeftOut, v) {
|
||||
e.LeftOut = append(slices.Clone(e.LeftOut), v)
|
||||
}
|
||||
}
|
||||
out[module] = e
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// seatCallsOf are the call words in the invokes form, `seat:<seat>.<verb>`, of the package artifacts entry a builds
|
||||
// against (their `calls`), sorted, each once.
|
||||
func seatCallsOf(a meshcatalog.CatalogEntry, all map[string]meshcatalog.CatalogEntry) []string {
|
||||
var out []string
|
||||
for _, name := range slices.Sorted(maps.Keys(a.Packages)) {
|
||||
for _, m := range slices.Sorted(maps.Keys(all)) {
|
||||
for _, art := range all[m].Artifacts {
|
||||
if art.Package == nil || art.Package.Name != name {
|
||||
continue
|
||||
}
|
||||
for _, word := range wordsOf(all[m], art.Ref, "calls") {
|
||||
if _, _, isSeat := catalogue.SeatInvoke(word); isSeat && !slices.Contains(out, word) {
|
||||
out = append(out, word)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
slices.Sort(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// kind5Steps are the steps of an acting rule that give an edge of kind 5 (§711): a grant or a credential, a composition, a
|
||||
// placement and a verdict, each made for the dependent's new version. `build` gives kind 3, `take-in` and `validate` none.
|
||||
var kind5Steps = []string{seatdef.StepGrant, seatdef.StepCompose, seatdef.StepPlace, seatdef.StepCheck, seatdef.StepPipeline}
|
||||
@@ -265,6 +322,8 @@ func edgesOf(a meshcatalog.CatalogEntry, all map[string]meshcatalog.CatalogEntry
|
||||
}
|
||||
// Kind 6 again: a package it builds against whose artifact takes `calls` (the capability words its code calls of the tool
|
||||
// runner) orders it after the artifact that `states` each word, so no manifest names the tool runner's module (§605, §545).
|
||||
// A call word in the invokes form, `seat:<seat>.<verb>` (a seat verb the package's code calls on the module's behalf),
|
||||
// orders it after the verb's server, exactly as the same word in its `invokes` would (design 47 G A-357).
|
||||
for _, name := range slices.Sorted(maps.Keys(a.Packages)) {
|
||||
for _, m := range modules {
|
||||
for _, art := range all[m].Artifacts {
|
||||
@@ -272,6 +331,13 @@ func edgesOf(a meshcatalog.CatalogEntry, all map[string]meshcatalog.CatalogEntry
|
||||
continue
|
||||
}
|
||||
for _, word := range wordsOf(all[m], art.Ref, "calls") {
|
||||
if _, _, isSeat := catalogue.SeatInvoke(word); isSeat && !slices.Contains(a.LeftOut, word) {
|
||||
for _, server := range serving.Servers(word) {
|
||||
if server != a.Module {
|
||||
add(meshcatalog.Edge{Kind: 6, From: primaryOf(all[server]), To: delivers, Word: word, Why: "calls of " + name})
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, other := range modules {
|
||||
for _, stating := range all[other].Artifacts {
|
||||
if other != a.Module && slices.Contains(wordsOf(all[other], stating.Ref, "states"), word) {
|
||||
|
||||
@@ -86,14 +86,17 @@ func TestAKindSixEdgeNamesTheFieldsALongFormInvokeRelies(t *testing.T) {
|
||||
}
|
||||
|
||||
// Design 47 G §605, §545, A-208: a module built against a package whose artifact takes `calls` is ordered after the artifact
|
||||
// that `states` each word, by an edge of kind 6 naming the word; a module built against no such package has none.
|
||||
// that `states` each word, by an edge of kind 6 naming the word; a module built against no such package has none. A-357: a
|
||||
// call word in the invokes form orders it after the verb's server, as from `invokes`, and the server after none of its own.
|
||||
func TestAModuleBuiltOnAPackageThatCallsTheToolRunnerIsOrderedAfterTheArtifactThatStatesTheWords(t *testing.T) {
|
||||
artifactsOf := func(module string, art map[string]any) map[string]any {
|
||||
return map[string]any{"module": module, "build": map[string]any{"artifacts": []any{art}}}
|
||||
}
|
||||
entries := map[string]meshcatalog.CatalogEntry{
|
||||
"sdk": {Module: "sdk", Artifacts: []meshcatalog.Artifact{{Ref: "sdk:go", Kind: "package", Language: "go", Package: &meshcatalog.ArtifactPackage{Ecosystem: EcosystemGo, Name: "git.example/sdk/go"}}},
|
||||
Manifest: artifactsOf("sdk", map[string]any{"name": "go", "calls": []any{"put-sessions", "finding-calls"}})},
|
||||
Manifest: artifactsOf("sdk", map[string]any{"name": "go", "calls": []any{"put-sessions", "finding-calls", "seat:mesh-catalog.registrations"}})},
|
||||
"catalog": {Module: "catalog", Artifacts: []meshcatalog.Artifact{{Ref: "catalog:program", Language: "go"}}, Serves: []string{"seat:mesh-catalog.registrations"},
|
||||
Packages: map[string]string{"git.example/sdk/go": "1.0.4"}},
|
||||
"runner": {Module: "runner", Artifacts: []meshcatalog.Artifact{{Ref: "runner:runtime"}},
|
||||
Manifest: artifactsOf("runner", map[string]any{"name": "runtime", "states": []any{"put-sessions"}})},
|
||||
"app": {Module: "app", Artifacts: []meshcatalog.Artifact{{Ref: "app:program", Language: "go"}}, Packages: map[string]string{"git.example/sdk/go": "1.0.4"}},
|
||||
@@ -106,15 +109,60 @@ func TestAModuleBuiltOnAPackageThatCallsTheToolRunnerIsOrderedAfterTheArtifactTh
|
||||
six = append(six, e)
|
||||
}
|
||||
}
|
||||
want := []meshcatalog.Edge{{Kind: 6, From: "runner:runtime", To: "app:program", Word: "put-sessions", Why: "calls of git.example/sdk/go"}}
|
||||
want := []meshcatalog.Edge{{Kind: 6, From: "catalog:program", To: "app:program", Word: "seat:mesh-catalog.registrations", Why: "calls of git.example/sdk/go"},
|
||||
{Kind: 6, From: "runner:runtime", To: "app:program", Word: "put-sessions", Why: "calls of git.example/sdk/go"}}
|
||||
if !slices.Equal(six, want) {
|
||||
t.Errorf("app's edges of kind 6 are %+v, want %+v", six, want)
|
||||
}
|
||||
if slices.ContainsFunc(got["catalog"].Edges, func(e meshcatalog.Edge) bool { return e.From == "catalog:program" }) {
|
||||
t.Errorf("the verb's server is ordered after itself: %+v", got["catalog"].Edges)
|
||||
}
|
||||
if len(got["other"].Edges) != 0 {
|
||||
t.Errorf("other builds on no such package, its edges %+v", got["other"].Edges)
|
||||
}
|
||||
}
|
||||
|
||||
// Design 47 G A-361: a package's seat call words count among the invokes of every module built against it when the graph
|
||||
// leaves out the edges on a cycle, so a call word's edge on a cycle is left out like an invoked verb's (the gate and the
|
||||
// indexer: the gate invokes the indexer's verb, the indexer's SDK calls the gate's), and an edge off the cycle stays.
|
||||
func TestASeatCallWordsEdgeOnACycleIsLeftOutLikeAnInvokedVerbs(t *testing.T) {
|
||||
sdk := map[string]any{"module": "sdk", "build": map[string]any{"artifacts": []any{map[string]any{"name": "go",
|
||||
"calls": []any{"seat:mesh-delivery.source", "seat:mesh-catalog.registrations"}}}}}
|
||||
pkgs := map[string]string{"git.example/sdk/go": "1.0.4"}
|
||||
entries := map[string]meshcatalog.CatalogEntry{
|
||||
"sdk": {Module: "sdk", Artifacts: []meshcatalog.Artifact{{Ref: "sdk:go", Kind: "package", Language: "go", Package: &meshcatalog.ArtifactPackage{Ecosystem: EcosystemGo, Name: "git.example/sdk/go"}}},
|
||||
Manifest: sdk},
|
||||
"gate": {Module: "gate", Artifacts: []meshcatalog.Artifact{{Ref: "gate:program", Language: "go"}}, Serves: []string{"seat:mesh-delivery.source"},
|
||||
Invokes: []string{"seat:mesh-catalog.registrations"}},
|
||||
"indexer": {Module: "indexer", Artifacts: []meshcatalog.Artifact{{Ref: "indexer:program", Language: "go"}}, Serves: []string{"seat:mesh-catalog.registrations"},
|
||||
Packages: pkgs},
|
||||
"app": {Module: "app", Artifacts: []meshcatalog.Artifact{{Ref: "app:program", Language: "go"}}, Packages: pkgs},
|
||||
}
|
||||
got := WithEdges(entries)
|
||||
for _, m := range []string{"gate", "indexer"} {
|
||||
for _, e := range got[m].Edges {
|
||||
if e.Kind == 6 {
|
||||
t.Errorf("%s keeps the edge %+v, which lies on the cycle gate-indexer", m, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
if !slices.Contains(got["indexer"].LeftOut, "seat:mesh-delivery.source") || !slices.Contains(got["gate"].LeftOut, "seat:mesh-catalog.registrations") {
|
||||
t.Errorf("left out: indexer %v, gate %v, want each one's word on the cycle", got["indexer"].LeftOut, got["gate"].LeftOut)
|
||||
}
|
||||
var six []meshcatalog.ArtifactRef
|
||||
for _, e := range got["app"].Edges {
|
||||
if e.Kind == 6 {
|
||||
six = append(six, e.From)
|
||||
}
|
||||
}
|
||||
if !slices.Equal(six, []meshcatalog.ArtifactRef{"gate:program", "indexer:program"}) || len(got["app"].LeftOut) != 0 {
|
||||
t.Errorf("app's edges of kind 6 come from %v, left out %v, want both servers and none left out", six, got["app"].LeftOut)
|
||||
}
|
||||
if len(entries["gate"].LeftOut) != 0 {
|
||||
t.Error("the entries given were changed")
|
||||
}
|
||||
}
|
||||
|
||||
func actingEntries() map[string]meshcatalog.CatalogEntry {
|
||||
rule := func(seat, step, on string) meshcatalog.ActingRule {
|
||||
return meshcatalog.ActingRule{Seat: seat, Step: step, On: on}
|
||||
|
||||
@@ -594,6 +594,17 @@ func TestBasesAreComputedFirstAndNoFingerprintTakesTheBuilder(t *testing.T) { //
|
||||
if keys, err := trunkStates.State("modules").Keys(); err != nil || len(keys) != len(builderModules(t)) {
|
||||
t.Errorf("the bundle's modules = %v (%v), want every module of revision 2", keys, err)
|
||||
}
|
||||
// Design 47 G A-354: the first view modules answers the grants part every module of the newest trunk revision in the
|
||||
// form it reads (meshcatalogclient's value, naming the manifest), at that revision.
|
||||
if !slices.Contains(FirstViewTools, ViewedTool{Tool: "modules", State: ModulesState, Words: "every module the indexer holds, its fingerprint, inputs and manifest digest."}) {
|
||||
t.Errorf("the first-view tools %+v serve no modules", FirstViewTools)
|
||||
}
|
||||
if a, err := views.FirstView(testSelf, ModulesState, trunkStates.State(ModulesState), trunkStates.State(views.MarksState), "", time.Now()); err != nil ||
|
||||
a.Revision != 2 || len(a.Values) != len(builderModules(t)) {
|
||||
t.Errorf("the first view modules answered %d values at %d (%v), want every module at revision 2", len(a.Values), a.Revision, err)
|
||||
} else if m, err := meshcatalogclient.DecodeModule(a.Values[viewKey]); err != nil || m.Manifest == "" {
|
||||
t.Errorf("the first view's avahi = %s (%v), want the module's value naming its manifest", a.Values[viewKey], err)
|
||||
}
|
||||
if e, ok, err := trunkStates.State(views.MarksState).Get("graph"); err != nil || !ok || !strings.Contains(string(e.Value), `"revision":2`) {
|
||||
t.Errorf("the bundle's mark of graph = %+v %v (%v), want revision 2", e, ok, err)
|
||||
}
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
package index
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-contracts/go/seats/meshcatalog"
|
||||
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-sdk/graph"
|
||||
"git.novox.be/novox/mesh-sdk/go/keys"
|
||||
"git.novox.be/novox/mesh-sdk/go/views"
|
||||
)
|
||||
|
||||
// FirstViewTools are the indexer's first-view tools (ADR 0621 §1, ADR 0622 §1, ADR 0631 §1): the tool, the state it answers, and
|
||||
// the words of what that state holds. Each answers only its callers list (CallersEnv), which the manifest writes. The fourth
|
||||
// first view, of which modules exist, is the seat mesh-catalog's verb revision (ADR 0663 §1, RevisionTool), granted by
|
||||
// declaration, not by a list.
|
||||
// the words of what that state holds. Each answers only its callers list (CallersEnv), which the manifest writes. Which
|
||||
// modules exist is the seat mesh-catalog's verb revision (ADR 0663 §1, RevisionTool), granted by declaration, not by a
|
||||
// list; the first view modules stays beside it, in its form before milestone G and answered from the newest trunk
|
||||
// revision (TrunkStates), until the grants part reads the catalog half (design 47 G A-354).
|
||||
var FirstViewTools = []ViewedTool{
|
||||
{Tool: "manifests", State: "manifests", Words: "every manifest the indexer keeps, by its digest."},
|
||||
{Tool: "modules", State: ModulesState, Words: "every module the indexer holds, its fingerprint, inputs and manifest digest."},
|
||||
{Tool: "verdicts", State: VerdictsState, Words: "the judge's verdict on each manifest: accepted, waiting or refused."},
|
||||
{Tool: "graph", State: graph.State, Words: "the module graph: each module's build, provision and verb edges, whose verb edges a want waits on."},
|
||||
}
|
||||
@@ -39,3 +46,19 @@ var ViewedStates = map[string]bool{
|
||||
ManifestsState: true, VerdictsState: true, PredefinedState: true, ConfigCandidatesState: true, CandidatesState: true,
|
||||
PackagesState: true, HeardState: true, TakenState: true, FindingsState: true, SeatDefinitionsState: true, UnjudgedState: true,
|
||||
}
|
||||
|
||||
// ModulesViewsEvent is what the indexer says on its views family of modules when a new revision of the trunk branch
|
||||
// changes the modules (design 47 G A-360, completing A-354): the first view modules answers every value at the trunk
|
||||
// revision's number, so one revision's changes are said as one event, the mark at that number, after which a reader
|
||||
// whose picture is short of it asks the first view once and holds the new revision whole. A revision of another watched
|
||||
// branch, or one that changes nothing, says nothing (false).
|
||||
func ModulesViewsEvent(owner string, e meshcatalog.CatalogRevisionEvent) (views.Event, bool) {
|
||||
if e.Kind != "trunk" || e.Branch != keys.EscapeName(TrunkBranch) || e.Revision <= 0 || len(e.Changed) == 0 {
|
||||
return views.Event{}, false
|
||||
}
|
||||
var at time.Time
|
||||
if e.At != nil {
|
||||
at = e.At.UTC()
|
||||
}
|
||||
return views.Event{V: views.Version, Owner: owner, State: ModulesState, Revision: uint64(e.Revision), At: at, Mark: true}, true
|
||||
}
|
||||
|
||||
@@ -187,8 +187,8 @@ func TestManifestDeclarations(t *testing.T) { // 1238/AC1, 1444 (reads the build
|
||||
// 1945: indexer_take_in, served by its tools bundle through the takeins state; the process serves none. The same bundle
|
||||
// reads the verdicts state (indexer_verdicts) and asks the indexer to judge again through the takeins state
|
||||
// (indexer_judge; issue 774, clause 3). 1966/AC11: indexer_old_sections reads the count of the old sections (state oldsections).
|
||||
// ADR 0621 §1, ADR 0622 §1, ADR 0631 §1, ADR 0663 §1: the first-view tools manifests, verdicts and graph (modules is the seat
|
||||
// verb revision's, served by the claim), and design 25 § 12.2's read
|
||||
// ADR 0621 §1, ADR 0622 §1, ADR 0631 §1, ADR 0663 §1: the first-view tools manifests, modules, verdicts and graph (modules
|
||||
// kept for the grants part until it reads the catalog half, design 47 G A-354), and design 25 § 12.2's read
|
||||
// tools, all of the tools bundle, which runs in a runtime of its own on the indexer's account (runs-as).
|
||||
want := []string{"indexer_take_in", "indexer_old_sections", "indexer_verdicts", "indexer_judge"}
|
||||
for _, v := range append(append([]index.ViewedTool{}, index.FirstViewTools...), index.ReadTools...) {
|
||||
@@ -206,7 +206,7 @@ func TestManifestDeclarations(t *testing.T) { // 1238/AC1, 1444 (reads the build
|
||||
index.EntryIndexTool, index.NamedDropTool, // design 47 G §675
|
||||
index.ProposalsTool, index.ProposalDropTool, index.SeedAgainTool, index.InputsTool, index.PinsTool, index.UnpinTool, index.ConsumerTool, index.RunsTool, index.RunDropTool, // design 47 G §667, §8, §579, §64 (A-194)
|
||||
index.ValidateAgainTool) // design 47 G §713
|
||||
if !reflect.DeepEqual(m.Tools, want) || len(want) != 50 || slices.Contains(m.Tools, "modules") {
|
||||
if !reflect.DeepEqual(m.Tools, want) || len(want) != 51 || !slices.Contains(m.Tools, "modules") {
|
||||
t.Errorf("tools = %v, want %v", m.Tools, want)
|
||||
}
|
||||
// Design 47 G §2, §9: every version of every registration is kept with no age limit, 64 MiB, on the old streams section.
|
||||
@@ -219,8 +219,8 @@ func TestManifestDeclarations(t *testing.T) { // 1238/AC1, 1444 (reads the build
|
||||
if !regexp.MustCompile(`^[0-9]+\.[0-9]+$`).MatchString(m.Version) {
|
||||
t.Errorf("version = %q, want MAJOR.MINOR (design 47 G §368)", m.Version)
|
||||
}
|
||||
// Design 47 G §196, §185 (test 61): no state modules, no tool or view of it, and no mesh-wide hold with its states, tools
|
||||
// and the engines' reads it weighed.
|
||||
// Design 47 G §196, §185 (test 61): no state modules, no read tool or view of it, and no mesh-wide hold with its states,
|
||||
// tools and the engines' reads it weighed; its first view and its views family stay for the grants part (A-354, A-360).
|
||||
for _, s := range m.State {
|
||||
if slices.Contains([]string{"modules", "holds", "engine-reads", "hold-bounds"}, s.Name) {
|
||||
t.Errorf("the indexer declares the state %s, which design 47 G retires", s.Name)
|
||||
@@ -231,8 +231,8 @@ func TestManifestDeclarations(t *testing.T) { // 1238/AC1, 1444 (reads the build
|
||||
t.Errorf("the indexer serves %s, which design 47 G retires", retired)
|
||||
}
|
||||
}
|
||||
if slices.Contains(m.Emits, "views.modules") || slices.Contains(m.Consumes, "node-engine.manifest-reads-changed") {
|
||||
t.Errorf("the indexer says views.modules or hears manifest-reads-changed: emits %v, consumes %v", m.Emits, m.Consumes)
|
||||
if slices.Contains(m.Consumes, "node-engine.manifest-reads-changed") {
|
||||
t.Errorf("the indexer hears manifest-reads-changed: consumes %v", m.Consumes)
|
||||
}
|
||||
// Every read tool of the indexer a reader of this repository invokes is one it serves (the checker's seventh first
|
||||
// view, indexer_read_seat_definitions, was invoked and not served).
|
||||
@@ -258,9 +258,7 @@ func TestManifestDeclarations(t *testing.T) { // 1238/AC1, 1444 (reads the build
|
||||
t.Fatalf("%s: %v", p, err)
|
||||
}
|
||||
for _, inv := range invoked {
|
||||
// The grants part (W8) and the assigner read modules until their own subtasks move them to revision; the join removes this line.
|
||||
if (p == "../mesh-grants/module.json" || p == "../mesh-delivery-assigner/module.json") && inv == "mesh-delivery-indexer.modules" ||
|
||||
inv == "mesh-delivery-indexer.indexer_read_modules" { // design 47 G §196: the readers' own subtasks move them to the catalog half
|
||||
if inv == "mesh-delivery-indexer.indexer_read_modules" { // design 47 G §196: the readers' own subtasks move them to the catalog half
|
||||
continue
|
||||
}
|
||||
if tool, ok := strings.CutPrefix(inv, "mesh-delivery-indexer."); ok && !slices.Contains(m.Tools, tool) {
|
||||
@@ -281,14 +279,15 @@ func TestManifestDeclarations(t *testing.T) { // 1238/AC1, 1444 (reads the build
|
||||
// ADR 0621 §2, ADR 0622 §1, ADR 0631 §1: each first-view tool answers exactly its readers; the indexer is on none, and
|
||||
// graph answers the assigner alone.
|
||||
// ADR 0663 §2: revision has no callers list; it answers the parts whose entry declares it.
|
||||
callers := map[string]string{"manifests": "mesh-grants,node-tools,mesh-delivery-assigner", "verdicts": "mesh-grants",
|
||||
// Design 47 G A-354: modules answers its readers as before milestone G.
|
||||
callers := map[string]string{"manifests": "mesh-grants,node-tools,mesh-delivery-assigner", "modules": "mesh-grants,node-tools,mesh-delivery-assigner", "verdicts": "mesh-grants",
|
||||
"graph": "mesh-delivery-assigner"}
|
||||
for _, a := range m.Build.Artifacts {
|
||||
if a.Name != "tools" {
|
||||
continue
|
||||
}
|
||||
if len(a.Env) != len(callers) {
|
||||
t.Errorf("tools env = %v, want only the three callers lists", a.Env)
|
||||
t.Errorf("tools env = %v, want only the four callers lists", a.Env)
|
||||
}
|
||||
for tool, list := range callers {
|
||||
if got := a.Env[views.CallersEnv(tool)]; got != list {
|
||||
@@ -310,7 +309,8 @@ func TestManifestDeclarations(t *testing.T) { // 1238/AC1, 1444 (reads the build
|
||||
// registrations' history (design 47 G §2, asserted above).
|
||||
// ADR 0636 §4: seat-definition-changed and -removed are the seat mesh-catalog's events, said on the seat's subject and kept
|
||||
// on the shared events stream, and not among its own emits.
|
||||
for _, state := range []string{"candidates", "config-candidates", "findings", "graph", "heard", "manifests", "packages", "predefined", "seat-definitions", "taken", "verdicts"} {
|
||||
// Design 47 G A-360: modules too, at each new trunk revision that changes it.
|
||||
for _, state := range []string{"candidates", "config-candidates", "findings", "graph", "heard", "manifests", "modules", "packages", "predefined", "seat-definitions", "taken", "verdicts"} {
|
||||
if !slices.Contains(m.Emits, views.EventPrefix+state) {
|
||||
t.Errorf("emits = %v: want %s%s", m.Emits, views.EventPrefix, state)
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
"indexer_verdicts",
|
||||
"indexer_judge",
|
||||
"manifests",
|
||||
"modules",
|
||||
"verdicts",
|
||||
"graph",
|
||||
"indexer_read_manifests",
|
||||
@@ -150,6 +151,7 @@
|
||||
"views.graph",
|
||||
"views.heard",
|
||||
"views.manifests",
|
||||
"views.modules",
|
||||
"views.packages",
|
||||
"views.predefined",
|
||||
"views.seat-definitions",
|
||||
@@ -244,6 +246,7 @@
|
||||
],
|
||||
"env": {
|
||||
"MESH_VIEW_CALLERS_MANIFESTS": "mesh-grants,node-tools,mesh-delivery-assigner",
|
||||
"MESH_VIEW_CALLERS_MODULES": "mesh-grants,node-tools,mesh-delivery-assigner",
|
||||
"MESH_VIEW_CALLERS_VERDICTS": "mesh-grants",
|
||||
"MESH_VIEW_CALLERS_GRAPH": "mesh-delivery-assigner"
|
||||
}
|
||||
|
||||
@@ -58,3 +58,12 @@ require (
|
||||
require git.novox.be/novox/mesh-delivery/modules/mesh-delivery-contracts/go/seats/nodebuildgo v0.0.0-00010101000000-000000000000
|
||||
|
||||
replace git.novox.be/novox/mesh-delivery/modules/mesh-delivery-contracts/go/seats/nodebuildgo => ../mesh-delivery-contracts/go/seats/nodebuildgo
|
||||
|
||||
require (
|
||||
git.novox.be/novox/mesh-delivery/modules/mesh-delivery-contracts/go/seats/nodebuildimage v0.0.0-00010101000000-000000000000
|
||||
git.novox.be/novox/mesh-delivery/modules/mesh-delivery-contracts/go/seats/nodebuildnode v0.0.0-00010101000000-000000000000
|
||||
)
|
||||
|
||||
replace git.novox.be/novox/mesh-delivery/modules/mesh-delivery-contracts/go/seats/nodebuildimage => ../mesh-delivery-contracts/go/seats/nodebuildimage
|
||||
|
||||
replace git.novox.be/novox/mesh-delivery/modules/mesh-delivery-contracts/go/seats/nodebuildnode => ../mesh-delivery-contracts/go/seats/nodebuildnode
|
||||
|
||||
Reference in New Issue
Block a user