diff --git a/imagebuildah/executor.go b/imagebuildah/executor.go index 1c7f7fd56..7c6520ba3 100644 --- a/imagebuildah/executor.go +++ b/imagebuildah/executor.go @@ -372,9 +372,12 @@ func newExecutor(logger *logrus.Logger, logPrefix string, store storage.Store, o // startStage creates a new stage executor that will be referenced whenever a // COPY or ADD statement uses a --from=NAME flag. func (b *Executor) startStage(ctx context.Context, stage *imagebuilder.Stage, stages imagebuilder.Stages, output string) *StageExecutor { + // create a copy of systemContext for each stage executor. + systemContext := *b.systemContext stageExec := &StageExecutor{ ctx: ctx, executor: b, + systemContext: &systemContext, log: b.log, index: stage.Position, stages: stages, diff --git a/imagebuildah/stage_executor.go b/imagebuildah/stage_executor.go index d738d172b..02901f565 100644 --- a/imagebuildah/stage_executor.go +++ b/imagebuildah/stage_executor.go @@ -59,6 +59,7 @@ import ( // name to the image that it produces. type StageExecutor struct { ctx context.Context + systemContext *types.SystemContext executor *Executor log func(format string, args ...interface{}) index int @@ -584,8 +585,8 @@ func (s *StageExecutor) performCopy(excludes []string, copies ...imagebuilder.Co // The values for these next two fields are ultimately // based on command line flags with names that sound // much more generic. - CertPath: s.executor.systemContext.DockerCertPath, - InsecureSkipTLSVerify: s.executor.systemContext.DockerInsecureSkipTLSVerify, + CertPath: s.systemContext.DockerCertPath, + InsecureSkipTLSVerify: s.systemContext.DockerInsecureSkipTLSVerify, MaxRetries: s.executor.maxPullPushRetries, RetryDelay: s.executor.retryPullPushDelay, Parents: copy.Parents, @@ -841,7 +842,7 @@ func (s *StageExecutor) Run(run imagebuilder.Run, config docker.Config) error { Stderr: s.executor.err, Stdin: stdin, Stdout: s.executor.out, - SystemContext: s.executor.systemContext, + SystemContext: s.systemContext, Terminal: buildah.WithoutTerminal, User: config.User, WorkingDir: config.WorkingDir, @@ -966,19 +967,20 @@ func (s *StageExecutor) prepare(ctx context.Context, from string, initializeIBCo } } - builderSystemContext := s.executor.systemContext - // get platform string from stage - if stage.Builder.Platform != "" { - os, arch, variant, err := parse.Platform(stage.Builder.Platform) + // 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) if err != nil { return nil, fmt.Errorf("unable to parse platform %q: %w", stage.Builder.Platform, err) } - if arch != "" || variant != "" { - builderSystemContext.ArchitectureChoice = arch - builderSystemContext.VariantChoice = variant + if imageArch != "" || imageVariant != "" { + s.systemContext.ArchitectureChoice = imageArch + s.systemContext.VariantChoice = imageVariant } - if os != "" { - builderSystemContext.OSChoice = os + if imageOS != "" { + s.systemContext.OSChoice = imageOS } } @@ -992,7 +994,7 @@ func (s *StageExecutor) prepare(ctx context.Context, from string, initializeIBCo BlobDirectory: s.executor.blobDirectory, SignaturePolicyPath: s.executor.signaturePolicyPath, ReportWriter: s.executor.reportWriter, - SystemContext: builderSystemContext, + SystemContext: s.systemContext, Isolation: s.executor.isolation, NamespaceOptions: s.executor.namespaceOptions, ConfigureNetwork: s.executor.configureNetwork, @@ -2058,7 +2060,7 @@ func (s *StageExecutor) tagExistingImage(ctx context.Context, cacheID, output st return "", nil, err } - policyContext, err := util.GetPolicyContext(s.executor.systemContext) + policyContext, err := util.GetPolicyContext(s.systemContext) if err != nil { return "", nil, err } @@ -2171,7 +2173,7 @@ func (s *StageExecutor) pushCache(ctx context.Context, src, cacheKey string) err Compression: s.executor.compression, SignaturePolicyPath: s.executor.signaturePolicyPath, Store: s.executor.store, - SystemContext: s.executor.systemContext, + SystemContext: s.systemContext, BlobDirectory: s.executor.blobDirectory, SignBy: s.executor.signBy, MaxRetries: s.executor.maxPullPushRetries, @@ -2209,7 +2211,7 @@ func (s *StageExecutor) pullCache(ctx context.Context, cacheKey string) (referen options := buildah.PullOptions{ SignaturePolicyPath: s.executor.signaturePolicyPath, Store: s.executor.store, - SystemContext: s.executor.systemContext, + SystemContext: s.systemContext, BlobDirectory: s.executor.blobDirectory, MaxRetries: s.executor.maxPullPushRetries, RetryDelay: s.executor.retryPullPushDelay, @@ -2431,7 +2433,7 @@ func (s *StageExecutor) commit(ctx context.Context, createdBy string, emptyLayer SignaturePolicyPath: s.executor.signaturePolicyPath, ReportWriter: writer, PreferredManifestType: s.executor.outputFormat, - SystemContext: s.executor.systemContext, + SystemContext: s.systemContext, Squash: squash, OmitHistory: s.executor.commonBuildOptions.OmitHistory, EmptyLayer: emptyLayer, diff --git a/tests/bud.bats b/tests/bud.bats index acc942ada..5e54a10a0 100644 --- a/tests/bud.bats +++ b/tests/bud.bats @@ -6573,6 +6573,18 @@ _EOF done } +@test "build must reset platform for stages if needed" { + run_buildah info --format '{{.host.arch}}' + myarch="$output" + otherarch="arm64" + # just make sure that other arch is not equivalent to host arch + if [[ "$otherarch" == "$myarch" ]]; then + otherarch="amd64" + fi + run_buildah build $WITH_POLICY_JSON --build-arg FOREIGNARCH=$otherarch -f $BUDFILES/multiarch/Containerfile.reset-platform $BUDFILES/multiarch + run_buildah build $WITH_POLICY_JSON --build-arg TARGETPLATFORM=linux/$myarch --build-arg FOREIGNARCH=$otherarch -f $BUDFILES/multiarch/Containerfile.reset-platform $BUDFILES/multiarch +} + # * Performs multi-stage build with label1=value1 and verifies # * Relabels build with label1=value2 and verifies # * Rebuild with label1=value1 and makes sure everything is used from cache diff --git a/tests/bud/multiarch/Containerfile.reset-platform b/tests/bud/multiarch/Containerfile.reset-platform new file mode 100644 index 000000000..3bc2509af --- /dev/null +++ b/tests/bud/multiarch/Containerfile.reset-platform @@ -0,0 +1,7 @@ +ARG FOREIGNARCH +FROM --platform=linux/$FOREIGNARCH busybox AS foreign +FROM busybox +COPY --from=foreign /bin/busybox /bin/busybox.foreign +RUN arch +RUN ls -l /bin/busybox /bin/busybox.foreign +RUN ! cmp /bin/busybox /bin/busybox.foreign