From f67342f0d86e2868524e5ee585a1c18eb97ac203 Mon Sep 17 00:00:00 2001 From: jochen Date: Fri, 23 Oct 2026 00:53:25 +0200 Subject: [PATCH] =?UTF-8?q?Ask=20build-agent=20for=20the=20build=20modules?= =?UTF-8?q?=20that=20write=20references,=20whatever=20the=20builder=20says?= =?UTF-8?q?=20(issue=201886,=20ADR=200522=20=C2=A76)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit They push and answer each reference, so a key they write themselves can never carry their own delivery; until R4g build-agent delivers them as it does the builder. --- cmd/mesh-controller/builder_says_test.go | 61 +++++++++++++++--------- cmd/mesh-controller/plan_keys.go | 7 ++- 2 files changed, 45 insertions(+), 23 deletions(-) diff --git a/cmd/mesh-controller/builder_says_test.go b/cmd/mesh-controller/builder_says_test.go index 39644f16..1fc5c5ab 100644 --- a/cmd/mesh-controller/builder_says_test.go +++ b/cmd/mesh-controller/builder_says_test.go @@ -38,9 +38,9 @@ func askingBuildAgent(t *testing.T) *[]string { func TestAWalkAsksBuildAgentWhileTheBuilderDoesNotSayItWritesReferences(t *testing.T) { theBuilderSays(t, false, nil) asked := askingBuildAgent(t) - p, byName := aWalkOfOne("mesh-build-go") - askModule(t.Context(), p, "mesh-build-go", byName) - s := p.Modules["mesh-build-go"] + p, byName := aWalkOfOne("sudo") + askModule(t.Context(), p, "sudo", byName) + s := p.Modules["sudo"] if s.Key || s.Build != "build-9" || s.State != "asked" || len(*asked) != 1 { t.Fatalf("the module is not asked of build-agent: %+v, asked %v", s, *asked) } @@ -51,9 +51,9 @@ func TestAWalkAsksBuildAgentWhileTheBuilderDoesNotSayItWritesReferences(t *testi func TestAWalkTakesTheKeyOnceTheBuilderSaysItWritesReferences(t *testing.T) { theBuilderSays(t, true, nil) asked := askingBuildAgent(t) - p, byName := aWalkOfOne("mesh-build-go") - askModule(t.Context(), p, "mesh-build-go", byName) - if s := p.Modules["mesh-build-go"]; !s.Key || s.Build != "" || len(*asked) != 0 { + p, byName := aWalkOfOne("sudo") + askModule(t.Context(), p, "sudo", byName) + if s := p.Modules["sudo"]; !s.Key || s.Build != "" || len(*asked) != 0 { t.Fatalf("the module is not taken from its key: %+v, asked %v", s, *asked) } } @@ -63,9 +63,9 @@ func TestAWalkTakesTheKeyOnceTheBuilderSaysItWritesReferences(t *testing.T) { func TestASayNotReadLeavesTheModuleUnasked(t *testing.T) { theBuilderSays(t, false, errors.New("the bus did not answer")) asked := askingBuildAgent(t) - p, byName := aWalkOfOne("mesh-build-go") - askModule(t.Context(), p, "mesh-build-go", byName) - s := p.Modules["mesh-build-go"] + p, byName := aWalkOfOne("sudo") + askModule(t.Context(), p, "sudo", byName) + s := p.Modules["sudo"] if s.State != "" || s.Key || len(*asked) != 0 || !strings.Contains(s.Why, "the bus did not answer") { t.Fatalf("a say not read: %+v, asked %v", s, *asked) } @@ -102,15 +102,15 @@ func TestAnOpenWalkWaitingOnAnUnreferencedKeyIsAskedOfBuildAgent(t *testing.T) { keyTakes.Unlock() }) fp := "src2:" + strings.Repeat("e", 64) - unreferenced := builderkeys.Key{Module: "mesh-build-go", Fingerprint: fp, Found: true, Revision: 1, + unreferenced := builderkeys.Key{Module: "sudo", Fingerprint: fp, Found: true, Revision: 1, Build: sdkdelivery.BuilderBuild{State: sdkdelivery.BuildDone, Manifest: strings.Repeat("b", 64), Artifacts: []sdkdelivery.BuiltArtifact{{Name: "tools", Kind: "bundle", Digest: "sha256:1"}}}} walk := func() (*inventory.Plan, map[string]inventory.Entry, planKeysOf) { theBuilderSays(t, true, nil) - p, byName := aWalkOfOne("mesh-build-go") - askModule(t.Context(), p, "mesh-build-go", byName) - keys := planKeysOf{values: map[string]sdkdelivery.Module{"mesh-build-go@c0ffee0123456789": {FP2: fp}}, - keys: map[string]builderkeys.Key{"mesh-build-go": unreferenced}} + p, byName := aWalkOfOne("sudo") + askModule(t.Context(), p, "sudo", byName) + keys := planKeysOf{values: map[string]sdkdelivery.Module{"sudo@c0ffee0123456789": {FP2: fp}}, + keys: map[string]builderkeys.Key{"sudo": unreferenced}} return p, byName, keys } take := func(string, sdkdelivery.Module, string) { t.Fatal("an unreferenced key was taken in") } @@ -121,7 +121,7 @@ func TestAnOpenWalkWaitingOnAnUnreferencedKeyIsAskedOfBuildAgent(t *testing.T) { if _, err := settleKeys(t.Context(), records, keys, p, p.Tiers[0], take); err != nil { t.Fatal(err) } - if s := p.Modules["mesh-build-go"]; s.State != "asked" || !s.Key || !strings.Contains(s.Why, "waits for the reference") { + if s := p.Modules["sudo"]; s.State != "asked" || !s.Key || !strings.Contains(s.Why, "waits for the reference") { t.Fatalf("with the builder saying, the walk does not wait for the reference: %+v", s) } @@ -134,14 +134,14 @@ func TestAnOpenWalkWaitingOnAnUnreferencedKeyIsAskedOfBuildAgent(t *testing.T) { if err != nil || !changed { t.Fatalf("the walk was not judged again: %v %v", changed, err) } - s := p.Modules["mesh-build-go"] + s := p.Modules["sudo"] if s.Key || s.State != "" || p.State != inventory.PlanBuilding || !strings.Contains(s.Why, "does not say") { t.Fatalf("the module is not handed back to be asked of build-agent: %+v %s", s, p.State) } for _, m := range unaskedOf(*p) { askModule(t.Context(), p, m, byName) } - if s := p.Modules["mesh-build-go"]; s.Key || s.Build != "build-9" || len(*asked) != 1 { + if s := p.Modules["sudo"]; s.Key || s.Build != "build-9" || len(*asked) != 1 { t.Fatalf("the next look did not ask build-agent: %+v, asked %v", s, *asked) } } @@ -150,17 +150,34 @@ func TestAnOpenWalkWaitingOnAnUnreferencedKeyIsAskedOfBuildAgent(t *testing.T) { // asked of build-agent, whose build is the one delivered. func TestAFailedKeyOfABuilderThatDoesNotSayFailsNoWalk(t *testing.T) { theBuilderSays(t, true, nil) - p, byName := aWalkOfOne("mesh-build-go") - askModule(t.Context(), p, "mesh-build-go", byName) + p, byName := aWalkOfOne("sudo") + askModule(t.Context(), p, "sudo", byName) _ = byName fp := "src2:" + strings.Repeat("f", 64) - keys := planKeysOf{notSaid: true, values: map[string]sdkdelivery.Module{"mesh-build-go@c0ffee0123456789": {FP2: fp}}, - keys: map[string]builderkeys.Key{"mesh-build-go": {Module: "mesh-build-go", Fingerprint: fp, Found: true, + keys := planKeysOf{notSaid: true, values: map[string]sdkdelivery.Module{"sudo@c0ffee0123456789": {FP2: fp}}, + keys: map[string]builderkeys.Key{"sudo": {Module: "sudo", Fingerprint: fp, Found: true, Build: sdkdelivery.BuilderBuild{State: sdkdelivery.BuildFailed, Error: "not stored: tools: no reference"}}}} if _, err := settleKeys(t.Context(), recordsOf{}, keys, p, p.Tiers[0], func(string, sdkdelivery.Module, string) {}); err != nil { t.Fatal(err) } - if s := p.Modules["mesh-build-go"]; p.State != inventory.PlanBuilding || s.State != "" || s.Key { + if s := p.Modules["sudo"]; p.State != inventory.PlanBuilding || s.State != "" || s.Key { t.Fatalf("a failed key of a builder that does not say failed the walk: %s %q %+v", p.State, p.Note, s) } } + +// 1886/AC6 (ADR 0522 §6): the parts that write references, the builder and the build modules, are build-agent's own until +// R4g: asked of build-agent and never taken from keys they write themselves, whatever the builder says. +func TestTheReferenceWritersAreAskedOfBuildAgentWhateverTheBuilderSays(t *testing.T) { + theBuilderSays(t, true, nil) + asked := askingBuildAgent(t) + for _, m := range []string{"mesh-build-go", "mesh-build-node", "mesh-build-image", "mesh-delivery-builder"} { + p, byName := aWalkOfOne(m) + askModule(t.Context(), p, m, byName) + if s := p.Modules[m]; s.Key || s.Build != "build-9" { + t.Errorf("%s is not asked of build-agent: %+v", m, s) + } + } + if len(*asked) != 4 { + t.Fatalf("build-agent was asked %v", *asked) + } +} diff --git a/cmd/mesh-controller/plan_keys.go b/cmd/mesh-controller/plan_keys.go index 5380e954..ade65305 100644 --- a/cmd/mesh-controller/plan_keys.go +++ b/cmd/mesh-controller/plan_keys.go @@ -32,7 +32,12 @@ func isBuildersOwn(module string) bool { return buildersOwn(module) } // repository's; and mesh-tools, the toolchain image build-agent's builds stand on. The builder writes no key of any of them, // and a walk waiting for one would hold its tier for ever. mesh-tools-go is not among them: it builds from a Dockerfile in // its own directory on a pinned base, and has its key from mesh-build-image. -var buildAgentsOwn = map[string]bool{sdkdelivery.BuilderModule: true, builder.AgentModule: true, "mesh-tools": true} +// +// **And the parts that write references** (ADR 0522 §6, issue 1886): the build modules push and answer each reference, +// and the builder writes it into the key, so none of them is ever taken from a key they write themselves. Until R4g +// build-agent is the way they are delivered whatever the builder says. +var buildAgentsOwn = map[string]bool{sdkdelivery.BuilderModule: true, builder.AgentModule: true, "mesh-tools": true, + "mesh-build-go": true, "mesh-build-node": true, "mesh-build-image": true} // buildersOwn is isBuildersOwn's rule: a variable only so a test of the asks left (their bus waits, their plan key) may // name modules of its own as build-agent's.