Skip to content

Commit d9cbbec

Browse files
committed
fix(build): cross-compile image builders on the build platform
The remaining Go builder stages, plus the kube-ovn, console and piraeus-server builders, ran on the target platform. A multi-platform build therefore ran the whole compiler under QEMU: slow, and for Go unreliable, as the migration-controller Dockerfile already records a "found bad pointer in Go heap" crash from exactly that. The builders now run on $BUILDPLATFORM and produce the target architecture from TARGETARCH. Two builders only produced the right architecture because they were emulated. token-proxy ran `go build` with no GOOS/GOARCH, and kube-ovn calls upstream `make build-go`, which hardcodes GOARCH=amd64 even on an arm64 build. On a native builder both would have shipped host binaries inside the other architecture's image, and nothing fails until a node of that architecture starts the container. token-proxy now passes the target explicitly; kube-ovn picks build-go or its arm64 twin build-go-arm from TARGETARCH. The console builder emits static JS and linstor-server's debian/control declares every package Architecture: all, so both run natively without a target. A bats test requires every stage that compiles to sit on a BUILDPLATFORM FROM and to read TARGETARCH or TARGETPLATFORM on a RUN or ENV line. A bare ARG declaration does not count, which is what let token-proxy through. Assisted-by: LLM Signed-off-by: Aleksei Sviridkin <[email protected]>
1 parent 623113f commit d9cbbec

26 files changed

Lines changed: 122 additions & 27 deletions

