diff --git a/distribution/errors.go b/distribution/errors.go index 7f9e20f279..b8cf9fb9e8 100644 --- a/distribution/errors.go +++ b/distribution/errors.go @@ -60,21 +60,25 @@ func shouldV2Fallback(err errcode.Error) bool { return false } -func translatePullError(err error, ref reference.Named) error { +// TranslatePullError is used to convert an error from a registry pull +// operation to an error representing the entire pull operation. Any error +// information which is not used by the returned error gets output to +// log at info level. +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) + 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()) + newErr = errors.Errorf("repository %s not found: does not exist or no pull access", ref.Name()) case v2.ErrorCodeManifestUnknown: newErr = errors.Errorf("manifest for %s not found", ref.String()) case v2.ErrorCodeNameUnknown: @@ -85,7 +89,7 @@ func translatePullError(err error, ref reference.Named) error { return newErr } case xfer.DoNotRetry: - return translatePullError(v.Err, ref) + return TranslatePullError(v.Err, ref) } return err diff --git a/distribution/pull.go b/distribution/pull.go index 5307d4ccc9..b631788b49 100644 --- a/distribution/pull.go +++ b/distribution/pull.go @@ -168,7 +168,7 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo continue } logrus.Errorf("Not continuing with pull after error: %v", err) - return translatePullError(err, ref) + return TranslatePullError(err, ref) } 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()) } - return translatePullError(lastErr, ref) + return TranslatePullError(lastErr, ref) } // writeStatus writes a status message to out. If layersDownloaded is true, the diff --git a/distribution/registry.go b/distribution/registry.go index d3c991e875..4c3513046d 100644 --- a/distribution/registry.go +++ b/distribution/registry.go @@ -70,17 +70,22 @@ func NewV2Repository(ctx context.Context, repoInfo *registry.RepositoryInfo, end passThruTokenHandler := &existingTokenHandler{token: authConfig.RegistryToken} modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, passThruTokenHandler)) } else { + scope := auth.RepositoryScope{ + Repository: repoName, + Actions: actions, + } + + // Keep image repositories blank for scope compatibility + if repoInfo.Class != "image" { + scope.Class = repoInfo.Class + } + creds := registry.NewStaticCredentialStore(authConfig) tokenHandlerOptions := auth.TokenHandlerOptions{ Transport: authTransport, Credentials: creds, - Scopes: []auth.Scope{ - auth.RepositoryScope{ - Repository: repoName, - Actions: actions, - }, - }, - ClientID: registry.AuthClientID, + Scopes: []auth.Scope{scope}, + ClientID: registry.AuthClientID, } tokenHandler := auth.NewTokenHandlerWithOptions(tokenHandlerOptions) basicHandler := auth.NewBasicHandler(creds) diff --git a/integration-cli/docker_cli_pull_test.go b/integration-cli/docker_cli_pull_test.go index c89adae0c6..a0118a8e95 100644 --- a/integration-cli/docker_cli_pull_test.go +++ b/integration-cli/docker_cli_pull_test.go @@ -98,7 +98,7 @@ func (s *DockerHubPullSuite) TestPullNonExistingImage(c *check.C) { for record := range recordChan { 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.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")) + c.Assert(record.out, checker.Contains, fmt.Sprintf("repository %s not found: does not exist or no pull access", record.e.repo), check.Commentf("expected image not found error messages")) } else { // 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)) diff --git a/plugin/distribution/pull.go b/plugin/distribution/pull.go index dba750f2a5..4e8992cc2e 100644 --- a/plugin/distribution/pull.go +++ b/plugin/distribution/pull.go @@ -85,6 +85,7 @@ func Pull(ref reference.Named, rs registry.Service, metaheader http.Header, auth logrus.Debugf("pull.go: error in ResolveRepository: %v", err) return nil, err } + repoInfo.Class = "plugin" if err := dockerdist.ValidateRepoName(repoInfo.Name()); err != nil { logrus.Debugf("pull.go: error in ValidateRepoName: %v", err) @@ -138,9 +139,8 @@ func Pull(ref reference.Named, rs registry.Service, metaheader http.Header, auth } manifest, err := msv.Get(context.Background(), "", distribution.WithTag(tag)) if err != nil { - // TODO: change 401 to 404 logrus.Debugf("pull.go: error in msv.Get(): %v", err) - return nil, err + return nil, dockerdist.TranslatePullError(err, repoInfo) } _, pl, err := manifest.Payload() diff --git a/plugin/distribution/push.go b/plugin/distribution/push.go index 104b684d54..86caadbc1e 100644 --- a/plugin/distribution/push.go +++ b/plugin/distribution/push.go @@ -27,6 +27,7 @@ func Push(name string, rs registry.Service, metaHeader http.Header, authConfig * if err != nil { return "", err } + repoInfo.Class = "plugin" if err := dockerdist.ValidateRepoName(repoInfo.Name()); err != nil { return "", err diff --git a/registry/config.go b/registry/config.go index b53c11bdef..9a4f6a9251 100644 --- a/registry/config.go +++ b/registry/config.go @@ -280,7 +280,11 @@ func newRepositoryInfo(config *serviceConfig, name reference.Named) (*Repository return nil, err } official := !strings.ContainsRune(name.Name(), '/') - return &RepositoryInfo{name, index, official}, nil + return &RepositoryInfo{ + Named: name, + Index: index, + Official: official, + }, nil } // ParseRepositoryInfo performs the breakdown of a repository name into a RepositoryInfo, but diff --git a/registry/types.go b/registry/types.go index 479d22a7a6..49c123a3e2 100644 --- a/registry/types.go +++ b/registry/types.go @@ -67,4 +67,7 @@ type RepositoryInfo struct { // If the registry is official, and the normalized name does not // contain a '/' (e.g. "foo"), then it is considered an official repo. Official bool + // Class represents the class of the repository, such as "plugin" + // or "image". + Class string }