Parcourir la source

Add schema2 push support

Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
Aaron Lehmann il y a 9 ans
Parent
commit
c8d277d228
2 fichiers modifiés avec 66 ajouts et 60 suppressions
  1. 66 53
      distribution/push_v2.go
  2. 0 7
      distribution/registry.go

+ 66 - 53
distribution/push_v2.go

@@ -1,6 +1,7 @@
 package distribution
 
 import (
+	"errors"
 	"fmt"
 	"io"
 	"sync"
@@ -13,6 +14,7 @@ import (
 	"github.com/docker/distribution/registry/client"
 	"github.com/docker/docker/distribution/metadata"
 	"github.com/docker/docker/distribution/xfer"
+	"github.com/docker/docker/image"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/progress"
@@ -73,44 +75,41 @@ func (p *v2Pusher) Push(ctx context.Context) (err error) {
 }
 
 func (p *v2Pusher) pushV2Repository(ctx context.Context) (err error) {
-	var associations []reference.Association
-	if _, isTagged := p.ref.(reference.NamedTagged); isTagged {
+	if namedTagged, isNamedTagged := p.ref.(reference.NamedTagged); isNamedTagged {
 		imageID, err := p.config.ReferenceStore.Get(p.ref)
 		if err != nil {
 			return fmt.Errorf("tag does not exist: %s", p.ref.String())
 		}
 
-		associations = []reference.Association{
-			{
-				Ref:     p.ref,
-				ImageID: imageID,
-			},
-		}
-	} else {
-		// Pull all tags
-		associations = p.config.ReferenceStore.ReferencesByName(p.ref)
-	}
-	if err != nil {
-		return fmt.Errorf("error getting tags for %s: %s", p.repoInfo.Name(), err)
+		return p.pushV2Tag(ctx, namedTagged, imageID)
 	}
-	if len(associations) == 0 {
-		return fmt.Errorf("no tags to push for %s", p.repoInfo.Name())
+
+	if !reference.IsNameOnly(p.ref) {
+		return errors.New("cannot push a digest reference")
 	}
 
-	for _, association := range associations {
-		if err := p.pushV2Tag(ctx, association); err != nil {
-			return err
+	// Pull all tags
+	pushed := 0
+	for _, association := range p.config.ReferenceStore.ReferencesByName(p.ref) {
+		if namedTagged, isNamedTagged := association.Ref.(reference.NamedTagged); isNamedTagged {
+			pushed++
+			if err := p.pushV2Tag(ctx, namedTagged, association.ImageID); err != nil {
+				return err
+			}
 		}
 	}
 
+	if pushed == 0 {
+		return fmt.Errorf("no tags to push for %s", p.repoInfo.Name())
+	}
+
 	return nil
 }
 
-func (p *v2Pusher) pushV2Tag(ctx context.Context, association reference.Association) error {
-	ref := association.Ref
+func (p *v2Pusher) pushV2Tag(ctx context.Context, ref reference.NamedTagged, imageID image.ID) error {
 	logrus.Debugf("Pushing repository: %s", ref.String())
 
-	img, err := p.config.ImageStore.Get(association.ImageID)
+	img, err := p.config.ImageStore.Get(imageID)
 	if err != nil {
 		return fmt.Errorf("could not find image from tag %s: %v", ref.String(), err)
 	}
@@ -149,50 +148,64 @@ func (p *v2Pusher) pushV2Tag(ctx context.Context, association reference.Associat
 		return err
 	}
 
-	var tag string
-	if tagged, isTagged := ref.(reference.NamedTagged); isTagged {
-		tag = tagged.Tag()
-	}
-	builder := schema1.NewConfigManifestBuilder(p.repo.Blobs(ctx), p.config.TrustKey, p.repo.Name(), tag, img.RawJSON())
-
-	// descriptors is in reverse order; iterate backwards to get references
-	// appended in the right order.
-	for i := len(descriptors) - 1; i >= 0; i-- {
-		if err := builder.AppendReference(descriptors[i].(*v2PushDescriptor)); err != nil {
-			return err
-		}
-	}
-
-	manifest, err := builder.Build(ctx)
+	// Try schema2 first
+	builder := schema2.NewManifestBuilder(p.repo.Blobs(ctx), img.RawJSON())
+	manifest, err := manifestFromBuilder(ctx, builder, descriptors)
 	if err != nil {
 		return err
 	}
 
-	manifestDigest, manifestSize, err := digestFromManifest(manifest.(*schema1.SignedManifest), ref)
+	manSvc, err := p.repo.Manifests(ctx)
 	if err != nil {
 		return err
 	}
-	if manifestDigest != "" {
-		if tagged, isTagged := ref.(reference.NamedTagged); isTagged {
-			progress.Messagef(p.config.ProgressOutput, "", "%s: digest: %s size: %d", tagged.Tag(), manifestDigest, manifestSize)
-			// Signal digest to the trust client so it can sign the
-			// push, if appropriate.
-			progress.Aux(p.config.ProgressOutput, PushResult{Tag: tagged.Tag(), Digest: manifestDigest, Size: manifestSize})
+
+	putOptions := []distribution.ManifestServiceOption{client.WithTag(ref.Tag())}
+	if _, err = manSvc.Put(ctx, manifest, putOptions...); err != nil {
+		logrus.Warnf("failed to upload schema2 manifest: %v - falling back to schema1", err)
+
+		builder = schema1.NewConfigManifestBuilder(p.repo.Blobs(ctx), p.config.TrustKey, p.repo.Name(), ref.Tag(), img.RawJSON())
+		manifest, err = manifestFromBuilder(ctx, builder, descriptors)
+		if err != nil {
+			return err
+		}
+
+		if _, err = manSvc.Put(ctx, manifest, putOptions...); err != nil {
+			return err
 		}
 	}
 
-	manSvc, err := p.repo.Manifests(ctx)
-	if err != nil {
-		return err
+	var canonicalManifest []byte
+
+	switch v := manifest.(type) {
+	case *schema1.SignedManifest:
+		canonicalManifest = v.Canonical
+	case *schema2.DeserializedManifest:
+		_, canonicalManifest, err = v.Payload()
+		if err != nil {
+			return err
+		}
 	}
 
-	if tagged, isTagged := ref.(reference.NamedTagged); isTagged {
-		_, err = manSvc.Put(ctx, manifest, client.WithTag(tagged.Tag()))
-	} else {
-		_, err = manSvc.Put(ctx, manifest)
+	manifestDigest := digest.FromBytes(canonicalManifest)
+	progress.Messagef(p.config.ProgressOutput, "", "%s: digest: %s size: %d", ref.Tag(), manifestDigest, len(canonicalManifest))
+	// Signal digest to the trust client so it can sign the
+	// push, if appropriate.
+	progress.Aux(p.config.ProgressOutput, PushResult{Tag: ref.Tag(), Digest: manifestDigest, Size: len(canonicalManifest)})
+
+	return nil
+}
+
+func manifestFromBuilder(ctx context.Context, builder distribution.ManifestBuilder, descriptors []xfer.UploadDescriptor) (distribution.Manifest, error) {
+	// descriptors is in reverse order; iterate backwards to get references
+	// appended in the right order.
+	for i := len(descriptors) - 1; i >= 0; i-- {
+		if err := builder.AppendReference(descriptors[i].(*v2PushDescriptor)); err != nil {
+			return nil, err
+		}
 	}
-	// FIXME create a tag
-	return err
+
+	return builder.Build(ctx)
 }
 
 type v2PushDescriptor struct {

+ 0 - 7
distribution/registry.go

@@ -9,14 +9,11 @@ import (
 	"time"
 
 	"github.com/docker/distribution"
-	"github.com/docker/distribution/digest"
-	"github.com/docker/distribution/manifest/schema1"
 	"github.com/docker/distribution/registry/api/errcode"
 	"github.com/docker/distribution/registry/client"
 	"github.com/docker/distribution/registry/client/auth"
 	"github.com/docker/distribution/registry/client/transport"
 	"github.com/docker/docker/distribution/xfer"
-	"github.com/docker/docker/reference"
 	"github.com/docker/docker/registry"
 	"github.com/docker/engine-api/types"
 	"golang.org/x/net/context"
@@ -124,10 +121,6 @@ func NewV2Repository(ctx context.Context, repoInfo *registry.RepositoryInfo, end
 	return repo, foundVersion, err
 }
 
-func digestFromManifest(m *schema1.SignedManifest, name reference.Named) (digest.Digest, int, error) {
-	return digest.FromBytes(m.Canonical), len(m.Canonical), nil
-}
-
 type existingTokenHandler struct {
 	token string
 }