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 }