From d1f5e0f7a68d8a43931b834ce342830956bdac32 Mon Sep 17 00:00:00 2001 From: Derek McGowan Date: Tue, 15 Nov 2016 17:31:54 -0800 Subject: [PATCH 1/2] Update vendor distribution Distribution client change for class in resource Signed-off-by: Derek McGowan (github: dmcgowan) --- vendor.conf | 2 +- .../docker/distribution/registry/client/auth/session.go | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/vendor.conf b/vendor.conf index c9cd61c390..1e897bb113 100644 --- a/vendor.conf +++ b/vendor.conf @@ -44,7 +44,7 @@ github.com/boltdb/bolt fff57c100f4dea1905678da7e90d92429dff2904 github.com/miekg/dns 75e6e86cc601825c5dbcd4e0c209eab180997cd7 # get graph and distribution packages -github.com/docker/distribution d22e09a6686c32be8c17b684b639da4b90efe320 +github.com/docker/distribution a6bf3dd064f15598166bca2d66a9962a9555139e github.com/vbatts/tar-split v0.10.1 # get go-zfs packages diff --git a/vendor/github.com/docker/distribution/registry/client/auth/session.go b/vendor/github.com/docker/distribution/registry/client/auth/session.go index ffc3384b19..d6d884ffd1 100644 --- a/vendor/github.com/docker/distribution/registry/client/auth/session.go +++ b/vendor/github.com/docker/distribution/registry/client/auth/session.go @@ -147,13 +147,18 @@ type Scope interface { // to a repository. type RepositoryScope struct { Repository string + Class string Actions []string } // String returns the string representation of the repository // using the scope grammar func (rs RepositoryScope) String() string { - return fmt.Sprintf("repository:%s:%s", rs.Repository, strings.Join(rs.Actions, ",")) + repoType := "repository" + if rs.Class != "" { + repoType = fmt.Sprintf("%s(%s)", repoType, rs.Class) + } + return fmt.Sprintf("%s:%s:%s", repoType, rs.Repository, strings.Join(rs.Actions, ",")) } // RegistryScope represents a token scope for access From a12b466183e03621bc9e1c1e4deab6db8ec93f0a Mon Sep 17 00:00:00 2001 From: Derek McGowan Date: Tue, 15 Nov 2016 15:06:48 -0800 Subject: [PATCH 2/2] Add class to repository scope Expose registry error translation for plugin distribution Signed-off-by: Derek McGowan (github: dmcgowan) --- distribution/errors.go | 12 ++++++++---- distribution/pull.go | 4 ++-- distribution/registry.go | 19 ++++++++++++------- integration-cli/docker_cli_pull_test.go | 2 +- plugin/distribution/pull.go | 4 ++-- plugin/distribution/push.go | 1 + registry/config.go | 6 +++++- registry/types.go | 3 +++ 8 files changed, 34 insertions(+), 17 deletions(-) diff --git a/distribution/errors.go b/distribution/errors.go index ed8c3c0534..5ea925c51f 100644 --- a/distribution/errors.go +++ b/distribution/errors.go @@ -59,21 +59,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: @@ -84,7 +88,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 5694be0573..f96b08db1a 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 }