@@ -45,6 +45,7 @@ import (
4545 "github.com/containerd/containerd/images"
4646 "github.com/containerd/containerd/namespaces"
4747 "github.com/containerd/containerd/pkg/dialer"
48+ "github.com/containerd/containerd/platforms"
4849 "github.com/containerd/containerd/plugin"
4950 "github.com/containerd/containerd/remotes"
5051 "github.com/containerd/containerd/remotes/docker"
@@ -284,98 +285,137 @@ func defaultRemoteContext() *RemoteContext {
284285 }
285286}
286287
288+ // Fetch downloads the provided content into containerd's content store
289+ // and returns a non-platform specific image reference
290+ func (c * Client ) Fetch (ctx context.Context , ref string , opts ... RemoteOpt ) (images.Image , error ) {
291+ fetchCtx := defaultRemoteContext ()
292+ for _ , o := range opts {
293+ if err := o (c , fetchCtx ); err != nil {
294+ return images.Image {}, err
295+ }
296+ }
297+
298+ if fetchCtx .Unpack {
299+ return images.Image {}, errors .New ("unpack on fetch not supported, try pull" )
300+ }
301+
302+ ctx , done , err := c .WithLease (ctx )
303+ if err != nil {
304+ return images.Image {}, err
305+ }
306+ defer done (ctx )
307+
308+ return c .fetch (ctx , fetchCtx , ref )
309+ }
310+
287311// Pull downloads the provided content into containerd's content store
312+ // and returns a platform specific image object
288313func (c * Client ) Pull (ctx context.Context , ref string , opts ... RemoteOpt ) (Image , error ) {
289314 pullCtx := defaultRemoteContext ()
290315 for _ , o := range opts {
291316 if err := o (c , pullCtx ); err != nil {
292317 return nil , err
293318 }
294319 }
295- store := c .ContentStore ()
320+
321+ if len (pullCtx .Platforms ) > 1 {
322+ return nil , errors .New ("cannot pull multiplatform image locally, try Fetch" )
323+ } else if len (pullCtx .Platforms ) == 0 {
324+ pullCtx .Platforms = []string {platforms .Default ()}
325+ }
296326
297327 ctx , done , err := c .WithLease (ctx )
298328 if err != nil {
299329 return nil , err
300330 }
301331 defer done (ctx )
302332
303- name , desc , err := pullCtx . Resolver . Resolve (ctx , ref )
333+ img , err := c . fetch (ctx , pullCtx , ref )
304334 if err != nil {
305- return nil , errors . Wrapf ( err , "failed to resolve reference %q" , ref )
335+ return nil , err
306336 }
307337
308- fetcher , err := pullCtx .Resolver .Fetcher (ctx , name )
338+ i := NewImageWithPlatform (c , img , pullCtx .Platforms [0 ])
339+
340+ if pullCtx .Unpack {
341+ if err := i .Unpack (ctx , pullCtx .Snapshotter ); err != nil {
342+ return nil , errors .Wrapf (err , "failed to unpack image on snapshotter %s" , pullCtx .Snapshotter )
343+ }
344+ }
345+
346+ return i , nil
347+ }
348+
349+ func (c * Client ) fetch (ctx context.Context , rCtx * RemoteContext , ref string ) (images.Image , error ) {
350+ store := c .ContentStore ()
351+ name , desc , err := rCtx .Resolver .Resolve (ctx , ref )
309352 if err != nil {
310- return nil , errors .Wrapf (err , "failed to get fetcher for %q" , name )
353+ return images.Image {}, errors .Wrapf (err , "failed to resolve reference %q" , ref )
354+ }
355+
356+ fetcher , err := rCtx .Resolver .Fetcher (ctx , name )
357+ if err != nil {
358+ return images.Image {}, errors .Wrapf (err , "failed to get fetcher for %q" , name )
311359 }
312360
313361 var (
314362 schema1Converter * schema1.Converter
315363 handler images.Handler
316364 )
317- if desc .MediaType == images .MediaTypeDockerSchema1Manifest && pullCtx .ConvertSchema1 {
365+ if desc .MediaType == images .MediaTypeDockerSchema1Manifest && rCtx .ConvertSchema1 {
318366 schema1Converter = schema1 .NewConverter (store , fetcher )
319- handler = images .Handlers (append (pullCtx .BaseHandlers , schema1Converter )... )
367+ handler = images .Handlers (append (rCtx .BaseHandlers , schema1Converter )... )
320368 } else {
321369 // Get all the children for a descriptor
322370 childrenHandler := images .ChildrenHandler (store )
323371 // Set any children labels for that content
324372 childrenHandler = images .SetChildrenLabels (store , childrenHandler )
325373 // Filter children by platforms
326- childrenHandler = images .FilterPlatforms (childrenHandler , pullCtx .Platforms ... )
374+ childrenHandler = images .FilterPlatforms (childrenHandler , rCtx .Platforms ... )
327375
328- handler = images .Handlers (append (pullCtx .BaseHandlers ,
376+ handler = images .Handlers (append (rCtx .BaseHandlers ,
329377 remotes .FetchHandler (store , fetcher ),
330378 childrenHandler ,
331379 )... )
332380 }
333381
334382 if err := images .Dispatch (ctx , handler , desc ); err != nil {
335- return nil , err
383+ return images. Image {} , err
336384 }
337385 if schema1Converter != nil {
338386 desc , err = schema1Converter .Convert (ctx )
339387 if err != nil {
340- return nil , err
388+ return images. Image {} , err
341389 }
342390 }
343391
344- img := & image {
345- client : c ,
346- i : images.Image {
347- Name : name ,
348- Target : desc ,
349- Labels : pullCtx .Labels ,
350- },
351- }
352-
353- if pullCtx .Unpack {
354- if err := img .Unpack (ctx , pullCtx .Snapshotter ); err != nil {
355- return nil , errors .Wrapf (err , "failed to unpack image on snapshotter %s" , pullCtx .Snapshotter )
356- }
392+ img := images.Image {
393+ Name : name ,
394+ Target : desc ,
395+ Labels : rCtx .Labels ,
357396 }
358397
359398 is := c .ImageService ()
360399 for {
361- if created , err := is .Create (ctx , img . i ); err != nil {
400+ if created , err := is .Create (ctx , img ); err != nil {
362401 if ! errdefs .IsAlreadyExists (err ) {
363- return nil , err
402+ return images. Image {} , err
364403 }
365404
366- updated , err := is .Update (ctx , img . i )
405+ updated , err := is .Update (ctx , img )
367406 if err != nil {
368407 // if image was removed, try create again
369408 if errdefs .IsNotFound (err ) {
370409 continue
371410 }
372- return nil , err
411+ return images. Image {} , err
373412 }
374413
375- img . i = updated
414+ img = updated
376415 } else {
377- img . i = created
416+ img = created
378417 }
418+
379419 return img , nil
380420 }
381421}
0 commit comments