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  }