A build declares the vendor image it stands on, and a recipe fetches nothing undeclared
build.on takes {arg, image@sha256:…} beside {arg, module, artifact}: the image is
copied into the mesh's registry before the build (ADR 0096) and the recipe reads the
copy from the argument. A FROM or COPY --from naming a registry image the manifest
did not declare is refused before the build, naming it and the remedy; stages,
declared arguments and scratch are not fetches (novox/hq 04-ISSUES/064, ADR 0097).
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -20,7 +22,7 @@ func TestABaseTheMeshHasNotBuiltIsRefused(t *testing.T) {
|
||||
On: []catalogue.BuildsOn{{Arg: "RUNTIME_BASE", Module: "mesh-tools", Artifact: "runtime"}},
|
||||
},
|
||||
}
|
||||
_, err := standingOn(manifest, map[string]string{})
|
||||
_, err := standingOn(context.Background(), manifest, map[string]string{}, noMirror)
|
||||
if err == nil {
|
||||
t.Fatal("a base nothing has built was accepted; the build would have failed on its first line")
|
||||
}
|
||||
@@ -40,7 +42,7 @@ func TestABaseTheMeshHoldsBecomesABuildArgument(t *testing.T) {
|
||||
},
|
||||
}
|
||||
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)
|
||||
args, err := standingOn(context.Background(), manifest, held, noMirror)
|
||||
if err != nil {
|
||||
t.Fatalf("a base this mesh holds was refused: %v", err)
|
||||
}
|
||||
@@ -52,7 +54,7 @@ func TestABaseTheMeshHoldsBecomesABuildArgument(t *testing.T) {
|
||||
|
||||
// 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)
|
||||
args, err := standingOn(context.Background(), catalogue.Manifest{Module: "hello-web", Build: &catalogue.Build{}}, nil, noMirror)
|
||||
if err != nil || args != nil {
|
||||
t.Fatalf("a module naming no base produced %v, %v", args, err)
|
||||
}
|
||||
@@ -64,7 +66,66 @@ func TestAnIncompleteBaseIsRefused(t *testing.T) {
|
||||
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 {
|
||||
if _, err := standingOn(context.Background(), manifest, map[string]string{"mesh-tools/runtime": "x"}, noMirror); err == nil {
|
||||
t.Fatal("a base with no build argument was accepted; nothing would have read it")
|
||||
}
|
||||
}
|
||||
|
||||
// noMirror is a mirror for tests whose bases are all the mesh's own.
|
||||
func noMirror(context.Context, string, string) (string, error) {
|
||||
return "", fmt.Errorf("nothing to copy in this test")
|
||||
}
|
||||
|
||||
// A build may stand on an image published elsewhere, declared and pinned (novox/hq 04-ISSUES/064,
|
||||
// ADR 0097): it is copied into the mesh's registry first and the recipe is handed the copy.
|
||||
func TestADeclaredVendorImageIsCopiedInAndHandedToTheRecipe(t *testing.T) {
|
||||
manifest := catalogue.Manifest{
|
||||
Module: "minio",
|
||||
Build: &catalogue.Build{
|
||||
On: []catalogue.BuildsOn{{Arg: "MC_BASE", Image: "quay.io/minio/mc@sha256:" + strings.Repeat("c", 64)}},
|
||||
},
|
||||
}
|
||||
var asked []string
|
||||
args, err := standingOn(context.Background(), manifest, nil, func(_ context.Context, from, repository string) (string, error) {
|
||||
asked = append(asked, from+" -> "+repository)
|
||||
return "127.0.0.1:5000/" + repository + "@sha256:" + strings.Repeat("d", 64), nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(asked) != 1 || asked[0] != "quay.io/minio/mc@sha256:"+strings.Repeat("c", 64)+" -> minio/on-mc_base" {
|
||||
t.Fatalf("the image was not copied under the module's repository: %v", asked)
|
||||
}
|
||||
if strings.Join(args, " ") != "--build-arg MC_BASE=127.0.0.1:5000/minio/on-mc_base@sha256:"+strings.Repeat("d", 64) {
|
||||
t.Fatalf("the recipe was not handed the copy: %v", args)
|
||||
}
|
||||
// Unpinned, it is refused: a tag is what somebody else can move.
|
||||
manifest.Build.On[0].Image = "quay.io/minio/mc:latest"
|
||||
if _, err := standingOn(context.Background(), manifest, nil, noMirror); err == nil || !strings.Contains(err.Error(), "not pinned") {
|
||||
t.Fatalf("an unpinned vendor image was accepted: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// A recipe reaching for an image the manifest did not declare is named, and its own stages,
|
||||
// declared arguments and scratch are not.
|
||||
func TestARecipeFetchingWhatTheManifestDidNotDeclareIsNamed(t *testing.T) {
|
||||
recipe := `
|
||||
ARG RUNTIME_BASE
|
||||
ARG MC_BASE
|
||||
FROM ${RUNTIME_BASE} AS build
|
||||
COPY --from=${MC_BASE} /usr/bin/mc /usr/local/bin/mc
|
||||
COPY --from=build /out /out
|
||||
COPY --from=0 /x /x
|
||||
FROM scratch
|
||||
COPY --from=vendor/tool:latest /tool /tool
|
||||
FROM golang:1.25-alpine AS go
|
||||
`
|
||||
got := undeclaredFetches(recipe, map[string]bool{"RUNTIME_BASE": true})
|
||||
want := []string{"${MC_BASE} (a build argument the manifest does not declare)", "vendor/tool:latest", "golang:1.25-alpine"}
|
||||
if strings.Join(got, "|") != strings.Join(want, "|") {
|
||||
t.Fatalf("got %v, want %v", got, want)
|
||||
}
|
||||
if got := undeclaredFetches(recipe, map[string]bool{"RUNTIME_BASE": true, "MC_BASE": true}); len(got) != 2 {
|
||||
t.Fatalf("declared arguments are not fetches: %v", got)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user