image_pull.go 3.9 KB

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