Require node-tools in the three build modules, so the delivery plan puts the tool runner they pull asks through before them on every node (issue 1911)

This commit is contained in:
2026-10-23 16:50:03 +02:00
parent a1febd3ad8
commit a493c90c0d
9 changed files with 43 additions and 9 deletions
@@ -50,10 +50,11 @@ func TestTheModuleComposesWhereItsFilesAreHandedToTheProvisionersClient(t *testi
"buildah": read("buildah"),
"forge": {Module: "forge", Version: "1", Provides: []catalogue.Offer{{Name: "forge-account"}}},
"store": {Module: "store", Version: "1", Provides: []catalogue.Offer{{Name: "artifact-store"}}},
"runner": {Module: "runner", Version: "1", Provides: []catalogue.Offer{{Name: "node-tools"}}}, // node-tools, which the module pulls its asks through (1911)
}
node := catalogue.Node{Name: "builder", Site: "house",
Capabilities: map[string]bool{"package-manager": true, "user-namespaces": true}}
r, err := catalogue.Resolve(shelf, []string{"forge", "store", "buildah", "mesh-build-image"}, node, catalogue.World{})
r, err := catalogue.Resolve(shelf, []string{"forge", "store", "runner", "buildah", "mesh-build-image"}, node, catalogue.World{})
if err != nil {
t.Fatal(err)
}
@@ -144,7 +144,7 @@ func TestForgeFilesRenderFromTheBoundValuesInTheModulesOwnHome(t *testing.T) {
// template in the files/ archive (novox/hq ADR 0349 §2).
func TestManifestRequiresForgeAccountKeepsKeysAndTakesFilesFromTemplates(t *testing.T) {
m := readForgeManifest(t)
if want := []any{"forge-account", "rootless-image-build", "artifact-store"}; !reflect.DeepEqual(m.Requires, want) {
if want := []any{"forge-account", "rootless-image-build", "artifact-store", "node-tools"}; !reflect.DeepEqual(m.Requires, want) {
t.Errorf("requires = %v, want %v", m.Requires, want)
}
keeps := false
@@ -163,6 +163,8 @@ func TestACompositionWithoutBuildahIsRefusedNamingItAndWithBuildahComposes(t *te
shelf := map[string]catalogue.Manifest{
"forge": {Module: "forge", Version: "1", Provides: []catalogue.Offer{{Name: "forge-account"}}},
"store": {Module: "store", Version: "1", Provides: []catalogue.Offer{{Name: "artifact-store"}}},
// node-tools, the tool runner the module pulls its asks through (1911), stood in for as the others are.
"runner": {Module: "runner", Version: "1", Provides: []catalogue.Offer{{Name: "node-tools"}}},
}
for _, name := range []string{"mesh-build-image", "buildah", "docker"} {
m, err := catalogue.ReadTreeManifest(catalogueRoot, filepath.Join(catalogueRoot, "modules", name))
@@ -173,7 +175,7 @@ func TestACompositionWithoutBuildahIsRefusedNamingItAndWithBuildahComposes(t *te
}
node := catalogue.Node{Name: "builder", Site: "house",
Capabilities: map[string]bool{"package-manager": true, "user-namespaces": true}}
_, err := catalogue.Resolve(shelf, []string{"forge", "store", "docker", "mesh-build-image"}, node, catalogue.World{})
_, err := catalogue.Resolve(shelf, []string{"forge", "store", "runner", "docker", "mesh-build-image"}, node, catalogue.World{})
if err == nil {
t.Fatal("mesh-build-image composed on a machine without buildah (docker does not stand in for it)")
}
@@ -182,7 +184,16 @@ func TestACompositionWithoutBuildahIsRefusedNamingItAndWithBuildahComposes(t *te
t.Errorf("the refusal does not say %q:\n%v", want, err)
}
}
if _, err := catalogue.Resolve(shelf, []string{"forge", "store", "buildah", "mesh-build-image"}, node, catalogue.World{}); err != nil {
if _, err := catalogue.Resolve(shelf, []string{"forge", "store", "runner", "buildah", "mesh-build-image"}, node, catalogue.World{}); err != nil {
t.Errorf("with buildah assigned the composition is refused: %v", err)
}
}
// 1911: the module pulls its asks through node-tools' seat pull (mesh-tools, R4g), so it requires node-tools on its node
// and the delivery plan puts node-tools first: a node's build module never pulls through a tool runner that cannot.
func TestTheModuleRequiresNodeToolsItPullsThrough(t *testing.T) {
m := readManifest(t)
if !slices.Contains(m.Requires, "node-tools") {
t.Errorf("requires %v lack node-tools", m.Requires)
}
}