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)
| ... | ... |
@@ -5,6 +5,7 @@ import ( |
| 5 | 5 |
"io" |
| 6 | 6 |
|
| 7 | 7 |
"github.com/Sirupsen/logrus" |
| 8 |
+ "github.com/docker/distribution/digest" |
|
| 8 | 9 |
"github.com/docker/docker/cliconfig" |
| 9 | 10 |
"github.com/docker/docker/pkg/streamformatter" |
| 10 | 11 |
"github.com/docker/docker/registry" |
| ... | ... |
@@ -44,12 +45,13 @@ func (s *TagStore) NewPusher(endpoint registry.APIEndpoint, localRepo Repository |
| 44 | 44 |
switch endpoint.Version {
|
| 45 | 45 |
case registry.APIVersion2: |
| 46 | 46 |
return &v2Pusher{
|
| 47 |
- TagStore: s, |
|
| 48 |
- endpoint: endpoint, |
|
| 49 |
- localRepo: localRepo, |
|
| 50 |
- repoInfo: repoInfo, |
|
| 51 |
- config: imagePushConfig, |
|
| 52 |
- sf: sf, |
|
| 47 |
+ TagStore: s, |
|
| 48 |
+ endpoint: endpoint, |
|
| 49 |
+ localRepo: localRepo, |
|
| 50 |
+ repoInfo: repoInfo, |
|
| 51 |
+ config: imagePushConfig, |
|
| 52 |
+ sf: sf, |
|
| 53 |
+ layersPushed: make(map[digest.Digest]bool), |
|
| 53 | 54 |
}, nil |
| 54 | 55 |
case registry.APIVersion1: |
| 55 | 56 |
return &v1Pusher{
|
| ... | ... |
@@ -27,6 +27,11 @@ type v2Pusher struct {
|
| 27 | 27 |
config *ImagePushConfig |
| 28 | 28 |
sf *streamformatter.StreamFormatter |
| 29 | 29 |
repo distribution.Repository |
| 30 |
+ |
|
| 31 |
+ // layersPushed is the set of layers known to exist on the remote side. |
|
| 32 |
+ // This avoids redundant queries when pushing multiple tags that |
|
| 33 |
+ // involve the same layers. |
|
| 34 |
+ layersPushed map[digest.Digest]bool |
|
| 30 | 35 |
} |
| 31 | 36 |
|
| 32 | 37 |
func (p *v2Pusher) Push() (fallback bool, err error) {
|
| ... | ... |
@@ -117,6 +122,10 @@ func (p *v2Pusher) pushV2Tag(tag string) error {
|
| 117 | 117 |
return err |
| 118 | 118 |
} |
| 119 | 119 |
|
| 120 |
+ // break early if layer has already been seen in this image, |
|
| 121 |
+ // this prevents infinite loops on layers which loopback, this |
|
| 122 |
+ // cannot be prevented since layer IDs are not merkle hashes |
|
| 123 |
+ // TODO(dmcgowan): throw error if no valid use case is found |
|
| 120 | 124 |
if layersSeen[layer.ID] {
|
| 121 | 125 |
break |
| 122 | 126 |
} |
| ... | ... |
@@ -138,6 +147,13 @@ func (p *v2Pusher) pushV2Tag(tag string) error {
|
| 138 | 138 |
dgst, err := p.graph.GetDigest(layer.ID) |
| 139 | 139 |
switch err {
|
| 140 | 140 |
case nil: |
| 141 |
+ if p.layersPushed[dgst] {
|
|
| 142 |
+ exists = true |
|
| 143 |
+ // break out of switch, it is already known that |
|
| 144 |
+ // the push is not needed and therefore doing a |
|
| 145 |
+ // stat is unnecessary |
|
| 146 |
+ break |
|
| 147 |
+ } |
|
| 141 | 148 |
_, err := p.repo.Blobs(context.Background()).Stat(context.Background(), dgst) |
| 142 | 149 |
switch err {
|
| 143 | 150 |
case nil: |
| ... | ... |
@@ -173,6 +189,7 @@ func (p *v2Pusher) pushV2Tag(tag string) error {
|
| 173 | 173 |
m.History = append(m.History, manifest.History{V1Compatibility: string(jsonData)})
|
| 174 | 174 |
|
| 175 | 175 |
layersSeen[layer.ID] = true |
| 176 |
+ p.layersPushed[dgst] = true |
|
| 176 | 177 |
} |
| 177 | 178 |
|
| 178 | 179 |
logrus.Infof("Signed manifest for %s:%s using daemon's key: %s", p.repo.Name(), tag, p.trustKey.KeyID())
|