1
0
mirror of https://github.com/containers/buildah.git synced 2026-10-23 06:48:59 +03:00

updating the incorrect resetting logic for multi-stage builds

fixes : https://github.com/podman-container-tools/podman/issues/29633
Signed-off-by: Himanshu Ravindra Iwanati <work.himanshu.r.v@gmail.com>
This commit is contained in:
Himanshu Ravindra Iwanati
2026-09-03 04:04:10 +05:30
parent e7c9bc5495
commit aedfa00ea3
2 changed files with 113 additions and 10 deletions

View File

@@ -1130,18 +1130,35 @@ func (s *stageExecutor) prepare(ctx context.Context, from string, initializeIBCo
// In a multi-stage build where `FROM --platform=<>` was used then we must
// reset context for new stages so that new stages don't inherit unexpected
// `--platform` from prior stages.
if stage.Builder.Platform != "" || (len(s.stages) > 1 && (s.systemContext.ArchitectureChoice == "" && s.systemContext.VariantChoice == "" && s.systemContext.OSChoice == "")) {
imageOS, imageArch, imageVariant, err := parse.Platform(stage.Builder.Platform)
effectivePlatform := stage.Builder.Platform
if effectivePlatform == "" &&
len(s.stages) > 1 &&
s.systemContext.ArchitectureChoice == "" &&
s.systemContext.VariantChoice == "" &&
s.systemContext.OSChoice == "" {
for _, prevStage := range s.stages[:s.index] {
if prevStage.Builder.Platform != "" {
if s.executor.systemContext.ArchitectureChoice != "" ||
s.executor.systemContext.OSChoice != "" ||
s.executor.systemContext.VariantChoice != "" {
s.systemContext.ArchitectureChoice = s.executor.systemContext.ArchitectureChoice
s.systemContext.VariantChoice = s.executor.systemContext.VariantChoice
s.systemContext.OSChoice = s.executor.systemContext.OSChoice
} else {
effectivePlatform = "local"
}
break
}
}
}
if effectivePlatform != "" {
imageOS, imageArch, imageVariant, err := parse.Platform(effectivePlatform)
if err != nil {
return nil, fmt.Errorf("unable to parse platform %q: %w", stage.Builder.Platform, err)
}
if imageArch != "" || imageVariant != "" {
s.systemContext.ArchitectureChoice = imageArch
s.systemContext.VariantChoice = imageVariant
}
if imageOS != "" {
s.systemContext.OSChoice = imageOS
return nil, fmt.Errorf("unable to parse platform %q: %w", effectivePlatform, err)
}
s.systemContext.ArchitectureChoice = imageArch
s.systemContext.VariantChoice = imageVariant
s.systemContext.OSChoice = imageOS
}
builderOptions := buildah.BuilderOptions{

View File

@@ -11116,3 +11116,89 @@ EOF
run_buildah build --pull=false --no-cache --jobs=2 --skip-unused-stages=false $WITH_POLICY_JSON $contextdir
}
@test "bud multi-stage build preserves architecture of local external images" {
# This tests that stages without a --platform flag are NOT aggressively
# reset to the host architecture. It ensures Buildah does not strict-match
# and crash when trying to resolve local images of a foreign architecture.
run_buildah info --format '{{.host.arch}}'
host_arch="$output"
# Select a foreign test architecture + variant
test_arch="arm"
test_variant="v7"
if [[ "$host_arch" == "arm" ]]; then
test_arch="arm64"
test_variant="v8"
fi
local contextdir="${TEST_SCRATCH_DIR}/multi-stage-variant"
mkdir -p "$contextdir"
# 1. Build an external local image with a foreign architecture
cat > "$contextdir/Dockerfile.base" << EOF
FROM busybox
ENV STAGE=base
EOF
run_buildah build $WITH_POLICY_JSON --platform=linux/${test_arch}/${test_variant} -t local-cross-builder -f "$contextdir/Dockerfile.base" "$contextdir"
# 2. Main Dockerfile
cat > "$contextdir/Dockerfile" << EOF
ARG BASE=local-cross-builder
# Stage 1: Exists purely to test multi-stage behavior with local
FROM alpine AS stage1
# Stage 2: Uses the local foreign image without a --platform flag.
# This should leave the context empty and successfully accept the local foreign image.
FROM \${BASE} AS final
ENV STAGE=final
EOF
# 3. Build the multi-stage image.
run_buildah build $WITH_POLICY_JSON --target final -t test-final "$contextdir"
# 4. Verify the final image successfully retained the exact foreign architecture and variant.
run_buildah inspect --format '{{.OCIv1.Architecture}}' test-final
expect_output "${test_arch}"
}
@test "bud multi-stage build does not re-pull and overwrite local image" {
_prefetch alpine
start_registry
local authfile="${TEST_SCRATCH_DIR}/test.auth"
run_buildah login --tls-verify=false --authfile "$authfile" --username testuser --password testpassword "localhost:${REGISTRY_PORT}"
local registry="localhost:${REGISTRY_PORT}"
local contextdir="${TEST_SCRATCH_DIR}/re-pull-local"
mkdir -p "$contextdir"
# Build an image without the test file and push it to the registry
cat > "$contextdir/Dockerfile.remote" << _EOF
FROM alpine
RUN echo "remote-version" > /version.txt
_EOF
run_buildah build $WITH_POLICY_JSON -t "${registry}/local-override:latest" -f "$contextdir/Dockerfile.remote" "$contextdir"
run_buildah push --authfile "$authfile" --tls-verify=false "${registry}/local-override:latest" "docker://${registry}/local-override:latest"
# Modify and rebuild the image locally with a local-only file (do not push!)
cat > "$contextdir/Dockerfile.local" << _EOF
FROM alpine
RUN echo "local-version" > /version.txt
RUN echo "local-only" > /local-file.txt
_EOF
run_buildah build $WITH_POLICY_JSON -t "${registry}/local-override:latest" -f "$contextdir/Dockerfile.local" "$contextdir"
# Multi-stage build that references the local image in stage 1
cat > "$contextdir/Dockerfile.multistage" << _EOF
FROM ${registry}/local-override:latest AS stage1
FROM alpine
COPY --from=stage1 /local-file.txt /local-file.txt
RUN cat /local-file.txt
_EOF
# Build must succeed using the local image without re-pulling from the registry
run_buildah build --authfile "$authfile" --tls-verify=false -f "$contextdir/Dockerfile.multistage" "$contextdir"
expect_output --substring "local-only"
}