@@ -259,9 +259,10 @@ type RemoteContext struct {
259259 // If no resolver is provided, defaults to Docker registry resolver.
260260 Resolver remotes.Resolver
261261
262- // Platforms defines which platforms to handle when doing the image operation.
263- // If this field is empty, content for all platforms will be pulled.
264- Platforms []string
262+ // PlatformMatcher is used to match the platforms for an image
263+ // operation and define the preference when a single match is required
264+ // from multiple platforms.
265+ PlatformMatcher platforms.MatchComparer
265266
266267 // Unpack is done after an image is pulled to extract into a snapshotter.
267268 // If an image is not unpacked on pull, it can be unpacked any time
@@ -283,6 +284,12 @@ type RemoteContext struct {
283284 // manifests. If this option is false then any image which resolves
284285 // to schema 1 will return an error since schema 1 is not supported.
285286 ConvertSchema1 bool
287+
288+ // Platforms defines which platforms to handle when doing the image operation.
289+ // Platforms is ignored when a PlatformMatcher is set, otherwise the
290+ // platforms will be used to create a PlatformMatcher with no ordering
291+ // preference.
292+ Platforms []string
286293}
287294
288295func defaultRemoteContext () * RemoteContext {
@@ -308,6 +315,23 @@ func (c *Client) Fetch(ctx context.Context, ref string, opts ...RemoteOpt) (imag
308315 return images.Image {}, errors .New ("unpack on fetch not supported, try pull" )
309316 }
310317
318+ if fetchCtx .PlatformMatcher == nil {
319+ if len (fetchCtx .Platforms ) == 0 {
320+ fetchCtx .PlatformMatcher = platforms .All
321+ } else {
322+ var ps []ocispec.Platform
323+ for _ , s := range fetchCtx .Platforms {
324+ p , err := platforms .Parse (s )
325+ if err != nil {
326+ return images.Image {}, errors .Wrapf (err , "invalid platform %s" , s )
327+ }
328+ ps = append (ps , p )
329+ }
330+
331+ fetchCtx .PlatformMatcher = platforms .Any (ps ... )
332+ }
333+ }
334+
311335 ctx , done , err := c .WithLease (ctx )
312336 if err != nil {
313337 return images.Image {}, err
@@ -327,10 +351,19 @@ func (c *Client) Pull(ctx context.Context, ref string, opts ...RemoteOpt) (Image
327351 }
328352 }
329353
330- if len (pullCtx .Platforms ) > 1 {
331- return nil , errors .New ("cannot pull multiplatform image locally, try Fetch" )
332- } else if len (pullCtx .Platforms ) == 0 {
333- pullCtx .Platforms = []string {platforms .Default ()}
354+ if pullCtx .PlatformMatcher == nil {
355+ if len (pullCtx .Platforms ) > 1 {
356+ return nil , errors .New ("cannot pull multiplatform image locally, try Fetch" )
357+ } else if len (pullCtx .Platforms ) == 0 {
358+ pullCtx .PlatformMatcher = platforms .Default ()
359+ } else {
360+ p , err := platforms .Parse (pullCtx .Platforms [0 ])
361+ if err != nil {
362+ return nil , errors .Wrapf (err , "invalid platform %s" , pullCtx .Platforms [0 ])
363+ }
364+
365+ pullCtx .PlatformMatcher = platforms .Only (p )
366+ }
334367 }
335368
336369 ctx , done , err := c .WithLease (ctx )
@@ -344,7 +377,7 @@ func (c *Client) Pull(ctx context.Context, ref string, opts ...RemoteOpt) (Image
344377 return nil , err
345378 }
346379
347- i := NewImageWithPlatform (c , img , pullCtx .Platforms [ 0 ] )
380+ i := NewImageWithPlatform (c , img , pullCtx .PlatformMatcher )
348381
349382 if pullCtx .Unpack {
350383 if err := i .Unpack (ctx , pullCtx .Snapshotter ); err != nil {
@@ -380,7 +413,7 @@ func (c *Client) fetch(ctx context.Context, rCtx *RemoteContext, ref string) (im
380413 // Set any children labels for that content
381414 childrenHandler = images .SetChildrenLabels (store , childrenHandler )
382415 // Filter children by platforms
383- childrenHandler = images .FilterPlatforms (childrenHandler , rCtx .Platforms ... )
416+ childrenHandler = images .FilterPlatforms (childrenHandler , rCtx .PlatformMatcher )
384417
385418 handler = images .Handlers (append (rCtx .BaseHandlers ,
386419 remotes .FetchHandler (store , fetcher ),
@@ -437,13 +470,28 @@ func (c *Client) Push(ctx context.Context, ref string, desc ocispec.Descriptor,
437470 return err
438471 }
439472 }
473+ if pushCtx .PlatformMatcher == nil {
474+ if len (pushCtx .Platforms ) > 0 {
475+ var ps []ocispec.Platform
476+ for _ , platform := range pushCtx .Platforms {
477+ p , err := platforms .Parse (platform )
478+ if err != nil {
479+ return errors .Wrapf (err , "invalid platform %s" , platform )
480+ }
481+ ps = append (ps , p )
482+ }
483+ pushCtx .PlatformMatcher = platforms .Any (ps ... )
484+ } else {
485+ pushCtx .PlatformMatcher = platforms .All
486+ }
487+ }
440488
441489 pusher , err := pushCtx .Resolver .Pusher (ctx , ref )
442490 if err != nil {
443491 return err
444492 }
445493
446- return remotes .PushContent (ctx , pusher , desc , c .ContentStore (), pushCtx .Platforms , pushCtx .BaseHandlers ... )
494+ return remotes .PushContent (ctx , pusher , desc , c .ContentStore (), pushCtx .PlatformMatcher , pushCtx .BaseHandlers ... )
447495}
448496
449497// GetImage returns an existing image
0 commit comments