Skip to content

Commit 495d623

Browse files
committed
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 <[email protected]>
1 parent ab373df commit 495d623

2 files changed

Lines changed: 54 additions & 6 deletions

File tree

distribution/errors.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,23 @@ func TranslatePullError(err error, ref reference.Named) error {
112112
return errdefs.Unknown(err)
113113
}
114114

115+
func isNotFound(err error) bool {
116+
switch v := err.(type) {
117+
case errcode.Errors:
118+
for _, e := range v {
119+
if isNotFound(e) {
120+
return true
121+
}
122+
}
123+
case errcode.Error:
124+
switch v.Code {
125+
case errcode.ErrorCodeDenied, v2.ErrorCodeManifestUnknown, v2.ErrorCodeNameUnknown:
126+
return true
127+
}
128+
}
129+
return false
130+
}
131+
115132
// continueOnError returns true if we should fallback to the next endpoint
116133
// as a result of this error.
117134
func continueOnError(err error, mirrorEndpoint bool) bool {

distribution/pull_v2.go

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -343,16 +343,19 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named, platform
343343
dgst digest.Digest
344344
mt string
345345
size int64
346+
tagged reference.NamedTagged
347+
isTagged bool
346348
)
347349
if digested, isDigested := ref.(reference.Canonical); isDigested {
348350
dgst = digested.Digest()
349351
tagOrDigest = digested.String()
350-
} else if tagged, isTagged := ref.(reference.NamedTagged); isTagged {
352+
} else if tagged, isTagged = ref.(reference.NamedTagged); isTagged {
351353
tagService := p.repo.Tags(ctx)
352354
desc, err := tagService.Get(ctx, tagged.Tag())
353355
if err != nil {
354356
return false, allowV1Fallback(err)
355357
}
358+
356359
dgst = desc.Digest
357360
tagOrDigest = tagged.Tag()
358361
mt = desc.MediaType
@@ -367,13 +370,40 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named, platform
367370
"remote": ref,
368371
}))
369372

370-
manifest, err := p.manifestStore.Get(ctx, specs.Descriptor{
373+
desc := specs.Descriptor{
371374
MediaType: mt,
372375
Digest: dgst,
373376
Size: size,
374-
})
377+
}
378+
manifest, err := p.manifestStore.Get(ctx, desc)
375379
if err != nil {
376-
return false, err
380+
if isTagged && isNotFound(errors.Cause(err)) {
381+
logrus.WithField("ref", ref).WithError(err).Debug("Falling back to pull manifest by tag")
382+
383+
msg := `%s Failed to pull manifest by the resolved digest. This registry does not
384+
appear to conform to the distribution registry specification; falling back to
385+
pull by tag. This fallback is DEPRECATED, and will be removed in a future
386+
release. Please contact admins of %s. %s
387+
`
388+
389+
warnEmoji := "\U000026A0\U0000FE0F"
390+
progress.Messagef(p.config.ProgressOutput, "WARNING", msg, warnEmoji, p.endpoint.URL, warnEmoji)
391+
392+
// Fetch by tag worked, but fetch by digest didn't.
393+
// This is a broken registry implementation.
394+
// We'll fallback to the old behavior and get the manifest by tag.
395+
var ms distribution.ManifestService
396+
ms, err = p.repo.Manifests(ctx)
397+
if err != nil {
398+
return false, err
399+
}
400+
401+
manifest, err = ms.Get(ctx, "", distribution.WithTag(tagged.Tag()))
402+
err = errors.Wrap(err, "error after falling back to get manifest by tag")
403+
}
404+
if err != nil {
405+
return false, err
406+
}
377407
}
378408

379409
if manifest == nil {
@@ -818,11 +848,12 @@ func (p *v2Puller) pullManifestList(ctx context.Context, ref reference.Named, mf
818848
return "", "", err
819849
}
820850

821-
manifest, err := p.manifestStore.Get(ctx, specs.Descriptor{
851+
desc := specs.Descriptor{
822852
Digest: match.Digest,
823853
Size: match.Size,
824854
MediaType: match.MediaType,
825-
})
855+
}
856+
manifest, err := p.manifestStore.Get(ctx, desc)
826857
if err != nil {
827858
return "", "", err
828859
}

0 commit comments

Comments
 (0)