@@ -83,8 +83,10 @@ func NewRunCommand(dockerCli command.Cli) *cobra.Command {
83
83
84
84
func runRun (ctx context.Context , dockerCli command.Cli , flags * pflag.FlagSet , ropts * runOptions , copts * containerOptions ) error {
85
85
if err := validatePullOpt (ropts .pull ); err != nil {
86
- reportError (dockerCli .Err (), "run" , err .Error (), true )
87
- return cli.StatusError {StatusCode : 125 }
86
+ return cli.StatusError {
87
+ Status : withHelp (err , "run" ).Error (),
88
+ StatusCode : 125 ,
89
+ }
88
90
}
89
91
proxyConfig := dockerCli .ConfigFile ().ParseProxyConfig (dockerCli .Client ().DaemonHost (), opts .ConvertKVStringsToMapWithNil (copts .env .GetAll ()))
90
92
newEnv := []string {}
@@ -99,12 +101,16 @@ func runRun(ctx context.Context, dockerCli command.Cli, flags *pflag.FlagSet, ro
99
101
containerCfg , err := parse (flags , copts , dockerCli .ServerInfo ().OSType )
100
102
// just in case the parse does not exit
101
103
if err != nil {
102
- reportError (dockerCli .Err (), "run" , err .Error (), true )
103
- return cli.StatusError {StatusCode : 125 }
104
+ return cli.StatusError {
105
+ Status : withHelp (err , "run" ).Error (),
106
+ StatusCode : 125 ,
107
+ }
104
108
}
105
109
if err = validateAPIVersion (containerCfg , dockerCli .CurrentVersion ()); err != nil {
106
- reportError (dockerCli .Err (), "run" , err .Error (), true )
107
- return cli.StatusError {StatusCode : 125 }
110
+ return cli.StatusError {
111
+ Status : withHelp (err , "run" ).Error (),
112
+ StatusCode : 125 ,
113
+ }
108
114
}
109
115
return runContainer (ctx , dockerCli , ropts , copts , containerCfg )
110
116
}
@@ -139,8 +145,7 @@ func runContainer(ctx context.Context, dockerCli command.Cli, runOpts *runOption
139
145
140
146
containerID , err := createContainer (ctx , dockerCli , containerCfg , & runOpts .createOptions )
141
147
if err != nil {
142
- reportError (stderr , "run" , err .Error (), true )
143
- return runStartContainerErr (err )
148
+ return toStatusError (err )
144
149
}
145
150
if runOpts .sigProxy {
146
151
sigc := notifyAllSignals ()
@@ -204,12 +209,11 @@ func runContainer(ctx context.Context, dockerCli command.Cli, runOpts *runOption
204
209
<- errCh
205
210
}
206
211
207
- reportError (stderr , "run" , err .Error (), false )
208
212
if copts .autoRemove {
209
213
// wait container to be removed
210
214
<- statusChan
211
215
}
212
- return runStartContainerErr (err )
216
+ return toStatusError (err )
213
217
}
214
218
215
219
if (config .AttachStdin || config .AttachStdout || config .AttachStderr ) && config .Tty && dockerCli .Out ().IsTerminal () {
@@ -292,30 +296,40 @@ func attachContainer(ctx context.Context, dockerCli command.Cli, containerID str
292
296
return resp .Close , nil
293
297
}
294
298
295
- // reportError is a utility method that prints a user-friendly message
296
- // containing the error that occurred during parsing and a suggestion to get help
297
- func reportError (stderr io.Writer , name string , str string , withHelp bool ) {
298
- str = strings .TrimSuffix (str , "." ) + "."
299
- if withHelp {
300
- str += "\n See 'docker " + name + " --help'."
301
- }
302
- _ , _ = fmt .Fprintln (stderr , "docker:" , str )
299
+ // withHelp decorates the error with a suggestion to use "--help".
300
+ func withHelp (err error , commandName string ) error {
301
+ return fmt .Errorf ("docker: %w\n \n Run 'docker %s --help' for more information" , err , commandName )
303
302
}
304
303
305
- // if container start fails with 'not found'/'no such' error, return 127
306
- // if container start fails with 'permission denied' error, return 126
307
- // return 125 for generic docker daemon failures
308
- func runStartContainerErr (err error ) error {
309
- trimmedErr := strings .TrimPrefix (err .Error (), "Error response from daemon: " )
310
- statusError := cli.StatusError {StatusCode : 125 }
311
- if strings .Contains (trimmedErr , "executable file not found" ) ||
312
- strings .Contains (trimmedErr , "no such file or directory" ) ||
313
- strings .Contains (trimmedErr , "system cannot find the file specified" ) {
314
- statusError = cli.StatusError {StatusCode : 127 }
315
- } else if strings .Contains (trimmedErr , syscall .EACCES .Error ()) ||
316
- strings .Contains (trimmedErr , syscall .EISDIR .Error ()) {
317
- statusError = cli.StatusError {StatusCode : 126 }
304
+ // toStatusError attempts to detect specific error-conditions to assign
305
+ // an appropriate exit-code for situations where the problem originates
306
+ // from the container. It returns [cli.StatusError] with the original
307
+ // error message and the Status field set as follows:
308
+ //
309
+ // - 125: for generic failures sent back from the daemon
310
+ // - 126: if container start fails with 'permission denied' error
311
+ // - 127: if container start fails with 'not found'/'no such' error
312
+ func toStatusError (err error ) error {
313
+ // TODO(thaJeztah): some of these errors originate from the container: should we actually suggest "--help" for those?
314
+
315
+ errMsg := err .Error ()
316
+
317
+ if strings .Contains (errMsg , "executable file not found" ) || strings .Contains (errMsg , "no such file or directory" ) || strings .Contains (errMsg , "system cannot find the file specified" ) {
318
+ return cli.StatusError {
319
+ Status : withHelp (err , "run" ).Error (),
320
+ StatusCode : 127 ,
321
+ }
318
322
}
319
323
320
- return statusError
324
+ if strings .Contains (errMsg , syscall .EACCES .Error ()) || strings .Contains (errMsg , syscall .EISDIR .Error ()) {
325
+ return cli.StatusError {
326
+ Status : withHelp (err , "run" ).Error (),
327
+ StatusCode : 126 ,
328
+ }
329
+ }
330
+
331
+ return cli.StatusError {
332
+ Status : withHelp (err , "run" ).Error (),
333
+ StatusCode : 125 ,
334
+ }
321
335
}
0 commit comments