Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
| ... | ... |
@@ -1,6 +1,7 @@ |
| 1 | 1 |
package distribution |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "errors" |
|
| 4 | 5 |
"fmt" |
| 5 | 6 |
"io" |
| 6 | 7 |
"sync" |
| ... | ... |
@@ -13,6 +14,7 @@ import ( |
| 13 | 13 |
"github.com/docker/distribution/registry/client" |
| 14 | 14 |
"github.com/docker/docker/distribution/metadata" |
| 15 | 15 |
"github.com/docker/docker/distribution/xfer" |
| 16 |
+ "github.com/docker/docker/image" |
|
| 16 | 17 |
"github.com/docker/docker/layer" |
| 17 | 18 |
"github.com/docker/docker/pkg/ioutils" |
| 18 | 19 |
"github.com/docker/docker/pkg/progress" |
| ... | ... |
@@ -73,44 +75,41 @@ func (p *v2Pusher) Push(ctx context.Context) (err error) {
|
| 73 | 73 |
} |
| 74 | 74 |
|
| 75 | 75 |
func (p *v2Pusher) pushV2Repository(ctx context.Context) (err error) {
|
| 76 |
- var associations []reference.Association |
|
| 77 |
- if _, isTagged := p.ref.(reference.NamedTagged); isTagged {
|
|
| 76 |
+ if namedTagged, isNamedTagged := p.ref.(reference.NamedTagged); isNamedTagged {
|
|
| 78 | 77 |
imageID, err := p.config.ReferenceStore.Get(p.ref) |
| 79 | 78 |
if err != nil {
|
| 80 | 79 |
return fmt.Errorf("tag does not exist: %s", p.ref.String())
|
| 81 | 80 |
} |
| 82 | 81 |
|
| 83 |
- associations = []reference.Association{
|
|
| 84 |
- {
|
|
| 85 |
- Ref: p.ref, |
|
| 86 |
- ImageID: imageID, |
|
| 87 |
- }, |
|
| 88 |
- } |
|
| 89 |
- } else {
|
|
| 90 |
- // Pull all tags |
|
| 91 |
- associations = p.config.ReferenceStore.ReferencesByName(p.ref) |
|
| 92 |
- } |
|
| 93 |
- if err != nil {
|
|
| 94 |
- return fmt.Errorf("error getting tags for %s: %s", p.repoInfo.Name(), err)
|
|
| 82 |
+ return p.pushV2Tag(ctx, namedTagged, imageID) |
|
| 95 | 83 |
} |
| 96 |
- if len(associations) == 0 {
|
|
| 97 |
- return fmt.Errorf("no tags to push for %s", p.repoInfo.Name())
|
|
| 84 |
+ |
|
| 85 |
+ if !reference.IsNameOnly(p.ref) {
|
|
| 86 |
+ return errors.New("cannot push a digest reference")
|
|
| 98 | 87 |
} |
| 99 | 88 |
|
| 100 |
- for _, association := range associations {
|
|
| 101 |
- if err := p.pushV2Tag(ctx, association); err != nil {
|
|
| 102 |
- return err |
|
| 89 |
+ // Pull all tags |
|
| 90 |
+ pushed := 0 |
|
| 91 |
+ for _, association := range p.config.ReferenceStore.ReferencesByName(p.ref) {
|
|
| 92 |
+ if namedTagged, isNamedTagged := association.Ref.(reference.NamedTagged); isNamedTagged {
|
|
| 93 |
+ pushed++ |
|
| 94 |
+ if err := p.pushV2Tag(ctx, namedTagged, association.ImageID); err != nil {
|
|
| 95 |
+ return err |
|
| 96 |
+ } |
|
| 103 | 97 |
} |
| 104 | 98 |
} |
| 105 | 99 |
|
| 100 |
+ if pushed == 0 {
|
|
| 101 |
+ return fmt.Errorf("no tags to push for %s", p.repoInfo.Name())
|
|
| 102 |
+ } |
|
| 103 |
+ |
|
| 106 | 104 |
return nil |
| 107 | 105 |
} |
| 108 | 106 |
|
| 109 |
-func (p *v2Pusher) pushV2Tag(ctx context.Context, association reference.Association) error {
|
|
| 110 |
- ref := association.Ref |
|
| 107 |
+func (p *v2Pusher) pushV2Tag(ctx context.Context, ref reference.NamedTagged, imageID image.ID) error {
|
|
| 111 | 108 |
logrus.Debugf("Pushing repository: %s", ref.String())
|
| 112 | 109 |
|
| 113 |
- img, err := p.config.ImageStore.Get(association.ImageID) |
|
| 110 |
+ img, err := p.config.ImageStore.Get(imageID) |
|
| 114 | 111 |
if err != nil {
|
| 115 | 112 |
return fmt.Errorf("could not find image from tag %s: %v", ref.String(), err)
|
| 116 | 113 |
} |
| ... | ... |
@@ -149,50 +148,64 @@ func (p *v2Pusher) pushV2Tag(ctx context.Context, association reference.Associat |
| 149 | 149 |
return err |
| 150 | 150 |
} |
| 151 | 151 |
|
| 152 |
- var tag string |
|
| 153 |
- if tagged, isTagged := ref.(reference.NamedTagged); isTagged {
|
|
| 154 |
- tag = tagged.Tag() |
|
| 155 |
- } |
|
| 156 |
- builder := schema1.NewConfigManifestBuilder(p.repo.Blobs(ctx), p.config.TrustKey, p.repo.Name(), tag, img.RawJSON()) |
|
| 157 |
- |
|
| 158 |
- // descriptors is in reverse order; iterate backwards to get references |
|
| 159 |
- // appended in the right order. |
|
| 160 |
- for i := len(descriptors) - 1; i >= 0; i-- {
|
|
| 161 |
- if err := builder.AppendReference(descriptors[i].(*v2PushDescriptor)); err != nil {
|
|
| 162 |
- return err |
|
| 163 |
- } |
|
| 164 |
- } |
|
| 165 |
- |
|
| 166 |
- manifest, err := builder.Build(ctx) |
|
| 152 |
+ // Try schema2 first |
|
| 153 |
+ builder := schema2.NewManifestBuilder(p.repo.Blobs(ctx), img.RawJSON()) |
|
| 154 |
+ manifest, err := manifestFromBuilder(ctx, builder, descriptors) |
|
| 167 | 155 |
if err != nil {
|
| 168 | 156 |
return err |
| 169 | 157 |
} |
| 170 | 158 |
|
| 171 |
- manifestDigest, manifestSize, err := digestFromManifest(manifest.(*schema1.SignedManifest), ref) |
|
| 159 |
+ manSvc, err := p.repo.Manifests(ctx) |
|
| 172 | 160 |
if err != nil {
|
| 173 | 161 |
return err |
| 174 | 162 |
} |
| 175 |
- if manifestDigest != "" {
|
|
| 176 |
- if tagged, isTagged := ref.(reference.NamedTagged); isTagged {
|
|
| 177 |
- progress.Messagef(p.config.ProgressOutput, "", "%s: digest: %s size: %d", tagged.Tag(), manifestDigest, manifestSize) |
|
| 178 |
- // Signal digest to the trust client so it can sign the |
|
| 179 |
- // push, if appropriate. |
|
| 180 |
- progress.Aux(p.config.ProgressOutput, PushResult{Tag: tagged.Tag(), Digest: manifestDigest, Size: manifestSize})
|
|
| 163 |
+ |
|
| 164 |
+ putOptions := []distribution.ManifestServiceOption{client.WithTag(ref.Tag())}
|
|
| 165 |
+ if _, err = manSvc.Put(ctx, manifest, putOptions...); err != nil {
|
|
| 166 |
+ logrus.Warnf("failed to upload schema2 manifest: %v - falling back to schema1", err)
|
|
| 167 |
+ |
|
| 168 |
+ builder = schema1.NewConfigManifestBuilder(p.repo.Blobs(ctx), p.config.TrustKey, p.repo.Name(), ref.Tag(), img.RawJSON()) |
|
| 169 |
+ manifest, err = manifestFromBuilder(ctx, builder, descriptors) |
|
| 170 |
+ if err != nil {
|
|
| 171 |
+ return err |
|
| 172 |
+ } |
|
| 173 |
+ |
|
| 174 |
+ if _, err = manSvc.Put(ctx, manifest, putOptions...); err != nil {
|
|
| 175 |
+ return err |
|
| 181 | 176 |
} |
| 182 | 177 |
} |
| 183 | 178 |
|
| 184 |
- manSvc, err := p.repo.Manifests(ctx) |
|
| 185 |
- if err != nil {
|
|
| 186 |
- return err |
|
| 179 |
+ var canonicalManifest []byte |
|
| 180 |
+ |
|
| 181 |
+ switch v := manifest.(type) {
|
|
| 182 |
+ case *schema1.SignedManifest: |
|
| 183 |
+ canonicalManifest = v.Canonical |
|
| 184 |
+ case *schema2.DeserializedManifest: |
|
| 185 |
+ _, canonicalManifest, err = v.Payload() |
|
| 186 |
+ if err != nil {
|
|
| 187 |
+ return err |
|
| 188 |
+ } |
|
| 187 | 189 |
} |
| 188 | 190 |
|
| 189 |
- if tagged, isTagged := ref.(reference.NamedTagged); isTagged {
|
|
| 190 |
- _, err = manSvc.Put(ctx, manifest, client.WithTag(tagged.Tag())) |
|
| 191 |
- } else {
|
|
| 192 |
- _, err = manSvc.Put(ctx, manifest) |
|
| 191 |
+ manifestDigest := digest.FromBytes(canonicalManifest) |
|
| 192 |
+ progress.Messagef(p.config.ProgressOutput, "", "%s: digest: %s size: %d", ref.Tag(), manifestDigest, len(canonicalManifest)) |
|
| 193 |
+ // Signal digest to the trust client so it can sign the |
|
| 194 |
+ // push, if appropriate. |
|
| 195 |
+ progress.Aux(p.config.ProgressOutput, PushResult{Tag: ref.Tag(), Digest: manifestDigest, Size: len(canonicalManifest)})
|
|
| 196 |
+ |
|
| 197 |
+ return nil |
|
| 198 |
+} |
|
| 199 |
+ |
|
| 200 |
+func manifestFromBuilder(ctx context.Context, builder distribution.ManifestBuilder, descriptors []xfer.UploadDescriptor) (distribution.Manifest, error) {
|
|
| 201 |
+ // descriptors is in reverse order; iterate backwards to get references |
|
| 202 |
+ // appended in the right order. |
|
| 203 |
+ for i := len(descriptors) - 1; i >= 0; i-- {
|
|
| 204 |
+ if err := builder.AppendReference(descriptors[i].(*v2PushDescriptor)); err != nil {
|
|
| 205 |
+ return nil, err |
|
| 206 |
+ } |
|
| 193 | 207 |
} |
| 194 |
- // FIXME create a tag |
|
| 195 |
- return err |
|
| 208 |
+ |
|
| 209 |
+ return builder.Build(ctx) |
|
| 196 | 210 |
} |
| 197 | 211 |
|
| 198 | 212 |
type v2PushDescriptor struct {
|
| ... | ... |
@@ -9,14 +9,11 @@ import ( |
| 9 | 9 |
"time" |
| 10 | 10 |
|
| 11 | 11 |
"github.com/docker/distribution" |
| 12 |
- "github.com/docker/distribution/digest" |
|
| 13 |
- "github.com/docker/distribution/manifest/schema1" |
|
| 14 | 12 |
"github.com/docker/distribution/registry/api/errcode" |
| 15 | 13 |
"github.com/docker/distribution/registry/client" |
| 16 | 14 |
"github.com/docker/distribution/registry/client/auth" |
| 17 | 15 |
"github.com/docker/distribution/registry/client/transport" |
| 18 | 16 |
"github.com/docker/docker/distribution/xfer" |
| 19 |
- "github.com/docker/docker/reference" |
|
| 20 | 17 |
"github.com/docker/docker/registry" |
| 21 | 18 |
"github.com/docker/engine-api/types" |
| 22 | 19 |
"golang.org/x/net/context" |
| ... | ... |
@@ -124,10 +121,6 @@ func NewV2Repository(ctx context.Context, repoInfo *registry.RepositoryInfo, end |
| 124 | 124 |
return repo, foundVersion, err |
| 125 | 125 |
} |
| 126 | 126 |
|
| 127 |
-func digestFromManifest(m *schema1.SignedManifest, name reference.Named) (digest.Digest, int, error) {
|
|
| 128 |
- return digest.FromBytes(m.Canonical), len(m.Canonical), nil |
|
| 129 |
-} |
|
| 130 |
- |
|
| 131 | 127 |
type existingTokenHandler struct {
|
| 132 | 128 |
token string |
| 133 | 129 |
} |