github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/daemon/containerd/image_pull.go (about) 1 package containerd 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 8 "github.com/containerd/containerd" 9 cerrdefs "github.com/containerd/containerd/errdefs" 10 "github.com/containerd/containerd/images" 11 "github.com/containerd/containerd/pkg/snapshotters" 12 "github.com/containerd/containerd/platforms" 13 "github.com/containerd/containerd/remotes/docker" 14 "github.com/containerd/log" 15 "github.com/distribution/reference" 16 "github.com/Prakhar-Agarwal-byte/moby/api/types/events" 17 "github.com/Prakhar-Agarwal-byte/moby/api/types/registry" 18 "github.com/Prakhar-Agarwal-byte/moby/distribution" 19 "github.com/Prakhar-Agarwal-byte/moby/errdefs" 20 "github.com/Prakhar-Agarwal-byte/moby/internal/compatcontext" 21 "github.com/Prakhar-Agarwal-byte/moby/pkg/progress" 22 "github.com/Prakhar-Agarwal-byte/moby/pkg/streamformatter" 23 ocispec "github.com/opencontainers/image-spec/specs-go/v1" 24 "github.com/pkg/errors" 25 ) 26 27 // PullImage initiates a pull operation. ref is the image to pull. 28 func (i *ImageService) PullImage(ctx context.Context, ref reference.Named, platform *ocispec.Platform, metaHeaders map[string][]string, authConfig *registry.AuthConfig, outStream io.Writer) error { 29 var opts []containerd.RemoteOpt 30 if platform != nil { 31 opts = append(opts, containerd.WithPlatform(platforms.Format(*platform))) 32 } 33 34 resolver, _ := i.newResolverFromAuthConfig(ctx, authConfig) 35 opts = append(opts, containerd.WithResolver(resolver)) 36 37 old, err := i.resolveDescriptor(ctx, ref.String()) 38 if err != nil && !errdefs.IsNotFound(err) { 39 return err 40 } 41 p := platforms.Default() 42 if platform != nil { 43 p = platforms.Only(*platform) 44 } 45 46 jobs := newJobs() 47 h := images.HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) { 48 if images.IsLayerType(desc.MediaType) { 49 jobs.Add(desc) 50 } 51 return nil, nil 52 }) 53 opts = append(opts, containerd.WithImageHandler(h)) 54 55 out := streamformatter.NewJSONProgressOutput(outStream, false) 56 pp := pullProgress{store: i.client.ContentStore(), showExists: true} 57 finishProgress := jobs.showProgress(ctx, out, pp) 58 59 var outNewImg *containerd.Image 60 defer func() { 61 finishProgress() 62 63 // Send final status message after the progress updater has finished. 64 // Otherwise the layer/manifest progress messages may arrive AFTER the 65 // status message have been sent, so they won't update the previous 66 // progress leaving stale progress like: 67 // 70f5ac315c5a: Downloading [> ] 0B/3.19kB 68 // Digest: sha256:4f53e2564790c8e7856ec08e384732aa38dc43c52f02952483e3f003afbf23db 69 // 70f5ac315c5a: Download complete 70 // Status: Downloaded newer image for hello-world:latest 71 // docker.io/library/hello-world:latest 72 if outNewImg != nil { 73 img := *outNewImg 74 progress.Message(out, "", "Digest: "+img.Target().Digest.String()) 75 writeStatus(out, reference.FamiliarString(ref), old.Digest != img.Target().Digest) 76 } 77 }() 78 79 var sentPullingFrom, sentSchema1Deprecation bool 80 ah := images.HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) { 81 if desc.MediaType == images.MediaTypeDockerSchema1Manifest && !sentSchema1Deprecation { 82 progress.Message(out, "", distribution.DeprecatedSchema1ImageMessage(ref)) 83 sentSchema1Deprecation = true 84 } 85 if images.IsManifestType(desc.MediaType) { 86 if !sentPullingFrom { 87 var tagOrDigest string 88 if tagged, ok := ref.(reference.Tagged); ok { 89 tagOrDigest = tagged.Tag() 90 } else { 91 tagOrDigest = ref.String() 92 } 93 progress.Message(out, tagOrDigest, "Pulling from "+reference.Path(ref)) 94 sentPullingFrom = true 95 } 96 97 available, _, _, missing, err := images.Check(ctx, i.client.ContentStore(), desc, p) 98 if err != nil { 99 return nil, err 100 } 101 // If we already have all the contents pull shouldn't show any layer 102 // download progress, not even a "Already present" message. 103 if available && len(missing) == 0 { 104 pp.hideLayers = true 105 } 106 } 107 return nil, nil 108 }) 109 opts = append(opts, containerd.WithImageHandler(ah)) 110 111 opts = append(opts, containerd.WithPullUnpack) 112 // 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 113 opts = append(opts, containerd.WithPullSnapshotter(i.snapshotter)) 114 115 // AppendInfoHandlerWrapper will annotate the image with basic information like manifest and layer digests as labels; 116 // this information is used to enable remote snapshotters like nydus and stargz to query a registry. 117 infoHandler := snapshotters.AppendInfoHandlerWrapper(ref.String()) 118 opts = append(opts, containerd.WithImageHandlerWrapper(infoHandler)) 119 120 // Allow pulling application/vnd.docker.distribution.manifest.v1+prettyjws images 121 // by converting them to OCI manifests. 122 opts = append(opts, containerd.WithSchema1Conversion) //nolint:staticcheck // Ignore SA1019: containerd.WithSchema1Conversion is deprecated: use Schema 2 or OCI images. 123 124 img, err := i.client.Pull(ctx, ref.String(), opts...) 125 if err != nil { 126 if errors.Is(err, docker.ErrInvalidAuthorization) { 127 return errdefs.NotFound(fmt.Errorf("pull access denied for %s, repository does not exist or may require 'docker login'", reference.FamiliarName(ref))) 128 } 129 return err 130 } 131 132 logger := log.G(ctx).WithFields(log.Fields{ 133 "digest": img.Target().Digest, 134 "remote": ref.String(), 135 }) 136 logger.Info("image pulled") 137 138 // The pull succeeded, so try to remove any dangling image we have for this target 139 err = i.client.ImageService().Delete(compatcontext.WithoutCancel(ctx), danglingImageName(img.Target().Digest)) 140 if err != nil && !cerrdefs.IsNotFound(err) { 141 // Image pull succeeded, but cleaning up the dangling image failed. Ignore the 142 // error to not mark the pull as failed. 143 logger.WithError(err).Warn("unexpected error while removing outdated dangling image reference") 144 } 145 146 i.LogImageEvent(reference.FamiliarString(ref), reference.FamiliarName(ref), events.ActionPull) 147 outNewImg = &img 148 return nil 149 } 150 151 // writeStatus writes a status message to out. If newerDownloaded is true, the 152 // status message indicates that a newer image was downloaded. Otherwise, it 153 // indicates that the image is up to date. requestedTag is the tag the message 154 // will refer to. 155 func writeStatus(out progress.Output, requestedTag string, newerDownloaded bool) { 156 if newerDownloaded { 157 progress.Message(out, "", "Status: Downloaded newer image for "+requestedTag) 158 } else { 159 progress.Message(out, "", "Status: Image is up to date for "+requestedTag) 160 } 161 }