indexer: the first fill's edge entries, marked filled, from the placement and the stream running read whole
VersionEdge gains filled (mesh-catalog seat contract, regenerated). Builds: design 47 G §377, A-78 c, A-62, §556 Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
This commit is contained in:
@@ -1034,9 +1034,11 @@ type VersionEdge struct {
|
||||
// the digest of the part pointed to, kinds 1-4
|
||||
Digest string `json:"digest,omitempty"`
|
||||
// kinds 5 and 6: the acting part, its step and the build that acted, or the verb or word used and the delivered version that serves it
|
||||
Fact string `json:"fact,omitempty"`
|
||||
Kind int64 `json:"kind"`
|
||||
Part ArtifactRef `json:"part"`
|
||||
Fact string `json:"fact,omitempty"`
|
||||
// the entry was derived at the first fill (design 47 G section 377), not recorded at a release or a delivery
|
||||
Filled bool `json:"filled,omitempty"`
|
||||
Kind int64 `json:"kind"`
|
||||
Part ArtifactRef `json:"part"`
|
||||
// the edge makes the dependent stale (kinds 1, 3) or only orders it (kinds 2, 4, 5, 6) (section 378)
|
||||
Stale bool `json:"stale"`
|
||||
// the dependency's version, kinds 1-4; "not yet delivered" until the first delivery for 2, 4 and 6
|
||||
|
||||
@@ -2555,6 +2555,10 @@
|
||||
"fact": {
|
||||
"type": "string",
|
||||
"description": "kinds 5 and 6: the acting part, its step and the build that acted, or the verb or word used and the delivered version that serves it"
|
||||
},
|
||||
"filled": {
|
||||
"type": "boolean",
|
||||
"description": "the entry was derived at the first fill (design 47 G section 377), not recorded at a release or a delivery"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
||||
+2
@@ -490,6 +490,8 @@ export interface VersionEdge {
|
||||
"digest"?: string;
|
||||
/** kinds 5 and 6: the acting part, its step and the build that acted, or the verb or word used and the delivered version that serves it */
|
||||
"fact"?: string;
|
||||
/** the entry was derived at the first fill (design 47 G section 377), not recorded at a release or a delivery */
|
||||
"filled"?: boolean;
|
||||
"kind": number;
|
||||
"part": ArtifactRef;
|
||||
/** the edge makes the dependent stale (kinds 1, 3) or only orders it (kinds 2, 4, 5, 6) (section 378) */
|
||||
|
||||
@@ -441,7 +441,7 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro
|
||||
Predefined: awaitedStore{b: predefined}, ConfigCandidates: awaitedStore{b: configCandidates}, Logf: logger.Printf,
|
||||
Named: namedVia(calls.Call), Trunk: trunkVia(calls.Call), Contains: containsVia(calls.Call), Read: readVia(calls.Call),
|
||||
ReadOpen: openVia(calls.Call), Packages: awaitedStore{b: packages, js: js, name: index.PackagesBucket}, Files: filesVia(calls.Call), GoSum: goSumVia(calls.Call), GoMod: goModVia(calls.Call),
|
||||
OwnBuild: ownBuildVia(calls.Call, cred.Node), OpenTries: openTriesVia(calls.Call), OwnBuildState: awaitedStore{b: ownBuildState}, Heard: heardStore, TryRecovery: awaitedStore{b: tryRecovery}, Graph: graph,
|
||||
OwnBuild: ownBuildVia(calls.Call, cred.Node), OpenTries: openTriesVia(calls.Call), OwnBuildState: awaitedStore{b: ownBuildState}, Heard: heardStore, Placement: placementVia(calls.CallKept), RunningWhole: runningWholeVia(js), TryRecovery: awaitedStore{b: tryRecovery}, Graph: graph,
|
||||
SeatDefinitions: awaitedStore{b: seatDefs}, ReadFile: readFileVia(calls.Call), SaySeatDefinition: saySeats,
|
||||
Message: messageVia(calls.CallKept), Revisions: revisions, EntryIndex: awaitedStore{b: process.AwaitBucket(ctx, js, conn, index.EntryIndexBucket, "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).
|
||||
|
||||
@@ -10,6 +10,8 @@ import (
|
||||
"github.com/nats-io/nats.go"
|
||||
"github.com/nats-io/nats.go/jetstream"
|
||||
|
||||
"git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner"
|
||||
"git.novox.be/novox/mesh-contracts/go/seats/nodeengine"
|
||||
"git.novox.be/novox/mesh-delivery/contracts/go/seats/meshbuilds"
|
||||
"git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog"
|
||||
"git.novox.be/novox/mesh-delivery/contracts/go/seats/meshwants"
|
||||
@@ -159,3 +161,58 @@ func afterRevision(say func(context.Context, meshcatalog.CatalogRevisionEvent) e
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// placementVia asks the seat mesh-module-assigner's verb placement for one module (A-62), a read.
|
||||
func placementVia(call toolCaller) index.PlacementAsker {
|
||||
return func(_ context.Context, module string) ([]meshmoduleassigner.ModulePlacement, error) {
|
||||
raw, err := call(meshmoduleassigner.Seat+"."+meshmoduleassigner.VerbPlacement, map[string]any{"module": module})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s.%s: %w", meshmoduleassigner.Seat, meshmoduleassigner.VerbPlacement, err)
|
||||
}
|
||||
var a meshmoduleassigner.PlacementAnswer
|
||||
if err := json.Unmarshal(raw, &a); err != nil {
|
||||
return nil, fmt.Errorf("the answer of placement is not a module's placements: %w", err)
|
||||
}
|
||||
return a.Placements, nil
|
||||
}
|
||||
}
|
||||
|
||||
// runningWholeVia reads every subject's last message of the node-engine seat's stream running through an ordered consumer made for
|
||||
// that one read (design 47 G §556), by node and module.
|
||||
func runningWholeVia(js jetstream.JetStream) index.RunningReader {
|
||||
return func(ctx context.Context) (map[string]map[string]nodeengine.Running, error) {
|
||||
stream, err := js.Stream(ctx, nodeengine.StreamRunning)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("the stream %s cannot be read whole: %w", nodeengine.StreamRunning, err)
|
||||
}
|
||||
cons, err := stream.OrderedConsumer(ctx, jetstream.OrderedConsumerConfig{DeliverPolicy: jetstream.DeliverLastPerSubjectPolicy})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("the ordered consumer for the whole read of %s cannot be made: %w", nodeengine.StreamRunning, err)
|
||||
}
|
||||
out := map[string]map[string]nodeengine.Running{}
|
||||
for {
|
||||
batch, err := cons.FetchNoWait(256)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("the whole read of %s failed: %w", nodeengine.StreamRunning, err)
|
||||
}
|
||||
n := 0
|
||||
for m := range batch.Messages() {
|
||||
n++
|
||||
var r nodeengine.Running
|
||||
if json.Unmarshal(m.Data(), &r) != nil || r.Node == "" || r.Module == "" {
|
||||
continue
|
||||
}
|
||||
if out[r.Node] == nil {
|
||||
out[r.Node] = map[string]nodeengine.Running{}
|
||||
}
|
||||
out[r.Node][r.Module] = r
|
||||
}
|
||||
if err := batch.Error(); err != nil {
|
||||
return nil, fmt.Errorf("the whole read of %s failed: %w", nodeengine.StreamRunning, err)
|
||||
}
|
||||
if n == 0 {
|
||||
return out, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,7 +193,11 @@ type Indexer struct {
|
||||
// Heard keeps what the indexer heard and compares later (A-80): the time a call is unanswered since (steps-since, call-since),
|
||||
// toolchain.<language>, delivered.<module>, pending.<module>.<version>, acted.<acting part>.<dependent>, each key part escaped as a name
|
||||
// (§73), and the mark versions-filled; nil keeps none. It is the state heard, whose key forge keeps the forge messages handled.
|
||||
Heard Store
|
||||
Heard Store
|
||||
// Placement asks the assigner's verb placement (A-62) and RunningWhole reads the node-engine's stream running whole (§556), both for the
|
||||
// edge entries of the first fill (§377); nil reads none, and the entries of kinds 2, 4 and 6 then read "not yet delivered".
|
||||
Placement PlacementAsker
|
||||
RunningWhole RunningReader
|
||||
judgePending atomic.Bool // the start's judge pass failed: judged again at the next message (verdicts.go)
|
||||
// Message asks the forge bridge for the merged message of a merge, repository, merge commit and pull request number in (the number lets the bridge read a merge made on the forge, not through the gate), the message whole out (a
|
||||
// pending value keeps a reference, not a copy: issue 1585); an error is the bridge's words. Nil asks none.
|
||||
|
||||
@@ -908,7 +908,7 @@ func (ix *Indexer) dry() *dryRun {
|
||||
// requirements, findings, seat definitions, holds and their bounds are not overlaid, nothing is said, and no hold is
|
||||
// weighed (Holds and Counted nil hold nothing); ReadFile only reads.
|
||||
Requirements: nil, Findings: nil, SeatDefinitions: nil, ReadFile: ix.ReadFile, SaySeatDefinition: nil,
|
||||
OwnBuildState: nil, Heard: nil, underSeats: true,
|
||||
OwnBuildState: nil, Heard: nil, Placement: nil, RunningWhole: nil, underSeats: true,
|
||||
// A dry run keeps no catalog graph revision (catalog_revisions.go): its revision is the take-in's to keep.
|
||||
Revisions: nil, SayCatalogRevision: nil, SayFill: nil, EntryIndex: nil, Versions: nil, SayVersionReleased: nil, SayVersionsFilled: nil, Inputs: nil,
|
||||
// A dry run writes no registration and asks no one.
|
||||
|
||||
@@ -1131,7 +1131,7 @@ func TestTakeIn(t *testing.T) { // 1585/T3-T13 (ADR 0633)
|
||||
for _, f := range []string{"Store", "Modules", "Manifests", "Predefined", "ConfigCandidates", "Taken", "Verdicts", "OldSections", "Unjudged",
|
||||
"JudgeID", "SayRefused", "SayAccepted", "Packages", "Graph", "Files", "GoSum", "GoMod", "Now", "Builds", "Named", "Trunk",
|
||||
"Read", "Contains", "Logf", "ReadOpen", "OwnBuild", "Message", "Pending", "SayTakeIn",
|
||||
"Requirements", "Findings", "SeatDefinitions", "ReadFile", "SaySeatDefinition", "OpenTries", "TryRecovery", "OwnBuildState", "Heard", "Revisions", "SayCatalogRevision", "SayFill", "EntryIndex", "Registry", "SeedModules", "Versions", "SayVersionReleased", "SayVersionsFilled", "Inputs"} {
|
||||
"Requirements", "Findings", "SeatDefinitions", "ReadFile", "SaySeatDefinition", "OpenTries", "TryRecovery", "OwnBuildState", "Heard", "Placement", "RunningWhole", "Revisions", "SayCatalogRevision", "SayFill", "EntryIndex", "Registry", "SeedModules", "Versions", "SayVersionReleased", "SayVersionsFilled", "Inputs"} {
|
||||
named[f] = true
|
||||
}
|
||||
ty := reflect.TypeOf(Indexer{})
|
||||
|
||||
@@ -5,9 +5,12 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"slices"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-contracts/go/seats/nodeengine"
|
||||
mcc "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient"
|
||||
"git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog"
|
||||
"git.novox.be/novox/mesh-sdk/go/keys"
|
||||
@@ -127,6 +130,12 @@ func (ix *Indexer) FillVersions(ctx context.Context, ask VersionsAsker) error {
|
||||
}
|
||||
now := ix.now().UTC()
|
||||
filled := 0
|
||||
built := map[string]meshcatalog.VersionEntry{}
|
||||
claimed := map[string]time.Time{}
|
||||
trunk := map[string]meshcatalog.CatalogEntry{}
|
||||
for _, e := range entries {
|
||||
trunk[e.Module] = e
|
||||
}
|
||||
for module, fs := range best {
|
||||
v := meshcatalog.VersionEntry{V: int64(keys.Version), Module: module, Version: fs[0].v.Version, At: &now}
|
||||
for _, f := range fs {
|
||||
@@ -134,7 +143,29 @@ func (ix *Indexer) FillVersions(ctx context.Context, ask VersionsAsker) error {
|
||||
if n, err := strconv.ParseInt(f.v.Revision, 10, 64); err == nil && n > v.Source {
|
||||
v.Source = n
|
||||
}
|
||||
if f.v.At.After(claimed[module]) {
|
||||
claimed[module] = f.v.At
|
||||
}
|
||||
}
|
||||
built[module] = v
|
||||
}
|
||||
// The edge entries (§377), each marked filled: where the stream running or the placement cannot be read the entries of kinds 2, 4
|
||||
// and 6 read "not yet delivered", as at a release, and the first delivery writes them.
|
||||
var running map[string]map[string]nodeengine.Running
|
||||
if ix.RunningWhole != nil {
|
||||
if running, err = ix.RunningWhole(ctx); err != nil {
|
||||
ix.logf("versions fill: the stream running is not read whole: %v", err)
|
||||
running = nil
|
||||
}
|
||||
}
|
||||
answers := map[string]BuilderVersionsAnswer{}
|
||||
for _, module := range slices.Sorted(maps.Keys(built)) {
|
||||
v := built[module]
|
||||
v.Edges = ix.filledEdges(ctx, trunk[module], claimed[module], ask, trunk, built, running, answers)
|
||||
built[module] = v
|
||||
}
|
||||
for _, module := range slices.Sorted(maps.Keys(built)) {
|
||||
v := built[module]
|
||||
data, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -142,6 +173,7 @@ func (ix *Indexer) FillVersions(ctx context.Context, ask VersionsAsker) error {
|
||||
if err := ix.Versions.Create(ctx, meshcatalog.VersionsByModuleVersionKey(module, v.Version), data); err != nil && !errors.Is(err, ErrConflict) {
|
||||
return fmt.Errorf("the version %s %s is not kept: %w", module, v.Version, err)
|
||||
}
|
||||
ix.keepFilledFacts(ctx, v, trunk[module], built, now)
|
||||
filled++
|
||||
}
|
||||
if ix.SayVersionsFilled != nil {
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
package index
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner"
|
||||
"git.novox.be/novox/mesh-contracts/go/seats/nodeengine"
|
||||
"git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog"
|
||||
"git.novox.be/novox/mesh-delivery/contracts/go/seats/meshwants"
|
||||
"git.novox.be/novox/mesh-sdk/go/keys"
|
||||
)
|
||||
|
||||
// The edge entries of the first fill (design 47 G §377, A-78 c): each module's newest version gets its entries once, each marked
|
||||
// filled. Kinds 1 and 3 come from the dependency version published latest before the version's build was claimed, read with
|
||||
// the times of builder_versions, and its part's digest; kinds 2, 4 and 6 from the version every machine placing the dependency
|
||||
// runs, present or away, the machines from the assigner's placement (A-62) and each node's last running from the node-engine seat's
|
||||
// stream read whole (§556); kind 5 as done for every acting part's running build.
|
||||
|
||||
// PlacementAsker asks the seat mesh-module-assigner's verb placement for a module (a read, A-62).
|
||||
type PlacementAsker func(ctx context.Context, module string) ([]meshmoduleassigner.ModulePlacement, error)
|
||||
|
||||
// RunningReader reads the node-engine seat's stream running whole, every subject's last message (§556): by node, then module.
|
||||
type RunningReader func(ctx context.Context) (map[string]map[string]nodeengine.Running, error)
|
||||
|
||||
// filledEdges are the edge entries of the entry's newest version, which was filled as built; claimed is when its build was claimed.
|
||||
func (ix *Indexer) filledEdges(ctx context.Context, entry meshcatalog.CatalogEntry, claimed time.Time, ask VersionsAsker, trunk map[string]meshcatalog.CatalogEntry,
|
||||
built map[string]meshcatalog.VersionEntry, running map[string]map[string]nodeengine.Running, answers map[string]BuilderVersionsAnswer) []meshcatalog.VersionEdge {
|
||||
var out []meshcatalog.VersionEdge
|
||||
seen := func(e meshcatalog.VersionEdge) bool {
|
||||
return slices.ContainsFunc(out, func(x meshcatalog.VersionEdge) bool { return x.Kind == e.Kind && x.Part == e.Part && x.Word == e.Word })
|
||||
}
|
||||
for _, g := range entry.Edges {
|
||||
part := cmp.Or(g.Part, g.From)
|
||||
module := strings.SplitN(part, ":", 2)[0]
|
||||
if part == "" || module == entry.Module {
|
||||
continue
|
||||
}
|
||||
e := meshcatalog.VersionEdge{Kind: g.Kind, Part: part, Word: g.Word, Filled: true}
|
||||
switch g.Kind {
|
||||
case 1, 3:
|
||||
dep, ok := trunk[module]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
var name string
|
||||
for _, a := range dep.Artifacts {
|
||||
if a.Ref == part {
|
||||
name = versionNameOf(module, a)
|
||||
}
|
||||
}
|
||||
if name == "" {
|
||||
continue
|
||||
}
|
||||
ans, ok := answers[name]
|
||||
if !ok {
|
||||
a, err := ask(ctx, name)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ans, answers[name] = a, a
|
||||
}
|
||||
var best *BuilderVersion
|
||||
for i, v := range ans.Versions {
|
||||
if _, ok := versionParts(v.Version); !ok || v.State != "published" || v.At.After(claimed) {
|
||||
continue
|
||||
}
|
||||
if best == nil || v.At.After(best.At) {
|
||||
best = &ans.Versions[i]
|
||||
}
|
||||
}
|
||||
if best == nil {
|
||||
continue
|
||||
}
|
||||
e.Version, e.Digest, e.Stale = best.Version, best.Digest, true
|
||||
case 2, 4, 6:
|
||||
e.Version = notYetDelivered
|
||||
if v, ok := ix.runningVersion(ctx, module, trunk, built, running); ok {
|
||||
e.Version = v.Version
|
||||
for _, a := range v.Artifacts {
|
||||
if a.Artifact == part {
|
||||
e.Digest = a.Digest
|
||||
}
|
||||
}
|
||||
}
|
||||
case 5:
|
||||
e.Fact = "acted for the running build " + entry.Fp
|
||||
default:
|
||||
continue
|
||||
}
|
||||
if !seen(e) {
|
||||
out = append(out, e)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// runningVersion is the version of a module that every machine placing it runs, present or away, when each runs the newest
|
||||
// build; false where no machine places it, a placed machine runs none or another build, or the placement is not answered.
|
||||
func (ix *Indexer) runningVersion(ctx context.Context, module string, trunk map[string]meshcatalog.CatalogEntry, built map[string]meshcatalog.VersionEntry,
|
||||
running map[string]map[string]nodeengine.Running) (meshcatalog.VersionEntry, bool) {
|
||||
v, ok := built[module]
|
||||
e, known := trunk[module]
|
||||
if !ok || !known || e.Fp == "" || ix.Placement == nil {
|
||||
return v, false
|
||||
}
|
||||
placements, err := ix.Placement(ctx, module)
|
||||
if err != nil {
|
||||
ix.logf("versions fill: the placement of %s is not answered: %v", module, err)
|
||||
return v, false
|
||||
}
|
||||
nodes := 0
|
||||
for _, p := range placements {
|
||||
for _, n := range p.Nodes {
|
||||
nodes++
|
||||
r, ok := running[n][module]
|
||||
if !ok || r.Stopped || r.Fingerprint != e.Fp {
|
||||
return v, false
|
||||
}
|
||||
}
|
||||
}
|
||||
return v, nodes > 0
|
||||
}
|
||||
|
||||
// keepFilledFacts keeps what the first fill derived of a version as heard: for each entry of kinds 2, 4 and 6 read as delivered,
|
||||
// the dependency's version as its newest delivered one (A-68), and for each kind-5 edge the act for the running build (A-78 a).
|
||||
func (ix *Indexer) keepFilledFacts(ctx context.Context, v meshcatalog.VersionEntry, entry meshcatalog.CatalogEntry, built map[string]meshcatalog.VersionEntry, at time.Time) {
|
||||
for _, e := range v.Edges {
|
||||
switch e.Kind {
|
||||
case 2, 4, 6:
|
||||
dep := built[strings.SplitN(e.Part, ":", 2)[0]]
|
||||
if e.Version == notYetDelivered || dep.Version != e.Version {
|
||||
continue
|
||||
}
|
||||
parts := map[string]string{}
|
||||
for _, a := range dep.Artifacts {
|
||||
parts[a.Artifact] = a.Digest
|
||||
}
|
||||
d := meshwants.VersionDeliveredEvent{V: 1, Module: dep.Module, Version: dep.Version, Parts: parts, At: &at}
|
||||
if err := ix.keepNewest(ctx, DeliveredPrefix+keys.EscapeName(dep.Module), d, func([]byte) bool { return false }); err != nil {
|
||||
ix.logf("versions fill: the delivered version of %s is not kept: %v", dep.Module, err)
|
||||
}
|
||||
case 5:
|
||||
data, _ := json.Marshal(Acted{V: 1, Part: e.Part, Build: entry.Fp, How: "filled", At: at})
|
||||
if err := write(ctx, ix.Heard, ActedKey(e.Part, v.Module), func(_ []byte, found bool) ([]byte, bool, error) {
|
||||
if found {
|
||||
return nil, false, nil
|
||||
}
|
||||
return data, false, nil
|
||||
}); err != nil {
|
||||
ix.logf("versions fill: the act for %s is not kept: %v", v.Module, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner"
|
||||
"git.novox.be/novox/mesh-contracts/go/seats/nodeengine"
|
||||
"git.novox.be/novox/mesh-delivery/contracts/go/seats/meshbuilds"
|
||||
"git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog"
|
||||
"git.novox.be/novox/mesh-delivery/contracts/go/seats/meshwants"
|
||||
@@ -411,3 +413,73 @@ func TestOutdatedAnswersKinds3And5And6(t *testing.T) {
|
||||
t.Errorf("kind 6 after a version that serves no more: %+v", o)
|
||||
}
|
||||
}
|
||||
|
||||
// Design 47 G §377, A-78 c: the first fill gives each module's newest version its edge entries, each marked filled: kind 1 from the
|
||||
// dependency version published latest before the build was claimed, kinds 2, 4 and 6 from the version every placing machine runs,
|
||||
// read from the assigner's placement and the stream running, kind 5 as acted for the running build; so the first outdated says only what is truly behind.
|
||||
func TestTheFirstFillGivesTheNewestVersionsTheirFilledEdges(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
t0 := time.Date(2026, 11, 1, 3, 0, 0, 0, time.UTC)
|
||||
placed := map[string][]string{"prov": {"n1", "n2"}}
|
||||
ix := &Indexer{Versions: newMem(), Heard: newMem(), Revisions: newMemObjects(1 << 20), Now: func() time.Time { return t0 },
|
||||
Placement: func(_ context.Context, m string) ([]meshmoduleassigner.ModulePlacement, error) {
|
||||
return []meshmoduleassigner.ModulePlacement{{Branch: "trunk", Nodes: placed[m]}}, nil
|
||||
},
|
||||
RunningWhole: func(context.Context) (map[string]map[string]nodeengine.Running, error) {
|
||||
return map[string]map[string]nodeengine.Running{"n1": {"prov": {Fingerprint: "fp-prov"}}, "n2": {"prov": {Fingerprint: "fp-prov"}}}, nil
|
||||
}}
|
||||
entries := map[string]meshcatalog.CatalogEntry{
|
||||
"lib": {Module: "lib", Manifest: map[string]any{}, Fp: "fp-lib", Artifacts: []meshcatalog.Artifact{{Ref: "lib:pkg", Package: &meshcatalog.ArtifactPackage{Ecosystem: "go", Name: "x/lib"}}}},
|
||||
"actor": {Module: "actor", Manifest: map[string]any{}, Fp: "fp-actor", Artifacts: []meshcatalog.Artifact{{Ref: "actor:program"}}},
|
||||
"prov": {Module: "prov", Manifest: map[string]any{}, Fp: "fp-prov", Artifacts: []meshcatalog.Artifact{{Ref: "prov:program"}}},
|
||||
"app": {Module: "app", Manifest: map[string]any{}, Fp: "fp-app", Artifacts: []meshcatalog.Artifact{{Ref: "app:program"}},
|
||||
Edges: []meshcatalog.Edge{{Kind: 1, From: "lib:pkg", To: "app:program", Part: "lib:pkg", Stale: true},
|
||||
{Kind: 4, From: "prov:program", To: "app:program", Part: "prov:program"},
|
||||
{Kind: 5, From: "actor:program", To: "app:program", Part: "actor:program"}}},
|
||||
}
|
||||
rev, objects, err := CatalogRevisionOf(meshcatalog.CatalogRevision{Revision: 3, Kind: "trunk"}, entries)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := KeepRevision(ctx, ix.Revisions, rev, objects); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ask := func(_ context.Context, name string) (BuilderVersionsAnswer, error) {
|
||||
pub := func(v, d string, at time.Time) BuilderVersion {
|
||||
return BuilderVersion{Version: v, State: "published", Digest: d, Revision: "9", At: at}
|
||||
}
|
||||
switch name {
|
||||
case "x/lib":
|
||||
return BuilderVersionsAnswer{Versions: []BuilderVersion{pub("0.1.1", "sha256:l1", t0), pub("0.1.2", "sha256:l2", t0.Add(2*time.Hour))}}, nil
|
||||
case "app/program":
|
||||
return BuilderVersionsAnswer{Versions: []BuilderVersion{pub("0.3.1", "sha256:a1", t0.Add(time.Hour))}}, nil
|
||||
}
|
||||
return BuilderVersionsAnswer{Versions: []BuilderVersion{pub("0.9.1", "sha256:"+name, t0)}}, nil
|
||||
}
|
||||
if err := ix.FillVersions(ctx, ask); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
app, _ := ModuleVersions(ctx, ix.Versions, "app")
|
||||
if len(app) != 1 || len(app[0].Edges) != 3 {
|
||||
t.Fatalf("app's filled version: %+v", app)
|
||||
}
|
||||
for _, e := range app[0].Edges {
|
||||
if !e.Filled {
|
||||
t.Errorf("a filled entry is not marked: %+v", e)
|
||||
}
|
||||
switch e.Kind {
|
||||
case 1:
|
||||
if e.Version != "0.1.1" || e.Digest != "sha256:l1" || !e.Stale {
|
||||
t.Errorf("kind 1 is the version published before the build was claimed: %+v", e)
|
||||
}
|
||||
case 4:
|
||||
if e.Version != "0.9.1" || e.Digest != "sha256:prov/program" {
|
||||
t.Errorf("kind 4 is the version every machine runs: %+v", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
// lib's 0.1.2 was published after app's build was claimed: that is behind, and nothing else (the delivered kind 4 and the act of kind 5 are not)
|
||||
if a, err := ix.Outdated(ctx, meshcatalog.OutdatedInput{}); err != nil || len(a.Behind) != 1 || a.Behind[0].Kind != 1 {
|
||||
t.Errorf("outdated after the first fill: %+v, %v", a.Behind, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ func TestManifestDeclarations(t *testing.T) { // 1238/AC1, 1444 (reads the build
|
||||
// The registry seats' published fill each (§413: no consume of version.numbered) package's series (DLV-I5, design 51 *The versions*).
|
||||
if want := []string{"go-package-registry.published", "mesh-build-go.toolchain.reported", "mesh-build-node.toolchain.reported", "mesh-builds.package-published", "mesh-builds.toolchain-agreed", "mesh-conditions.findings-asked", "mesh-delivery-assigner.views.assignments",
|
||||
"mesh-delivery-checker.preview.written", "mesh-delivery-checker.try.ended", "mesh-delivery-checker.try.started", "mesh-delivery-checker.verdict", "mesh-delivery-forge-bridge.forge.**",
|
||||
"mesh-delivery.source-moved", "mesh-wants.version-delivered", "mesh-wants.wait-ended", "npm-package-registry.published", "operator-channel.decided"}; !reflect.DeepEqual(sorted(m.Consumes), want) {
|
||||
"mesh-delivery.source-moved", "mesh-wants.version-delivered", "mesh-wants.wait-ended", "node-engine.running", "npm-package-registry.published", "operator-channel.decided"}; !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" {
|
||||
@@ -173,7 +173,7 @@ func TestManifestDeclarations(t *testing.T) { // 1238/AC1, 1444 (reads the build
|
||||
// Three invokes: the file of a registered module's manifest and lock files (registered.go), the forge's trees (the named paths' objects, since the indexer is the one reader of a manifest's paths:
|
||||
// ADR 0499 clauses 2 and 3, ADR 0495 clause 1; and the module directories of the trunk, issue 1900) and whether a
|
||||
// commit is in another (issue 1900).
|
||||
if want := []string{"mesh-delivery-assigner.assignments", "seat:git.trees", "seat:git.file", "seat:git.contains", "seat:git.open-pulls", "seat:git.branch", "mesh-delivery-checker.checker_tries", "mesh-delivery-forge-bridge.bridge_merged_message", "seat:node-engine.composition", "seat:mesh-delivery.source", "seat:mesh-hand-acts.hand-act", "seat:mesh-watchdogs.presence", "seat:node-build-go.steps", "seat:node-build-node.steps", "seat:node-build-image.steps", "mesh-delivery-builder.builder_versions"}; !reflect.DeepEqual(m.Invokes, want) {
|
||||
if want := []string{"mesh-delivery-assigner.assignments", "seat:git.trees", "seat:git.file", "seat:git.contains", "seat:git.open-pulls", "seat:git.branch", "mesh-delivery-checker.checker_tries", "mesh-delivery-forge-bridge.bridge_merged_message", "seat:node-engine.composition", "seat:mesh-module-assigner.placement", "seat:mesh-delivery.source", "seat:mesh-hand-acts.hand-act", "seat:mesh-watchdogs.presence", "seat:node-build-go.steps", "seat:node-build-node.steps", "seat:node-build-image.steps", "mesh-delivery-builder.builder_versions"}; !reflect.DeepEqual(m.Invokes, want) {
|
||||
t.Errorf("invokes = %v, want %v", m.Invokes, want)
|
||||
}
|
||||
// 1945: indexer_take_in, served by its tools bundle through the takeins state; the process serves none. The same bundle
|
||||
@@ -195,7 +195,7 @@ func TestManifestDeclarations(t *testing.T) { // 1238/AC1, 1444 (reads the build
|
||||
index.PendingTool, index.TakeInPendingTool, index.DropPendingTool, index.TryCandidatesTool, index.DropTryCandidateTool, index.RecoverTriesTool, index.TakeInMergeTool, index.FingerprintsAgainTool, // issue 1966; 1585/T12, T13 (ADR 0633 §5)
|
||||
// milestone 47/G W4.12 (design 47, the indexer's tools): the catalog graph revisions read and steered.
|
||||
index.CatalogRevisionTool, index.CatalogRevisionsTool, index.CandidateGraphsTool, index.RecomputeTool, index.DropCandidateGraphTool,
|
||||
index.EntryIndexTool, // design 47 G §675
|
||||
index.EntryIndexTool, // design 47 G §675
|
||||
index.ProposalsTool, index.ProposalDropTool, index.SeedAgainTool, index.InputsTool) // design 47 G §667, §8
|
||||
if !reflect.DeepEqual(m.Tools, want) || len(want) != 46 || slices.Contains(m.Tools, "modules") {
|
||||
t.Errorf("tools = %v, want %v", m.Tools, want)
|
||||
|
||||
@@ -86,6 +86,7 @@
|
||||
"mesh-builds.package-published",
|
||||
"mesh-builds.toolchain-agreed",
|
||||
"mesh-wants.version-delivered",
|
||||
"node-engine.running",
|
||||
"mesh-wants.wait-ended",
|
||||
"mesh-delivery-checker.verdict",
|
||||
"mesh-delivery-forge-bridge.forge.**",
|
||||
@@ -157,6 +158,7 @@
|
||||
"mesh-delivery-checker.checker_tries",
|
||||
"mesh-delivery-forge-bridge.bridge_merged_message",
|
||||
"seat:node-engine.composition",
|
||||
"seat:mesh-module-assigner.placement",
|
||||
"seat:mesh-delivery.source",
|
||||
"seat:mesh-hand-acts.hand-act",
|
||||
"seat:mesh-watchdogs.presence",
|
||||
|
||||
Reference in New Issue
Block a user