Tizen_4.0 base
[platform/upstream/docker-engine.git] / distribution / errors.go
1 package distribution
2
3 import (
4         "net/url"
5         "strings"
6         "syscall"
7
8         "github.com/Sirupsen/logrus"
9         "github.com/docker/distribution"
10         "github.com/docker/distribution/reference"
11         "github.com/docker/distribution/registry/api/errcode"
12         "github.com/docker/distribution/registry/api/v2"
13         "github.com/docker/distribution/registry/client"
14         "github.com/docker/distribution/registry/client/auth"
15         "github.com/docker/docker/distribution/xfer"
16         "github.com/pkg/errors"
17 )
18
19 // ErrNoSupport is an error type used for errors indicating that an operation
20 // is not supported. It encapsulates a more specific error.
21 type ErrNoSupport struct{ Err error }
22
23 func (e ErrNoSupport) Error() string {
24         if e.Err == nil {
25                 return "not supported"
26         }
27         return e.Err.Error()
28 }
29
30 // fallbackError wraps an error that can possibly allow fallback to a different
31 // endpoint.
32 type fallbackError struct {
33         // err is the error being wrapped.
34         err error
35         // confirmedV2 is set to true if it was confirmed that the registry
36         // supports the v2 protocol. This is used to limit fallbacks to the v1
37         // protocol.
38         confirmedV2 bool
39         // transportOK is set to true if we managed to speak HTTP with the
40         // registry. This confirms that we're using appropriate TLS settings
41         // (or lack of TLS).
42         transportOK bool
43 }
44
45 // Error renders the FallbackError as a string.
46 func (f fallbackError) Error() string {
47         return f.Cause().Error()
48 }
49
50 func (f fallbackError) Cause() error {
51         return f.err
52 }
53
54 // shouldV2Fallback returns true if this error is a reason to fall back to v1.
55 func shouldV2Fallback(err errcode.Error) bool {
56         switch err.Code {
57         case errcode.ErrorCodeUnauthorized, v2.ErrorCodeManifestUnknown, v2.ErrorCodeNameUnknown:
58                 return true
59         }
60         return false
61 }
62
63 // TranslatePullError is used to convert an error from a registry pull
64 // operation to an error representing the entire pull operation. Any error
65 // information which is not used by the returned error gets output to
66 // log at info level.
67 func TranslatePullError(err error, ref reference.Named) error {
68         switch v := err.(type) {
69         case errcode.Errors:
70                 if len(v) != 0 {
71                         for _, extra := range v[1:] {
72                                 logrus.Infof("Ignoring extra error returned from registry: %v", extra)
73                         }
74                         return TranslatePullError(v[0], ref)
75                 }
76         case errcode.Error:
77                 var newErr error
78                 switch v.Code {
79                 case errcode.ErrorCodeDenied:
80                         // ErrorCodeDenied is used when access to the repository was denied
81                         newErr = errors.Errorf("pull access denied for %s, repository does not exist or may require 'docker login'", reference.FamiliarName(ref))
82                 case v2.ErrorCodeManifestUnknown:
83                         newErr = errors.Errorf("manifest for %s not found", reference.FamiliarString(ref))
84                 case v2.ErrorCodeNameUnknown:
85                         newErr = errors.Errorf("repository %s not found", reference.FamiliarName(ref))
86                 }
87                 if newErr != nil {
88                         logrus.Infof("Translating %q to %q", err, newErr)
89                         return newErr
90                 }
91         case xfer.DoNotRetry:
92                 return TranslatePullError(v.Err, ref)
93         }
94
95         return err
96 }
97
98 // continueOnError returns true if we should fallback to the next endpoint
99 // as a result of this error.
100 func continueOnError(err error) bool {
101         switch v := err.(type) {
102         case errcode.Errors:
103                 if len(v) == 0 {
104                         return true
105                 }
106                 return continueOnError(v[0])
107         case ErrNoSupport:
108                 return continueOnError(v.Err)
109         case errcode.Error:
110                 return shouldV2Fallback(v)
111         case *client.UnexpectedHTTPResponseError:
112                 return true
113         case ImageConfigPullError:
114                 return false
115         case error:
116                 return !strings.Contains(err.Error(), strings.ToLower(syscall.ENOSPC.Error()))
117         }
118         // let's be nice and fallback if the error is a completely
119         // unexpected one.
120         // If new errors have to be handled in some way, please
121         // add them to the switch above.
122         return true
123 }
124
125 // retryOnError wraps the error in xfer.DoNotRetry if we should not retry the
126 // operation after this error.
127 func retryOnError(err error) error {
128         switch v := err.(type) {
129         case errcode.Errors:
130                 if len(v) != 0 {
131                         return retryOnError(v[0])
132                 }
133         case errcode.Error:
134                 switch v.Code {
135                 case errcode.ErrorCodeUnauthorized, errcode.ErrorCodeUnsupported, errcode.ErrorCodeDenied, errcode.ErrorCodeTooManyRequests, v2.ErrorCodeNameUnknown:
136                         return xfer.DoNotRetry{Err: err}
137                 }
138         case *url.Error:
139                 switch v.Err {
140                 case auth.ErrNoBasicAuthCredentials, auth.ErrNoToken:
141                         return xfer.DoNotRetry{Err: v.Err}
142                 }
143                 return retryOnError(v.Err)
144         case *client.UnexpectedHTTPResponseError:
145                 return xfer.DoNotRetry{Err: err}
146         case error:
147                 if err == distribution.ErrBlobUnknown {
148                         return xfer.DoNotRetry{Err: err}
149                 }
150                 if strings.Contains(err.Error(), strings.ToLower(syscall.ENOSPC.Error())) {
151                         return xfer.DoNotRetry{Err: err}
152                 }
153         }
154         // let's be nice and fallback if the error is a completely
155         // unexpected one.
156         // If new errors have to be handled in some way, please
157         // add them to the switch above.
158         return err
159 }