mesh-build-image runs its asks on the Delivery SDK's neutral build ask, step and answer forms instead of mesh-sdk's go/delivery; its state names are its seat's (B5i; ADR 0634 §1, §6; issue 1966)

Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
This commit is contained in:
2026-11-05 16:06:48 +01:00
parent cfde90906f
commit aa29d8feb8
19 changed files with 121 additions and 118 deletions
@@ -5,9 +5,10 @@ import (
"time"
"git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep"
seat "git.novox.be/novox/mesh-delivery/contracts/go/seats/nodebuildimage"
buildrun "git.novox.be/novox/mesh-delivery/sdk/buildrun"
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
stdio "git.novox.be/novox/mesh-sdk/go"
"git.novox.be/novox/mesh-sdk/go/delivery"
)
// **The module pulls its seat's asks** (novox/hq ADR 0525 §2-§6; issue 1887 A3image), as mesh-build-go does: the seat
@@ -36,7 +37,7 @@ func (m *Module) askSteps(steps []StepTool) buildstep.AskSteps {
// Puller is the module's pull of its seat's asks, on source (the runtime's seat pull when nil).
func (m *Module) Puller(steps []StepTool, source buildstep.AskSource) *buildstep.Puller {
if source == nil {
source = buildstep.SeatAsks{Language: delivery.AskImage}
source = buildstep.SeatAsks{Language: sdkstep.AskImage}
}
capacity := m.Capacity
// The capacity's own change wakes the puller (issue 1887): the runtime's watch of the config value as mesh-config
@@ -54,16 +55,16 @@ func (m *Module) Puller(steps []StepTool, source buildstep.AskSource) *buildstep
capacity = buildstep.NodeCapacity(m.Settings, m.Node, m.Say)
settings = m.Settings
watch = buildstep.WatchConfigServed
cores = buildstep.NodeCoresPerStep(m.Settings, buildstep.DefaultCoresPerStep[delivery.AskImage], m.Say)
cores = buildstep.NodeCoresPerStep(m.Settings, buildstep.DefaultCoresPerStep[sdkstep.AskImage], m.Say)
}
ask := m.askSteps(steps)
var load func(string) ([]byte, error)
if m.ReadPauses.Get != nil {
load = func(key string) ([]byte, error) { return m.ReadPauses.Get(key) }
}
return &buildstep.Puller{Source: source, Language: delivery.AskImage, Node: m.Node, Put: m.Steps, PausePut: m.Pauses, Load: load,
return &buildstep.Puller{Source: source, Language: sdkstep.AskImage, Node: m.Node, Put: m.Steps, PausePut: m.Pauses, Load: load,
Capacity: capacity, SettingsFile: settings, WatchConfig: watch, CoresPerStep: cores, BuildHours: hours, Eligible: m.Eligible, Logf: m.Say,
Run: func(ctx context.Context, a delivery.StepAsk) (any, error) {
Run: func(ctx context.Context, a sdkstep.StepAsk) (any, error) {
if err := m.Check(ctx); err != nil {
return nil, err
}
@@ -83,13 +84,13 @@ func (m *Module) SeatTools(p *buildstep.Puller) []stdio.Tool {
const StepEndTool = "build_image_step_end"
// endLeftRunning is the module's start sweep (issue 1890): it ends the steps of its node that a previous process left
// running (sdkstep.EndStaleAtStart; every step names its owner). main runs it once at the start, and the module's test
// running (buildrun.EndStaleAtStart; every step names its owner). main runs it once at the start, and the module's test
// runs it through the conformance kit's left-running case.
func (m *Module) endLeftRunning(ctx context.Context, scan sdkstep.Scan, booted time.Time, logf func(string, ...any)) {
sdkstep.EndStaleAtStart(ctx, scan, m.Node, booted, logf)
func (m *Module) endLeftRunning(ctx context.Context, scan buildrun.Scan, booted time.Time, logf func(string, ...any)) {
buildrun.EndStaleAtStart(ctx, scan, m.Node, booted, logf)
}
// stepScan is the module's steps state as buildstep reads it.
func (m *Module) stepScan() sdkstep.Scan {
return sdkstep.ScanOf(stdio.State(delivery.StepsState))
func (m *Module) stepScan() buildrun.Scan {
return buildrun.ScanOf(stdio.State(seat.StateSteps))
}
@@ -8,7 +8,6 @@ import (
"git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep"
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
"git.novox.be/novox/mesh-sdk/go/delivery"
)
const askCommit = "0123456789abcdef0123456789abcdef01234567"
@@ -32,23 +31,23 @@ func TestAPulledImageAskCopiesThenBuilds(t *testing.T) {
answer("build_image_copy", map[string]any{"digest": "sha256:dd", "reference": "registry/m/base@sha256:dd"}),
}
src := buildstep.NewFakeAsks()
a := delivery.StepAsk{ID: sdkstep.NewID(), Kind: delivery.AskDelivery, Language: delivery.AskImage, Asker: "mesh-delivery-builder",
At: time.Now(), Build: &delivery.BuildJob{Module: "m", Path: "modules/m", Publish: true,
Fetch: map[string]any{"repository": "novox/mesh-catalog", "commit": askCommit},
Artifacts: []delivery.AskArtifact{{Artifact: "base", From: "docker.io/library/alpine@sha256:00"}, {Artifact: "server"}}}}
a := sdkstep.StepAsk{ID: sdkstep.NewID(), Kind: sdkstep.AskDelivery, Language: sdkstep.AskImage, Asker: "mesh-delivery-builder",
At: time.Now(), Build: &sdkstep.BuildJob{Module: "m", Path: "modules/m", Publish: true,
Fetch: &sdkstep.Fetch{Repository: "novox/mesh-catalog", Commit: askCommit},
Artifacts: []sdkstep.AskArtifact{{Artifact: "base", From: "docker.io/library/alpine@sha256:00"}, {Artifact: "server"}}}}
_ = src.PublishAsk(a)
p := m.Puller(steps, src)
p.PullWait = 10 * time.Millisecond
ctx, stop := context.WithCancel(context.Background())
defer stop()
go p.Pull(ctx)
key, _ := delivery.AskStepKey("ace", a.ID)
var step delivery.Step
key, _ := sdkstep.AskStepKey("ace", a.ID)
var step sdkstep.Step
for deadline := time.Now().Add(3 * time.Second); ; time.Sleep(5 * time.Millisecond) {
states.mu.Lock()
v := states.steps[key]
states.mu.Unlock()
if len(v) > 0 && v[len(v)-1].State != delivery.StepRunning {
if len(v) > 0 && v[len(v)-1].State != sdkstep.StepRunning {
step = v[len(v)-1]
break
}
@@ -56,7 +55,7 @@ func TestAPulledImageAskCopiesThenBuilds(t *testing.T) {
t.Fatalf("the ask's step never ended: calls %v", calls)
}
}
if step.State != delivery.StepPassed || !strings.Contains(string(step.Result), "registry/m/server@sha256:cc") ||
if step.State != sdkstep.StepPassed || !strings.Contains(string(step.Result), "registry/m/server@sha256:cc") ||
!strings.Contains(string(step.Result), "registry/m/base@sha256:dd") {
t.Fatalf("the step = %+v", step)
}
@@ -2,14 +2,14 @@ package main
import (
"encoding/json"
"git.novox.be/novox/mesh-sdk/go/forgekey"
"os"
"path/filepath"
"strings"
"testing"
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
"git.novox.be/novox/mesh-sdk/go/catalogue"
"git.novox.be/novox/mesh-sdk/go/delivery"
"git.novox.be/novox/mesh-sdk/go/forgekey"
)
// The fixture module (testdata/repos/image): a Dockerfile that adds a file to an upstream base the manifest pins in
@@ -38,7 +38,7 @@ func place(t *testing.T, m *Module, edit func(string) string) {
func same(s string) string { return s }
func runBuild(t *testing.T, m *Module, states *keptStates, extra map[string]any) (BuildResult, delivery.Step) {
func runBuild(t *testing.T, m *Module, states *keptStates, extra map[string]any) (BuildResult, sdkstep.Step) {
t.Helper()
args := map[string]any{"workspace": "ws1", "module": "modules/demo", "artifact": "server"}
for k, v := range extra {
@@ -49,7 +49,7 @@ func runBuild(t *testing.T, m *Module, states *keptStates, extra map[string]any)
}
st := states.waitEnd(t)
var res BuildResult
if st.State == delivery.StepPassed {
if st.State == sdkstep.StepPassed {
if err := json.Unmarshal(st.Result, &res); err != nil {
t.Fatal(err)
}
@@ -73,7 +73,7 @@ func TestBuildRunsBuildahRootlessAndAnswersTheDigest(t *testing.T) {
t.Setenv("MESH_BUILD_CPU_SHARE", "50") // the composer's share (ADR 0553): buildah is given its threads
m, f, states := setup(t, same)
res, st := runBuild(t, m, states, nil)
if st.State != delivery.StepPassed {
if st.State != sdkstep.StepPassed {
t.Fatalf("step = %+v", st)
}
if res.Digest != imageDigest || res.Artifact != "server" || res.Pushed || res.Reference != "" {
@@ -144,7 +144,7 @@ func TestBuildContextIsTheModulesDirectoryForADockerfileInASubdirectory(t *testi
}
}
_, st := runBuild(t, m, states, nil)
if st.State != delivery.StepPassed {
if st.State != sdkstep.StepPassed {
t.Fatalf("step = %+v", st)
}
bud := f.called("bud")
@@ -183,7 +183,7 @@ func TestBuildFailsWithBuildahsError(t *testing.T) {
m, f, states := setup(t, same)
f.failBud = c.ran
_, st := runBuild(t, m, states, nil)
if st.State != delivery.StepFailed {
if st.State != sdkstep.StepFailed {
t.Errorf("%s: step = %+v, want failed", name, st)
continue
}
@@ -223,7 +223,7 @@ func TestBuildRefusesAnImageWithARepositoryContextOrAModuleBase(t *testing.T) {
for name, c := range cases {
m, f, states := setup(t, c.edit)
_, st := runBuild(t, m, states, nil)
if st.State != delivery.StepRefused {
if st.State != sdkstep.StepRefused {
t.Errorf("%s: step = %+v, want refused", name, st)
continue
}
@@ -266,7 +266,7 @@ func TestBuildFailsForWhatIsNotAnImageOrNotThere(t *testing.T) {
} {
m, f, states := setup(t, tc.edit)
_, st := runBuild(t, m, states, tc.args)
if st.State != delivery.StepFailed {
if st.State != sdkstep.StepFailed {
t.Errorf("%s: step = %+v, want failed", name, st)
}
if len(f.called("bud")) != 0 {
@@ -317,7 +317,7 @@ func TestAManifestFallingBackToAPredefinedKeyBuilds(t *testing.T) {
return string(out)
})
res, st := runBuild(t, m, states, nil)
if st.State != delivery.StepPassed {
if st.State != sdkstep.StepPassed {
t.Fatalf("step = %+v", st)
}
if res.Digest != imageDigest || res.Artifact != "server" {
@@ -16,7 +16,7 @@ import (
"testing"
"time"
"git.novox.be/novox/mesh-sdk/go/delivery"
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
"github.com/nats-io/nats-server/v2/server"
"github.com/nats-io/nats.go"
"github.com/nats-io/nats.go/jetstream"
@@ -100,23 +100,23 @@ func call(t *testing.T, nc *nats.Conn, tool string, args map[string]any) map[str
}
// ended waits for the step's key in the bucket to leave running and answers the step as the bucket holds it.
func ended(t *testing.T, kv jetstream.KeyValue, key string) delivery.Step {
func ended(t *testing.T, kv jetstream.KeyValue, key string) sdkstep.Step {
t.Helper()
deadline := time.Now().Add(30 * time.Second)
for time.Now().Before(deadline) {
if e, err := kv.Get(context.Background(), key); err == nil {
st, err := delivery.DecodeStep(e.Value())
st, err := sdkstep.DecodeStep(e.Value())
if err != nil {
t.Fatal(err)
}
if st.State != delivery.StepRunning {
if st.State != sdkstep.StepRunning {
return st
}
}
time.Sleep(20 * time.Millisecond)
}
t.Fatalf("the key %s never left running", key)
return delivery.Step{}
return sdkstep.Step{}
}
func TestTheStepsAnswerThroughARealBusWithTheCapturedShapes(t *testing.T) {
@@ -128,7 +128,7 @@ func TestTheStepsAnswerThroughARealBusWithTheCapturedShapes(t *testing.T) {
// The fetch: a running key at once, then the workspace on the key.
started := call(t, nc, FetchTool, map[string]any{"repository": "novox/mesh-catalog", "commit": commitSHA})
if started["state"] != delivery.StepRunning || started["key"] == "" {
if started["state"] != sdkstep.StepRunning || started["key"] == "" {
t.Fatalf("the fetch answered %v, want a running step and its key", started)
}
st := ended(t, kv, started["key"].(string))
@@ -142,7 +142,7 @@ func TestTheStepsAnswerThroughARealBusWithTheCapturedShapes(t *testing.T) {
}
started := call(t, nc, BuildTool, args)
st := ended(t, kv, started["key"].(string))
if st.State != delivery.StepPassed {
if st.State != sdkstep.StepPassed {
t.Fatalf("%s: step = %+v", name, st)
}
var res BuildResult
@@ -17,7 +17,7 @@ import (
"testing"
"time"
"git.novox.be/novox/mesh-sdk/go/delivery"
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
)
const capturedDir = "testdata/captured"
@@ -25,7 +25,7 @@ const capturedDir = "testdata/captured"
var hexDigest = regexp.MustCompile(`sha256:[0-9a-f]{64}`)
// normalized is a stored step with the words that differ between runs put to fixed ones.
func normalized(t *testing.T, st delivery.Step, workspace, commit, registry string) []byte {
func normalized(t *testing.T, st sdkstep.Step, workspace, commit, registry string) []byte {
t.Helper()
raw, err := json.Marshal(st)
if err != nil {
@@ -75,7 +75,7 @@ func compareCaptured(t *testing.T, name string, got []byte) {
}
}
func workspaceOf(t *testing.T, st delivery.Step) string {
func workspaceOf(t *testing.T, st sdkstep.Step) string {
var r struct {
Workspace string `json:"workspace"`
}
@@ -23,9 +23,10 @@ import (
reg "git.novox.be/novox/mesh-delivery/contracts/go/seats/containerimageregistry"
stdio "git.novox.be/novox/mesh-sdk/go"
"git.novox.be/novox/mesh-sdk/go/delivery"
"git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep"
builds "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds"
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
)
// CopyTool is the copy step's tool.
@@ -90,7 +91,7 @@ func (m *Module) copy(ctx context.Context, args map[string]any) (any, error) {
return nil, fmt.Errorf("not pinned: %s: %v; an upstream artifact is copied by the digest its manifest pins", name, err)
}
if m.Outcomes == nil || m.Outcomes.Waits == nil {
return nil, fmt.Errorf("%s%s: this process hears no outcome of the image seat", delivery.NotStored, name)
return nil, fmt.Errorf("%s%s: this process hears no outcome of the image seat", builds.NotStored, name)
}
key := MirrorWaitKey(digest)
now := time.Now
@@ -104,14 +105,14 @@ func (m *Module) copy(ctx context.Context, args map[string]any) (any, error) {
heard, stop := m.Outcomes.Expect(digest)
defer stop()
if _, err := m.Outcomes.Waits.Put(key, asked); err != nil {
return nil, fmt.Errorf("%s%s: keeping the copy's wait in %s: %v", delivery.NotStored, name, MirrorWaitsState, err)
return nil, fmt.Errorf("%s%s: keeping the copy's wait in %s: %v", builds.NotStored, name, MirrorWaitsState, err)
}
defer m.Outcomes.drop(key, asked.Token)
said := buildstep.NarrateSeat(ctx, buildstep.ImageSeat, "mirror of "+from)
raw, err := m.call()("seat:"+buildstep.ImageSeat+"."+reg.VerbMirror, reg.MirrorInput{Name: module + "/" + name, Source: from, Digest: digest})
if err != nil {
said.Refused(err)
return nil, fmt.Errorf("%s%s: %s's mirror: %v", delivery.NotStored, name, buildstep.ImageSeat, err)
return nil, fmt.Errorf("%s%s: %s's mirror: %v", builds.NotStored, name, buildstep.ImageSeat, err)
}
var answer reg.MirrorAnswer
_ = json.Unmarshal(raw, &answer)
@@ -139,10 +140,10 @@ func (m *Module) copy(ctx context.Context, args map[string]any) (any, error) {
case <-bound:
err := fmt.Errorf("%s said no outcome of the copy of %s in %s", buildstep.ImageSeat, from, wait)
said.Refused(err)
return nil, fmt.Errorf("%s%s: %w", delivery.NotStored, name, err)
return nil, fmt.Errorf("%s%s: %w", builds.NotStored, name, err)
case <-ctx.Done():
said.Refused(ctx.Err())
return nil, fmt.Errorf("%s%s: stopped while waiting for the copy of %s: %w", delivery.NotStored, name, from, ctx.Err())
return nil, fmt.Errorf("%s%s: stopped while waiting for the copy of %s: %w", builds.NotStored, name, from, ctx.Err())
}
}
}
@@ -150,7 +151,7 @@ func (m *Module) copy(ctx context.Context, args map[string]any) (any, error) {
// copyEnd is a copy's end on the outcome kept for it.
func copyEnd(name string, o MirrorOutcome, asked string) (any, error) {
if o.Event == reg.EventRefused {
return nil, fmt.Errorf("%s%s: %s refused the copy: %s", delivery.NotStored, name, buildstep.ImageSeat, o.Reason)
return nil, fmt.Errorf("%s%s: %s refused the copy: %s", builds.NotStored, name, buildstep.ImageSeat, o.Reason)
}
ref := o.Reference
if ref == "" {
@@ -377,7 +378,7 @@ func (m *Module) Plain(tool string, run func(ctx context.Context, args map[strin
if err != nil {
return nil, err
}
key, _ := delivery.StepKey(m.Node, id)
return map[string]any{"step": id, "key": key, "state": delivery.StepRunning}, nil
key, _ := sdkstep.StepKey(m.Node, id)
return map[string]any{"step": id, "key": key, "state": sdkstep.StepRunning}, nil
}
}
@@ -7,17 +7,17 @@ import (
"time"
reg "git.novox.be/novox/mesh-delivery/contracts/go/seats/containerimageregistry"
"git.novox.be/novox/mesh-sdk/go/delivery"
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
)
func runCopy(t *testing.T, m *Module, states *keptStates, args map[string]any) (CopyResult, delivery.Step) {
func runCopy(t *testing.T, m *Module, states *keptStates, args map[string]any) (CopyResult, sdkstep.Step) {
t.Helper()
if _, err := tool(t, m, CopyTool)(args); err != nil {
t.Fatal(err)
}
st := states.waitEnd(t)
var res CopyResult
if st.State == delivery.StepPassed {
if st.State == sdkstep.StepPassed {
if err := json.Unmarshal(st.Result, &res); err != nil {
t.Fatal(err)
}
@@ -67,7 +67,7 @@ func TestTheCopyIsTheImageSeatsMirrorAndEndsOnItsOutcome(t *testing.T) {
f.atAsk = func() { asked, _ = m.Waiting() }
res, st := runCopy(t, m, states, map[string]any{"module": "nats", "artifact": "server", "from": c.from})
if c.reason != "" {
if st.State != delivery.StepFailed || !strings.Contains(st.Reason, c.reason) {
if st.State != sdkstep.StepFailed || !strings.Contains(st.Reason, c.reason) {
t.Fatalf("step = %+v, want failed with %q", st, c.reason)
}
if c.reason == "not pinned: server" && len(f.mirrors) != 0 {
@@ -75,7 +75,7 @@ func TestTheCopyIsTheImageSeatsMirrorAndEndsOnItsOutcome(t *testing.T) {
}
return
}
if st.State != delivery.StepPassed || res.Digest != d || !res.Pushed ||
if st.State != sdkstep.StepPassed || res.Digest != d || !res.Pushed ||
res.Reference != reg.Seat+"/upstream/nats/server@"+d {
t.Fatalf("step = %+v result = %+v", st, res)
}
@@ -7,7 +7,7 @@ import (
"strings"
"testing"
"git.novox.be/novox/mesh-sdk/go/delivery"
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
)
// 1703b: the fetch step asks git, through the SDK's FetchCommit, for the ssh remote of the forge as the account, makes
@@ -20,11 +20,11 @@ func TestFetchAsksGitForTheSshRemoteIntoAWorkspace(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if a := ans.(map[string]any); a["state"] != delivery.StepRunning || a["step"] == "" {
if a := ans.(map[string]any); a["state"] != sdkstep.StepRunning || a["step"] == "" {
t.Errorf("the call answered %v, want a running step", a)
}
st := states.waitEnd(t)
if st.State != delivery.StepPassed {
if st.State != sdkstep.StepPassed {
t.Fatalf("step = %+v", st)
}
var res FetchResult
@@ -60,7 +60,7 @@ func TestFetchRefusesWhatIsNotARepositoryOrACommit(t *testing.T) {
if _, err := tool(t, m, FetchTool)(args); err != nil {
t.Fatal(err)
}
if st := states.waitEnd(t); st.State != delivery.StepFailed {
if st := states.waitEnd(t); st.State != sdkstep.StepFailed {
t.Errorf("%s: step = %+v, want failed", name, st)
}
}
@@ -16,7 +16,7 @@ import (
stdio "git.novox.be/novox/mesh-sdk/go"
reg "git.novox.be/novox/mesh-delivery/contracts/go/seats/containerimageregistry"
"git.novox.be/novox/mesh-sdk/go/delivery"
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
"git.novox.be/novox/mesh-sdk/go/forgekey"
)
@@ -25,29 +25,29 @@ const commitSHA = "0123456789abcdef0123456789abcdef01234567"
// keptStates is the steps state, kept in memory: every write of a key, and the keys that ended.
type keptStates struct {
mu sync.Mutex
steps map[string][]delivery.Step
steps map[string][]sdkstep.Step
ended chan string
}
func newStates() *keptStates {
return &keptStates{steps: map[string][]delivery.Step{}, ended: make(chan string, 16)}
return &keptStates{steps: map[string][]sdkstep.Step{}, ended: make(chan string, 16)}
}
func (s *keptStates) putStep(key string, value any) (uint64, error) {
st, err := delivery.DecodeStep(value.(json.RawMessage))
st, err := sdkstep.DecodeStep(value.(json.RawMessage))
if err != nil {
return 0, err
}
s.mu.Lock()
s.steps[key] = append(s.steps[key], st)
s.mu.Unlock()
if st.State != delivery.StepRunning {
if st.State != sdkstep.StepRunning {
s.ended <- key
}
return 1, nil
}
func (s *keptStates) waitEnd(t *testing.T) delivery.Step {
func (s *keptStates) waitEnd(t *testing.T) sdkstep.Step {
t.Helper()
select {
case key := <-s.ended:
@@ -57,7 +57,7 @@ func (s *keptStates) waitEnd(t *testing.T) delivery.Step {
return v[len(v)-1]
case <-time.After(5 * time.Second):
t.Fatal("the step never ended on its key")
return delivery.Step{}
return sdkstep.Step{}
}
}
@@ -19,7 +19,7 @@ import (
"time"
"git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep"
"git.novox.be/novox/mesh-sdk/go/delivery"
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
)
func needTools(t *testing.T, what string, tools ...string) {
@@ -91,7 +91,7 @@ func TestFetchOnARealRepository(t *testing.T) {
t.Fatal(err)
}
st := waitLong(t, states)
if st.State != delivery.StepPassed {
if st.State != sdkstep.StepPassed {
t.Fatalf("step = %+v", st)
}
var res FetchResult
@@ -106,7 +106,7 @@ func TestFetchOnARealRepository(t *testing.T) {
}
}
func waitLong(t *testing.T, s *keptStates) delivery.Step {
func waitLong(t *testing.T, s *keptStates) sdkstep.Step {
t.Helper()
select {
case key := <-s.ended:
@@ -116,7 +116,7 @@ func waitLong(t *testing.T, s *keptStates) delivery.Step {
return v[len(v)-1]
case <-time.After(5 * time.Minute):
t.Fatal("the step never ended on its key")
return delivery.Step{}
return sdkstep.Step{}
}
}
@@ -133,14 +133,14 @@ func imageModule(t *testing.T, m *Module, dockerfile string, files map[string]st
}
}
func buildReal(t *testing.T, m *Module, states *keptStates) (BuildResult, delivery.Step) {
func buildReal(t *testing.T, m *Module, states *keptStates) (BuildResult, sdkstep.Step) {
t.Helper()
if _, err := tool(t, m, BuildTool)(map[string]any{"workspace": "ws1", "module": "modules/img", "artifact": "server"}); err != nil {
t.Fatal(err)
}
st := waitLong(t, states)
var res BuildResult
if st.State == delivery.StepPassed {
if st.State == sdkstep.StepPassed {
if err := json.Unmarshal(st.Result, &res); err != nil {
t.Fatal(err)
}
@@ -154,7 +154,7 @@ func TestARealBuildAnswersTheImagesDigest(t *testing.T) {
m, states := realModule(t)
imageModule(t, m, "FROM scratch\nCOPY hello.txt /hello.txt\n", map[string]string{"hello.txt": "hello\n"})
res, st := buildReal(t, m, states)
if st.State != delivery.StepPassed || !digestOK.MatchString(res.Digest) {
if st.State != sdkstep.StepPassed || !digestOK.MatchString(res.Digest) {
t.Fatalf("step = %+v result = %+v", st, res)
}
}
@@ -191,17 +191,17 @@ func TestARealBuildIsRootlessWithNoSocketAndNoWayOut(t *testing.T) {
"RUN awk 'NR==1 { exit ($2 == 0) }' /proc/self/uid_map\n"+
"RUN ! ls /var/run/docker.sock /run/docker.sock /run/podman/podman.sock /run/containerd/containerd.sock 2>/dev/null\n"+
"RUN ! grep -E 'docker.sock|podman.sock|containerd.sock' /proc/self/mountinfo\n", nil)
if _, st := buildReal(t, m, states); st.State != delivery.StepPassed {
if _, st := buildReal(t, m, states); st.State != sdkstep.StepPassed {
t.Errorf("the isolation checks inside the build failed: %+v", st)
}
// A step that writes outside its workspace fails: the context cannot be left, and the host's kernel files are not writable.
imageModule(t, m, "FROM "+base+"\nCOPY ../outside /outside\n", nil)
if _, st := buildReal(t, m, states); st.State != delivery.StepFailed {
if _, st := buildReal(t, m, states); st.State != sdkstep.StepFailed {
t.Errorf("a COPY out of the context passed: %+v", st)
}
imageModule(t, m, "FROM "+base+"\nRUN echo b > /proc/sysrq-trigger\n", nil)
if _, st := buildReal(t, m, states); st.State != delivery.StepFailed {
if _, st := buildReal(t, m, states); st.State != sdkstep.StepFailed {
t.Errorf("a write to the host's /proc/sysrq-trigger passed: %+v", st)
} else if strings.Contains(st.Reason, "setup network") {
t.Errorf("the RUN never started, so the write was not what failed (issue 1703): %s", st.Reason)
@@ -220,7 +220,7 @@ func TestARealBuildFindsNoCredentialsOfTheAccount(t *testing.T) {
"RUN ! find / -xdev -name mesh_ed25519 2>/dev/null | grep .\n"+
"RUN ! test -e /root/.ssh\n"+
"RUN ! env | grep -E 'REGISTRY_AUTH|MESH_|SSH_AUTH'\n", nil)
if _, st := buildReal(t, m, states); st.State != delivery.StepPassed {
if _, st := buildReal(t, m, states); st.State != sdkstep.StepPassed {
t.Errorf("a step found a credential path of the account: %+v", st)
}
}
@@ -6,14 +6,14 @@ import (
"time"
"git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep/conformance"
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
buildrun "git.novox.be/novox/mesh-delivery/sdk/buildrun"
)
// 1890/AC7: the module's own start sweep (endLeftRunning, as main runs it) keeps the conformance kit's left-running
// case: a step a previous process left running is ended at once and said, whatever its age, and a step of this process
// is kept.
func TestTheStartSweepEndsWhatAPreviousProcessLeft(t *testing.T) {
conformance.LeftRunning(t, func(ctx context.Context, scan sdkstep.Scan, node string, before time.Time, logf func(string, ...any)) {
conformance.LeftRunning(t, func(ctx context.Context, scan buildrun.Scan, node string, before time.Time, logf func(string, ...any)) {
(&Module{Node: node}).endLeftRunning(ctx, scan, before, logf)
})
}
@@ -9,14 +9,15 @@ package main
import (
"context"
"fmt"
reg "git.novox.be/novox/mesh-delivery/contracts/go/seats/containerimageregistry"
"os"
"os/user"
"strings"
"time"
reg "git.novox.be/novox/mesh-delivery/contracts/go/seats/containerimageregistry"
seat "git.novox.be/novox/mesh-delivery/contracts/go/seats/nodebuildimage"
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
stdio "git.novox.be/novox/mesh-sdk/go"
"git.novox.be/novox/mesh-sdk/go/delivery"
)
// The directories the module's manifest places outside its home (ADR 0466 §1): the workspaces, and buildah's image
@@ -32,10 +33,10 @@ func main() {
ctx := context.Background()
m := &Module{
Node: strings.TrimSpace(os.Getenv("MESH_NODE")),
Steps: stdio.State(delivery.StepsState).Put,
ReadSteps: readOf(stdio.State(delivery.StepsState)),
Pauses: stdio.State(delivery.PausesState).Put,
ReadPauses: readOf(stdio.State(delivery.PausesState)),
Steps: stdio.State(seat.StateSteps).Put,
ReadSteps: readOf(stdio.State(seat.StateSteps)),
Pauses: stdio.State(sdkstep.PausesState).Put,
ReadPauses: readOf(stdio.State(sdkstep.PausesState)),
Ctx: ctx,
Say: logf,
Work: WorkDir,
@@ -8,8 +8,8 @@ import (
"time"
"git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep"
buildrun "git.novox.be/novox/mesh-delivery/sdk/buildrun"
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
"git.novox.be/novox/mesh-sdk/go/delivery"
"git.novox.be/novox/mesh-sdk/go/forgekey"
"git.novox.be/novox/mesh-sdk/go/health"
)
@@ -164,7 +164,7 @@ func (m *Module) Start(ctx context.Context) {
m.Say("this machine cannot build images: %v", err)
}
m.CleanWorkspaces(m.clock())
sdkstep.EndStaleRound(m.stepScan(), m.Node, m.Say) // asks past the step bound, on the module's own round (issue 1890)
buildrun.EndStaleRound(m.stepScan(), m.Node, m.Say) // asks past the step bound, on the module's own round (issue 1890)
select {
case <-ctx.Done():
return
@@ -230,8 +230,8 @@ func (m *Module) Long(tool string, run func(ctx context.Context, args map[string
if err != nil {
return nil, err
}
key, _ := delivery.StepKey(m.Node, id)
return map[string]any{"step": id, "key": key, "state": delivery.StepRunning}, nil
key, _ := sdkstep.StepKey(m.Node, id)
return map[string]any{"step": id, "key": key, "state": sdkstep.StepRunning}, nil
}
}
@@ -8,7 +8,7 @@ import (
"strings"
"testing"
"git.novox.be/novox/mesh-sdk/go/delivery"
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
"git.novox.be/novox/mesh-sdk/go/forgekey"
"git.novox.be/novox/mesh-sdk/go/health"
)
@@ -167,7 +167,7 @@ func TestAStepOnAMachineThatCannotBuildEndsRefused(t *testing.T) {
t.Fatal(err)
}
st := states.waitEnd(t)
if st.State != delivery.StepRefused || !strings.Contains(st.Reason, "/nonexistent/subgid") {
if st.State != sdkstep.StepRefused || !strings.Contains(st.Reason, "/nonexistent/subgid") {
t.Errorf("step = %+v, want refused naming the file", st)
}
for _, l := range f.lines() {
@@ -19,9 +19,9 @@ import (
reg "git.novox.be/novox/mesh-delivery/contracts/go/seats/containerimageregistry"
stdio "git.novox.be/novox/mesh-sdk/go"
"git.novox.be/novox/mesh-sdk/go/catalogue"
"git.novox.be/novox/mesh-sdk/go/delivery"
"git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep"
builds "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds"
)
// KindImage is the kind an image is published as on its seat.
@@ -116,16 +116,16 @@ func (m *Module) publish(ctx context.Context, workspace, module, local string, r
argv := append(m.storeFlags(), "push", "--digestfile", digestFile, local, "oci:"+dir)
if r := m.Run(ctx, m.env(), "buildah", argv...); r.Status != 0 || r.Err != "" {
m.sayFailure("buildah push of "+res.Artifact+" to its layout", r)
return nil, fmt.Errorf("%s%s: writing its layout: %s", delivery.NotStored, res.Artifact, cause(r))
return nil, fmt.Errorf("%s%s: writing its layout: %s", builds.NotStored, res.Artifact, cause(r))
}
img, err := readLayout(dir)
if err != nil {
return nil, fmt.Errorf("%s%s: %w", delivery.NotStored, res.Artifact, err)
return nil, fmt.Errorf("%s%s: %w", builds.NotStored, res.Artifact, err)
}
// What buildah wrote is checked against the layout: a digest never goes out unchecked.
raw, err := os.ReadFile(digestFile)
if pushed := strings.TrimSpace(string(raw)); err != nil || pushed != img.Digest {
return nil, fmt.Errorf("%s%s: buildah wrote the digest %q (%v), but its layout's manifest is %s", delivery.NotStored, res.Artifact, pushed, err, img.Digest)
return nil, fmt.Errorf("%s%s: buildah wrote the digest %q (%v), but its layout's manifest is %s", builds.NotStored, res.Artifact, pushed, err, img.Digest)
}
name := module + "/" + res.Artifact
q := KindImage
@@ -136,7 +136,7 @@ func (m *Module) publish(ctx context.Context, workspace, module, local string, r
Transfer: buildstep.TransferName(name, q, img.Digest)}
sent, err := buildstep.PublishFiles(ctx, m.call(), buildstep.ImageSeat, in, img.Files)
if err != nil {
return nil, fmt.Errorf("%s%s: %v", delivery.NotStored, res.Artifact, err)
return nil, fmt.Errorf("%s%s: %v", builds.NotStored, res.Artifact, err)
}
m.say("published %s on %s: %d of its %d parts sent, the rest held", name, buildstep.ImageSeat, len(sent.Parts), len(img.Files))
res.Digest, res.Pushed = img.Digest, true
@@ -9,14 +9,15 @@ import (
"testing"
reg "git.novox.be/novox/mesh-delivery/contracts/go/seats/containerimageregistry"
"git.novox.be/novox/mesh-sdk/go/delivery"
builds "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds"
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
)
// 1703d/AC (ADR 0487 §5): a candidate's build leaves the image seat unchanged: nothing is written, nothing is asked.
func TestACandidatesBuildLeavesTheRegistryUnchanged(t *testing.T) {
m, f, states := setup(t, same)
res, st := runBuild(t, m, states, nil) // no publish
if st.State != delivery.StepPassed || res.Pushed {
if st.State != sdkstep.StepPassed || res.Pushed {
t.Fatalf("step = %+v result = %+v", st, res)
}
if len(f.called("push")) != 0 || len(f.published) != 0 {
@@ -57,12 +58,12 @@ func TestATrunkBuildPublishesThroughTheImageSeatOnlyWhatTheHolderLacks(t *testin
}
res, st := runBuild(t, m, states, args)
if c.reason != "" {
if (st.State != delivery.StepFailed && st.State != delivery.StepRefused) || !strings.Contains(st.Reason, c.reason) {
if (st.State != sdkstep.StepFailed && st.State != sdkstep.StepRefused) || !strings.Contains(st.Reason, c.reason) {
t.Fatalf("step = %+v, want failed with %q", st, c.reason)
}
return
}
if st.State != delivery.StepPassed {
if st.State != sdkstep.StepPassed {
t.Fatalf("step = %+v", st)
}
if !res.Pushed || res.Digest != f.pushed || res.Reference != reg.Seat+"/fixture-image/server@"+f.pushed {
@@ -118,7 +119,7 @@ func TestATrunkBuildPublishesThroughTheImageSeatOnlyWhatTheHolderLacks(t *testin
})
}
m2, f2, states2 := setup(t, same)
if _, st := runBuild(t, m2, states2, map[string]any{"publish": true, "tag": "a/b:c"}); st.State != delivery.StepFailed || len(f2.called("bud")) != 0 {
if _, st := runBuild(t, m2, states2, map[string]any{"publish": true, "tag": "a/b:c"}); st.State != sdkstep.StepFailed || len(f2.called("bud")) != 0 {
t.Errorf("a tag that is not one: step = %+v", st)
}
}
@@ -128,7 +129,7 @@ func TestATrunkBuildPublishesThroughTheImageSeatOnlyWhatTheHolderLacks(t *testin
func TestTheNumberedEventPublishesAHeldImageUnderItsVersion(t *testing.T) {
digest := "sha256:" + strings.Repeat("ab", 32)
numbered := func(kind, seat, digest string) []byte {
raw, err := delivery.EncodeVersionNumbered(delivery.VersionNumbered{Name: "fixture-image/server", Kind: kind, Version: "1.4.0",
raw, err := builds.EncodeVersionNumbered(builds.VersionNumbered{Name: "fixture-image/server", Kind: kind, Version: "1.4.0",
Module: "fixture-image", Fingerprint: "fp", Commit: "c0", Digest: digest, Seat: seat})
if err != nil {
t.Fatal(err)
@@ -14,8 +14,8 @@ import (
"sync"
"time"
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
stdio "git.novox.be/novox/mesh-sdk/go"
"git.novox.be/novox/mesh-sdk/go/delivery"
)
// The read tools, named as the module is: build_image_ and the verb.
@@ -97,26 +97,26 @@ func (m *Module) stepKey(id string) (string, error) {
}
id = rest
}
return delivery.StepKey(m.Node, id)
return sdkstep.StepKey(m.Node, id)
}
// ReadStep is the stored value of this node's step id (state, reason, result).
func (m *Module) ReadStep(id string) (delivery.Step, error) {
func (m *Module) ReadStep(id string) (sdkstep.Step, error) {
key, err := m.stepKey(id)
if err != nil {
return delivery.Step{}, err
return sdkstep.Step{}, err
}
if m.ReadSteps.Get == nil {
return delivery.Step{}, fmt.Errorf("the steps state cannot be read here")
return sdkstep.Step{}, fmt.Errorf("the steps state cannot be read here")
}
raw, err := m.ReadSteps.Get(key)
if err != nil {
return delivery.Step{}, err
return sdkstep.Step{}, err
}
if len(raw) == 0 {
return delivery.Step{}, fmt.Errorf("no step %s in the steps state", key)
return sdkstep.Step{}, fmt.Errorf("no step %s in the steps state", key)
}
return delivery.DecodeStep(raw)
return sdkstep.DecodeStep(raw)
}
// StepLine is a step without its result: the list answers these.
@@ -149,7 +149,7 @@ func (m *Module) RecentSteps(limit int) (lines []StepLine, cut bool, err error)
own := keys[:0:0]
for _, k := range keys {
if strings.HasPrefix(k, m.Node+".") {
if node, _, err := delivery.ParseStepKey(k); err == nil && node == m.Node {
if node, _, err := sdkstep.ParseStepKey(k); err == nil && node == m.Node {
own = append(own, k)
}
}
@@ -163,12 +163,12 @@ func (m *Module) RecentSteps(limit int) (lines []StepLine, cut bool, err error)
if err != nil {
return nil, cut, err
}
var steps []delivery.Step
var steps []sdkstep.Step
for _, raw := range raws {
if len(raw) == 0 {
continue
}
s, err := delivery.DecodeStep(raw)
s, err := sdkstep.DecodeStep(raw)
if err != nil {
continue // a value this module cannot read is not one of its own steps
}
@@ -8,14 +8,14 @@ import (
"testing"
"time"
"git.novox.be/novox/mesh-sdk/go/delivery"
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
)
// The read tools answer what the steps state holds: a finished build's result is there, read by the step's id.
func TestTheReadToolsAnswerAStepsResult(t *testing.T) {
m, _, states := setup(t, same)
res, st := runBuild(t, m, states, nil)
if st.State != delivery.StepPassed {
if st.State != sdkstep.StepPassed {
t.Fatalf("step = %+v", st)
}
m.ReadSteps = StateRead{
@@ -36,7 +36,7 @@ func TestTheReadToolsAnswerAStepsResult(t *testing.T) {
},
}
got, err := m.ReadStep(st.ID)
if err != nil || got.State != delivery.StepPassed {
if err != nil || got.State != sdkstep.StepPassed {
t.Fatalf("ReadStep = %+v, %v", got, err)
}
var back BuildResult
@@ -67,7 +67,7 @@ func TestRecentStepsReadsOnlyTheNodesKeysInParallelAndBounded(t *testing.T) {
stored := map[string][]byte{}
for _, n := range []string{"ace", "g14", "novox", "shanks"} {
for i := 0; i < row.perNode; i++ {
b, _ := delivery.EncodeStep(delivery.Step{ID: fmt.Sprintf("s%04d", i), Tool: "t", Node: n, State: delivery.StepPassed, Started: t0.Add(time.Duration(i) * time.Second)})
b, _ := sdkstep.EncodeStep(sdkstep.Step{ID: fmt.Sprintf("s%04d", i), Tool: "t", Node: n, State: sdkstep.StepPassed, Started: t0.Add(time.Duration(i) * time.Second)})
stored[n+".s"+fmt.Sprintf("%04d", i)] = b
}
}
@@ -9,7 +9,7 @@ import (
"time"
"git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep"
"git.novox.be/novox/mesh-sdk/go/delivery"
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
"git.novox.be/novox/mesh-sdk/go/forgekey"
"git.novox.be/novox/mesh-sdk/go/health"
)
@@ -43,7 +43,7 @@ func TestUnderTheFloorAFetchAndABuildAreRefusedAndTheModuleIsUnhealthy(t *testin
if _, err := tool(t, m, name)(args); err != nil {
t.Fatalf("%s: %v", name, err)
}
if st := states.waitEnd(t); st.State != delivery.StepRefused || !strings.HasPrefix(st.Reason, underTheFloor) {
if st := states.waitEnd(t); st.State != sdkstep.StepRefused || !strings.HasPrefix(st.Reason, underTheFloor) {
t.Errorf("%s ended %s: %q, want refused: %q", name, st.State, st.Reason, underTheFloor)
}
}
@@ -87,7 +87,7 @@ func TestAFailedFetchLeavesNoWorkspace(t *testing.T) {
if _, err := tool(t, m, FetchTool)(map[string]any{"repository": "novox/mesh-catalog", "commit": commitSHA}); err != nil {
t.Fatal(err)
}
if st := states.waitEnd(t); st.State != delivery.StepFailed {
if st := states.waitEnd(t); st.State != sdkstep.StepFailed {
t.Fatalf("the step ended %s (%s), want failed", st.State, st.Reason)
}
if entries, _ := os.ReadDir(m.Work); len(entries) != 0 {