@@ -47,6 +47,7 @@ import (
4747 leasesproxy "github.com/containerd/containerd/leases/proxy"
4848 "github.com/containerd/containerd/namespaces"
4949 "github.com/containerd/containerd/pkg/dialer"
50+ "github.com/containerd/containerd/platforms"
5051 "github.com/containerd/containerd/plugin"
5152 "github.com/containerd/containerd/remotes"
5253 "github.com/containerd/containerd/remotes/docker"
@@ -290,98 +291,137 @@ func defaultRemoteContext() *RemoteContext {
290291 }
291292}
292293
294+ // Fetch downloads the provided content into containerd's content store
295+ // and returns a non-platform specific image reference
296+ func (c * Client ) Fetch (ctx context.Context , ref string , opts ... RemoteOpt ) (images.Image , error ) {
297+ fetchCtx := defaultRemoteContext ()
298+ for _ , o := range opts {
299+ if err := o (c , fetchCtx ); err != nil {
300+ return images.Image {}, err
301+ }
302+ }
303+
304+ if fetchCtx .Unpack {
305+ return images.Image {}, errors .New ("unpack on fetch not supported, try pull" )
306+ }
307+
308+ ctx , done , err := c .WithLease (ctx )
309+ if err != nil {
310+ return images.Image {}, err
311+ }
312+ defer done (ctx )
313+
314+ return c .fetch (ctx , fetchCtx , ref )
315+ }
316+
293317// Pull downloads the provided content into containerd's content store
318+ // and returns a platform specific image object
294319func (c * Client ) Pull (ctx context.Context , ref string , opts ... RemoteOpt ) (Image , error ) {
295320 pullCtx := defaultRemoteContext ()
296321 for _ , o := range opts {
297322 if err := o (c , pullCtx ); err != nil {
298323 return nil , err
299324 }
300325 }
301- store := c .ContentStore ()
326+
327+ if len (pullCtx .Platforms ) > 1 {
328+ return nil , errors .New ("cannot pull multiplatform image locally, try Fetch" )
329+ } else if len (pullCtx .Platforms ) == 0 {
330+ pullCtx .Platforms = []string {platforms .Default ()}
331+ }
302332
303333 ctx , done , err := c .WithLease (ctx )
304334 if err != nil {
305335 return nil , err
306336 }
307337 defer done (ctx )
308338
309- name , desc , err := pullCtx . Resolver . Resolve (ctx , ref )
339+ img , err := c . fetch (ctx , pullCtx , ref )
310340 if err != nil {
311- return nil , errors . Wrapf ( err , "failed to resolve reference %q" , ref )
341+ return nil , err
312342 }
313343
314- fetcher , err := pullCtx .Resolver .Fetcher (ctx , name )
344+ i := NewImageWithPlatform (c , img , pullCtx .Platforms [0 ])
345+
346+ if pullCtx .Unpack {
347+ if err := i .Unpack (ctx , pullCtx .Snapshotter ); err != nil {
348+ return nil , errors .Wrapf (err , "failed to unpack image on snapshotter %s" , pullCtx .Snapshotter )
349+ }
350+ }
351+
352+ return i , nil
353+ }
354+
355+ func (c * Client ) fetch (ctx context.Context , rCtx * RemoteContext , ref string ) (images.Image , error ) {
356+ store := c .ContentStore ()
357+ name , desc , err := rCtx .Resolver .Resolve (ctx , ref )
315358 if err != nil {
316- return nil , errors .Wrapf (err , "failed to get fetcher for %q" , name )
359+ return images.Image {}, errors .Wrapf (err , "failed to resolve reference %q" , ref )
360+ }
361+
362+ fetcher , err := rCtx .Resolver .Fetcher (ctx , name )
363+ if err != nil {
364+ return images.Image {}, errors .Wrapf (err , "failed to get fetcher for %q" , name )
317365 }
318366
319367 var (
320368 schema1Converter * schema1.Converter
321369 handler images.Handler
322370 )
323- if desc .MediaType == images .MediaTypeDockerSchema1Manifest && pullCtx .ConvertSchema1 {
371+ if desc .MediaType == images .MediaTypeDockerSchema1Manifest && rCtx .ConvertSchema1 {
324372 schema1Converter = schema1 .NewConverter (store , fetcher )
325- handler = images .Handlers (append (pullCtx .BaseHandlers , schema1Converter )... )
373+ handler = images .Handlers (append (rCtx .BaseHandlers , schema1Converter )... )
326374 } else {
327375 // Get all the children for a descriptor
328376 childrenHandler := images .ChildrenHandler (store )
329377 // Set any children labels for that content
330378 childrenHandler = images .SetChildrenLabels (store , childrenHandler )
331379 // Filter children by platforms
332- childrenHandler = images .FilterPlatforms (childrenHandler , pullCtx .Platforms ... )
380+ childrenHandler = images .FilterPlatforms (childrenHandler , rCtx .Platforms ... )
333381
334- handler = images .Handlers (append (pullCtx .BaseHandlers ,
382+ handler = images .Handlers (append (rCtx .BaseHandlers ,
335383 remotes .FetchHandler (store , fetcher ),
336384 childrenHandler ,
337385 )... )
338386 }
339387
340388 if err := images .Dispatch (ctx , handler , desc ); err != nil {
341- return nil , err
389+ return images. Image {} , err
342390 }
343391 if schema1Converter != nil {
344392 desc , err = schema1Converter .Convert (ctx )
345393 if err != nil {
346- return nil , err
394+ return images. Image {} , err
347395 }
348396 }
349397
350- img := & image {
351- client : c ,
352- i : images.Image {
353- Name : name ,
354- Target : desc ,
355- Labels : pullCtx .Labels ,
356- },
357- }
358-
359- if pullCtx .Unpack {
360- if err := img .Unpack (ctx , pullCtx .Snapshotter ); err != nil {
361- return nil , errors .Wrapf (err , "failed to unpack image on snapshotter %s" , pullCtx .Snapshotter )
362- }
398+ img := images.Image {
399+ Name : name ,
400+ Target : desc ,
401+ Labels : rCtx .Labels ,
363402 }
364403
365404 is := c .ImageService ()
366405 for {
367- if created , err := is .Create (ctx , img . i ); err != nil {
406+ if created , err := is .Create (ctx , img ); err != nil {
368407 if ! errdefs .IsAlreadyExists (err ) {
369- return nil , err
408+ return images. Image {} , err
370409 }
371410
372- updated , err := is .Update (ctx , img . i )
411+ updated , err := is .Update (ctx , img )
373412 if err != nil {
374413 // if image was removed, try create again
375414 if errdefs .IsNotFound (err ) {
376415 continue
377416 }
378- return nil , err
417+ return images. Image {} , err
379418 }
380419
381- img . i = updated
420+ img = updated
382421 } else {
383- img . i = created
422+ img = created
384423 }
424+
385425 return img , nil
386426 }
387427}
0 commit comments