File tree

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#!/usr/bin/env bats
2+
# A builder stage that compiles something for the image must run on the build
3+
# platform. `FROM --platform=$BUILDPLATFORM` keeps the toolchain native under a
4+
# multi-platform build; without it the compiler itself runs under QEMU, which
5+
# is slow and, for Go, unreliable (the migration-controller Dockerfile records
6+
# a "found bad pointer in Go heap" crash from exactly that). A native builder
7+
# must then be told which architecture to produce: a `go build` with no GOARCH
8+
# silently emits a binary for the build host, and nothing fails until a node
9+
# of the other architecture pulls it.
10+
#
11+
# A stage compiles when one of its RUN commands is go build/test/install, make,
12+
# gradle, dpkg-buildpackage, a node package manager or a *build.sh script, at
13+
# command position (after RUN and its --flag options, `&&`, `;` or `||`, past
14+
# any VAR=value prefixes). Such a stage must sit on a BUILDPLATFORM FROM and
15+
# use TARGETARCH or TARGETPLATFORM on a RUN or ENV line, unless its output
16+
# carries no architecture at all (ARCH_NEUTRAL below). A bare
17+
# `ARG TARGETARCH` does not count: it declares the variable, and a `go build`
18+
# that never reads it still builds for the host. The check is per stage, not
19+
# per command: a stage with `RUN echo $TARGETARCH` and a bare `go build` would
20+
# pass.
21+
#
22+
# Written for POSIX sh: hack/cozytest.sh sources this file into /bin/sh.
23+
#
24+
# Requires: awk, sed.
25+
26+
REPO_ROOT="$(cd "$(dirname "${BATS_TEST_FILENAME:-$0}")/.." && pwd)"
27+
28+
# Outputs with no architecture: the console builds static JS; piraeus-server
29+
# builds Java .debs that linstor-server's debian/control declares
30+
# `Architecture: all`.
31+
ARCH_NEUTRAL="packages/system/dashboard/images/console/Containerfile
32+
packages/system/linstor/images/piraeus-server/Dockerfile"
33+
34+
# One line per stage: "<compiles 0|1> <targets 0|1> <FROM line>".
35+
# Continuation lines are joined first so a command after `&& \` is seen at
36+
# command position.
37+
stages() {
38+
sed -e ':a' -e '/\\$/N; s/\\\n//; ta' "$1" | awk '
39+
function flush() { if (from != "") printf "%d %d %s\n", compiles, targets, from }
40+
/^FROM[[:space:]]/ { flush(); from = $0; compiles = 0; targets = 0; next }
41+
/^RUN[[:space:]]/ && /(^RUN([[:space:]]+--[^[:space:]]+)*|&&|;|[|][|])[[:space:]]+([A-Za-z_][A-Za-z0-9_]*=[^[:space:]]*[[:space:]]+)*(go[[:space:]]+(build|test|install)|make|gradle|[.]\/gradlew|dpkg-buildpackage|pnpm|npm|yarn|[^[:space:]]*build[.]sh)([[:space:]]|$)/ { compiles = 1 }
42+
/^(RUN|ENV)[[:space:]]/ && /TARGETARCH|TARGETPLATFORM/ { targets = 1 }
43+
END { flush() }
44+
'
45+
}
46+
47+
@test "every compiling builder stage runs on BUILDPLATFORM and targets TARGETARCH" {
48+
failed=""
49+
checked=0
50+
stagelist="$(mktemp)"
51+
# Package paths carry no whitespace (the same assumption hack/build-matrix.sh
52+
# makes), so word-splitting find's output is safe.
53+
for f in $(find "$REPO_ROOT"/packages -path '*/charts' -prune -o \( -name Dockerfile -o -name Containerfile \) -type f -print | sort); do
54+
rel="${f#"$REPO_ROOT"/}"
55+
neutral=0
56+
case "$ARCH_NEUTRAL" in *"$rel"*) neutral=1 ;; esac
57+
stages "$f" >"$stagelist"
58+
while read -r compiles targets from; do
59+
[ "$compiles" = 1 ] || continue
60+
checked=$((checked + 1))
61+
case "$from" in
62+
*'--platform=$BUILDPLATFORM'*|*'--platform=${BUILDPLATFORM}'*) ;;
63+
*) failed="$failed
64+
$rel: $from (not on BUILDPLATFORM)"; continue ;;
65+
esac
66+
if [ "$neutral" = 0 ] && [ "$targets" = 0 ]; then
67+
failed="$failed
68+
$rel: $from (compiles without TARGETARCH)"
69+
fi
70+
done <"$stagelist"
71+
done
72+
rm -f "$stagelist"
73+
74+
[ "$checked" -gt 0 ] # the find must reach compiling stages at all
75+
if [ -n "$failed" ]; then
76+
echo "builder stages that would compile under emulation or for the wrong arch:$failed" >&2
77+
exit 1
78+
fi
79+
}
80+
81+
@test "a compile after RUN options still counts as a compile" {
82+
fixture="$(mktemp)"
83+
printf '%s\n' 'FROM golang AS builder' 'RUN --mount=type=cache,target=/root/.cache go build .' >"$fixture"
84+
out="$(stages "$fixture")"
85+
rm -f "$fixture"
86+
[ "$out" = "1 0 FROM golang AS builder" ]
87+
}

‎packages/apps/kubernetes/images/cluster-autoscaler/Dockerfile‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
ARG builder_image=docker.io/library/golang:1.27@sha256:3680233e3204827fbdc66088528ae6d4b3d034f51d03a99d454f6de034888244
33
ARG BASEIMAGE=gcr.io/distroless/static:nonroot-${TARGETARCH}
44

5-
FROM ${builder_image} AS builder
5+
FROM --platform=$BUILDPLATFORM ${builder_image} AS builder
66

77
ARG TARGETOS
88
ARG TARGETARCH

‎packages/apps/kubernetes/images/kubevirt-cloud-provider/Dockerfile‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Source: https://github.com/kubevirt/cloud-provider-kubevirt/blob/main/build/images/kubevirt-cloud-controller-manager/Dockerfile
2-
FROM golang:1.27@sha256:3680233e3204827fbdc66088528ae6d4b3d034f51d03a99d454f6de034888244 AS builder
2+
FROM --platform=$BUILDPLATFORM golang:1.27@sha256:3680233e3204827fbdc66088528ae6d4b3d034f51d03a99d454f6de034888244 AS builder
33

44
ARG TARGETOS
55
ARG TARGETARCH

