فهرست منبع

Fix layer exclusion bug on multiple tag push

Ensure that layers are not excluded from manifests based on previous pushes.
Continue skipping pushes on layers which were pushed by a previous tag.

Update push multiple tag tests.
Ensure that each tag pushed exists on the registry and is pullable.
Add output comparison on multiple tag push check.

fixes #15536

Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)
Derek McGowan 10 سال پیش
والد
کامیت
a0d9ffd6f7
2فایلهای تغییر یافته به همراه25 افزوده شده و 6 حذف شده
  1. 8 6
      graph/push.go
  2. 17 0
      graph/push_v2.go

+ 8 - 6
graph/push.go

@@ -5,6 +5,7 @@ import (
 	"io"
 	"io"
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
+	"github.com/docker/distribution/digest"
 	"github.com/docker/docker/cliconfig"
 	"github.com/docker/docker/cliconfig"
 	"github.com/docker/docker/pkg/streamformatter"
 	"github.com/docker/docker/pkg/streamformatter"
 	"github.com/docker/docker/registry"
 	"github.com/docker/docker/registry"
@@ -44,12 +45,13 @@ func (s *TagStore) NewPusher(endpoint registry.APIEndpoint, localRepo Repository
 	switch endpoint.Version {
 	switch endpoint.Version {
 	case registry.APIVersion2:
 	case registry.APIVersion2:
 		return &v2Pusher{
 		return &v2Pusher{
-			TagStore:  s,
-			endpoint:  endpoint,
-			localRepo: localRepo,
-			repoInfo:  repoInfo,
-			config:    imagePushConfig,
-			sf:        sf,
+			TagStore:     s,
+			endpoint:     endpoint,
+			localRepo:    localRepo,
+			repoInfo:     repoInfo,
+			config:       imagePushConfig,
+			sf:           sf,
+			layersPushed: make(map[digest.Digest]bool),
 		}, nil
 		}, nil
 	case registry.APIVersion1:
 	case registry.APIVersion1:
 		return &v1Pusher{
 		return &v1Pusher{

+ 17 - 0
graph/push_v2.go

@@ -27,6 +27,11 @@ type v2Pusher struct {
 	config    *ImagePushConfig
 	config    *ImagePushConfig
 	sf        *streamformatter.StreamFormatter
 	sf        *streamformatter.StreamFormatter
 	repo      distribution.Repository
 	repo      distribution.Repository
+
+	// layersPushed is the set of layers known to exist on the remote side.
+	// This avoids redundant queries when pushing multiple tags that
+	// involve the same layers.
+	layersPushed map[digest.Digest]bool
 }
 }
 
 
 func (p *v2Pusher) Push() (fallback bool, err error) {
 func (p *v2Pusher) Push() (fallback bool, err error) {
@@ -117,6 +122,10 @@ func (p *v2Pusher) pushV2Tag(tag string) error {
 			return err
 			return err
 		}
 		}
 
 
+		// break early if layer has already been seen in this image,
+		// this prevents infinite loops on layers which loopback, this
+		// cannot be prevented since layer IDs are not merkle hashes
+		// TODO(dmcgowan): throw error if no valid use case is found
 		if layersSeen[layer.ID] {
 		if layersSeen[layer.ID] {
 			break
 			break
 		}
 		}
@@ -138,6 +147,13 @@ func (p *v2Pusher) pushV2Tag(tag string) error {
 		dgst, err := p.graph.GetDigest(layer.ID)
 		dgst, err := p.graph.GetDigest(layer.ID)
 		switch err {
 		switch err {
 		case nil:
 		case nil:
+			if p.layersPushed[dgst] {
+				exists = true
+				// break out of switch, it is already known that
+				// the push is not needed and therefore doing a
+				// stat is unnecessary
+				break
+			}
 			_, err := p.repo.Blobs(context.Background()).Stat(context.Background(), dgst)
 			_, err := p.repo.Blobs(context.Background()).Stat(context.Background(), dgst)
 			switch err {
 			switch err {
 			case nil:
 			case nil:
@@ -173,6 +189,7 @@ func (p *v2Pusher) pushV2Tag(tag string) error {
 		m.History = append(m.History, manifest.History{V1Compatibility: string(jsonData)})
 		m.History = append(m.History, manifest.History{V1Compatibility: string(jsonData)})
 
 
 		layersSeen[layer.ID] = true
 		layersSeen[layer.ID] = true
+		p.layersPushed[dgst] = true
 	}
 	}
 
 
 	logrus.Infof("Signed manifest for %s:%s using daemon's key: %s", p.repo.Name(), tag, p.trustKey.KeyID())
 	logrus.Infof("Signed manifest for %s:%s using daemon's key: %s", p.repo.Name(), tag, p.trustKey.KeyID())