github.com/zhuohuang-hust/src-cbuild@v0.0.0-20230105071821-c7aab3e7c840/cli/command/container/exec.go (about) 1 package container 2 3 import ( 4 "fmt" 5 "io" 6 7 "golang.org/x/net/context" 8 9 // "github.com/docker/docker/api/types/versions" 10 // "github.com/docker/docker/cli/command/commands" 11 // cliflags "github.com/docker/docker/cli/flags" 12 // "github.com/docker/docker/cliconfig" 13 // "github.com/docker/docker/dockerversion" 14 // "github.com/docker/docker/pkg/term" 15 // "github.com/docker/docker/utils" 16 // "github.com/spf13/pflag" 17 18 "github.com/Sirupsen/logrus" 19 "github.com/docker/docker/api/types" 20 "github.com/docker/docker/cli" 21 "github.com/docker/docker/cli/command" 22 apiclient "github.com/docker/docker/client" 23 options "github.com/docker/docker/opts" 24 "github.com/docker/docker/pkg/promise" 25 runconfigopts "github.com/docker/docker/runconfig/opts" 26 "github.com/spf13/cobra" 27 ) 28 29 type execOptions struct { 30 detachKeys string 31 interactive bool 32 tty bool 33 detach bool 34 user string 35 privileged bool 36 env *options.ListOpts 37 } 38 39 func newExecOptions() *execOptions { 40 var values []string 41 return &execOptions{ 42 env: options.NewListOptsRef(&values, runconfigopts.ValidateEnv), 43 } 44 } 45 46 // NewExecCommand creats a new cobra.Command for `docker exec` 47 func NewExecCommand(dockerCli *command.DockerCli) *cobra.Command { 48 fmt.Println("cli/command/container/exec.go NewExecCommand()") 49 50 opts := newExecOptions() 51 52 cmd := &cobra.Command{ 53 Use: "exec [OPTIONS] CONTAINER COMMAND [ARG...]", 54 Short: "Run a command in a running container", 55 Args: cli.RequiresMinArgs(2), 56 RunE: func(cmd *cobra.Command, args []string) error { 57 container := args[0] 58 execCmd := args[1:] 59 fmt.Println("cli/command/container/exec.go NewExecCommand() container : ", container) 60 fmt.Println("cli/command/container/exec.go NewExecCommand() execCmd : ", execCmd) 61 fmt.Println("cli/command/container/exec.go NewExecCommand() before runExec()") 62 return runExec(dockerCli, opts, container, execCmd) 63 }, 64 } 65 66 fmt.Println("cli/command/container/exec.go NewExecCommand() after runExec()") 67 68 flags := cmd.Flags() 69 flags.SetInterspersed(false) 70 71 flags.StringVarP(&opts.detachKeys, "detach-keys", "", "", "Override the key sequence for detaching a container") 72 flags.BoolVarP(&opts.interactive, "interactive", "i", false, "Keep STDIN open even if not attached") 73 flags.BoolVarP(&opts.tty, "tty", "t", false, "Allocate a pseudo-TTY") 74 flags.BoolVarP(&opts.detach, "detach", "d", false, "Detached mode: run command in the background") 75 flags.StringVarP(&opts.user, "user", "u", "", "Username or UID (format: <name|uid>[:<group|gid>])") 76 flags.BoolVarP(&opts.privileged, "privileged", "", false, "Give extended privileges to the command") 77 flags.VarP(opts.env, "env", "e", "Set environment variables") 78 flags.SetAnnotation("env", "version", []string{"1.25"}) 79 80 return cmd 81 } 82 83 func runExec(dockerCli *command.DockerCli, opts *execOptions, container string, execCmd []string) error { 84 fmt.Println("cli/command/container/exec.go runExec()") 85 86 execConfig, err := parseExec(opts, execCmd) 87 // just in case the ParseExec does not exit 88 if container == "" || err != nil { 89 return cli.StatusError{StatusCode: 1} 90 } 91 92 if opts.detachKeys != "" { 93 fmt.Println("cli/command/container/exec.go runExec() opts.detachKeys!=null") 94 dockerCli.ConfigFile().DetachKeys = opts.detachKeys 95 } 96 97 // Send client escape keys 98 execConfig.DetachKeys = dockerCli.ConfigFile().DetachKeys 99 100 ctx := context.Background() 101 client := dockerCli.Client() 102 fmt.Println("cli/command/container/exec.go runExec() Client : ", client) 103 fmt.Println("cli/command/container/exec.go runExec() ClientVersion : ", client.ClientVersion()) 104 105 response, err := client.ContainerExecCreate(ctx, container, *execConfig) 106 if err != nil { 107 return err 108 } 109 110 execID := response.ID 111 if execID == "" { 112 fmt.Fprintf(dockerCli.Out(), "exec ID empty") 113 return nil 114 } 115 116 fmt.Println("cli/command/container/exec.go runExec() execConfig.Detach : ", execConfig.Detach) 117 //Temp struct for execStart so that we don't need to transfer all the execConfig 118 if !execConfig.Detach { 119 if err := dockerCli.In().CheckTty(execConfig.AttachStdin, execConfig.Tty); err != nil { 120 return err 121 } 122 } else { 123 execStartCheck := types.ExecStartCheck{ 124 Detach: execConfig.Detach, 125 Tty: execConfig.Tty, 126 } 127 128 fmt.Println("cli/command/container/exec.go runExec() ctx : ", ctx) 129 fmt.Println("cli/command/container/exec.go runExec() execConfig : ", execConfig) 130 fmt.Println("cli/command/container/exec.go runExec() execStartCheck : ", execStartCheck) 131 fmt.Println("cli/command/container/exec.go runExec() client : ", client) 132 fmt.Println("cli/command/container/exec.go runExec() response : ", response) 133 134 if err := client.ContainerExecStart(ctx, execID, execStartCheck); err != nil { 135 return err 136 } 137 // For now don't print this - wait for when we support exec wait() 138 // fmt.Fprintf(dockerCli.Out(), "%s\n", execID) 139 return nil 140 } 141 142 // Interactive exec requested. 143 var ( 144 out, stderr io.Writer 145 in io.ReadCloser 146 errCh chan error 147 ) 148 149 if execConfig.AttachStdin { 150 in = dockerCli.In() 151 } 152 if execConfig.AttachStdout { 153 out = dockerCli.Out() 154 } 155 if execConfig.AttachStderr { 156 if execConfig.Tty { 157 stderr = dockerCli.Out() 158 } else { 159 stderr = dockerCli.Err() 160 } 161 } 162 163 resp, err := client.ContainerExecAttach(ctx, execID, *execConfig) 164 if err != nil { 165 return err 166 } 167 defer resp.Close() 168 errCh = promise.Go(func() error { 169 return holdHijackedConnection(ctx, dockerCli, execConfig.Tty, in, out, stderr, resp) 170 }) 171 172 if execConfig.Tty && dockerCli.In().IsTerminal() { 173 if err := MonitorTtySize(ctx, dockerCli, execID, true); err != nil { 174 fmt.Fprintf(dockerCli.Err(), "Error monitoring TTY size: %s\n", err) 175 } 176 } 177 178 if err := <-errCh; err != nil { 179 logrus.Debugf("Error hijack: %s", err) 180 return err 181 } 182 183 var status int 184 if _, status, err = getExecExitCode(ctx, client, execID); err != nil { 185 return err 186 } 187 188 if status != 0 { 189 return cli.StatusError{StatusCode: status} 190 } 191 192 return nil 193 } 194 195 196 197 func RunExecInFirstContainer(dockerCli *command.DockerCli) error { 198 fmt.Println("cli/command/container/exec.go RunExecInFirstContainer()") 199 200 //execConfig, err := parseExec(opts, execCmd) 201 // just in case the ParseExec does not exit 202 //if container == "" || err != nil { 203 // return cli.StatusError{StatusCode: 1} 204 //} 205 206 opts := newExecOptions() 207 if opts.detachKeys != "" { 208 dockerCli.ConfigFile().DetachKeys = opts.detachKeys 209 } 210 211 // Send client escape keys 212 execConfig := dockerCli.GetCliexecconfig() 213 // execConfig.DetachKeys = dockerCli.ConfigFile().DetachKeys 214 215 container := dockerCli.GetClicontainer() 216 217 ctx := context.Background() 218 219 /* var flags *pflag.FlagSet 220 cliopts := cliflags.NewClientOptions() 221 if dockerCli.Client() == nil { 222 fmt.Println("cli/command/container/exec.go RunExecInFirstContainer() dockerCli.Client() is null!!!") 223 cliopts.Common.SetDefaultOptions(flags) 224 dockerPreRun(cliopts) 225 if err := dockerCli.Initialize(cliopts); err != nil { 226 fmt.Println("cli/command/container/exec.go RunExecInFirstContainer() dockerCli.Initialize is err!!!") 227 } 228 } 229 */ 230 231 // client := dockerCli.Client() 232 tmpclient, err := apiclient.NewEnvClient() 233 if err != nil { 234 fmt.Println("cli/command/container/exec.go RunExecInFirstContainer() NewEnvClient() is err!!!") 235 } 236 headers := map[string]string{"User-Agent":"Docker-Client/1.14.0-dev (linux)"} 237 tmpclient.SetCustomHTTPHeaders(headers) 238 if error := dockerCli.SetCliclient(tmpclient); error != nil { 239 fmt.Println("cli/command/container/exec.go RunExecInFirstContainer() dockerCli.SetClient is err!!!") 240 } 241 242 client := dockerCli.Client() 243 fmt.Println("cli/command/container/exec.go RunExecInFirstContainer() NewEnvClient() client : ", client) 244 245 /* client = &http.Client{ 246 Transport: &http.Transport{ 247 TLSClientConfig: tlsc, 248 }, 249 } 250 } 251 252 host := os.Getenv("DOCKER_HOST") 253 if host == "" { 254 host = DefaultDockerHost 255 } 256 version := os.Getenv("DOCKER_API_VERSION") 257 if version == "" { 258 version = DefaultVersion 259 } 260 261 cli, err := NewClient(host, version, client, nil) 262 */ 263 fmt.Println("cli/command/container/exec.go RunExecInFirstContainer() *execConfig : ", execConfig) 264 response, err := client.ContainerExecCreate(ctx, container, *execConfig) 265 if err != nil { 266 return err 267 } 268 269 execID := response.ID 270 if execID == "" { 271 fmt.Fprintf(dockerCli.Out(), "exec ID empty") 272 return nil 273 } 274 fmt.Println("cli/command/container/exec.go RunExecInFirstContainer() after ContainerExecCreate()") 275 276 fmt.Println("cli/command/container/exec.go RunExecInFirstContainer() execConfig.Detach : ", execConfig.Detach) 277 //Temp struct for execStart so that we don't need to transfer all the execConfig 278 if !execConfig.Detach { 279 if err := dockerCli.In().CheckTty(execConfig.AttachStdin, execConfig.Tty); err != nil { 280 return err 281 } 282 } else { 283 execStartCheck := types.ExecStartCheck{ 284 Detach: execConfig.Detach, 285 Tty: execConfig.Tty, 286 } 287 288 fmt.Println("cli/command/container/exec.go RunExecInFirstContainer() execStarCheck() : ", execStartCheck) 289 fmt.Println("cli/command/container/exec.go RunExecInFirstContainer() response : ", response) 290 fmt.Println("cli/command/container/exec.go RunExecInFirstContainer() ctx : ", ctx) 291 292 fmt.Println("cli/command/container/exec.go RunExecInFirstContainer() before ContainerExecStart()") 293 if err := client.ContainerExecStart(ctx, execID, execStartCheck); err != nil { 294 fmt.Println("cli/command/container/exec.go RunExecInFirstContainer() ContainerExecStart()i is err!!!!") 295 return err 296 } 297 // For now don't print this - wait for when we support exec wait() 298 // fmt.Fprintf(dockerCli.Out(), "%s\n", execID) 299 return nil 300 } 301 fmt.Println("cli/command/container/exec.go RunExecInFirstContainer() after ContainerExecStart()") 302 303 // Interactive exec requested. 304 var ( 305 out, stderr io.Writer 306 in io.ReadCloser 307 errCh chan error 308 ) 309 310 if execConfig.AttachStdin { 311 in = dockerCli.In() 312 } 313 if execConfig.AttachStdout { 314 out = dockerCli.Out() 315 } 316 if execConfig.AttachStderr { 317 if execConfig.Tty { 318 stderr = dockerCli.Out() 319 } else { 320 stderr = dockerCli.Err() 321 } 322 } 323 324 resp, err := client.ContainerExecAttach(ctx, execID, *execConfig) 325 if err != nil { 326 return err 327 } 328 defer resp.Close() 329 errCh = promise.Go(func() error { 330 return holdHijackedConnection(ctx, dockerCli, execConfig.Tty, in, out, stderr, resp) 331 }) 332 333 if execConfig.Tty && dockerCli.In().IsTerminal() { 334 if err := MonitorTtySize(ctx, dockerCli, execID, true); err != nil { 335 fmt.Fprintf(dockerCli.Err(), "Error monitoring TTY size: %s\n", err) 336 } 337 } 338 339 if err := <-errCh; err != nil { 340 logrus.Debugf("Error hijack: %s", err) 341 return err 342 } 343 344 var status int 345 if _, status, err = getExecExitCode(ctx, client, execID); err != nil { 346 return err 347 } 348 349 if status != 0 { 350 return cli.StatusError{StatusCode: status} 351 } 352 353 fmt.Println("cli/command/container/exec.go RunExecInFirstContainer() end") 354 355 return nil 356 } 357 358 359 360 361 362 363 364 // getExecExitCode perform an inspect on the exec command. It returns 365 // the running state and the exit code. 366 func getExecExitCode(ctx context.Context, client apiclient.ContainerAPIClient, execID string) (bool, int, error) { 367 resp, err := client.ContainerExecInspect(ctx, execID) 368 if err != nil { 369 // If we can't connect, then the daemon probably died. 370 if !apiclient.IsErrConnectionFailed(err) { 371 return false, -1, err 372 } 373 return false, -1, nil 374 } 375 376 return resp.Running, resp.ExitCode, nil 377 } 378 379 // parseExec parses the specified args for the specified command and generates 380 // an ExecConfig from it. 381 func parseExec(opts *execOptions, execCmd []string) (*types.ExecConfig, error) { 382 execConfig := &types.ExecConfig{ 383 User: opts.user, 384 Privileged: opts.privileged, 385 Tty: opts.tty, 386 Cmd: execCmd, 387 Detach: opts.detach, 388 } 389 390 // If -d is not set, attach to everything by default 391 if !opts.detach { 392 execConfig.AttachStdout = true 393 execConfig.AttachStderr = true 394 if opts.interactive { 395 execConfig.AttachStdin = true 396 } 397 } 398 399 if opts.env != nil { 400 execConfig.Env = opts.env.GetAll() 401 } 402 403 return execConfig, nil 404 } 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 /* 422 func newFirstDockerCommand(dockerCli *command.DockerCli) *cobra.Command { 423 fmt.Println("cli/command/container/exec.go newFirstDockerCommand()") 424 opts := cliflags.NewClientOptions() 425 var flags *pflag.FlagSet 426 427 cmd := &cobra.Command{ 428 Use: "docker [OPTIONS] COMMAND [ARG...]", 429 Short: "A self-sufficient runtime for containers", 430 SilenceUsage: true, 431 SilenceErrors: true, 432 TraverseChildren: true, 433 Args: noArgs, 434 RunE: func(cmd *cobra.Command, args []string) error { 435 if opts.Version { 436 showVersion() 437 return nil 438 } 439 return dockerCli.ShowHelp(cmd, args) 440 }, 441 PersistentPreRunE: func(cmd *cobra.Command, args []string) error { 442 // daemon command is special, we redirect directly to another binary 443 if cmd.Name() == "daemon" { 444 return nil 445 } 446 // flags must be the top-level command flags, not cmd.Flags() 447 opts.Common.SetDefaultOptions(flags) 448 dockerPreRun(opts) 449 if err := dockerCli.Initialize(opts); err != nil { 450 return err 451 } 452 return isSupported(cmd, dockerCli.Client().ClientVersion(), dockerCli.HasExperimental()) 453 }, 454 } 455 cli.SetupRootCommand(cmd) 456 457 cmd.SetHelpFunc(func(ccmd *cobra.Command, args []string) { 458 if dockerCli.Client() == nil { // when using --help, PersistenPreRun is not called, so initialization is needed. 459 // flags must be the top-level command flags, not cmd.Flags() 460 opts.Common.SetDefaultOptions(flags) 461 dockerPreRun(opts) 462 dockerCli.Initialize(opts) 463 } 464 465 if err := isSupported(ccmd, dockerCli.Client().ClientVersion(), dockerCli.HasExperimental()); err != nil { 466 ccmd.Println(err) 467 return 468 } 469 470 hideUnsupportedFeatures(ccmd, dockerCli.Client().ClientVersion(), dockerCli.HasExperimental()) 471 472 if err := ccmd.Help(); err != nil { 473 ccmd.Println(err) 474 } 475 }) 476 477 flags = cmd.Flags() 478 flags.BoolVarP(&opts.Version, "version", "v", false, "Print version information and quit") 479 flags.StringVar(&opts.ConfigDir, "config", cliconfig.ConfigDir(), "Location of client config files") 480 opts.Common.InstallFlags(flags) 481 482 cmd.SetOutput(dockerCli.Out()) 483 cmd.AddCommand(newDaemonCommand()) 484 commands.AddCommands(cmd, dockerCli) 485 486 fmt.Println("cli/command/container/exec.go newFirstDockerCommand() end ") 487 488 return cmd 489 } 490 491 func noArgs(cmd *cobra.Command, args []string) error { 492 if len(args) == 0 { 493 return nil 494 } 495 fmt.Println("cli/command/container/exec.go noArgs() err : is not a docker cmmand") 496 return fmt.Errorf( 497 "docker: '%s' is not a docker command.\nSee 'docker --help'", args[0]) 498 } 499 500 501 func showVersion() { 502 fmt.Printf("Docker version %s, build %s\n", dockerversion.Version, dockerversion.GitCommit) 503 } 504 505 func dockerPreRun(opts *cliflags.ClientOptions) { 506 cliflags.SetLogLevel(opts.Common.LogLevel) 507 508 if opts.ConfigDir != "" { 509 cliconfig.SetConfigDir(opts.ConfigDir) 510 } 511 512 if opts.Common.Debug { 513 utils.EnableDebug() 514 } 515 } 516 517 func hideUnsupportedFeatures(cmd *cobra.Command, clientVersion string, hasExperimental bool) { 518 cmd.Flags().VisitAll(func(f *pflag.Flag) { 519 // hide experimental flags 520 if !hasExperimental { 521 if _, ok := f.Annotations["experimental"]; ok { 522 f.Hidden = true 523 } 524 } 525 526 // hide flags not supported by the server 527 if flagVersion, ok := f.Annotations["version"]; ok && len(flagVersion) == 1 && versions.LessThan(clientVersion, flagVersion[0]) { 528 f.Hidden = true 529 } 530 531 }) 532 533 for _, subcmd := range cmd.Commands() { 534 // hide experimental subcommands 535 if !hasExperimental { 536 if _, ok := subcmd.Tags["experimental"]; ok { 537 subcmd.Hidden = true 538 } 539 } 540 541 // hide subcommands not supported by the server 542 if subcmdVersion, ok := subcmd.Tags["version"]; ok && versions.LessThan(clientVersion, subcmdVersion) { 543 subcmd.Hidden = true 544 } 545 } 546 } 547 548 func isSupported(cmd *cobra.Command, clientVersion string, hasExperimental bool) error { 549 if !hasExperimental { 550 if _, ok := cmd.Tags["experimental"]; ok { 551 return errors.New("only supported with experimental daemon") 552 } 553 } 554 555 if cmdVersion, ok := cmd.Tags["version"]; ok && versions.LessThan(clientVersion, cmdVersion) { 556 return fmt.Errorf("only supported with daemon version >= %s", cmdVersion) 557 } 558 559 return nil 560 } 561 */