зеркало из https://github.com/microsoft/docker.git
Add schema2 push support
Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
This commit is contained in:
Родитель
94726f7c73
Коммит
c8d277d228
|
@ -1,6 +1,7 @@
|
||||||
package distribution
|
package distribution
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -13,6 +14,7 @@ import (
|
||||||
"github.com/docker/distribution/registry/client"
|
"github.com/docker/distribution/registry/client"
|
||||||
"github.com/docker/docker/distribution/metadata"
|
"github.com/docker/docker/distribution/metadata"
|
||||||
"github.com/docker/docker/distribution/xfer"
|
"github.com/docker/docker/distribution/xfer"
|
||||||
|
"github.com/docker/docker/image"
|
||||||
"github.com/docker/docker/layer"
|
"github.com/docker/docker/layer"
|
||||||
"github.com/docker/docker/pkg/ioutils"
|
"github.com/docker/docker/pkg/ioutils"
|
||||||
"github.com/docker/docker/pkg/progress"
|
"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) {
|
func (p *v2Pusher) pushV2Repository(ctx context.Context) (err error) {
|
||||||
var associations []reference.Association
|
if namedTagged, isNamedTagged := p.ref.(reference.NamedTagged); isNamedTagged {
|
||||||
if _, isTagged := p.ref.(reference.NamedTagged); isTagged {
|
|
||||||
imageID, err := p.config.ReferenceStore.Get(p.ref)
|
imageID, err := p.config.ReferenceStore.Get(p.ref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("tag does not exist: %s", p.ref.String())
|
return fmt.Errorf("tag does not exist: %s", p.ref.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
associations = []reference.Association{
|
return p.pushV2Tag(ctx, namedTagged, imageID)
|
||||||
{
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
if len(associations) == 0 {
|
|
||||||
return fmt.Errorf("no tags to push for %s", p.repoInfo.Name())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, association := range associations {
|
if !reference.IsNameOnly(p.ref) {
|
||||||
if err := p.pushV2Tag(ctx, association); err != nil {
|
return errors.New("cannot push a digest reference")
|
||||||
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *v2Pusher) pushV2Tag(ctx context.Context, association reference.Association) error {
|
func (p *v2Pusher) pushV2Tag(ctx context.Context, ref reference.NamedTagged, imageID image.ID) error {
|
||||||
ref := association.Ref
|
|
||||||
logrus.Debugf("Pushing repository: %s", ref.String())
|
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 {
|
if err != nil {
|
||||||
return fmt.Errorf("could not find image from tag %s: %v", ref.String(), err)
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var tag string
|
// Try schema2 first
|
||||||
if tagged, isTagged := ref.(reference.NamedTagged); isTagged {
|
builder := schema2.NewManifestBuilder(p.repo.Blobs(ctx), img.RawJSON())
|
||||||
tag = tagged.Tag()
|
manifest, err := manifestFromBuilder(ctx, builder, descriptors)
|
||||||
}
|
|
||||||
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)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
manifestDigest, manifestSize, err := digestFromManifest(manifest.(*schema1.SignedManifest), ref)
|
|
||||||
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})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
manSvc, err := p.repo.Manifests(ctx)
|
manSvc, err := p.repo.Manifests(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if tagged, isTagged := ref.(reference.NamedTagged); isTagged {
|
putOptions := []distribution.ManifestServiceOption{client.WithTag(ref.Tag())}
|
||||||
_, err = manSvc.Put(ctx, manifest, client.WithTag(tagged.Tag()))
|
if _, err = manSvc.Put(ctx, manifest, putOptions...); err != nil {
|
||||||
} else {
|
logrus.Warnf("failed to upload schema2 manifest: %v - falling back to schema1", err)
|
||||||
_, err = manSvc.Put(ctx, manifest)
|
|
||||||
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// FIXME create a tag
|
|
||||||
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.Build(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
type v2PushDescriptor struct {
|
type v2PushDescriptor struct {
|
||||||
|
|
|
@ -9,14 +9,11 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/docker/distribution"
|
"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/api/errcode"
|
||||||
"github.com/docker/distribution/registry/client"
|
"github.com/docker/distribution/registry/client"
|
||||||
"github.com/docker/distribution/registry/client/auth"
|
"github.com/docker/distribution/registry/client/auth"
|
||||||
"github.com/docker/distribution/registry/client/transport"
|
"github.com/docker/distribution/registry/client/transport"
|
||||||
"github.com/docker/docker/distribution/xfer"
|
"github.com/docker/docker/distribution/xfer"
|
||||||
"github.com/docker/docker/reference"
|
|
||||||
"github.com/docker/docker/registry"
|
"github.com/docker/docker/registry"
|
||||||
"github.com/docker/engine-api/types"
|
"github.com/docker/engine-api/types"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
|
@ -124,10 +121,6 @@ func NewV2Repository(ctx context.Context, repoInfo *registry.RepositoryInfo, end
|
||||||
return repo, foundVersion, err
|
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 {
|
type existingTokenHandler struct {
|
||||||
token string
|
token string
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче