This is the first step in converting out static strings into well-defined
error types. This shows just a few examples of it to get a feel for how things
will look. Once we agree on the basic outline we can then work on converting
the rest of the code over.
Signed-off-by: Doug Davis <dug@us.ibm.com>
| 1 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,58 @@ |
| 0 |
+Docker 'errors' package |
|
| 1 |
+======================= |
|
| 2 |
+ |
|
| 3 |
+This package contains all of the error messages generated by the Docker |
|
| 4 |
+engine that might be exposed via the Docker engine's REST API. |
|
| 5 |
+ |
|
| 6 |
+Each top-level engine package will have its own file in this directory |
|
| 7 |
+so that there's a clear grouping of errors, instead of just one big |
|
| 8 |
+file. The errors for each package are defined here instead of within |
|
| 9 |
+their respective package structure so that Docker CLI code that may need |
|
| 10 |
+to import these error definition files will not need to know or understand |
|
| 11 |
+the engine's package/directory structure. In other words, all they should |
|
| 12 |
+need to do is import `.../docker/api/errors` and they will automatically |
|
| 13 |
+pick up all Docker engine defined errors. This also gives the engine |
|
| 14 |
+developers the freedom to change the engine packaging structure (e.g. to |
|
| 15 |
+CRUD packages) without worrying about breaking existing clients. |
|
| 16 |
+ |
|
| 17 |
+These errors are defined using the 'errcode' package. The `errcode` package |
|
| 18 |
+allows for each error to be typed and include all information necessary to |
|
| 19 |
+have further processing done on them if necessary. In particular, each error |
|
| 20 |
+includes: |
|
| 21 |
+ |
|
| 22 |
+* Value - a unique string (in all caps) associated with this error. |
|
| 23 |
+Typically, this string is the same name as the variable name of the error |
|
| 24 |
+(w/o the `ErrorCode` text) but in all caps. |
|
| 25 |
+ |
|
| 26 |
+* Message - the human readable sentence that will be displayed for this |
|
| 27 |
+error. It can contain '%s' substitutions that allows for the code generating |
|
| 28 |
+the error to specify values that will be inserted in the string prior to |
|
| 29 |
+being displayed to the end-user. The `WithArgs()` function can be used to |
|
| 30 |
+specify the insertion strings. Note, the evaluation of the strings will be |
|
| 31 |
+done at the time `WithArgs()` is called. |
|
| 32 |
+ |
|
| 33 |
+* Description - additional human readable text to further explain the |
|
| 34 |
+circumstances of the error situation. |
|
| 35 |
+ |
|
| 36 |
+* HTTPStatusCode - when the error is returned back to a CLI, this value |
|
| 37 |
+will be used to populate the HTTP status code. If not present the default |
|
| 38 |
+value will be `StatusInternalServerError`, 500. |
|
| 39 |
+ |
|
| 40 |
+Not all errors generated within the engine's executable will be propagated |
|
| 41 |
+back to the engine's API layer. For example, it is expected that errors |
|
| 42 |
+generated by vendored code (under `docker/vendor`) and packaged code |
|
| 43 |
+(under `docker/pkg`) will be converted into errors defined by this package. |
|
| 44 |
+ |
|
| 45 |
+When processing an errcode error, if you are looking for a particular |
|
| 46 |
+error then you can do something like: |
|
| 47 |
+ |
|
| 48 |
+``` |
|
| 49 |
+import derr "github.com/docker/docker/api/errors" |
|
| 50 |
+ |
|
| 51 |
+... |
|
| 52 |
+ |
|
| 53 |
+err := someFunc() |
|
| 54 |
+if err.ErrorCode() == derr.ErrorCodeNoSuchContainer {
|
|
| 55 |
+ ... |
|
| 56 |
+} |
|
| 57 |
+``` |
| 0 | 58 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,93 @@ |
| 0 |
+package errors |
|
| 1 |
+ |
|
| 2 |
+// This file contains all of the errors that can be generated from the |
|
| 3 |
+// docker/builder component. |
|
| 4 |
+ |
|
| 5 |
+import ( |
|
| 6 |
+ "net/http" |
|
| 7 |
+ |
|
| 8 |
+ "github.com/docker/distribution/registry/api/errcode" |
|
| 9 |
+) |
|
| 10 |
+ |
|
| 11 |
+var ( |
|
| 12 |
+ // ErrorCodeAtLeastOneArg is generated when the parser comes across a |
|
| 13 |
+ // Dockerfile command that doesn't have any args. |
|
| 14 |
+ ErrorCodeAtLeastOneArg = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
|
| 15 |
+ Value: "ATLEASTONEARG", |
|
| 16 |
+ Message: "%s requires at least one argument", |
|
| 17 |
+ Description: "The specified command requires at least one argument", |
|
| 18 |
+ HTTPStatusCode: http.StatusInternalServerError, |
|
| 19 |
+ }) |
|
| 20 |
+ |
|
| 21 |
+ // ErrorCodeExactlyOneArg is generated when the parser comes across a |
|
| 22 |
+ // Dockerfile command that requires exactly one arg but got less/more. |
|
| 23 |
+ ErrorCodeExactlyOneArg = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
|
| 24 |
+ Value: "EXACTLYONEARG", |
|
| 25 |
+ Message: "%s requires exactly one argument", |
|
| 26 |
+ Description: "The specified command requires exactly one argument", |
|
| 27 |
+ HTTPStatusCode: http.StatusInternalServerError, |
|
| 28 |
+ }) |
|
| 29 |
+ |
|
| 30 |
+ // ErrorCodeAtLeastTwoArgs is generated when the parser comes across a |
|
| 31 |
+ // Dockerfile command that requires at least two args but got less. |
|
| 32 |
+ ErrorCodeAtLeastTwoArgs = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
|
| 33 |
+ Value: "ATLEASTTWOARGS", |
|
| 34 |
+ Message: "%s requires at least two arguments", |
|
| 35 |
+ Description: "The specified command requires at least two arguments", |
|
| 36 |
+ HTTPStatusCode: http.StatusInternalServerError, |
|
| 37 |
+ }) |
|
| 38 |
+ |
|
| 39 |
+ // ErrorCodeTooManyArgs is generated when the parser comes across a |
|
| 40 |
+ // Dockerfile command that has more args than it should |
|
| 41 |
+ ErrorCodeTooManyArgs = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
|
| 42 |
+ Value: "TOOMANYARGS", |
|
| 43 |
+ Message: "Bad input to %s, too many args", |
|
| 44 |
+ Description: "The specified command was passed too many arguments", |
|
| 45 |
+ HTTPStatusCode: http.StatusInternalServerError, |
|
| 46 |
+ }) |
|
| 47 |
+ |
|
| 48 |
+ // ErrorCodeChainOnBuild is generated when the parser comes across a |
|
| 49 |
+ // Dockerfile command that is trying to chain ONBUILD commands. |
|
| 50 |
+ ErrorCodeChainOnBuild = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
|
| 51 |
+ Value: "CHAINONBUILD", |
|
| 52 |
+ Message: "Chaining ONBUILD via `ONBUILD ONBUILD` isn't allowed", |
|
| 53 |
+ Description: "ONBUILD Dockerfile commands aren't allow on ONBUILD commands", |
|
| 54 |
+ HTTPStatusCode: http.StatusInternalServerError, |
|
| 55 |
+ }) |
|
| 56 |
+ |
|
| 57 |
+ // ErrorCodeBadOnBuildCmd is generated when the parser comes across a |
|
| 58 |
+ // an ONBUILD Dockerfile command with an invalid trigger/command. |
|
| 59 |
+ ErrorCodeBadOnBuildCmd = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
|
| 60 |
+ Value: "BADONBUILDCMD", |
|
| 61 |
+ Message: "%s isn't allowed as an ONBUILD trigger", |
|
| 62 |
+ Description: "The specified ONBUILD command isn't allowed", |
|
| 63 |
+ HTTPStatusCode: http.StatusInternalServerError, |
|
| 64 |
+ }) |
|
| 65 |
+ |
|
| 66 |
+ // ErrorCodeMissingFrom is generated when the Dockerfile is missing |
|
| 67 |
+ // a FROM command. |
|
| 68 |
+ ErrorCodeMissingFrom = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
|
| 69 |
+ Value: "MISSINGFROM", |
|
| 70 |
+ Message: "Please provide a source image with `from` prior to run", |
|
| 71 |
+ Description: "The Dockerfile is missing a FROM command", |
|
| 72 |
+ HTTPStatusCode: http.StatusInternalServerError, |
|
| 73 |
+ }) |
|
| 74 |
+ |
|
| 75 |
+ // ErrorCodeNotOnWindows is generated when the specified Dockerfile |
|
| 76 |
+ // command is not supported on Windows. |
|
| 77 |
+ ErrorCodeNotOnWindows = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
|
| 78 |
+ Value: "NOTONWINDOWS", |
|
| 79 |
+ Message: "%s is not supported on Windows", |
|
| 80 |
+ Description: "The specified Dockerfile command is not supported on Windows", |
|
| 81 |
+ HTTPStatusCode: http.StatusInternalServerError, |
|
| 82 |
+ }) |
|
| 83 |
+ |
|
| 84 |
+ // ErrorCodeVolumeEmpty is generated when the specified Volume string |
|
| 85 |
+ // is empty. |
|
| 86 |
+ ErrorCodeVolumeEmpty = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
|
| 87 |
+ Value: "VOLUMEEMPTY", |
|
| 88 |
+ Message: "Volume specified can not be an empty string", |
|
| 89 |
+ Description: "The specified volume can not be an empty string", |
|
| 90 |
+ HTTPStatusCode: http.StatusInternalServerError, |
|
| 91 |
+ }) |
|
| 92 |
+) |
| 0 | 93 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,21 @@ |
| 0 |
+package errors |
|
| 1 |
+ |
|
| 2 |
+// This file contains all of the errors that can be generated from the |
|
| 3 |
+// docker/daemon component. |
|
| 4 |
+ |
|
| 5 |
+import ( |
|
| 6 |
+ "net/http" |
|
| 7 |
+ |
|
| 8 |
+ "github.com/docker/distribution/registry/api/errcode" |
|
| 9 |
+) |
|
| 10 |
+ |
|
| 11 |
+var ( |
|
| 12 |
+ // ErrorCodeNoSuchContainer is generated when we look for a container by |
|
| 13 |
+ // name or ID and we can't find it. |
|
| 14 |
+ ErrorCodeNoSuchContainer = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
|
| 15 |
+ Value: "NOSUCHCONTAINER", |
|
| 16 |
+ Message: "no such id: %s", |
|
| 17 |
+ Description: "The specified container can not be found", |
|
| 18 |
+ HTTPStatusCode: http.StatusNotFound, |
|
| 19 |
+ }) |
|
| 20 |
+) |
| ... | ... |
@@ -3,6 +3,7 @@ package server |
| 3 | 3 |
import ( |
| 4 | 4 |
"encoding/base64" |
| 5 | 5 |
"encoding/json" |
| 6 |
+ "errors" |
|
| 6 | 7 |
"fmt" |
| 7 | 8 |
"io" |
| 8 | 9 |
"net/http" |
| ... | ... |
@@ -335,7 +336,7 @@ func (s *Server) postBuild(version version.Version, w http.ResponseWriter, r *ht |
| 335 | 335 |
return err |
| 336 | 336 |
} |
| 337 | 337 |
sf := streamformatter.NewJSONStreamFormatter() |
| 338 |
- w.Write(sf.FormatError(err)) |
|
| 338 |
+ w.Write(sf.FormatError(errors.New(utils.GetErrorMessage(err)))) |
|
| 339 | 339 |
} |
| 340 | 340 |
return nil |
| 341 | 341 |
} |
| ... | ... |
@@ -14,6 +14,7 @@ import ( |
| 14 | 14 |
"github.com/gorilla/mux" |
| 15 | 15 |
|
| 16 | 16 |
"github.com/Sirupsen/logrus" |
| 17 |
+ "github.com/docker/distribution/registry/api/errcode" |
|
| 17 | 18 |
"github.com/docker/docker/api" |
| 18 | 19 |
"github.com/docker/docker/autogen/dockerversion" |
| 19 | 20 |
"github.com/docker/docker/daemon" |
| ... | ... |
@@ -187,28 +188,71 @@ func httpError(w http.ResponseWriter, err error) {
|
| 187 | 187 |
logrus.WithFields(logrus.Fields{"error": err, "writer": w}).Error("unexpected HTTP error handling")
|
| 188 | 188 |
return |
| 189 | 189 |
} |
| 190 |
+ |
|
| 190 | 191 |
statusCode := http.StatusInternalServerError |
| 191 |
- // FIXME: this is brittle and should not be necessary. |
|
| 192 |
- // If we need to differentiate between different possible error types, we should |
|
| 193 |
- // create appropriate error types with clearly defined meaning. |
|
| 194 |
- errStr := strings.ToLower(err.Error()) |
|
| 195 |
- for keyword, status := range map[string]int{
|
|
| 196 |
- "not found": http.StatusNotFound, |
|
| 197 |
- "no such": http.StatusNotFound, |
|
| 198 |
- "bad parameter": http.StatusBadRequest, |
|
| 199 |
- "conflict": http.StatusConflict, |
|
| 200 |
- "impossible": http.StatusNotAcceptable, |
|
| 201 |
- "wrong login/password": http.StatusUnauthorized, |
|
| 202 |
- "hasn't been activated": http.StatusForbidden, |
|
| 203 |
- } {
|
|
| 204 |
- if strings.Contains(errStr, keyword) {
|
|
| 205 |
- statusCode = status |
|
| 206 |
- break |
|
| 192 |
+ errMsg := err.Error() |
|
| 193 |
+ |
|
| 194 |
+ // Based on the type of error we get we need to process things |
|
| 195 |
+ // slightly differently to extract the error message. |
|
| 196 |
+ // In the 'errcode.*' cases there are two different type of |
|
| 197 |
+ // error that could be returned. errocode.ErrorCode is the base |
|
| 198 |
+ // type of error object - it is just an 'int' that can then be |
|
| 199 |
+ // used as the look-up key to find the message. errorcode.Error |
|
| 200 |
+ // extends errorcode.Error by adding error-instance specific |
|
| 201 |
+ // data, like 'details' or variable strings to be inserted into |
|
| 202 |
+ // the message. |
|
| 203 |
+ // |
|
| 204 |
+ // Ideally, we should just be able to call err.Error() for all |
|
| 205 |
+ // cases but the errcode package doesn't support that yet. |
|
| 206 |
+ // |
|
| 207 |
+ // Additionally, in both errcode cases, there might be an http |
|
| 208 |
+ // status code associated with it, and if so use it. |
|
| 209 |
+ switch err.(type) {
|
|
| 210 |
+ case errcode.ErrorCode: |
|
| 211 |
+ daError, _ := err.(errcode.ErrorCode) |
|
| 212 |
+ statusCode = daError.Descriptor().HTTPStatusCode |
|
| 213 |
+ errMsg = daError.Message() |
|
| 214 |
+ |
|
| 215 |
+ case errcode.Error: |
|
| 216 |
+ // For reference, if you're looking for a particular error |
|
| 217 |
+ // then you can do something like : |
|
| 218 |
+ // import ( derr "github.com/docker/docker/api/errors" ) |
|
| 219 |
+ // if daError.ErrorCode() == derr.ErrorCodeNoSuchContainer { ... }
|
|
| 220 |
+ |
|
| 221 |
+ daError, _ := err.(errcode.Error) |
|
| 222 |
+ statusCode = daError.ErrorCode().Descriptor().HTTPStatusCode |
|
| 223 |
+ errMsg = daError.Message |
|
| 224 |
+ |
|
| 225 |
+ default: |
|
| 226 |
+ // This part of will be removed once we've |
|
| 227 |
+ // converted everything over to use the errcode package |
|
| 228 |
+ |
|
| 229 |
+ // FIXME: this is brittle and should not be necessary. |
|
| 230 |
+ // If we need to differentiate between different possible error types, |
|
| 231 |
+ // we should create appropriate error types with clearly defined meaning |
|
| 232 |
+ errStr := strings.ToLower(err.Error()) |
|
| 233 |
+ for keyword, status := range map[string]int{
|
|
| 234 |
+ "not found": http.StatusNotFound, |
|
| 235 |
+ "no such": http.StatusNotFound, |
|
| 236 |
+ "bad parameter": http.StatusBadRequest, |
|
| 237 |
+ "conflict": http.StatusConflict, |
|
| 238 |
+ "impossible": http.StatusNotAcceptable, |
|
| 239 |
+ "wrong login/password": http.StatusUnauthorized, |
|
| 240 |
+ "hasn't been activated": http.StatusForbidden, |
|
| 241 |
+ } {
|
|
| 242 |
+ if strings.Contains(errStr, keyword) {
|
|
| 243 |
+ statusCode = status |
|
| 244 |
+ break |
|
| 245 |
+ } |
|
| 207 | 246 |
} |
| 208 | 247 |
} |
| 209 | 248 |
|
| 249 |
+ if statusCode == 0 {
|
|
| 250 |
+ statusCode = http.StatusInternalServerError |
|
| 251 |
+ } |
|
| 252 |
+ |
|
| 210 | 253 |
logrus.WithFields(logrus.Fields{"statusCode": statusCode, "err": err}).Error("HTTP Error")
|
| 211 |
- http.Error(w, err.Error(), statusCode) |
|
| 254 |
+ http.Error(w, errMsg, statusCode) |
|
| 212 | 255 |
} |
| 213 | 256 |
|
| 214 | 257 |
// writeJSON writes the value v to the http response stream as json with standard |
| ... | ... |
@@ -18,6 +18,7 @@ import ( |
| 18 | 18 |
"strings" |
| 19 | 19 |
|
| 20 | 20 |
"github.com/Sirupsen/logrus" |
| 21 |
+ derr "github.com/docker/docker/api/errors" |
|
| 21 | 22 |
flag "github.com/docker/docker/pkg/mflag" |
| 22 | 23 |
"github.com/docker/docker/pkg/nat" |
| 23 | 24 |
"github.com/docker/docker/runconfig" |
| ... | ... |
@@ -41,12 +42,12 @@ func nullDispatch(b *builder, args []string, attributes map[string]bool, origina |
| 41 | 41 |
// |
| 42 | 42 |
func env(b *builder, args []string, attributes map[string]bool, original string) error {
|
| 43 | 43 |
if len(args) == 0 {
|
| 44 |
- return fmt.Errorf("ENV requires at least one argument")
|
|
| 44 |
+ return derr.ErrorCodeAtLeastOneArg.WithArgs("ENV")
|
|
| 45 | 45 |
} |
| 46 | 46 |
|
| 47 | 47 |
if len(args)%2 != 0 {
|
| 48 | 48 |
// should never get here, but just in case |
| 49 |
- return fmt.Errorf("Bad input to ENV, too many args")
|
|
| 49 |
+ return derr.ErrorCodeTooManyArgs.WithArgs("ENV")
|
|
| 50 | 50 |
} |
| 51 | 51 |
|
| 52 | 52 |
if err := b.BuilderFlags.Parse(); err != nil {
|
| ... | ... |
@@ -100,7 +101,7 @@ func env(b *builder, args []string, attributes map[string]bool, original string) |
| 100 | 100 |
// Sets the maintainer metadata. |
| 101 | 101 |
func maintainer(b *builder, args []string, attributes map[string]bool, original string) error {
|
| 102 | 102 |
if len(args) != 1 {
|
| 103 |
- return fmt.Errorf("MAINTAINER requires exactly one argument")
|
|
| 103 |
+ return derr.ErrorCodeExactlyOneArg.WithArgs("MAINTAINER")
|
|
| 104 | 104 |
} |
| 105 | 105 |
|
| 106 | 106 |
if err := b.BuilderFlags.Parse(); err != nil {
|
| ... | ... |
@@ -117,11 +118,11 @@ func maintainer(b *builder, args []string, attributes map[string]bool, original |
| 117 | 117 |
// |
| 118 | 118 |
func label(b *builder, args []string, attributes map[string]bool, original string) error {
|
| 119 | 119 |
if len(args) == 0 {
|
| 120 |
- return fmt.Errorf("LABEL requires at least one argument")
|
|
| 120 |
+ return derr.ErrorCodeAtLeastOneArg.WithArgs("LABEL")
|
|
| 121 | 121 |
} |
| 122 | 122 |
if len(args)%2 != 0 {
|
| 123 | 123 |
// should never get here, but just in case |
| 124 |
- return fmt.Errorf("Bad input to LABEL, too many args")
|
|
| 124 |
+ return derr.ErrorCodeTooManyArgs.WithArgs("LABEL")
|
|
| 125 | 125 |
} |
| 126 | 126 |
|
| 127 | 127 |
if err := b.BuilderFlags.Parse(); err != nil {
|
| ... | ... |
@@ -153,7 +154,7 @@ func label(b *builder, args []string, attributes map[string]bool, original strin |
| 153 | 153 |
// |
| 154 | 154 |
func add(b *builder, args []string, attributes map[string]bool, original string) error {
|
| 155 | 155 |
if len(args) < 2 {
|
| 156 |
- return fmt.Errorf("ADD requires at least two arguments")
|
|
| 156 |
+ return derr.ErrorCodeAtLeastTwoArgs.WithArgs("ADD")
|
|
| 157 | 157 |
} |
| 158 | 158 |
|
| 159 | 159 |
if err := b.BuilderFlags.Parse(); err != nil {
|
| ... | ... |
@@ -169,7 +170,7 @@ func add(b *builder, args []string, attributes map[string]bool, original string) |
| 169 | 169 |
// |
| 170 | 170 |
func dispatchCopy(b *builder, args []string, attributes map[string]bool, original string) error {
|
| 171 | 171 |
if len(args) < 2 {
|
| 172 |
- return fmt.Errorf("COPY requires at least two arguments")
|
|
| 172 |
+ return derr.ErrorCodeAtLeastTwoArgs.WithArgs("COPY")
|
|
| 173 | 173 |
} |
| 174 | 174 |
|
| 175 | 175 |
if err := b.BuilderFlags.Parse(); err != nil {
|
| ... | ... |
@@ -185,7 +186,7 @@ func dispatchCopy(b *builder, args []string, attributes map[string]bool, origina |
| 185 | 185 |
// |
| 186 | 186 |
func from(b *builder, args []string, attributes map[string]bool, original string) error {
|
| 187 | 187 |
if len(args) != 1 {
|
| 188 |
- return fmt.Errorf("FROM requires one argument")
|
|
| 188 |
+ return derr.ErrorCodeExactlyOneArg.WithArgs("FROM")
|
|
| 189 | 189 |
} |
| 190 | 190 |
|
| 191 | 191 |
if err := b.BuilderFlags.Parse(); err != nil {
|
| ... | ... |
@@ -233,7 +234,7 @@ func from(b *builder, args []string, attributes map[string]bool, original string |
| 233 | 233 |
// |
| 234 | 234 |
func onbuild(b *builder, args []string, attributes map[string]bool, original string) error {
|
| 235 | 235 |
if len(args) == 0 {
|
| 236 |
- return fmt.Errorf("ONBUILD requires at least one argument")
|
|
| 236 |
+ return derr.ErrorCodeAtLeastOneArg.WithArgs("ONBUILD")
|
|
| 237 | 237 |
} |
| 238 | 238 |
|
| 239 | 239 |
if err := b.BuilderFlags.Parse(); err != nil {
|
| ... | ... |
@@ -243,9 +244,9 @@ func onbuild(b *builder, args []string, attributes map[string]bool, original str |
| 243 | 243 |
triggerInstruction := strings.ToUpper(strings.TrimSpace(args[0])) |
| 244 | 244 |
switch triggerInstruction {
|
| 245 | 245 |
case "ONBUILD": |
| 246 |
- return fmt.Errorf("Chaining ONBUILD via `ONBUILD ONBUILD` isn't allowed")
|
|
| 246 |
+ return derr.ErrorCodeChainOnBuild |
|
| 247 | 247 |
case "MAINTAINER", "FROM": |
| 248 |
- return fmt.Errorf("%s isn't allowed as an ONBUILD trigger", triggerInstruction)
|
|
| 248 |
+ return derr.ErrorCodeBadOnBuildCmd.WithArgs(triggerInstruction) |
|
| 249 | 249 |
} |
| 250 | 250 |
|
| 251 | 251 |
original = regexp.MustCompile(`(?i)^\s*ONBUILD\s*`).ReplaceAllString(original, "") |
| ... | ... |
@@ -260,7 +261,7 @@ func onbuild(b *builder, args []string, attributes map[string]bool, original str |
| 260 | 260 |
// |
| 261 | 261 |
func workdir(b *builder, args []string, attributes map[string]bool, original string) error {
|
| 262 | 262 |
if len(args) != 1 {
|
| 263 |
- return fmt.Errorf("WORKDIR requires exactly one argument")
|
|
| 263 |
+ return derr.ErrorCodeExactlyOneArg.WithArgs("WORKDIR")
|
|
| 264 | 264 |
} |
| 265 | 265 |
|
| 266 | 266 |
if err := b.BuilderFlags.Parse(); err != nil {
|
| ... | ... |
@@ -317,7 +318,7 @@ func workdir(b *builder, args []string, attributes map[string]bool, original str |
| 317 | 317 |
// |
| 318 | 318 |
func run(b *builder, args []string, attributes map[string]bool, original string) error {
|
| 319 | 319 |
if b.image == "" && !b.noBaseImage {
|
| 320 |
- return fmt.Errorf("Please provide a source image with `from` prior to run")
|
|
| 320 |
+ return derr.ErrorCodeMissingFrom |
|
| 321 | 321 |
} |
| 322 | 322 |
|
| 323 | 323 |
if err := b.BuilderFlags.Parse(); err != nil {
|
| ... | ... |
@@ -467,7 +468,7 @@ func expose(b *builder, args []string, attributes map[string]bool, original stri |
| 467 | 467 |
portsTab := args |
| 468 | 468 |
|
| 469 | 469 |
if len(args) == 0 {
|
| 470 |
- return fmt.Errorf("EXPOSE requires at least one argument")
|
|
| 470 |
+ return derr.ErrorCodeAtLeastOneArg.WithArgs("EXPOSE")
|
|
| 471 | 471 |
} |
| 472 | 472 |
|
| 473 | 473 |
if err := b.BuilderFlags.Parse(); err != nil {
|
| ... | ... |
@@ -506,11 +507,11 @@ func expose(b *builder, args []string, attributes map[string]bool, original stri |
| 506 | 506 |
// |
| 507 | 507 |
func user(b *builder, args []string, attributes map[string]bool, original string) error {
|
| 508 | 508 |
if runtime.GOOS == "windows" {
|
| 509 |
- return fmt.Errorf("USER is not supported on Windows")
|
|
| 509 |
+ return derr.ErrorCodeNotOnWindows.WithArgs("USER")
|
|
| 510 | 510 |
} |
| 511 | 511 |
|
| 512 | 512 |
if len(args) != 1 {
|
| 513 |
- return fmt.Errorf("USER requires exactly one argument")
|
|
| 513 |
+ return derr.ErrorCodeExactlyOneArg.WithArgs("USER")
|
|
| 514 | 514 |
} |
| 515 | 515 |
|
| 516 | 516 |
if err := b.BuilderFlags.Parse(); err != nil {
|
| ... | ... |
@@ -527,10 +528,10 @@ func user(b *builder, args []string, attributes map[string]bool, original string |
| 527 | 527 |
// |
| 528 | 528 |
func volume(b *builder, args []string, attributes map[string]bool, original string) error {
|
| 529 | 529 |
if runtime.GOOS == "windows" {
|
| 530 |
- return fmt.Errorf("VOLUME is not supported on Windows")
|
|
| 530 |
+ return derr.ErrorCodeNotOnWindows.WithArgs("VOLUME")
|
|
| 531 | 531 |
} |
| 532 | 532 |
if len(args) == 0 {
|
| 533 |
- return fmt.Errorf("VOLUME requires at least one argument")
|
|
| 533 |
+ return derr.ErrorCodeAtLeastOneArg.WithArgs("VOLUME")
|
|
| 534 | 534 |
} |
| 535 | 535 |
|
| 536 | 536 |
if err := b.BuilderFlags.Parse(); err != nil {
|
| ... | ... |
@@ -543,7 +544,7 @@ func volume(b *builder, args []string, attributes map[string]bool, original stri |
| 543 | 543 |
for _, v := range args {
|
| 544 | 544 |
v = strings.TrimSpace(v) |
| 545 | 545 |
if v == "" {
|
| 546 |
- return fmt.Errorf("Volume specified can not be an empty string")
|
|
| 546 |
+ return derr.ErrorCodeVolumeEmpty |
|
| 547 | 547 |
} |
| 548 | 548 |
b.Config.Volumes[v] = struct{}{}
|
| 549 | 549 |
} |
| ... | ... |
@@ -15,6 +15,7 @@ import ( |
| 15 | 15 |
|
| 16 | 16 |
"github.com/Sirupsen/logrus" |
| 17 | 17 |
"github.com/docker/docker/api" |
| 18 |
+ derr "github.com/docker/docker/api/errors" |
|
| 18 | 19 |
"github.com/docker/docker/daemon/events" |
| 19 | 20 |
"github.com/docker/docker/daemon/execdriver" |
| 20 | 21 |
"github.com/docker/docker/daemon/execdriver/execdrivers" |
| ... | ... |
@@ -125,6 +126,10 @@ func (daemon *Daemon) Get(prefixOrName string) (*Container, error) {
|
| 125 | 125 |
|
| 126 | 126 |
containerId, indexError := daemon.idIndex.Get(prefixOrName) |
| 127 | 127 |
if indexError != nil {
|
| 128 |
+ // When truncindex defines an error type, use that instead |
|
| 129 |
+ if strings.Contains(indexError.Error(), "no such id") {
|
|
| 130 |
+ return nil, derr.ErrorCodeNoSuchContainer.WithArgs(prefixOrName) |
|
| 131 |
+ } |
|
| 128 | 132 |
return nil, indexError |
| 129 | 133 |
} |
| 130 | 134 |
return daemon.containers.Get(containerId), nil |
| ... | ... |
@@ -13,6 +13,7 @@ import ( |
| 13 | 13 |
"runtime" |
| 14 | 14 |
"strings" |
| 15 | 15 |
|
| 16 |
+ "github.com/docker/distribution/registry/api/errcode" |
|
| 16 | 17 |
"github.com/docker/docker/autogen/dockerversion" |
| 17 | 18 |
"github.com/docker/docker/pkg/archive" |
| 18 | 19 |
"github.com/docker/docker/pkg/fileutils" |
| ... | ... |
@@ -286,3 +287,22 @@ func ImageReference(repo, ref string) string {
|
| 286 | 286 |
func DigestReference(ref string) bool {
|
| 287 | 287 |
return strings.Contains(ref, ":") |
| 288 | 288 |
} |
| 289 |
+ |
|
| 290 |
+// GetErrorMessage returns the human readable message associated with |
|
| 291 |
+// the passed-in error. In some cases the default Error() func returns |
|
| 292 |
+// something that is less than useful so based on its types this func |
|
| 293 |
+// will go and get a better piece of text. |
|
| 294 |
+func GetErrorMessage(err error) string {
|
|
| 295 |
+ switch err.(type) {
|
|
| 296 |
+ case errcode.Error: |
|
| 297 |
+ e, _ := err.(errcode.Error) |
|
| 298 |
+ return e.Message |
|
| 299 |
+ |
|
| 300 |
+ case errcode.ErrorCode: |
|
| 301 |
+ ec, _ := err.(errcode.ErrorCode) |
|
| 302 |
+ return ec.Message() |
|
| 303 |
+ |
|
| 304 |
+ default: |
|
| 305 |
+ return err.Error() |
|
| 306 |
+ } |
|
| 307 |
+} |