daemon/image_pull.go
47afe6bd
 package daemon
 
 import (
 	"io"
3aa4a007
 	"runtime"
47afe6bd
 	"strings"
 
764a9ed3
 	dist "github.com/docker/distribution"
3a127939
 	"github.com/docker/distribution/reference"
91e197d6
 	"github.com/docker/docker/api/types"
47afe6bd
 	"github.com/docker/docker/distribution"
3d86b0c7
 	progressutils "github.com/docker/docker/distribution/utils"
d453fe35
 	"github.com/docker/docker/errdefs"
47afe6bd
 	"github.com/docker/docker/pkg/progress"
 	"github.com/docker/docker/registry"
7a855799
 	"github.com/opencontainers/go-digest"
47afe6bd
 	"golang.org/x/net/context"
 )
 
 // PullImage initiates a pull operation. image is the repository name to pull, and
 // tag may be either empty, or indicate a specific tag to pull.
ce8e529e
 func (daemon *Daemon) PullImage(ctx context.Context, image, tag, os string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error {
47afe6bd
 	// Special case: "pull -a" may send an image name with a
 	// trailing :. This is ugly, but let's not break API
 	// compatibility.
 	image = strings.TrimSuffix(image, ":")
 
3a127939
 	ref, err := reference.ParseNormalizedNamed(image)
47afe6bd
 	if err != nil {
87a12421
 		return errdefs.InvalidParameter(err)
47afe6bd
 	}
 
 	if tag != "" {
 		// The "tag" could actually be a digest.
 		var dgst digest.Digest
7a855799
 		dgst, err = digest.Parse(tag)
47afe6bd
 		if err == nil {
c85eb008
 			ref, err = reference.WithDigest(reference.TrimNamed(ref), dgst)
47afe6bd
 		} else {
 			ref, err = reference.WithTag(ref, tag)
 		}
 		if err != nil {
87a12421
 			return errdefs.InvalidParameter(err)
47afe6bd
 		}
 	}
 
ce8e529e
 	return daemon.pullImageWithReference(ctx, ref, os, metaHeaders, authConfig, outStream)
47afe6bd
 }
 
ce8e529e
 func (daemon *Daemon) pullImageWithReference(ctx context.Context, ref reference.Named, os string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error {
47afe6bd
 	// Include a buffer so that slow client connections don't affect
 	// transfer performance.
 	progressChan := make(chan progress.Progress, 100)
 
 	writesDone := make(chan struct{})
 
 	ctx, cancelFunc := context.WithCancel(ctx)
 
 	go func() {
3d86b0c7
 		progressutils.WriteDistributionProgress(cancelFunc, outStream, progressChan)
47afe6bd
 		close(writesDone)
 	}()
 
a1fe1dc7
 	// Default to the host OS platform in case it hasn't been populated with an explicit value.
ce8e529e
 	if os == "" {
 		os = runtime.GOOS
a1fe1dc7
 	}
3aa4a007
 
47afe6bd
 	imagePullConfig := &distribution.ImagePullConfig{
3c7676a0
 		Config: distribution.Config{
 			MetaHeaders:      metaHeaders,
 			AuthConfig:       authConfig,
 			ProgressOutput:   progress.ChanOutput(progressChan),
 			RegistryService:  daemon.RegistryService,
 			ImageEventLogger: daemon.LogImageEvent,
ce8e529e
 			MetadataStore:    daemon.distributionMetadataStore,
 			ImageStore:       distribution.NewImageConfigStoreFromStore(daemon.imageStore),
7b9a8f46
 			ReferenceStore:   daemon.referenceStore,
3c7676a0
 		},
 		DownloadManager: daemon.downloadManager,
 		Schema2Types:    distribution.ImageTypes,
ce8e529e
 		OS:              os,
47afe6bd
 	}
 
 	err := distribution.Pull(ctx, ref, imagePullConfig)
 	close(progressChan)
 	<-writesDone
 	return err
 }
87075353
 
9c559e6d
 // GetRepository returns a repository from the registry.
e842c653
 func (daemon *Daemon) GetRepository(ctx context.Context, ref reference.Named, authConfig *types.AuthConfig) (dist.Repository, bool, error) {
87075353
 	// get repository info
 	repoInfo, err := daemon.RegistryService.ResolveRepository(ref)
 	if err != nil {
764a9ed3
 		return nil, false, err
87075353
 	}
 	// makes sure name is not empty or `scratch`
3a127939
 	if err := distribution.ValidateRepoName(repoInfo.Name); err != nil {
87a12421
 		return nil, false, errdefs.InvalidParameter(err)
87075353
 	}
 
 	// get endpoints
3a127939
 	endpoints, err := daemon.RegistryService.LookupPullEndpoints(reference.Domain(repoInfo.Name))
87075353
 	if err != nil {
764a9ed3
 		return nil, false, err
87075353
 	}
 
 	// retrieve repository
764a9ed3
 	var (
 		confirmedV2 bool
 		repository  dist.Repository
 		lastError   error
 	)
 
 	for _, endpoint := range endpoints {
 		if endpoint.Version == registry.APIVersion1 {
 			continue
 		}
87075353
 
764a9ed3
 		repository, confirmedV2, lastError = distribution.NewV2Repository(ctx, repoInfo, endpoint, nil, authConfig, "pull")
 		if lastError == nil && confirmedV2 {
 			break
87075353
 		}
 	}
764a9ed3
 	return repository, confirmedV2, lastError
87075353
 }