github.com/uppal0016/docker_new@v0.0.0-20240123060250-1c98be13ac2c/api/client/run.go (about) 1 package client 2 3 import ( 4 "fmt" 5 "io" 6 "net/http/httputil" 7 "os" 8 "runtime" 9 "strings" 10 11 "golang.org/x/net/context" 12 13 "github.com/Sirupsen/logrus" 14 Cli "github.com/docker/docker/cli" 15 "github.com/docker/docker/opts" 16 "github.com/docker/docker/pkg/promise" 17 "github.com/docker/docker/pkg/signal" 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 = "not found or does not exist" 25 errCmdCouldNotBeInvoked = "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.TrimPrefix(err.Error(), "Error response from daemon: ") 53 statusError := Cli.StatusError{StatusCode: 125} 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 cancelFun context.CancelFunc 163 ctx context.Context 164 ) 165 if !config.AttachStdout && !config.AttachStderr { 166 // Make this asynchronous to allow the client to write to stdin before having to read the ID 167 waitDisplayID = make(chan struct{}) 168 go func() { 169 defer close(waitDisplayID) 170 fmt.Fprintf(cli.out, "%s\n", createResponse.ID) 171 }() 172 } 173 if *flAutoRemove && (hostConfig.RestartPolicy.IsAlways() || hostConfig.RestartPolicy.IsOnFailure()) { 174 return ErrConflictRestartPolicyAndAutoRemove 175 } 176 attach := config.AttachStdin || config.AttachStdout || config.AttachStderr 177 if attach { 178 var ( 179 out, stderr io.Writer 180 in io.ReadCloser 181 ) 182 if config.AttachStdin { 183 in = cli.in 184 } 185 if config.AttachStdout { 186 out = cli.out 187 } 188 if config.AttachStderr { 189 if config.Tty { 190 stderr = cli.out 191 } else { 192 stderr = cli.err 193 } 194 } 195 196 if *flDetachKeys != "" { 197 cli.configFile.DetachKeys = *flDetachKeys 198 } 199 200 options := types.ContainerAttachOptions{ 201 ContainerID: createResponse.ID, 202 Stream: true, 203 Stdin: config.AttachStdin, 204 Stdout: config.AttachStdout, 205 Stderr: config.AttachStderr, 206 DetachKeys: cli.configFile.DetachKeys, 207 } 208 209 resp, errAttach := cli.client.ContainerAttach(context.Background(), options) 210 if errAttach != nil && errAttach != httputil.ErrPersistEOF { 211 // ContainerAttach returns an ErrPersistEOF (connection closed) 212 // means server met an error and put it in Hijacked connection 213 // keep the error and read detailed error message from hijacked connection later 214 return errAttach 215 } 216 ctx, cancelFun = context.WithCancel(context.Background()) 217 errCh = promise.Go(func() error { 218 errHijack := cli.holdHijackedConnection(ctx, config.Tty, in, out, stderr, resp) 219 if errHijack == nil { 220 return errAttach 221 } 222 return errHijack 223 }) 224 } 225 226 if *flAutoRemove { 227 defer func() { 228 if err := cli.removeContainer(createResponse.ID, true, false, true); err != nil { 229 fmt.Fprintf(cli.err, "%v\n", err) 230 } 231 }() 232 } 233 234 //start the container 235 if err := cli.client.ContainerStart(context.Background(), createResponse.ID); err != nil { 236 // If we have holdHijackedConnection, we should notify 237 // holdHijackedConnection we are going to exit and wait 238 // to avoid the terminal are not restored. 239 if attach { 240 cancelFun() 241 <-errCh 242 } 243 244 cmd.ReportError(err.Error(), false) 245 return runStartContainerErr(err) 246 } 247 248 if (config.AttachStdin || config.AttachStdout || config.AttachStderr) && config.Tty && cli.isTerminalOut { 249 if err := cli.monitorTtySize(createResponse.ID, false); err != nil { 250 fmt.Fprintf(cli.err, "Error monitoring TTY size: %s\n", err) 251 } 252 } 253 254 if errCh != nil { 255 if err := <-errCh; err != nil { 256 logrus.Debugf("Error hijack: %s", err) 257 return err 258 } 259 } 260 261 // Detached mode: wait for the id to be displayed and return. 262 if !config.AttachStdout && !config.AttachStderr { 263 // Detached mode 264 <-waitDisplayID 265 return nil 266 } 267 268 var status int 269 270 // Attached mode 271 if *flAutoRemove { 272 // Autoremove: wait for the container to finish, retrieve 273 // the exit code and remove the container 274 if status, err = cli.client.ContainerWait(context.Background(), createResponse.ID); err != nil { 275 return runStartContainerErr(err) 276 } 277 if _, status, err = getExitCode(cli, createResponse.ID); err != nil { 278 return err 279 } 280 } else { 281 // No Autoremove: Simply retrieve the exit code 282 if !config.Tty { 283 // In non-TTY mode, we can't detach, so we must wait for container exit 284 if status, err = cli.client.ContainerWait(context.Background(), createResponse.ID); err != nil { 285 return err 286 } 287 } else { 288 // In TTY mode, there is a race: if the process dies too slowly, the state could 289 // be updated after the getExitCode call and result in the wrong exit code being reported 290 if _, status, err = getExitCode(cli, createResponse.ID); err != nil { 291 return err 292 } 293 } 294 } 295 if status != 0 { 296 return Cli.StatusError{StatusCode: status} 297 } 298 return nil 299 }