Update pull error handling
Translate pull errors to provide a more consistent and user friendly error message. Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)
This commit is contained in:
parent
c85eb00841
commit
19a93a6e3d
|
@ -5,11 +5,14 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/docker/distribution/registry/api/errcode"
|
"github.com/docker/distribution/registry/api/errcode"
|
||||||
"github.com/docker/distribution/registry/api/v2"
|
"github.com/docker/distribution/registry/api/v2"
|
||||||
"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/docker/distribution/xfer"
|
"github.com/docker/docker/distribution/xfer"
|
||||||
|
"github.com/docker/docker/reference"
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrNoSupport is an error type used for errors indicating that an operation
|
// ErrNoSupport is an error type used for errors indicating that an operation
|
||||||
|
@ -56,6 +59,37 @@ func shouldV2Fallback(err errcode.Error) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func translatePullError(err error, ref reference.Named) error {
|
||||||
|
switch v := err.(type) {
|
||||||
|
case errcode.Errors:
|
||||||
|
if len(v) != 0 {
|
||||||
|
for _, extra := range v[1:] {
|
||||||
|
logrus.Infof("Ignoring extra error returned from registry: %v", extra)
|
||||||
|
}
|
||||||
|
return translatePullError(v[0], ref)
|
||||||
|
}
|
||||||
|
case errcode.Error:
|
||||||
|
var newErr error
|
||||||
|
switch v.Code {
|
||||||
|
case errcode.ErrorCodeDenied:
|
||||||
|
// ErrorCodeDenied is used when access to the repository was denied
|
||||||
|
newErr = errors.Errorf("repository %s not found: does not exist or no read access", ref.Name())
|
||||||
|
case v2.ErrorCodeManifestUnknown:
|
||||||
|
newErr = errors.Errorf("manifest for %s not found", ref.String())
|
||||||
|
case v2.ErrorCodeNameUnknown:
|
||||||
|
newErr = errors.Errorf("repository %s not found", ref.Name())
|
||||||
|
}
|
||||||
|
if newErr != nil {
|
||||||
|
logrus.Infof("Translating %q to %q", err, newErr)
|
||||||
|
return newErr
|
||||||
|
}
|
||||||
|
case xfer.DoNotRetry:
|
||||||
|
return translatePullError(v.Err, ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// 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) bool {
|
func continueOnError(err error) bool {
|
||||||
|
|
|
@ -168,7 +168,7 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
logrus.Errorf("Not continuing with pull after error: %v", err)
|
logrus.Errorf("Not continuing with pull after error: %v", err)
|
||||||
return err
|
return translatePullError(err, ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
imagePullConfig.ImageEventLogger(ref.String(), repoInfo.Name(), "pull")
|
imagePullConfig.ImageEventLogger(ref.String(), repoInfo.Name(), "pull")
|
||||||
|
@ -179,7 +179,7 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo
|
||||||
lastErr = fmt.Errorf("no endpoints found for %s", ref.String())
|
lastErr = fmt.Errorf("no endpoints found for %s", ref.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
return lastErr
|
return translatePullError(lastErr, ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeStatus writes a status message to out. If layersDownloaded is true, the
|
// writeStatus writes a status message to out. If layersDownloaded is true, the
|
||||||
|
|
|
@ -114,7 +114,7 @@ func testPullByDigestNoFallback(c *check.C) {
|
||||||
imageReference := fmt.Sprintf("%s@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", repoName)
|
imageReference := fmt.Sprintf("%s@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", repoName)
|
||||||
out, _, err := dockerCmdWithError("pull", imageReference)
|
out, _, err := dockerCmdWithError("pull", imageReference)
|
||||||
c.Assert(err, checker.NotNil, check.Commentf("expected non-zero exit status and correct error message when pulling non-existing image"))
|
c.Assert(err, checker.NotNil, check.Commentf("expected non-zero exit status and correct error message when pulling non-existing image"))
|
||||||
c.Assert(out, checker.Contains, "manifest unknown", check.Commentf("expected non-zero exit status and correct error message when pulling non-existing image"))
|
c.Assert(out, checker.Contains, fmt.Sprintf("manifest for %s not found", imageReference), check.Commentf("expected non-zero exit status and correct error message when pulling non-existing image"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerRegistrySuite) TestPullByDigestNoFallback(c *check.C) {
|
func (s *DockerRegistrySuite) TestPullByDigestNoFallback(c *check.C) {
|
||||||
|
|
|
@ -48,12 +48,12 @@ func (s *DockerHubPullSuite) TestPullNonExistingImage(c *check.C) {
|
||||||
}
|
}
|
||||||
|
|
||||||
entries := []entry{
|
entries := []entry{
|
||||||
{"library/asdfasdf", "asdfasdf", "foobar"},
|
{"asdfasdf", "asdfasdf", "foobar"},
|
||||||
{"library/asdfasdf", "library/asdfasdf", "foobar"},
|
{"asdfasdf", "library/asdfasdf", "foobar"},
|
||||||
{"library/asdfasdf", "asdfasdf", ""},
|
{"asdfasdf", "asdfasdf", ""},
|
||||||
{"library/asdfasdf", "asdfasdf", "latest"},
|
{"asdfasdf", "asdfasdf", "latest"},
|
||||||
{"library/asdfasdf", "library/asdfasdf", ""},
|
{"asdfasdf", "library/asdfasdf", ""},
|
||||||
{"library/asdfasdf", "library/asdfasdf", "latest"},
|
{"asdfasdf", "library/asdfasdf", "latest"},
|
||||||
}
|
}
|
||||||
|
|
||||||
// The option field indicates "-a" or not.
|
// The option field indicates "-a" or not.
|
||||||
|
@ -98,18 +98,11 @@ func (s *DockerHubPullSuite) TestPullNonExistingImage(c *check.C) {
|
||||||
for record := range recordChan {
|
for record := range recordChan {
|
||||||
if len(record.option) == 0 {
|
if len(record.option) == 0 {
|
||||||
c.Assert(record.err, checker.NotNil, check.Commentf("expected non-zero exit status when pulling non-existing image: %s", record.out))
|
c.Assert(record.err, checker.NotNil, check.Commentf("expected non-zero exit status when pulling non-existing image: %s", record.out))
|
||||||
// Hub returns 401 rather than 404 for nonexistent repos over
|
c.Assert(record.out, checker.Contains, fmt.Sprintf("repository %s not found: does not exist or no read access", record.e.repo), check.Commentf("expected image not found error messages"))
|
||||||
// the v2 protocol - but we should end up falling back to v1,
|
|
||||||
// which does return a 404.
|
|
||||||
tag := record.e.tag
|
|
||||||
if tag == "" {
|
|
||||||
tag = "latest"
|
|
||||||
}
|
|
||||||
c.Assert(record.out, checker.Contains, fmt.Sprintf("Error: image %s:%s not found", record.e.repo, tag), check.Commentf("expected image not found error messages"))
|
|
||||||
} else {
|
} else {
|
||||||
// pull -a on a nonexistent registry should fall back as well
|
// pull -a on a nonexistent registry should fall back as well
|
||||||
c.Assert(record.err, checker.NotNil, check.Commentf("expected non-zero exit status when pulling non-existing image: %s", record.out))
|
c.Assert(record.err, checker.NotNil, check.Commentf("expected non-zero exit status when pulling non-existing image: %s", record.out))
|
||||||
c.Assert(record.out, checker.Contains, fmt.Sprintf("Error: image %s not found", record.e.repo), check.Commentf("expected image not found error messages"))
|
c.Assert(record.out, checker.Contains, fmt.Sprintf("repository %s not found", record.e.repo), check.Commentf("expected image not found error messages"))
|
||||||
c.Assert(record.out, checker.Not(checker.Contains), "unauthorized", check.Commentf(`message should not contain "unauthorized"`))
|
c.Assert(record.out, checker.Not(checker.Contains), "unauthorized", check.Commentf(`message should not contain "unauthorized"`))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue