A module names the module its build stands on, not a copy of it
A fingerprint written into a recipe names one particular copy of the base — the copy on whichever machine the person typing it was using. On any other mesh that copy has never existed, so the build stops on its first line with a message about an image nobody can look up. Three modules in the catalogue were in exactly that state, and the line each of them replaced was equally dead. A module now names the module and artifact instead, and the mesh answers with what it holds. The builder is still a thing that clones, builds and answers: the answer travels with the question, because only the mesh knows what it has. A base the mesh has not built is refused before anything is built, naming which module has to exist first.
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/novox/mesh-control/internal/catalogue"
|
||||
)
|
||||
|
||||
// A module naming a base the mesh has not built is refused, and the refusal names what is missing.
|
||||
//
|
||||
// **This is the whole point of naming a base rather than pinning one** (novox/hq issue 044). A
|
||||
// recipe with a fingerprint typed into it fails inside a container runtime, on its first line, with
|
||||
// a message about an image nobody can look up. This fails before anything is built, saying which
|
||||
// module has to exist first.
|
||||
func TestABaseTheMeshHasNotBuiltIsRefused(t *testing.T) {
|
||||
manifest := catalogue.Manifest{
|
||||
Module: "postgres",
|
||||
Build: &catalogue.Build{
|
||||
On: []catalogue.BuildsOn{{Arg: "RUNTIME_BASE", Module: "mesh-tools", Artifact: "runtime"}},
|
||||
},
|
||||
}
|
||||
_, err := standingOn(manifest, map[string]string{})
|
||||
if err == nil {
|
||||
t.Fatal("a base nothing has built was accepted; the build would have failed on its first line")
|
||||
}
|
||||
for _, want := range []string{"mesh-tools", "postgres"} {
|
||||
if !strings.Contains(err.Error(), want) {
|
||||
t.Errorf("the refusal does not name %s: %v", want, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// And one the mesh holds becomes the argument the recipe reads it from.
|
||||
func TestABaseTheMeshHoldsBecomesABuildArgument(t *testing.T) {
|
||||
manifest := catalogue.Manifest{
|
||||
Module: "postgres",
|
||||
Build: &catalogue.Build{
|
||||
On: []catalogue.BuildsOn{{Arg: "RUNTIME_BASE", Module: "mesh-tools", Artifact: "runtime"}},
|
||||
},
|
||||
}
|
||||
held := map[string]string{"mesh-tools/runtime": "127.0.0.1:5000/mesh-tools/runtime@sha256:" + strings.Repeat("a", 64)}
|
||||
args, err := standingOn(manifest, held)
|
||||
if err != nil {
|
||||
t.Fatalf("a base this mesh holds was refused: %v", err)
|
||||
}
|
||||
want := []string{"--build-arg", "RUNTIME_BASE=" + held["mesh-tools/runtime"]}
|
||||
if len(args) != len(want) || args[0] != want[0] || args[1] != want[1] {
|
||||
t.Fatalf("the build was invoked with %v, not %v", args, want)
|
||||
}
|
||||
}
|
||||
|
||||
// A module naming no base asks for nothing, which is most modules.
|
||||
func TestAModuleNamingNoBaseAddsNoArguments(t *testing.T) {
|
||||
args, err := standingOn(catalogue.Manifest{Module: "hello-web", Build: &catalogue.Build{}}, nil)
|
||||
if err != nil || args != nil {
|
||||
t.Fatalf("a module naming no base produced %v, %v", args, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Half a base is refused rather than half-applied.
|
||||
func TestAnIncompleteBaseIsRefused(t *testing.T) {
|
||||
manifest := catalogue.Manifest{
|
||||
Module: "postgres",
|
||||
Build: &catalogue.Build{On: []catalogue.BuildsOn{{Module: "mesh-tools", Artifact: "runtime"}}},
|
||||
}
|
||||
if _, err := standingOn(manifest, map[string]string{"mesh-tools/runtime": "x"}); err == nil {
|
||||
t.Fatal("a base with no build argument was accepted; nothing would have read it")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user