‎packages/apps/kubernetes/images/kubevirt-csi-driver/Dockerfile‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
ARG builder_image=docker.io/library/golang:1.27@sha256:3680233e3204827fbdc66088528ae6d4b3d034f51d03a99d454f6de034888244
2-
FROM ${builder_image} AS builder
2+
FROM --platform=$BUILDPLATFORM ${builder_image} AS builder
33

44
ARG TARGETOS
55
ARG TARGETARCH

‎packages/apps/kubernetes/images/talos-csr-signer/Dockerfile‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
# To bump the upstream version, update COMMIT below to the desired master
1111
# SHA and rerun `make image-talos-csr-signer` in packages/apps/kubernetes;
1212
# the regenerated images/talos-csr-signer.tag must be committed alongside.
13-
FROM golang:1.27@sha256:3680233e3204827fbdc66088528ae6d4b3d034f51d03a99d454f6de034888244 AS builder
13+
FROM --platform=$BUILDPLATFORM golang:1.27@sha256:3680233e3204827fbdc66088528ae6d4b3d034f51d03a99d454f6de034888244 AS builder
1414

1515
# Upstream is at clastix/talos-csr-signer. Pin to a master SHA so rebuilds
1616
# are reproducible; the resulting image digest is what CI fingerprints

‎packages/core/installer/images/cozystack-operator/Dockerfile‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM golang:1.27-alpine@sha256:8a5910f31396cd4d89662f56c68b3ae31d374308270a1c3bd96672ee5ed43414 AS builder
1+
FROM --platform=$BUILDPLATFORM golang:1.27-alpine@sha256:8a5910f31396cd4d89662f56c68b3ae31d374308270a1c3bd96672ee5ed43414 AS builder
22

33
ARG TARGETOS
44
ARG TARGETARCH

‎packages/system/backup-controller/images/backup-controller/Dockerfile‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM golang:1.27-alpine@sha256:8a5910f31396cd4d89662f56c68b3ae31d374308270a1c3bd96672ee5ed43414 AS builder
1+
FROM --platform=$BUILDPLATFORM golang:1.27-alpine@sha256:8a5910f31396cd4d89662f56c68b3ae31d374308270a1c3bd96672ee5ed43414 AS builder
22

33
ARG TARGETOS
44
ARG TARGETARCH

‎packages/system/backupstrategy-controller/images/backupstrategy-controller/Dockerfile‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM golang:1.27-alpine@sha256:8a5910f31396cd4d89662f56c68b3ae31d374308270a1c3bd96672ee5ed43414 AS builder
1+
FROM --platform=$BUILDPLATFORM golang:1.27-alpine@sha256:8a5910f31396cd4d89662f56c68b3ae31d374308270a1c3bd96672ee5ed43414 AS builder
22

33
ARG TARGETOS
44
ARG TARGETARCH

‎packages/system/bucket/images/s3manager/Dockerfile‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Source: https://github.com/cloudlena/s3manager/blob/main/Dockerfile
22

3-
FROM docker.io/library/golang:1.27@sha256:3680233e3204827fbdc66088528ae6d4b3d034f51d03a99d454f6de034888244 AS builder
3+
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.27@sha256:3680233e3204827fbdc66088528ae6d4b3d034f51d03a99d454f6de034888244 AS builder
44

55
ARG TARGETOS
66
ARG TARGETARCH

‎packages/system/capi-providers-cpprovider/images/cluster-api-control-plane-provider-kamaji/Dockerfile‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
# which our chart relies on for the talos-trustd sidecar port. Once
1111
# upstream ships a real migration path, drop both this Dockerfile and the
1212
# patch and consume vanilla upstream images directly.
13-
FROM golang:1.27@sha256:3680233e3204827fbdc66088528ae6d4b3d034f51d03a99d454f6de034888244 AS builder
13+
FROM --platform=$BUILDPLATFORM golang:1.27@sha256:3680233e3204827fbdc66088528ae6d4b3d034f51d03a99d454f6de034888244 AS builder
1414

1515
# Upstream release tag. Bump together with patches/ when refreshing.
1616
ARG VERSION=v0.19.0

0 commit comments

Comments
 (0)