mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Add fallback for pull by tag
Some registries seem to be non-conformant and return a not found error when pulling by digest (which docker now does all the time). To work around this, fallback when all of the following are true: 1. Image reference is a tag 2. Tag->digest resolution succeeds 3. Fetch by resolved digest fails with a "not found" error. This is intentionally not caching the manifests to reduce complexity for this edge case. Signed-off-by: Brian Goff <cpuguy83@gmail.com>
This commit is contained in:
parent
ab373df112
commit
495d623ae5
2 changed files with 54 additions and 6 deletions
|
@ -112,6 +112,23 @@ func TranslatePullError(err error, ref reference.Named) error {
|
||||||
return errdefs.Unknown(err)
|
return errdefs.Unknown(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isNotFound(err error) bool {
|
||||||
|
switch v := err.(type) {
|
||||||
|
case errcode.Errors:
|
||||||
|
for _, e := range v {
|
||||||
|
if isNotFound(e) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case errcode.Error:
|
||||||
|
switch v.Code {
|
||||||
|
case errcode.ErrorCodeDenied, v2.ErrorCodeManifestUnknown, v2.ErrorCodeNameUnknown:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// continueOnError returns true if we should fallback to the next endpoint
|
// continueOnError returns true if we should fallback to the next endpoint
|
||||||
// as a result of this error.
|
// as a result of this error.
|
||||||
func continueOnError(err error, mirrorEndpoint bool) bool {
|
func continueOnError(err error, mirrorEndpoint bool) bool {
|
||||||
|
|
|
@ -343,16 +343,19 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named, platform
|
||||||
dgst digest.Digest
|
dgst digest.Digest
|
||||||
mt string
|
mt string
|
||||||
size int64
|
size int64
|
||||||
|
tagged reference.NamedTagged
|
||||||
|
isTagged bool
|
||||||
)
|
)
|
||||||
if digested, isDigested := ref.(reference.Canonical); isDigested {
|
if digested, isDigested := ref.(reference.Canonical); isDigested {
|
||||||
dgst = digested.Digest()
|
dgst = digested.Digest()
|
||||||
tagOrDigest = digested.String()
|
tagOrDigest = digested.String()
|
||||||
} else if tagged, isTagged := ref.(reference.NamedTagged); isTagged {
|
} else if tagged, isTagged = ref.(reference.NamedTagged); isTagged {
|
||||||
tagService := p.repo.Tags(ctx)
|
tagService := p.repo.Tags(ctx)
|
||||||
desc, err := tagService.Get(ctx, tagged.Tag())
|
desc, err := tagService.Get(ctx, tagged.Tag())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, allowV1Fallback(err)
|
return false, allowV1Fallback(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
dgst = desc.Digest
|
dgst = desc.Digest
|
||||||
tagOrDigest = tagged.Tag()
|
tagOrDigest = tagged.Tag()
|
||||||
mt = desc.MediaType
|
mt = desc.MediaType
|
||||||
|
@ -367,13 +370,40 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named, platform
|
||||||
"remote": ref,
|
"remote": ref,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
manifest, err := p.manifestStore.Get(ctx, specs.Descriptor{
|
desc := specs.Descriptor{
|
||||||
MediaType: mt,
|
MediaType: mt,
|
||||||
Digest: dgst,
|
Digest: dgst,
|
||||||
Size: size,
|
Size: size,
|
||||||
})
|
}
|
||||||
|
manifest, err := p.manifestStore.Get(ctx, desc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
if isTagged && isNotFound(errors.Cause(err)) {
|
||||||
|
logrus.WithField("ref", ref).WithError(err).Debug("Falling back to pull manifest by tag")
|
||||||
|
|
||||||
|
msg := `%s Failed to pull manifest by the resolved digest. This registry does not
|
||||||
|
appear to conform to the distribution registry specification; falling back to
|
||||||
|
pull by tag. This fallback is DEPRECATED, and will be removed in a future
|
||||||
|
release. Please contact admins of %s. %s
|
||||||
|
`
|
||||||
|
|
||||||
|
warnEmoji := "\U000026A0\U0000FE0F"
|
||||||
|
progress.Messagef(p.config.ProgressOutput, "WARNING", msg, warnEmoji, p.endpoint.URL, warnEmoji)
|
||||||
|
|
||||||
|
// Fetch by tag worked, but fetch by digest didn't.
|
||||||
|
// This is a broken registry implementation.
|
||||||
|
// We'll fallback to the old behavior and get the manifest by tag.
|
||||||
|
var ms distribution.ManifestService
|
||||||
|
ms, err = p.repo.Manifests(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
manifest, err = ms.Get(ctx, "", distribution.WithTag(tagged.Tag()))
|
||||||
|
err = errors.Wrap(err, "error after falling back to get manifest by tag")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if manifest == nil {
|
if manifest == nil {
|
||||||
|
@ -818,11 +848,12 @@ func (p *v2Puller) pullManifestList(ctx context.Context, ref reference.Named, mf
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
manifest, err := p.manifestStore.Get(ctx, specs.Descriptor{
|
desc := specs.Descriptor{
|
||||||
Digest: match.Digest,
|
Digest: match.Digest,
|
||||||
Size: match.Size,
|
Size: match.Size,
|
||||||
MediaType: match.MediaType,
|
MediaType: match.MediaType,
|
||||||
})
|
}
|
||||||
|
manifest, err := p.manifestStore.Get(ctx, desc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue