Add class to repository scope

Expose registry error translation for plugin distribution

Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)
(cherry picked from commit a12b466183)
Signed-off-by: Victor Vieux <vieux@docker.com>
This commit is contained in:
Derek McGowan 2016-11-15 15:06:48 -08:00 committed by Victor Vieux
parent 9ecbaa77ae
commit cca0132fcf
8 changed files with 34 additions and 17 deletions

View File

@ -60,21 +60,25 @@ func shouldV2Fallback(err errcode.Error) bool {
return false 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) { switch v := err.(type) {
case errcode.Errors: case errcode.Errors:
if len(v) != 0 { if len(v) != 0 {
for _, extra := range v[1:] { for _, extra := range v[1:] {
logrus.Infof("Ignoring extra error returned from registry: %v", extra) logrus.Infof("Ignoring extra error returned from registry: %v", extra)
} }
return translatePullError(v[0], ref) return TranslatePullError(v[0], ref)
} }
case errcode.Error: case errcode.Error:
var newErr error var newErr error
switch v.Code { switch v.Code {
case errcode.ErrorCodeDenied: case errcode.ErrorCodeDenied:
// ErrorCodeDenied is used when access to the repository was denied // 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: case v2.ErrorCodeManifestUnknown:
newErr = errors.Errorf("manifest for %s not found", ref.String()) newErr = errors.Errorf("manifest for %s not found", ref.String())
case v2.ErrorCodeNameUnknown: case v2.ErrorCodeNameUnknown:
@ -85,7 +89,7 @@ func translatePullError(err error, ref reference.Named) error {
return newErr return newErr
} }
case xfer.DoNotRetry: case xfer.DoNotRetry:
return translatePullError(v.Err, ref) return TranslatePullError(v.Err, ref)
} }
return err return err

View File

@ -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 translatePullError(err, ref) 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 translatePullError(lastErr, ref) 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

View File

@ -70,16 +70,21 @@ func NewV2Repository(ctx context.Context, repoInfo *registry.RepositoryInfo, end
passThruTokenHandler := &existingTokenHandler{token: authConfig.RegistryToken} passThruTokenHandler := &existingTokenHandler{token: authConfig.RegistryToken}
modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, passThruTokenHandler)) modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, passThruTokenHandler))
} else { } 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) creds := registry.NewStaticCredentialStore(authConfig)
tokenHandlerOptions := auth.TokenHandlerOptions{ tokenHandlerOptions := auth.TokenHandlerOptions{
Transport: authTransport, Transport: authTransport,
Credentials: creds, Credentials: creds,
Scopes: []auth.Scope{ Scopes: []auth.Scope{scope},
auth.RepositoryScope{
Repository: repoName,
Actions: actions,
},
},
ClientID: registry.AuthClientID, ClientID: registry.AuthClientID,
} }
tokenHandler := auth.NewTokenHandlerWithOptions(tokenHandlerOptions) tokenHandler := auth.NewTokenHandlerWithOptions(tokenHandlerOptions)

View File

@ -98,7 +98,7 @@ 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))
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 { } 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))

View File

@ -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) logrus.Debugf("pull.go: error in ResolveRepository: %v", err)
return nil, err return nil, err
} }
repoInfo.Class = "plugin"
if err := dockerdist.ValidateRepoName(repoInfo.Name()); err != nil { if err := dockerdist.ValidateRepoName(repoInfo.Name()); err != nil {
logrus.Debugf("pull.go: error in ValidateRepoName: %v", err) 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)) manifest, err := msv.Get(context.Background(), "", distribution.WithTag(tag))
if err != nil { if err != nil {
// TODO: change 401 to 404
logrus.Debugf("pull.go: error in msv.Get(): %v", err) logrus.Debugf("pull.go: error in msv.Get(): %v", err)
return nil, err return nil, dockerdist.TranslatePullError(err, repoInfo)
} }
_, pl, err := manifest.Payload() _, pl, err := manifest.Payload()

View File

@ -27,6 +27,7 @@ func Push(name string, rs registry.Service, metaHeader http.Header, authConfig *
if err != nil { if err != nil {
return "", err return "", err
} }
repoInfo.Class = "plugin"
if err := dockerdist.ValidateRepoName(repoInfo.Name()); err != nil { if err := dockerdist.ValidateRepoName(repoInfo.Name()); err != nil {
return "", err return "", err

View File

@ -280,7 +280,11 @@ func newRepositoryInfo(config *serviceConfig, name reference.Named) (*Repository
return nil, err return nil, err
} }
official := !strings.ContainsRune(name.Name(), '/') 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 // ParseRepositoryInfo performs the breakdown of a repository name into a RepositoryInfo, but

View File

@ -67,4 +67,7 @@ type RepositoryInfo struct {
// If the registry is official, and the normalized name does not // If the registry is official, and the normalized name does not
// contain a '/' (e.g. "foo"), then it is considered an official repo. // contain a '/' (e.g. "foo"), then it is considered an official repo.
Official bool Official bool
// Class represents the class of the repository, such as "plugin"
// or "image".
Class string
} }