github.com/docker/docker@v299999999.0.0-20200612211812-aaf470eca7b5+incompatible/daemon/images/image_pull.go (about)

     1  package images // import "github.com/docker/docker/daemon/images"
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  	"strings"
     7  	"time"
     8  
     9  	dist "github.com/docker/distribution"
    10  	"github.com/docker/distribution/reference"
    11  	"github.com/docker/docker/api/types"
    12  	"github.com/docker/docker/distribution"
    13  	progressutils "github.com/docker/docker/distribution/utils"
    14  	"github.com/docker/docker/errdefs"
    15  	"github.com/docker/docker/pkg/progress"
    16  	"github.com/docker/docker/registry"
    17  	digest "github.com/opencontainers/go-digest"
    18  	specs "github.com/opencontainers/image-spec/specs-go/v1"
    19  )
    20  
    21  // PullImage initiates a pull operation. image is the repository name to pull, and
    22  // tag may be either empty, or indicate a specific tag to pull.
    23  func (i *ImageService) PullImage(ctx context.Context, image, tag string, platform *specs.Platform, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error {
    24  	start := time.Now()
    25  	// Special case: "pull -a" may send an image name with a
    26  	// trailing :. This is ugly, but let's not break API
    27  	// compatibility.
    28  	image = strings.TrimSuffix(image, ":")
    29  
    30  	ref, err := reference.ParseNormalizedNamed(image)
    31  	if err != nil {
    32  		return errdefs.InvalidParameter(err)
    33  	}
    34  
    35  	if tag != "" {
    36  		// The "tag" could actually be a digest.
    37  		var dgst digest.Digest
    38  		dgst, err = digest.Parse(tag)
    39  		if err == nil {
    40  			ref, err = reference.WithDigest(reference.TrimNamed(ref), dgst)
    41  		} else {
    42  			ref, err = reference.WithTag(ref, tag)
    43  		}
    44  		if err != nil {
    45  			return errdefs.InvalidParameter(err)
    46  		}
    47  	}
    48  
    49  	err = i.pullImageWithReference(ctx, ref, platform, metaHeaders, authConfig, outStream)
    50  	imageActions.WithValues("pull").UpdateSince(start)
    51  	return err
    52  }
    53  
    54  func (i *ImageService) pullImageWithReference(ctx context.Context, ref reference.Named, platform *specs.Platform, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error {
    55  	// Include a buffer so that slow client connections don't affect
    56  	// transfer performance.
    57  	progressChan := make(chan progress.Progress, 100)
    58  
    59  	writesDone := make(chan struct{})
    60  
    61  	ctx, cancelFunc := context.WithCancel(ctx)
    62  
    63  	go func() {
    64  		progressutils.WriteDistributionProgress(cancelFunc, outStream, progressChan)
    65  		close(writesDone)
    66  	}()
    67  
    68  	imagePullConfig := &distribution.ImagePullConfig{
    69  		Config: distribution.Config{
    70  			MetaHeaders:      metaHeaders,
    71  			AuthConfig:       authConfig,
    72  			ProgressOutput:   progress.ChanOutput(progressChan),
    73  			RegistryService:  i.registryService,
    74  			ImageEventLogger: i.LogImageEvent,
    75  			MetadataStore:    i.distributionMetadataStore,
    76  			ImageStore:       distribution.NewImageConfigStoreFromStore(i.imageStore),
    77  			ReferenceStore:   i.referenceStore,
    78  		},
    79  		DownloadManager: i.downloadManager,
    80  		Schema2Types:    distribution.ImageTypes,
    81  		Platform:        platform,
    82  	}
    83  
    84  	err := distribution.Pull(ctx, ref, imagePullConfig)
    85  	close(progressChan)
    86  	<-writesDone
    87  	return err
    88  }
    89  
    90  // GetRepository returns a repository from the registry.
    91  func (i *ImageService) GetRepository(ctx context.Context, ref reference.Named, authConfig *types.AuthConfig) (dist.Repository, bool, error) {
    92  	// get repository info
    93  	repoInfo, err := i.registryService.ResolveRepository(ref)
    94  	if err != nil {
    95  		return nil, false, errdefs.InvalidParameter(err)
    96  	}
    97  	// makes sure name is not empty or `scratch`
    98  	if err := distribution.ValidateRepoName(repoInfo.Name); err != nil {
    99  		return nil, false, errdefs.InvalidParameter(err)
   100  	}
   101  
   102  	// get endpoints
   103  	endpoints, err := i.registryService.LookupPullEndpoints(reference.Domain(repoInfo.Name))
   104  	if err != nil {
   105  		return nil, false, err
   106  	}
   107  
   108  	// retrieve repository
   109  	var (
   110  		confirmedV2 bool
   111  		repository  dist.Repository
   112  		lastError   error
   113  	)
   114  
   115  	for _, endpoint := range endpoints {
   116  		if endpoint.Version == registry.APIVersion1 {
   117  			continue
   118  		}
   119  
   120  		repository, confirmedV2, lastError = distribution.NewV2Repository(ctx, repoInfo, endpoint, nil, authConfig, "pull")
   121  		if lastError == nil && confirmedV2 {
   122  			break
   123  		}
   124  	}
   125  	return repository, confirmedV2, lastError
   126  }