push.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. package distribution
  2. import (
  3. "crypto/sha256"
  4. "io"
  5. "net/http"
  6. "github.com/Sirupsen/logrus"
  7. "github.com/docker/distribution"
  8. "github.com/docker/distribution/digest"
  9. "github.com/docker/distribution/manifest/schema2"
  10. "github.com/docker/docker/api/types"
  11. dockerdist "github.com/docker/docker/distribution"
  12. "github.com/docker/docker/reference"
  13. "github.com/docker/docker/registry"
  14. "golang.org/x/net/context"
  15. )
  16. // Push pushes a plugin to a registry.
  17. func Push(name string, rs registry.Service, metaHeader http.Header, authConfig *types.AuthConfig, config io.ReadCloser, layers io.ReadCloser) (digest.Digest, error) {
  18. ref, err := reference.ParseNamed(name)
  19. if err != nil {
  20. return "", err
  21. }
  22. repoInfo, err := rs.ResolveRepository(ref)
  23. if err != nil {
  24. return "", err
  25. }
  26. if err := dockerdist.ValidateRepoName(repoInfo.Name()); err != nil {
  27. return "", err
  28. }
  29. endpoints, err := rs.LookupPushEndpoints(repoInfo.Hostname())
  30. if err != nil {
  31. return "", err
  32. }
  33. var confirmedV2 bool
  34. var repository distribution.Repository
  35. for _, endpoint := range endpoints {
  36. if confirmedV2 && endpoint.Version == registry.APIVersion1 {
  37. logrus.Debugf("Skipping v1 endpoint %s because v2 registry was detected", endpoint.URL)
  38. continue
  39. }
  40. repository, confirmedV2, err = dockerdist.NewV2Repository(context.Background(), repoInfo, endpoint, metaHeader, authConfig, "push", "pull")
  41. if err != nil {
  42. return "", err
  43. }
  44. if !confirmedV2 {
  45. return "", ErrUnsupportedRegistry
  46. }
  47. logrus.Debugf("Trying to push %s to %s %s", repoInfo.Name(), endpoint.URL, endpoint.Version)
  48. // This means that we found an endpoint. and we are ready to push
  49. break
  50. }
  51. // Returns a reference to the repository's blob service.
  52. blobs := repository.Blobs(context.Background())
  53. // Descriptor = {mediaType, size, digest}
  54. var descs []distribution.Descriptor
  55. for i, f := range []io.ReadCloser{config, layers} {
  56. bw, err := blobs.Create(context.Background())
  57. if err != nil {
  58. logrus.Debugf("Error in blobs.Create: %v", err)
  59. return "", err
  60. }
  61. h := sha256.New()
  62. r := io.TeeReader(f, h)
  63. _, err = io.Copy(bw, r)
  64. if err != nil {
  65. f.Close()
  66. logrus.Debugf("Error in io.Copy: %v", err)
  67. return "", err
  68. }
  69. f.Close()
  70. mt := schema2.MediaTypeLayer
  71. if i == 0 {
  72. mt = schema2.MediaTypePluginConfig
  73. }
  74. // Commit completes the write process to the BlobService.
  75. // The descriptor arg to Commit is called the "provisional" descriptor and
  76. // used for validation.
  77. // The returned descriptor should be the one used. Its called the "Canonical"
  78. // descriptor.
  79. desc, err := bw.Commit(context.Background(), distribution.Descriptor{
  80. MediaType: mt,
  81. // XXX: What about the Size?
  82. Digest: digest.NewDigest("sha256", h),
  83. })
  84. if err != nil {
  85. logrus.Debugf("Error in bw.Commit: %v", err)
  86. return "", err
  87. }
  88. // The canonical descriptor is set the mediatype again, just in case.
  89. // Don't touch the digest or the size here.
  90. desc.MediaType = mt
  91. logrus.Debugf("pushed blob: %s %s", desc.MediaType, desc.Digest)
  92. descs = append(descs, desc)
  93. }
  94. // XXX: schema2.Versioned needs a MediaType as well.
  95. // "application/vnd.docker.distribution.manifest.v2+json"
  96. m, err := schema2.FromStruct(schema2.Manifest{Versioned: schema2.SchemaVersion, Config: descs[0], Layers: descs[1:]})
  97. if err != nil {
  98. logrus.Debugf("error in schema2.FromStruct: %v", err)
  99. return "", err
  100. }
  101. msv, err := repository.Manifests(context.Background())
  102. if err != nil {
  103. logrus.Debugf("error in repository.Manifests: %v", err)
  104. return "", err
  105. }
  106. _, pl, err := m.Payload()
  107. if err != nil {
  108. logrus.Debugf("error in m.Payload: %v", err)
  109. return "", err
  110. }
  111. logrus.Debugf("Pushed manifest: %s", pl)
  112. tag := DefaultTag
  113. if tagged, ok := ref.(reference.NamedTagged); ok {
  114. tag = tagged.Tag()
  115. }
  116. return msv.Put(context.Background(), m, distribution.WithTag(tag))
  117. }