pull.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. package distribution
  2. import (
  3. "fmt"
  4. "github.com/Sirupsen/logrus"
  5. "github.com/docker/docker/api"
  6. "github.com/docker/docker/distribution/metadata"
  7. "github.com/docker/docker/distribution/xfer"
  8. "github.com/docker/docker/image"
  9. "github.com/docker/docker/pkg/progress"
  10. "github.com/docker/docker/reference"
  11. "github.com/docker/docker/registry"
  12. "github.com/docker/engine-api/types"
  13. "golang.org/x/net/context"
  14. )
  15. // ImagePullConfig stores pull configuration.
  16. type ImagePullConfig struct {
  17. // MetaHeaders stores HTTP headers with metadata about the image
  18. MetaHeaders map[string][]string
  19. // AuthConfig holds authentication credentials for authenticating with
  20. // the registry.
  21. AuthConfig *types.AuthConfig
  22. // ProgressOutput is the interface for showing the status of the pull
  23. // operation.
  24. ProgressOutput progress.Output
  25. // RegistryService is the registry service to use for TLS configuration
  26. // and endpoint lookup.
  27. RegistryService *registry.Service
  28. // ImageEventLogger notifies events for a given image
  29. ImageEventLogger func(id, name, action string)
  30. // MetadataStore is the storage backend for distribution-specific
  31. // metadata.
  32. MetadataStore metadata.Store
  33. // ImageStore manages images.
  34. ImageStore image.Store
  35. // ReferenceStore manages tags.
  36. ReferenceStore reference.Store
  37. // DownloadManager manages concurrent pulls.
  38. DownloadManager *xfer.LayerDownloadManager
  39. }
  40. // Puller is an interface that abstracts pulling for different API versions.
  41. type Puller interface {
  42. // Pull tries to pull the image referenced by `tag`
  43. // Pull returns an error if any, as well as a boolean that determines whether to retry Pull on the next configured endpoint.
  44. //
  45. Pull(ctx context.Context, ref reference.Named) error
  46. }
  47. // newPuller returns a Puller interface that will pull from either a v1 or v2
  48. // registry. The endpoint argument contains a Version field that determines
  49. // whether a v1 or v2 puller will be created. The other parameters are passed
  50. // through to the underlying puller implementation for use during the actual
  51. // pull operation.
  52. func newPuller(endpoint registry.APIEndpoint, repoInfo *registry.RepositoryInfo, imagePullConfig *ImagePullConfig) (Puller, error) {
  53. switch endpoint.Version {
  54. case registry.APIVersion2:
  55. return &v2Puller{
  56. V2MetadataService: metadata.NewV2MetadataService(imagePullConfig.MetadataStore),
  57. endpoint: endpoint,
  58. config: imagePullConfig,
  59. repoInfo: repoInfo,
  60. }, nil
  61. case registry.APIVersion1:
  62. return &v1Puller{
  63. v1IDService: metadata.NewV1IDService(imagePullConfig.MetadataStore),
  64. endpoint: endpoint,
  65. config: imagePullConfig,
  66. repoInfo: repoInfo,
  67. }, nil
  68. }
  69. return nil, fmt.Errorf("unknown version %d for registry %s", endpoint.Version, endpoint.URL)
  70. }
  71. // Pull initiates a pull operation. image is the repository name to pull, and
  72. // tag may be either empty, or indicate a specific tag to pull.
  73. func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullConfig) error {
  74. // Resolve the Repository name from fqn to RepositoryInfo
  75. repoInfo, err := imagePullConfig.RegistryService.ResolveRepository(ref)
  76. if err != nil {
  77. return err
  78. }
  79. // makes sure name is not empty or `scratch`
  80. if err := validateRepoName(repoInfo.Name()); err != nil {
  81. return err
  82. }
  83. endpoints, err := imagePullConfig.RegistryService.LookupPullEndpoints(repoInfo)
  84. if err != nil {
  85. return err
  86. }
  87. var (
  88. lastErr error
  89. // discardNoSupportErrors is used to track whether an endpoint encountered an error of type registry.ErrNoSupport
  90. // By default it is false, which means that if a ErrNoSupport error is encountered, it will be saved in lastErr.
  91. // As soon as another kind of error is encountered, discardNoSupportErrors is set to true, avoiding the saving of
  92. // any subsequent ErrNoSupport errors in lastErr.
  93. // It's needed for pull-by-digest on v1 endpoints: if there are only v1 endpoints configured, the error should be
  94. // returned and displayed, but if there was a v2 endpoint which supports pull-by-digest, then the last relevant
  95. // error is the ones from v2 endpoints not v1.
  96. discardNoSupportErrors bool
  97. // confirmedV2 is set to true if a pull attempt managed to
  98. // confirm that it was talking to a v2 registry. This will
  99. // prevent fallback to the v1 protocol.
  100. confirmedV2 bool
  101. )
  102. for _, endpoint := range endpoints {
  103. if confirmedV2 && endpoint.Version == registry.APIVersion1 {
  104. logrus.Debugf("Skipping v1 endpoint %s because v2 registry was detected", endpoint.URL)
  105. continue
  106. }
  107. logrus.Debugf("Trying to pull %s from %s %s", repoInfo.Name(), endpoint.URL, endpoint.Version)
  108. puller, err := newPuller(endpoint, repoInfo, imagePullConfig)
  109. if err != nil {
  110. lastErr = err
  111. continue
  112. }
  113. if err := puller.Pull(ctx, ref); err != nil {
  114. // Was this pull cancelled? If so, don't try to fall
  115. // back.
  116. fallback := false
  117. select {
  118. case <-ctx.Done():
  119. default:
  120. if fallbackErr, ok := err.(fallbackError); ok {
  121. fallback = true
  122. confirmedV2 = confirmedV2 || fallbackErr.confirmedV2
  123. err = fallbackErr.err
  124. }
  125. }
  126. if fallback {
  127. if _, ok := err.(registry.ErrNoSupport); !ok {
  128. // Because we found an error that's not ErrNoSupport, discard all subsequent ErrNoSupport errors.
  129. discardNoSupportErrors = true
  130. // append subsequent errors
  131. lastErr = err
  132. } else if !discardNoSupportErrors {
  133. // Save the ErrNoSupport error, because it's either the first error or all encountered errors
  134. // were also ErrNoSupport errors.
  135. // append subsequent errors
  136. lastErr = err
  137. }
  138. continue
  139. }
  140. logrus.Debugf("Not continuing with error: %v", err)
  141. return err
  142. }
  143. imagePullConfig.ImageEventLogger(ref.String(), repoInfo.Name(), "pull")
  144. return nil
  145. }
  146. if lastErr == nil {
  147. lastErr = fmt.Errorf("no endpoints found for %s", ref.String())
  148. }
  149. return lastErr
  150. }
  151. // writeStatus writes a status message to out. If layersDownloaded is true, the
  152. // status message indicates that a newer image was downloaded. Otherwise, it
  153. // indicates that the image is up to date. requestedTag is the tag the message
  154. // will refer to.
  155. func writeStatus(requestedTag string, out progress.Output, layersDownloaded bool) {
  156. if layersDownloaded {
  157. progress.Message(out, "", "Status: Downloaded newer image for "+requestedTag)
  158. } else {
  159. progress.Message(out, "", "Status: Image is up to date for "+requestedTag)
  160. }
  161. }
  162. // validateRepoName validates the name of a repository.
  163. func validateRepoName(name string) error {
  164. if name == "" {
  165. return fmt.Errorf("Repository name can't be empty")
  166. }
  167. if name == api.NoBaseImageSpecifier {
  168. return fmt.Errorf("'%s' is a reserved name", api.NoBaseImageSpecifier)
  169. }
  170. return nil
  171. }