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:
Djordje Lukic 2023-09-06 12:06:32 +02:00
parent efea287b44
commit 776c376227
No known key found for this signature in database
3 changed files with 57 additions and 8 deletions

View file

@ -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)
}
}

View file

@ -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()

View file

@ -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",