diff --git a/copy/copy.go b/copy/copy.go index bf8f4015b6..1223e726b6 100644 --- a/copy/copy.go +++ b/copy/copy.go @@ -23,6 +23,7 @@ import ( "github.com/containers/image/v5/types" encconfig "github.com/containers/ocicrypt/config" digest "github.com/opencontainers/go-digest" + imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/sirupsen/logrus" "golang.org/x/exp/slices" "golang.org/x/sync/semaphore" @@ -91,8 +92,9 @@ type Options struct { PreserveDigests bool // manifest MIME type of image set by user. "" is default and means use the autodetection to the manifest MIME type ForceManifestMIMEType string - ImageListSelection ImageListSelection // set to either CopySystemImage (the default), CopyAllImages, or CopySpecificImages to control which instances we copy when the source reference is a list; ignored if the source reference is not a list - Instances []digest.Digest // if ImageListSelection is CopySpecificImages, copy only these instances and the list itself + ImageListSelection ImageListSelection // set to either CopySystemImage (the default), CopyAllImages, or CopySpecificImages to control which instances we copy when the source reference is a list; ignored if the source reference is not a list + Instances []digest.Digest // if ImageListSelection is CopySpecificImages, copy only these instances, instances matching the InstancePlatforms list, and the list itself + InstancePlatforms []imgspecv1.Platform // if ImageListSelection is CopySpecificImages, copy only matching instances, instances listed in the Instances list, and the list itself // Give priority to pulling gzip images if multiple images are present when configured to OptionalBoolTrue, // prefers the best compression if this is configured as OptionalBoolFalse. Choose automatically (and the choice may change over time) // if this is set to OptionalBoolUndefined (which is the default behavior, and recommended for most callers). diff --git a/copy/multiple.go b/copy/multiple.go index 097a18855e..c6e1034a08 100644 --- a/copy/multiple.go +++ b/copy/multiple.go @@ -12,6 +12,8 @@ import ( internalManifest "github.com/containers/image/v5/internal/manifest" "github.com/containers/image/v5/manifest" "github.com/containers/image/v5/signature" + "github.com/containers/image/v5/types" + digest "github.com/opencontainers/go-digest" imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/sirupsen/logrus" "golang.org/x/exp/slices" @@ -88,6 +90,31 @@ func (c *copier) copyMultipleImages(ctx context.Context, policyContext *signatur // Copy each image, or just the ones we want to copy, in turn. instanceDigests := updatedList.Instances() + if len(options.InstancePlatforms) > 0 { + // Build a map for helping us avoid duplication of instance digests. + instanceDigestMap := make(map[digest.Digest]struct{}) + for _, instanceDigest := range instanceDigests { + instanceDigestMap[instanceDigest] = struct{}{} + } + // Choose the best match for each platform we were asked to also copy. + for _, platform := range options.InstancePlatforms { + platformContext := types.SystemContext{ + OSChoice: platform.OS, + ArchitectureChoice: platform.Architecture, + VariantChoice: platform.Variant, + } + instanceDigest, err := updatedList.ChooseInstance(&platformContext) + if err != nil { + return nil, fmt.Errorf("No instance in list matched platform spec %v", platform) + } + instanceDigestMap[instanceDigest] = struct{}{} + } + // Deduplicate the set of digests. + instanceDigests = nil + for instanceDigest := range instanceDigestMap { + instanceDigests = append(instanceDigests, instanceDigest) + } + } imagesToCopy := len(instanceDigests) if options.ImageListSelection == CopySpecificImages { imagesToCopy = len(options.Instances)