c8d: Better pull progress
- check if we have to download layers and print the approriate message - show the digest of the pulled manifest(list) - skip pulling if we already have the right manifest Signed-off-by: Djordje Lukic <djordje.lukic@docker.com>
This commit is contained in:
parent
efea287b44
commit
776c376227
3 changed files with 57 additions and 8 deletions
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/docker/docker/api/types/events"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/docker/pkg/progress"
|
||||
"github.com/docker/docker/pkg/streamformatter"
|
||||
"github.com/opencontainers/go-digest"
|
||||
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)
|
||||
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()
|
||||
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)
|
||||
}
|
||||
return nil, nil
|
||||
|
@ -59,9 +69,26 @@ func (i *ImageService) PullImage(ctx context.Context, image, tagOrDigest string,
|
|||
opts = append(opts, containerd.WithImageHandler(h))
|
||||
|
||||
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()
|
||||
|
||||
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)
|
||||
// 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))
|
||||
|
@ -71,6 +98,8 @@ func (i *ImageService) PullImage(ctx context.Context, image, tagOrDigest string,
|
|||
infoHandler := snapshotters.AppendInfoHandlerWrapper(ref.String())
|
||||
opts = append(opts, containerd.WithImageHandlerWrapper(infoHandler))
|
||||
|
||||
progress.Message(out, tagOrDigest, "Pulling from "+reference.Path(ref))
|
||||
|
||||
img, err := i.client.Pull(ctx, ref.String(), opts...)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -81,6 +110,9 @@ func (i *ImageService) PullImage(ctx context.Context, image, tagOrDigest string,
|
|||
"remote": ref.String(),
|
||||
})
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ func (i *ImageService) PushImage(ctx context.Context, targetRef reference.Named,
|
|||
jobsQueue := newJobs()
|
||||
finishProgress := jobsQueue.showProgress(ctx, out, combinedProgress([]progressUpdater{
|
||||
&pp,
|
||||
pullProgress{ShowExists: false, Store: store},
|
||||
pullProgress{showExists: false, store: store},
|
||||
}))
|
||||
defer func() {
|
||||
finishProgress()
|
||||
|
|
|
@ -104,12 +104,13 @@ func (j *jobs) Jobs() []ocispec.Descriptor {
|
|||
}
|
||||
|
||||
type pullProgress struct {
|
||||
Store content.Store
|
||||
ShowExists bool
|
||||
store content.Store
|
||||
showExists bool
|
||||
hideLayers bool
|
||||
}
|
||||
|
||||
func (p pullProgress) UpdateProgress(ctx context.Context, ongoing *jobs, out progress.Output, start time.Time) error {
|
||||
actives, err := p.Store.ListStatuses(ctx, "")
|
||||
actives, err := p.store.ListStatuses(ctx, "")
|
||||
if err != nil {
|
||||
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
|
||||
return err
|
||||
|
@ -125,6 +126,10 @@ func (p pullProgress) UpdateProgress(ctx context.Context, ongoing *jobs, out pro
|
|||
}
|
||||
|
||||
for _, j := range ongoing.Jobs() {
|
||||
if p.hideLayers {
|
||||
ongoing.Remove(j)
|
||||
continue
|
||||
}
|
||||
key := remotes.MakeRefKey(ctx, j)
|
||||
if info, ok := pulling[key]; ok {
|
||||
out.WriteProgress(progress.Progress{
|
||||
|
@ -136,7 +141,7 @@ func (p pullProgress) UpdateProgress(ctx context.Context, ongoing *jobs, out pro
|
|||
continue
|
||||
}
|
||||
|
||||
info, err := p.Store.Info(ctx, j.Digest)
|
||||
info, err := p.store.Info(ctx, j.Digest)
|
||||
if err != nil {
|
||||
if !cerrdefs.IsNotFound(err) {
|
||||
return err
|
||||
|
@ -149,7 +154,7 @@ func (p pullProgress) UpdateProgress(ctx context.Context, ongoing *jobs, out pro
|
|||
LastUpdate: true,
|
||||
})
|
||||
ongoing.Remove(j)
|
||||
} else if p.ShowExists {
|
||||
} else if p.showExists {
|
||||
out.WriteProgress(progress.Progress{
|
||||
ID: stringid.TruncateID(j.Digest.Encoded()),
|
||||
Action: "Already exists",
|
||||
|
|
Loading…
Reference in a new issue