start.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. package client
  2. import (
  3. "fmt"
  4. "io"
  5. "os"
  6. "strings"
  7. "golang.org/x/net/context"
  8. "github.com/Sirupsen/logrus"
  9. Cli "github.com/docker/docker/cli"
  10. flag "github.com/docker/docker/pkg/mflag"
  11. "github.com/docker/docker/pkg/promise"
  12. "github.com/docker/docker/pkg/signal"
  13. "github.com/docker/engine-api/types"
  14. )
  15. func (cli *DockerCli) forwardAllSignals(cid string) chan os.Signal {
  16. sigc := make(chan os.Signal, 128)
  17. signal.CatchAll(sigc)
  18. go func() {
  19. for s := range sigc {
  20. if s == signal.SIGCHLD || s == signal.SIGPIPE {
  21. continue
  22. }
  23. var sig string
  24. for sigStr, sigN := range signal.SignalMap {
  25. if sigN == s {
  26. sig = sigStr
  27. break
  28. }
  29. }
  30. if sig == "" {
  31. fmt.Fprintf(cli.err, "Unsupported signal: %v. Discarding.\n", s)
  32. continue
  33. }
  34. if err := cli.client.ContainerKill(context.Background(), cid, sig); err != nil {
  35. logrus.Debugf("Error sending signal: %s", err)
  36. }
  37. }
  38. }()
  39. return sigc
  40. }
  41. // CmdStart starts one or more containers.
  42. //
  43. // Usage: docker start [OPTIONS] CONTAINER [CONTAINER...]
  44. func (cli *DockerCli) CmdStart(args ...string) error {
  45. cmd := Cli.Subcmd("start", []string{"CONTAINER [CONTAINER...]"}, Cli.DockerCommands["start"].Description, true)
  46. attach := cmd.Bool([]string{"a", "-attach"}, false, "Attach STDOUT/STDERR and forward signals")
  47. openStdin := cmd.Bool([]string{"i", "-interactive"}, false, "Attach container's STDIN")
  48. detachKeys := cmd.String([]string{"-detach-keys"}, "", "Override the key sequence for detaching a container")
  49. cmd.Require(flag.Min, 1)
  50. cmd.ParseFlags(args, true)
  51. if *attach || *openStdin {
  52. // We're going to attach to a container.
  53. // 1. Ensure we only have one container.
  54. if cmd.NArg() > 1 {
  55. return fmt.Errorf("You cannot start and attach multiple containers at once.")
  56. }
  57. // 2. Attach to the container.
  58. containerID := cmd.Arg(0)
  59. c, err := cli.client.ContainerInspect(context.Background(), containerID)
  60. if err != nil {
  61. return err
  62. }
  63. if !c.Config.Tty {
  64. sigc := cli.forwardAllSignals(containerID)
  65. defer signal.StopCatch(sigc)
  66. }
  67. if *detachKeys != "" {
  68. cli.configFile.DetachKeys = *detachKeys
  69. }
  70. options := types.ContainerAttachOptions{
  71. ContainerID: containerID,
  72. Stream: true,
  73. Stdin: *openStdin && c.Config.OpenStdin,
  74. Stdout: true,
  75. Stderr: true,
  76. DetachKeys: cli.configFile.DetachKeys,
  77. }
  78. var in io.ReadCloser
  79. if options.Stdin {
  80. in = cli.in
  81. }
  82. resp, err := cli.client.ContainerAttach(context.Background(), options)
  83. if err != nil {
  84. return err
  85. }
  86. defer resp.Close()
  87. if in != nil && c.Config.Tty {
  88. if err := cli.setRawTerminal(); err != nil {
  89. return err
  90. }
  91. defer cli.restoreTerminal(in)
  92. }
  93. cErr := promise.Go(func() error {
  94. return cli.holdHijackedConnection(c.Config.Tty, in, cli.out, cli.err, resp)
  95. })
  96. // 3. Start the container.
  97. if err := cli.client.ContainerStart(context.Background(), containerID); err != nil {
  98. return err
  99. }
  100. // 4. Wait for attachment to break.
  101. if c.Config.Tty && cli.isTerminalOut {
  102. if err := cli.monitorTtySize(containerID, false); err != nil {
  103. fmt.Fprintf(cli.err, "Error monitoring TTY size: %s\n", err)
  104. }
  105. }
  106. if attchErr := <-cErr; attchErr != nil {
  107. return attchErr
  108. }
  109. _, status, err := getExitCode(cli, containerID)
  110. if err != nil {
  111. return err
  112. }
  113. if status != 0 {
  114. return Cli.StatusError{StatusCode: status}
  115. }
  116. } else {
  117. // We're not going to attach to anything.
  118. // Start as many containers as we want.
  119. return cli.startContainersWithoutAttachments(cmd.Args())
  120. }
  121. return nil
  122. }
  123. func (cli *DockerCli) startContainersWithoutAttachments(containerIDs []string) error {
  124. var failedContainers []string
  125. for _, containerID := range containerIDs {
  126. if err := cli.client.ContainerStart(context.Background(), containerID); err != nil {
  127. fmt.Fprintf(cli.err, "%s\n", err)
  128. failedContainers = append(failedContainers, containerID)
  129. } else {
  130. fmt.Fprintf(cli.out, "%s\n", containerID)
  131. }
  132. }
  133. if len(failedContainers) > 0 {
  134. return fmt.Errorf("Error: failed to start containers: %v", strings.Join(failedContainers, ", "))
  135. }
  136. return nil
  137. }