diff --git a/modules/mesh-delivery-builder/builds/busfree_test.go b/modules/mesh-delivery-builder/builds/busfree_test.go new file mode 100644 index 00000000..6d4af86b --- /dev/null +++ b/modules/mesh-delivery-builder/builds/busfree_test.go @@ -0,0 +1,21 @@ +package builds + +import ( + "os/exec" + "strings" + "testing" +) + +// The builder's published value is read by modules that never touch the bus (mesh-host's composer tool): nothing it +// imports reaches the bus library. +func TestThePackageReachesNoBus(t *testing.T) { + out, err := exec.Command("go", "list", "-deps", ".").Output() + if err != nil { + t.Fatalf("go list: %v", err) + } + for _, p := range strings.Fields(string(out)) { + if strings.HasPrefix(p, "github.com/nats-io/") { + t.Errorf("builds depends on %s", p) + } + } +} diff --git a/sdk/buildrun/keys_test.go b/sdk/buildrun/keys_test.go new file mode 100644 index 00000000..8ce937d1 --- /dev/null +++ b/sdk/buildrun/keys_test.go @@ -0,0 +1,52 @@ +package buildrun + +import ( + "encoding/json" + "sync" + "testing" + "time" + + "git.novox.be/novox/mesh-delivery/sdk/buildstep" +) + +// keys is a steps state in memory: every value put, in order, per key, and a signal for each end. +type keys struct { + mu sync.Mutex + puts map[string][]buildstep.Step + ended chan string +} + +func newKeys() *keys { return &keys{puts: map[string][]buildstep.Step{}, ended: make(chan string, 16)} } + +func (k *keys) put(key string, value any) (uint64, error) { + step, err := buildstep.DecodeStep(value.(json.RawMessage)) + if err != nil { + return 0, err + } + k.mu.Lock() + k.puts[key] = append(k.puts[key], step) + n := uint64(len(k.puts[key])) + k.mu.Unlock() + if step.State != buildstep.StepRunning { + k.ended <- key + } + return n, nil +} + +func (k *keys) last(key string) buildstep.Step { + k.mu.Lock() + defer k.mu.Unlock() + s := k.puts[key] + return s[len(s)-1] +} + +func (k *keys) waitEnd(t *testing.T) string { + t.Helper() + select { + case key := <-k.ended: + return key + case <-time.After(3 * time.Second): + t.Fatal("the step never ended on its key") + return "" + } +} diff --git a/sdk/buildstep/stale.go b/sdk/buildrun/stale.go similarity index 83% rename from sdk/buildstep/stale.go rename to sdk/buildrun/stale.go index 7e0de98e..6e0b6075 100644 --- a/sdk/buildstep/stale.go +++ b/sdk/buildrun/stale.go @@ -1,10 +1,11 @@ -package buildstep +package buildrun import ( "context" "encoding/json" "errors" "fmt" + "git.novox.be/novox/mesh-delivery/sdk/buildstep" "os" "strconv" "strings" @@ -20,7 +21,7 @@ const RestartReason = "the build module restarted while it ran" // for this process's, which may still run it, so EndOne refuses it. var Boot = time.Now() -// Owner is this process as a step's owner (Step.Owner): `..`, the node's boot id +// Owner is this process as a step's owner (buildstep.Step.Owner): `..`, the node's boot id // (/proc/sys/kernel/random/boot_id), the pid and Boot in milliseconds, base 36. The start keeps two processes apart // that a reboot or a pid namespace gave the same pid. A running step whose owner is another is a previous process's. var Owner = ownerOf(bootID, os.Getpid(), Boot) @@ -84,15 +85,15 @@ func ScanOf(s stdio.KeptState) Scan { // has the tool's name). An ask's key is the stream's to settle, never a restart's: the puller leaves an ask it was // running unacknowledged so the stream redelivers it, and the asker's wait takes the first end it reads on the key. // A key a gone process owns is deleted, never ended (EndStale); one with no owner is ended only once the asker's bound -// has passed (StepBound), when it is nobody's. -func isAsk(key string, s Step) bool { - _, _, err := ParseAskStepKey(key) +// has passed (buildstep.StepBound), when it is nobody's. +func isAsk(key string, s buildstep.Step) bool { + _, _, err := buildstep.ParseAskStepKey(key) return err == nil && strings.HasSuffix(s.Tool, " ask") } // AskWhat says what an ask is, in one line: its kind, and its repository, head and levels, or its module and // fingerprint. -func AskWhat(a Ask) string { +func AskWhat(a buildstep.Ask) string { short := func(s string) string { if len(s) > 12 { return s[:12] @@ -122,9 +123,9 @@ func AskWhat(a Ask) string { } // endRunning writes step ended failed with why, only while its key is still at revision. -func endRunning(scan Scan, key string, step Step, revision uint64, why string) error { - step.State, step.Reason, step.Ended, step.Result = StepFailed, why, now().UTC(), nil - raw, err := EncodeStep(step) +func endRunning(scan Scan, key string, step buildstep.Step, revision uint64, why string) error { + step.State, step.Reason, step.Ended, step.Result = buildstep.StepFailed, why, now().UTC(), nil + raw, err := buildstep.EncodeStep(step) if err != nil { return err } @@ -134,7 +135,7 @@ func endRunning(scan Scan, key string, step Step, revision uint64, why string) e // EndStale ends this node's running steps that no process of this module runs any more, and answers their ids. Only keys // of node are read; a value that is no step is left alone. -// - A hand-started step with an owner (Step.Owner) that is not this process (Owner) is ended failed with +// - A hand-started step with an owner (buildstep.Step.Owner) that is not this process (Owner) is ended failed with // RestartReason at once, whatever its age: its process is gone and nothing redelivers it, so nothing else would // write its end (issue 1890, note 21). // - A pulled ask's step with an owner that is not this process is deleted, not ended (novox/hq ADR 0525 ยง6): the @@ -146,7 +147,7 @@ func endRunning(scan Scan, key string, step Step, revision uint64, why string) e // - A step owned by this process is left: it may run now. A redelivered ask begun on this node is owned by it. // - A step with no owner (written by an older build) keeps the earlier rule: a hand-started step started before // `before` (the start of this process) is ended; a pulled ask is left to the stream's redelivery (isAsk) until its -// start is older than StepBound, and then ended "past the step bound: its asker stopped waiting at