From d4be2a3142aa945aa9d0b2c7a2bea41305378704 Mon Sep 17 00:00:00 2001 From: Giacomo Lanciano Date: Tue, 9 Dec 2025 18:36:03 +0100 Subject: [PATCH] Add support for image platform --- extractor/docker/docker.go | 6 ++++++ extractor/image/image.go | 36 ++++++++++++++++++++++++++++++++++-- types/docker.go | 3 +++ 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/extractor/docker/docker.go b/extractor/docker/docker.go index 2f38fc45d..dcb83ee1e 100644 --- a/extractor/docker/docker.go +++ b/extractor/docker/docker.go @@ -54,6 +54,12 @@ func init() { func NewDockerExtractor(ctx context.Context, imageName string, option types.DockerOption) (Extractor, func(), error) { ref := image.Reference{Name: imageName, IsFile: false} transports := []string{"docker-daemon:", "docker://"} + if option.Platform != "" { + // When platform is specified, prioritize registry lookup so the manifest list + // can be filtered by OS/arch and fail fast if unsupported, instead of silently + // using a pre-existing local image from the daemon. + transports = []string{"docker://"} + } return newDockerExtractor(ctx, ref, transports, option) } diff --git a/extractor/image/image.go b/extractor/image/image.go index 41ff67387..52c501294 100644 --- a/extractor/image/image.go +++ b/extractor/image/image.go @@ -3,6 +3,7 @@ package image import ( "context" "io" + "strings" "github.com/containers/image/v5/image" "github.com/containers/image/v5/pkg/blobinfocache" @@ -53,6 +54,11 @@ func NewImage(ctx context.Context, image Reference, transports []string, option var domain string var auth *imageTypes.DockerAuthConfig + osChoice, archChoice, variantChoice, err := parsePlatform(option.Platform) + if err != nil { + return RealImage{}, err + } + originalName := image.Name if !image.IsFile { named, err := reference.ParseNormalizedNamed(image.Name) @@ -70,8 +76,9 @@ func NewImage(ctx context.Context, image Reference, transports []string, option } sys := &imageTypes.SystemContext{ - // TODO: make OSChoice configurable - OSChoice: "linux", + OSChoice: osChoice, + ArchitectureChoice: archChoice, + VariantChoice: variantChoice, DockerAuthConfig: auth, DockerDisableV1Ping: option.SkipPing, DockerInsecureSkipTLSVerify: imageTypes.NewOptionalBool(option.InsecureSkipTLSVerify), @@ -94,6 +101,31 @@ func NewImage(ctx context.Context, image Reference, transports []string, option }, nil } +func parsePlatform(p string) (osChoice, archChoice, variantChoice string, err error) { + osChoice = "linux" + if p == "" { + return osChoice, "", "", nil + } + parts := strings.Split(p, "/") + switch len(parts) { + case 1: + archChoice = parts[0] + case 2: + osChoice = parts[0] + archChoice = parts[1] + case 3: + osChoice = parts[0] + archChoice = parts[1] + variantChoice = parts[2] + default: + return "", "", "", xerrors.Errorf("invalid platform %q", p) + } + if osChoice == "" || archChoice == "" { + return "", "", "", xerrors.Errorf("invalid platform %q", p) + } + return osChoice, archChoice, variantChoice, nil +} + func newSource(ctx context.Context, imageName string, transports []string, sys *imageTypes.SystemContext) ( ImageSource, ImageCloser, error) { err := xerrors.New("no valid transport") diff --git a/types/docker.go b/types/docker.go index ab6a535f7..56c72084b 100644 --- a/types/docker.go +++ b/types/docker.go @@ -7,6 +7,9 @@ type DockerOption struct { UserName string Password string + // Platform (e.g. linux/amd64, linux/arm64/v8) + Platform string + // ECR AwsAccessKey string AwsSecretKey string