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..f90377c0b9 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" @@ -87,17 +89,43 @@ func (c *copier) copyMultipleImages(ctx context.Context, policyContext *signatur } // Copy each image, or just the ones we want to copy, in turn. + optionsInstances := options.Instances 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 optionsInstances { + 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. + optionsInstances = nil + for instanceDigest := range instanceDigestMap { + optionsInstances = append(optionsInstances, instanceDigest) + } + } imagesToCopy := len(instanceDigests) if options.ImageListSelection == CopySpecificImages { - imagesToCopy = len(options.Instances) + imagesToCopy = len(optionsInstances) } c.Printf("Copying %d of %d images in list\n", imagesToCopy, len(instanceDigests)) updates := make([]manifest.ListUpdate, len(instanceDigests)) instancesCopied := 0 for i, instanceDigest := range instanceDigests { if options.ImageListSelection == CopySpecificImages && - !slices.Contains(options.Instances, instanceDigest) { + !slices.Contains(optionsInstances, instanceDigest) { update, err := updatedList.Instance(instanceDigest) if err != nil { return nil, err