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