Browse code

Add schema2 push support

Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>

Aaron Lehmann authored on 2015/12/15 08:44:45
Showing 2 changed files
... ...
@@ -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
 }