start.go 4.4 KB

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