github.com/dinever/docker@v1.11.1/api/client/run.go (about) 1 package client 2 3 import ( 4 "fmt" 5 "io" 6 "os" 7 "runtime" 8 "strings" 9 10 "golang.org/x/net/context" 11 12 "github.com/Sirupsen/logrus" 13 Cli "github.com/docker/docker/cli" 14 "github.com/docker/docker/opts" 15 "github.com/docker/docker/pkg/promise" 16 "github.com/docker/docker/pkg/signal" 17 runconfigopts "github.com/docker/docker/runconfig/opts" 18 "github.com/docker/engine-api/types" 19 "github.com/docker/libnetwork/resolvconf/dns" 20 ) 21 22 const ( 23 errCmdNotFound = "not found or does not exist." 24 errCmdCouldNotBeInvoked = "could not be invoked." 25 ) 26 27 func (cid *cidFile) Close() error { 28 cid.file.Close() 29 30 if !cid.written { 31 if err := os.Remove(cid.path); err != nil { 32 return fmt.Errorf("failed to remove the CID file '%s': %s \n", cid.path, err) 33 } 34 } 35 36 return nil 37 } 38 39 func (cid *cidFile) Write(id string) error { 40 if _, err := cid.file.Write([]byte(id)); err != nil { 41 return fmt.Errorf("Failed to write the container ID to the file: %s", err) 42 } 43 cid.written = true 44 return nil 45 } 46 47 // if container start fails with 'command not found' error, return 127 48 // if container start fails with 'command cannot be invoked' error, return 126 49 // return 125 for generic docker daemon failures 50 func runStartContainerErr(err error) error { 51 trimmedErr := strings.Trim(err.Error(), "Error response from daemon: ") 52 statusError := Cli.StatusError{StatusCode: 125} 53 54 if strings.HasPrefix(trimmedErr, "Container command") { 55 if strings.Contains(trimmedErr, errCmdNotFound) { 56 statusError = Cli.StatusError{StatusCode: 127} 57 } else if strings.Contains(trimmedErr, errCmdCouldNotBeInvoked) { 58 statusError = Cli.StatusError{StatusCode: 126} 59 } 60 } 61 62 return statusError 63 } 64 65 // CmdRun runs a command in a new container. 66 // 67 // Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...] 68 func (cli *DockerCli) CmdRun(args ...string) error { 69 cmd := Cli.Subcmd("run", []string{"IMAGE [COMMAND] [ARG...]"}, Cli.DockerCommands["run"].Description, true) 70 addTrustedFlags(cmd, true) 71 72 // These are flags not stored in Config/HostConfig 73 var ( 74 flAutoRemove = cmd.Bool([]string{"-rm"}, false, "Automatically remove the container when it exits") 75 flDetach = cmd.Bool([]string{"d", "-detach"}, false, "Run container in background and print container ID") 76 flSigProxy = cmd.Bool([]string{"-sig-proxy"}, true, "Proxy received signals to the process") 77 flName = cmd.String([]string{"-name"}, "", "Assign a name to the container") 78 flDetachKeys = cmd.String([]string{"-detach-keys"}, "", "Override the key sequence for detaching a container") 79 flAttach *opts.ListOpts 80 81 ErrConflictAttachDetach = fmt.Errorf("Conflicting options: -a and -d") 82 ErrConflictRestartPolicyAndAutoRemove = fmt.Errorf("Conflicting options: --restart and --rm") 83 ErrConflictDetachAutoRemove = fmt.Errorf("Conflicting options: --rm and -d") 84 ) 85 86 config, hostConfig, networkingConfig, cmd, err := runconfigopts.Parse(cmd, args) 87 88 // just in case the Parse does not exit 89 if err != nil { 90 cmd.ReportError(err.Error(), true) 91 os.Exit(125) 92 } 93 94 if hostConfig.OomKillDisable != nil && *hostConfig.OomKillDisable && hostConfig.Memory == 0 { 95 fmt.Fprintf(cli.err, "WARNING: Disabling the OOM killer on containers without setting a '-m/--memory' limit may be dangerous.\n") 96 } 97 98 if len(hostConfig.DNS) > 0 { 99 // check the DNS settings passed via --dns against 100 // localhost regexp to warn if they are trying to 101 // set a DNS to a localhost address 102 for _, dnsIP := range hostConfig.DNS { 103 if dns.IsLocalhost(dnsIP) { 104 fmt.Fprintf(cli.err, "WARNING: Localhost DNS setting (--dns=%s) may fail in containers.\n", dnsIP) 105 break 106 } 107 } 108 } 109 if config.Image == "" { 110 cmd.Usage() 111 return nil 112 } 113 114 config.ArgsEscaped = false 115 116 if !*flDetach { 117 if err := cli.CheckTtyInput(config.AttachStdin, config.Tty); err != nil { 118 return err 119 } 120 } else { 121 if fl := cmd.Lookup("-attach"); fl != nil { 122 flAttach = fl.Value.(*opts.ListOpts) 123 if flAttach.Len() != 0 { 124 return ErrConflictAttachDetach 125 } 126 } 127 if *flAutoRemove { 128 return ErrConflictDetachAutoRemove 129 } 130 131 config.AttachStdin = false 132 config.AttachStdout = false 133 config.AttachStderr = false 134 config.StdinOnce = false 135 } 136 137 // Disable flSigProxy when in TTY mode 138 sigProxy := *flSigProxy 139 if config.Tty { 140 sigProxy = false 141 } 142 143 // Telling the Windows daemon the initial size of the tty during start makes 144 // a far better user experience rather than relying on subsequent resizes 145 // to cause things to catch up. 146 if runtime.GOOS == "windows" { 147 hostConfig.ConsoleSize[0], hostConfig.ConsoleSize[1] = cli.getTtySize() 148 } 149 150 createResponse, err := cli.createContainer(config, hostConfig, networkingConfig, hostConfig.ContainerIDFile, *flName) 151 if err != nil { 152 cmd.ReportError(err.Error(), true) 153 return runStartContainerErr(err) 154 } 155 if sigProxy { 156 sigc := cli.forwardAllSignals(createResponse.ID) 157 defer signal.StopCatch(sigc) 158 } 159 var ( 160 waitDisplayID chan struct{} 161 errCh chan error 162 ) 163 if !config.AttachStdout && !config.AttachStderr { 164 // Make this asynchronous to allow the client to write to stdin before having to read the ID 165 waitDisplayID = make(chan struct{}) 166 go func() { 167 defer close(waitDisplayID) 168 fmt.Fprintf(cli.out, "%s\n", createResponse.ID) 169 }() 170 } 171 if *flAutoRemove && (hostConfig.RestartPolicy.IsAlways() || hostConfig.RestartPolicy.IsOnFailure()) { 172 return ErrConflictRestartPolicyAndAutoRemove 173 } 174 175 if config.AttachStdin || config.AttachStdout || config.AttachStderr { 176 var ( 177 out, stderr io.Writer 178 in io.ReadCloser 179 ) 180 if config.AttachStdin { 181 in = cli.in 182 } 183 if config.AttachStdout { 184 out = cli.out 185 } 186 if config.AttachStderr { 187 if config.Tty { 188 stderr = cli.out 189 } else { 190 stderr = cli.err 191 } 192 } 193 194 if *flDetachKeys != "" { 195 cli.configFile.DetachKeys = *flDetachKeys 196 } 197 198 options := types.ContainerAttachOptions{ 199 ContainerID: createResponse.ID, 200 Stream: true, 201 Stdin: config.AttachStdin, 202 Stdout: config.AttachStdout, 203 Stderr: config.AttachStderr, 204 DetachKeys: cli.configFile.DetachKeys, 205 } 206 207 resp, err := cli.client.ContainerAttach(context.Background(), options) 208 if err != nil { 209 return err 210 } 211 if in != nil && config.Tty { 212 if err := cli.setRawTerminal(); err != nil { 213 return err 214 } 215 defer cli.restoreTerminal(in) 216 } 217 errCh = promise.Go(func() error { 218 return cli.holdHijackedConnection(config.Tty, in, out, stderr, resp) 219 }) 220 } 221 222 if *flAutoRemove { 223 defer func() { 224 if err := cli.removeContainer(createResponse.ID, true, false, false); err != nil { 225 fmt.Fprintf(cli.err, "%v\n", err) 226 } 227 }() 228 } 229 230 //start the container 231 if err := cli.client.ContainerStart(context.Background(), createResponse.ID); err != nil { 232 cmd.ReportError(err.Error(), false) 233 return runStartContainerErr(err) 234 } 235 236 if (config.AttachStdin || config.AttachStdout || config.AttachStderr) && config.Tty && cli.isTerminalOut { 237 if err := cli.monitorTtySize(createResponse.ID, false); err != nil { 238 fmt.Fprintf(cli.err, "Error monitoring TTY size: %s\n", err) 239 } 240 } 241 242 if errCh != nil { 243 if err := <-errCh; err != nil { 244 logrus.Debugf("Error hijack: %s", err) 245 return err 246 } 247 } 248 249 // Detached mode: wait for the id to be displayed and return. 250 if !config.AttachStdout && !config.AttachStderr { 251 // Detached mode 252 <-waitDisplayID 253 return nil 254 } 255 256 var status int 257 258 // Attached mode 259 if *flAutoRemove { 260 // Autoremove: wait for the container to finish, retrieve 261 // the exit code and remove the container 262 if status, err = cli.client.ContainerWait(context.Background(), createResponse.ID); err != nil { 263 return runStartContainerErr(err) 264 } 265 if _, status, err = getExitCode(cli, createResponse.ID); err != nil { 266 return err 267 } 268 } else { 269 // No Autoremove: Simply retrieve the exit code 270 if !config.Tty { 271 // In non-TTY mode, we can't detach, so we must wait for container exit 272 if status, err = cli.client.ContainerWait(context.Background(), createResponse.ID); err != nil { 273 return err 274 } 275 } else { 276 // In TTY mode, there is a race: if the process dies too slowly, the state could 277 // be updated after the getExitCode call and result in the wrong exit code being reported 278 if _, status, err = getExitCode(cli, createResponse.ID); err != nil { 279 return err 280 } 281 } 282 } 283 if status != 0 { 284 return Cli.StatusError{StatusCode: status} 285 } 286 return nil 287 }