github.com/rawahars/moby@v24.0.4+incompatible/daemon/containerd/image_pull.go (about) 1 package containerd 2 3 import ( 4 "context" 5 "io" 6 7 "github.com/containerd/containerd" 8 cerrdefs "github.com/containerd/containerd/errdefs" 9 "github.com/containerd/containerd/images" 10 "github.com/containerd/containerd/pkg/snapshotters" 11 "github.com/containerd/containerd/platforms" 12 "github.com/docker/distribution/reference" 13 "github.com/docker/docker/api/types/registry" 14 "github.com/docker/docker/errdefs" 15 "github.com/docker/docker/pkg/streamformatter" 16 "github.com/opencontainers/go-digest" 17 ocispec "github.com/opencontainers/image-spec/specs-go/v1" 18 "github.com/sirupsen/logrus" 19 ) 20 21 // PullImage initiates a pull operation. image is the repository name to pull, and 22 // tagOrDigest may be either empty, or indicate a specific tag or digest to pull. 23 func (i *ImageService) PullImage(ctx context.Context, image, tagOrDigest string, platform *ocispec.Platform, metaHeaders map[string][]string, authConfig *registry.AuthConfig, outStream io.Writer) error { 24 var opts []containerd.RemoteOpt 25 if platform != nil { 26 opts = append(opts, containerd.WithPlatform(platforms.Format(*platform))) 27 } 28 ref, err := reference.ParseNormalizedNamed(image) 29 if err != nil { 30 return errdefs.InvalidParameter(err) 31 } 32 33 // TODO(thaJeztah) this could use a WithTagOrDigest() utility 34 if tagOrDigest != "" { 35 // The "tag" could actually be a digest. 36 var dgst digest.Digest 37 dgst, err = digest.Parse(tagOrDigest) 38 if err == nil { 39 ref, err = reference.WithDigest(reference.TrimNamed(ref), dgst) 40 } else { 41 ref, err = reference.WithTag(ref, tagOrDigest) 42 } 43 if err != nil { 44 return errdefs.InvalidParameter(err) 45 } 46 } 47 48 resolver, _ := i.newResolverFromAuthConfig(ctx, authConfig) 49 opts = append(opts, containerd.WithResolver(resolver)) 50 51 jobs := newJobs() 52 h := images.HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) { 53 if desc.MediaType != images.MediaTypeDockerSchema1Manifest { 54 jobs.Add(desc) 55 } 56 return nil, nil 57 }) 58 opts = append(opts, containerd.WithImageHandler(h)) 59 60 out := streamformatter.NewJSONProgressOutput(outStream, false) 61 finishProgress := jobs.showProgress(ctx, out, pullProgress{Store: i.client.ContentStore(), ShowExists: true}) 62 defer finishProgress() 63 64 opts = append(opts, containerd.WithPullUnpack) 65 // TODO(thaJeztah): we may have to pass the snapshotter to use if the pull is part of a "docker run" (container create -> pull image if missing). See https://github.com/moby/moby/issues/45273 66 opts = append(opts, containerd.WithPullSnapshotter(i.snapshotter)) 67 68 // AppendInfoHandlerWrapper will annotate the image with basic information like manifest and layer digests as labels; 69 // this information is used to enable remote snapshotters like nydus and stargz to query a registry. 70 infoHandler := snapshotters.AppendInfoHandlerWrapper(ref.String()) 71 opts = append(opts, containerd.WithImageHandlerWrapper(infoHandler)) 72 73 img, err := i.client.Pull(ctx, ref.String(), opts...) 74 if err != nil { 75 return err 76 } 77 78 logger := logrus.WithFields(logrus.Fields{ 79 "digest": img.Target().Digest, 80 "remote": ref.String(), 81 }) 82 logger.Info("image pulled") 83 84 // The pull succeeded, so try to remove any dangling image we have for this target 85 err = i.client.ImageService().Delete(context.Background(), danglingImageName(img.Target().Digest)) 86 if err != nil && !cerrdefs.IsNotFound(err) { 87 // Image pull succeeded, but cleaning up the dangling image failed. Ignore the 88 // error to not mark the pull as failed. 89 logger.WithError(err).Warn("unexpected error while removing outdated dangling image reference") 90 } 91 return nil 92 }