The control plane's recipe declares its base, and an undeclared FROM is refused (ADR 0097 live) #39
+2
-1
@@ -1,3 +1,4 @@
|
|||||||
|
ARG GO_BASE=golang:1.25-alpine
|
||||||
# The control plane's image.
|
# The control plane's image.
|
||||||
#
|
#
|
||||||
# novox/hq ADR 0006: this image is pinned by digest in the bundle the host carries, fetched on a
|
# novox/hq ADR 0006: this image is pinned by digest in the bundle the host carries, fetched on a
|
||||||
@@ -11,7 +12,7 @@
|
|||||||
# (novox/hq ADR 0004). Adding them "just in case" would put a trust store in the one image whose
|
# (novox/hq ADR 0004). Adding them "just in case" would put a trust store in the one image whose
|
||||||
# whole argument is that it contains nothing to reason about.
|
# whole argument is that it contains nothing to reason about.
|
||||||
|
|
||||||
FROM golang:1.25-alpine AS build
|
FROM ${GO_BASE} AS build
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
|
|
||||||
# Dependencies first, so a change to the source does not refetch them.
|
# Dependencies first, so a change to the source does not refetch them.
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
|
ARG ALPINE_BASE=alpine:3
|
||||||
|
ARG GO_BASE=golang:1.25-alpine
|
||||||
# The builder, as a module ships one.
|
# The builder, as a module ships one.
|
||||||
#
|
#
|
||||||
# Not FROM scratch, unlike the control plane: this one runs git and a container client, so it
|
# Not FROM scratch, unlike the control plane: this one runs git and a container client, so it
|
||||||
# needs a filesystem with them on it. That is the honest cost of a machine whose job is to build —
|
# needs a filesystem with them on it. That is the honest cost of a machine whose job is to build —
|
||||||
# and it is why building is a MODULE on a machine that has a runtime rather than something the
|
# and it is why building is a MODULE on a machine that has a runtime rather than something the
|
||||||
# control plane does (novox/hq ADR 0005).
|
# control plane does (novox/hq ADR 0005).
|
||||||
FROM golang:1.25-alpine AS build
|
FROM ${GO_BASE} AS build
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
COPY go.mod go.sum ./
|
COPY go.mod go.sum ./
|
||||||
RUN go mod download
|
RUN go mod download
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN CGO_ENABLED=0 go build -trimpath -ldflags '-s -w' -o /mesh-builder ./cmd/mesh-builder
|
RUN CGO_ENABLED=0 go build -trimpath -ldflags '-s -w' -o /mesh-builder ./cmd/mesh-builder
|
||||||
|
|
||||||
FROM alpine:3
|
FROM ${ALPINE_BASE}
|
||||||
# git to clone what it is asked to build, and the docker client to build and push it. The daemon
|
# git to clone what it is asked to build, and the docker client to build and push it. The daemon
|
||||||
# is the machine's, reached through its socket — a build machine shares the runtime it was given
|
# is the machine's, reached through its socket — a build machine shares the runtime it was given
|
||||||
# rather than running one inside itself.
|
# rather than running one inside itself.
|
||||||
|
|||||||
@@ -397,13 +397,13 @@ func one(ctx context.Context, run Runner, publish Publisher,
|
|||||||
module, a.From, strings.Join(copies, ", "))
|
module, a.From, strings.Join(copies, ", "))
|
||||||
}
|
}
|
||||||
if len(bases) > 0 {
|
if len(bases) > 0 {
|
||||||
// Said, not yet refused: the mesh's own images start FROM a public base — the control
|
// Refused, since the mesh's own images declare theirs (ADR 0097): a base fetched on
|
||||||
// plane's, the builder's, the tool runtime's — and refusing those refuses genesis.
|
// its own is a build that works when a public registry answers, which is sometimes.
|
||||||
// They declare their bases next; until then a base fetched on its own is named here,
|
return catalogue.Built{}, fmt.Errorf(
|
||||||
// with the remedy, every build.
|
"%s: the recipe %s starts FROM %s, which the manifest does not declare. Declare "+
|
||||||
say("recipe", "UNDECLARED base(s) %s in %s — declare each under build.on as "+
|
"each under build.on as {\"arg\": \"<NAME>\", \"image\": \"<image>@sha256:…\"} "+
|
||||||
"{arg, image@sha256:…} and read it from that argument (novox/hq ADR 0097)",
|
"and start FROM ${<NAME>} (novox/hq ADR 0097)",
|
||||||
strings.Join(bases, ", "), a.From)
|
module, a.From, strings.Join(bases, ", "))
|
||||||
}
|
}
|
||||||
invocation := append([]string{"build", "-f", a.From, "-t", local}, args...)
|
invocation := append([]string{"build", "-f", a.From, "-t", local}, args...)
|
||||||
if a.Target != "" {
|
if a.Target != "" {
|
||||||
|
|||||||
@@ -124,8 +124,7 @@ FROM golang:1.25-alpine AS go
|
|||||||
if strings.Join(copies, "|") != "${MC_BASE} (a build argument the manifest does not declare)|vendor/tool:latest" {
|
if strings.Join(copies, "|") != "${MC_BASE} (a build argument the manifest does not declare)|vendor/tool:latest" {
|
||||||
t.Fatalf("copies out of undeclared images: %v", copies)
|
t.Fatalf("copies out of undeclared images: %v", copies)
|
||||||
}
|
}
|
||||||
// A base fetched on its own is named apart: the mesh's own images still start FROM one, so
|
// A base fetched on its own is named apart, and refused like a copy (ADR 0097).
|
||||||
// it is said rather than refused until they declare theirs.
|
|
||||||
if strings.Join(bases, "|") != "golang:1.25-alpine" {
|
if strings.Join(bases, "|") != "golang:1.25-alpine" {
|
||||||
t.Fatalf("undeclared bases: %v", bases)
|
t.Fatalf("undeclared bases: %v", bases)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,6 +66,12 @@
|
|||||||
"kind": "image",
|
"kind": "image",
|
||||||
"from": "Dockerfile"
|
"from": "Dockerfile"
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"on": [
|
||||||
|
{
|
||||||
|
"arg": "GO_BASE",
|
||||||
|
"image": "golang@sha256:1ae0735f00daffa3aaf1363a5184c0d2dc55c78e3db4ec70241cdac97bf84b59"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user