push.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. package graph
  2. import (
  3. "fmt"
  4. "io"
  5. "github.com/Sirupsen/logrus"
  6. "github.com/docker/distribution/digest"
  7. "github.com/docker/docker/cliconfig"
  8. "github.com/docker/docker/context"
  9. "github.com/docker/docker/pkg/streamformatter"
  10. "github.com/docker/docker/registry"
  11. )
  12. // ImagePushConfig stores push configuration.
  13. type ImagePushConfig struct {
  14. // MetaHeaders store HTTP headers with metadata about the image
  15. // (DockerHeaders with prefix X-Meta- in the request).
  16. MetaHeaders map[string][]string
  17. // AuthConfig holds authentication credentials for authenticating with
  18. // the registry.
  19. AuthConfig *cliconfig.AuthConfig
  20. // Tag is the specific variant of the image to be pushed.
  21. // If no tag is provided, all tags will be pushed.
  22. Tag string
  23. // OutStream is the output writer for showing the status of the push
  24. // operation.
  25. OutStream io.Writer
  26. }
  27. // Pusher is an interface that abstracts pushing for different API versions.
  28. type Pusher interface {
  29. // Push tries to push the image configured at the creation of Pusher.
  30. // Push returns an error if any, as well as a boolean that determines whether to retry Push on the next configured endpoint.
  31. //
  32. // TODO(tiborvass): have Push() take a reference to repository + tag, so that the pusher itself is repository-agnostic.
  33. Push() (fallback bool, err error)
  34. }
  35. // NewPusher creates a new Pusher interface that will push to either a v1 or v2
  36. // registry. The endpoint argument contains a Version field that determines
  37. // whether a v1 or v2 pusher will be created. The other parameters are passed
  38. // through to the underlying pusher implementation for use during the actual
  39. // push operation.
  40. func (s *TagStore) NewPusher(endpoint registry.APIEndpoint, localRepo Repository, repoInfo *registry.RepositoryInfo, imagePushConfig *ImagePushConfig, sf *streamformatter.StreamFormatter) (Pusher, error) {
  41. switch endpoint.Version {
  42. case registry.APIVersion2:
  43. return &v2Pusher{
  44. TagStore: s,
  45. endpoint: endpoint,
  46. localRepo: localRepo,
  47. repoInfo: repoInfo,
  48. config: imagePushConfig,
  49. sf: sf,
  50. layersPushed: make(map[digest.Digest]bool),
  51. }, nil
  52. case registry.APIVersion1:
  53. return &v1Pusher{
  54. TagStore: s,
  55. endpoint: endpoint,
  56. localRepo: localRepo,
  57. repoInfo: repoInfo,
  58. config: imagePushConfig,
  59. sf: sf,
  60. }, nil
  61. }
  62. return nil, fmt.Errorf("unknown version %d for registry %s", endpoint.Version, endpoint.URL)
  63. }
  64. // Push initiates a push operation on the repository named localName.
  65. func (s *TagStore) Push(ctx context.Context, localName string, imagePushConfig *ImagePushConfig) error {
  66. // FIXME: Allow to interrupt current push when new push of same image is done.
  67. var sf = streamformatter.NewJSONStreamFormatter()
  68. // Resolve the Repository name from fqn to RepositoryInfo
  69. repoInfo, err := s.registryService.ResolveRepository(localName)
  70. if err != nil {
  71. return err
  72. }
  73. endpoints, err := s.registryService.LookupPushEndpoints(repoInfo.CanonicalName)
  74. if err != nil {
  75. return err
  76. }
  77. reposLen := 1
  78. if imagePushConfig.Tag == "" {
  79. reposLen = len(s.Repositories[repoInfo.LocalName])
  80. }
  81. imagePushConfig.OutStream.Write(sf.FormatStatus("", "The push refers to a repository [%s] (len: %d)", repoInfo.CanonicalName, reposLen))
  82. // If it fails, try to get the repository
  83. localRepo, exists := s.Repositories[repoInfo.LocalName]
  84. if !exists {
  85. return fmt.Errorf("Repository does not exist: %s", repoInfo.LocalName)
  86. }
  87. var lastErr error
  88. for _, endpoint := range endpoints {
  89. logrus.Debugf("Trying to push %s to %s %s", repoInfo.CanonicalName, endpoint.URL, endpoint.Version)
  90. pusher, err := s.NewPusher(endpoint, localRepo, repoInfo, imagePushConfig, sf)
  91. if err != nil {
  92. lastErr = err
  93. continue
  94. }
  95. if fallback, err := pusher.Push(); err != nil {
  96. if fallback {
  97. lastErr = err
  98. continue
  99. }
  100. logrus.Debugf("Not continuing with error: %v", err)
  101. return err
  102. }
  103. s.eventsService.Log(ctx, "push", repoInfo.LocalName, "")
  104. return nil
  105. }
  106. if lastErr == nil {
  107. lastErr = fmt.Errorf("no endpoints found for %s", repoInfo.CanonicalName)
  108. }
  109. return lastErr
  110. }