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