push.go 3.8 KB

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