|
@@ -14,6 +14,7 @@ import (
|
|
"github.com/docker/docker/api/types/events"
|
|
"github.com/docker/docker/api/types/events"
|
|
"github.com/docker/docker/api/types/registry"
|
|
"github.com/docker/docker/api/types/registry"
|
|
"github.com/docker/docker/errdefs"
|
|
"github.com/docker/docker/errdefs"
|
|
|
|
+ "github.com/docker/docker/pkg/progress"
|
|
"github.com/docker/docker/pkg/streamformatter"
|
|
"github.com/docker/docker/pkg/streamformatter"
|
|
"github.com/opencontainers/go-digest"
|
|
"github.com/opencontainers/go-digest"
|
|
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
|
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
|
@@ -49,9 +50,18 @@ func (i *ImageService) PullImage(ctx context.Context, image, tagOrDigest string,
|
|
resolver, _ := i.newResolverFromAuthConfig(ctx, authConfig)
|
|
resolver, _ := i.newResolverFromAuthConfig(ctx, authConfig)
|
|
opts = append(opts, containerd.WithResolver(resolver))
|
|
opts = append(opts, containerd.WithResolver(resolver))
|
|
|
|
|
|
|
|
+ old, err := i.resolveDescriptor(ctx, ref.String())
|
|
|
|
+ if err != nil && !errdefs.IsNotFound(err) {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+ p := platforms.Default()
|
|
|
|
+ if platform != nil {
|
|
|
|
+ p = platforms.Only(*platform)
|
|
|
|
+ }
|
|
|
|
+
|
|
jobs := newJobs()
|
|
jobs := newJobs()
|
|
h := images.HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
|
|
h := images.HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
|
|
- if desc.MediaType != images.MediaTypeDockerSchema1Manifest {
|
|
|
|
|
|
+ if images.IsLayerType(desc.MediaType) {
|
|
jobs.Add(desc)
|
|
jobs.Add(desc)
|
|
}
|
|
}
|
|
return nil, nil
|
|
return nil, nil
|
|
@@ -59,9 +69,26 @@ func (i *ImageService) PullImage(ctx context.Context, image, tagOrDigest string,
|
|
opts = append(opts, containerd.WithImageHandler(h))
|
|
opts = append(opts, containerd.WithImageHandler(h))
|
|
|
|
|
|
out := streamformatter.NewJSONProgressOutput(outStream, false)
|
|
out := streamformatter.NewJSONProgressOutput(outStream, false)
|
|
- finishProgress := jobs.showProgress(ctx, out, pullProgress{Store: i.client.ContentStore(), ShowExists: true})
|
|
|
|
|
|
+ pp := pullProgress{store: i.client.ContentStore(), showExists: true}
|
|
|
|
+ finishProgress := jobs.showProgress(ctx, out, pp)
|
|
defer finishProgress()
|
|
defer finishProgress()
|
|
|
|
|
|
|
|
+ ah := images.HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
|
|
|
|
+ if images.IsManifestType(desc.MediaType) {
|
|
|
|
+ available, _, _, missing, err := images.Check(ctx, i.client.ContentStore(), desc, p)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+ // If we already have all the contents pull shouldn't show any layer
|
|
|
|
+ // download progress, not even a "Already present" message.
|
|
|
|
+ if available && len(missing) == 0 {
|
|
|
|
+ pp.hideLayers = true
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return nil, nil
|
|
|
|
+ })
|
|
|
|
+ opts = append(opts, containerd.WithImageHandler(ah))
|
|
|
|
+
|
|
opts = append(opts, containerd.WithPullUnpack)
|
|
opts = append(opts, containerd.WithPullUnpack)
|
|
// 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
|
|
// 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
|
|
opts = append(opts, containerd.WithPullSnapshotter(i.snapshotter))
|
|
opts = append(opts, containerd.WithPullSnapshotter(i.snapshotter))
|
|
@@ -71,6 +98,8 @@ func (i *ImageService) PullImage(ctx context.Context, image, tagOrDigest string,
|
|
infoHandler := snapshotters.AppendInfoHandlerWrapper(ref.String())
|
|
infoHandler := snapshotters.AppendInfoHandlerWrapper(ref.String())
|
|
opts = append(opts, containerd.WithImageHandlerWrapper(infoHandler))
|
|
opts = append(opts, containerd.WithImageHandlerWrapper(infoHandler))
|
|
|
|
|
|
|
|
+ progress.Message(out, tagOrDigest, "Pulling from "+reference.Path(ref))
|
|
|
|
+
|
|
img, err := i.client.Pull(ctx, ref.String(), opts...)
|
|
img, err := i.client.Pull(ctx, ref.String(), opts...)
|
|
if err != nil {
|
|
if err != nil {
|
|
return err
|
|
return err
|
|
@@ -81,6 +110,9 @@ func (i *ImageService) PullImage(ctx context.Context, image, tagOrDigest string,
|
|
"remote": ref.String(),
|
|
"remote": ref.String(),
|
|
})
|
|
})
|
|
logger.Info("image pulled")
|
|
logger.Info("image pulled")
|
|
|
|
+ progress.Message(out, "", "Digest: "+img.Target().Digest.String())
|
|
|
|
+
|
|
|
|
+ writeStatus(out, reference.FamiliarString(ref), old.Digest != img.Target().Digest)
|
|
|
|
|
|
// The pull succeeded, so try to remove any dangling image we have for this target
|
|
// The pull succeeded, so try to remove any dangling image we have for this target
|
|
err = i.client.ImageService().Delete(context.Background(), danglingImageName(img.Target().Digest))
|
|
err = i.client.ImageService().Delete(context.Background(), danglingImageName(img.Target().Digest))
|
|
@@ -94,3 +126,15 @@ func (i *ImageService) PullImage(ctx context.Context, image, tagOrDigest string,
|
|
|
|
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+// writeStatus writes a status message to out. If newerDownloaded is true, the
|
|
|
|
+// status message indicates that a newer image was downloaded. Otherwise, it
|
|
|
|
+// indicates that the image is up to date. requestedTag is the tag the message
|
|
|
|
+// will refer to.
|
|
|
|
+func writeStatus(out progress.Output, requestedTag string, newerDownloaded bool) {
|
|
|
|
+ if newerDownloaded {
|
|
|
|
+ progress.Message(out, "", "Status: Downloaded newer image for "+requestedTag)
|
|
|
|
+ } else {
|
|
|
|
+ progress.Message(out, "", "Status: Image is up to date for "+requestedTag)
|
|
|
|
+ }
|
|
|
|
+}
|