docker_cli_attach_unix_test.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. // +build !windows
  2. package main
  3. import (
  4. "bufio"
  5. "io/ioutil"
  6. "os/exec"
  7. "strings"
  8. "time"
  9. "github.com/docker/docker/pkg/integration/checker"
  10. "github.com/docker/docker/pkg/stringid"
  11. "github.com/go-check/check"
  12. "github.com/kr/pty"
  13. )
  14. // #9860 Make sure attach ends when container ends (with no errors)
  15. func (s *DockerSuite) TestAttachClosedOnContainerStop(c *check.C) {
  16. testRequires(c, SameHostDaemon)
  17. out, _ := dockerCmd(c, "run", "-dti", "busybox", "/bin/sh", "-c", `trap 'exit 0' SIGTERM; while true; do sleep 1; done`)
  18. id := strings.TrimSpace(out)
  19. c.Assert(waitRun(id), check.IsNil)
  20. pty, tty, err := pty.Open()
  21. c.Assert(err, check.IsNil)
  22. attachCmd := exec.Command(dockerBinary, "attach", id)
  23. attachCmd.Stdin = tty
  24. attachCmd.Stdout = tty
  25. attachCmd.Stderr = tty
  26. err = attachCmd.Start()
  27. c.Assert(err, check.IsNil)
  28. errChan := make(chan error)
  29. go func() {
  30. time.Sleep(300 * time.Millisecond)
  31. defer close(errChan)
  32. // Container is waiting for us to signal it to stop
  33. dockerCmd(c, "stop", id)
  34. // And wait for the attach command to end
  35. errChan <- attachCmd.Wait()
  36. }()
  37. // Wait for the docker to end (should be done by the
  38. // stop command in the go routine)
  39. dockerCmd(c, "wait", id)
  40. select {
  41. case err := <-errChan:
  42. tty.Close()
  43. out, _ := ioutil.ReadAll(pty)
  44. c.Assert(err, check.IsNil, check.Commentf("out: %v", string(out)))
  45. case <-time.After(attachWait):
  46. c.Fatal("timed out without attach returning")
  47. }
  48. }
  49. func (s *DockerSuite) TestAttachAfterDetach(c *check.C) {
  50. name := "detachtest"
  51. cpty, tty, err := pty.Open()
  52. c.Assert(err, checker.IsNil, check.Commentf("Could not open pty: %v", err))
  53. cmd := exec.Command(dockerBinary, "run", "-ti", "--name", name, "busybox")
  54. cmd.Stdin = tty
  55. cmd.Stdout = tty
  56. cmd.Stderr = tty
  57. errChan := make(chan error)
  58. go func() {
  59. errChan <- cmd.Run()
  60. close(errChan)
  61. }()
  62. c.Assert(waitRun(name), check.IsNil)
  63. cpty.Write([]byte{16})
  64. time.Sleep(100 * time.Millisecond)
  65. cpty.Write([]byte{17})
  66. select {
  67. case err := <-errChan:
  68. if err != nil {
  69. buff := make([]byte, 200)
  70. tty.Read(buff)
  71. c.Fatalf("%s: %s", err, buff)
  72. }
  73. case <-time.After(5 * time.Second):
  74. c.Fatal("timeout while detaching")
  75. }
  76. cpty, tty, err = pty.Open()
  77. c.Assert(err, checker.IsNil, check.Commentf("Could not open pty: %v", err))
  78. cmd = exec.Command(dockerBinary, "attach", name)
  79. cmd.Stdin = tty
  80. cmd.Stdout = tty
  81. cmd.Stderr = tty
  82. err = cmd.Start()
  83. c.Assert(err, checker.IsNil)
  84. bytes := make([]byte, 10)
  85. var nBytes int
  86. readErr := make(chan error, 1)
  87. go func() {
  88. time.Sleep(500 * time.Millisecond)
  89. cpty.Write([]byte("\n"))
  90. time.Sleep(500 * time.Millisecond)
  91. nBytes, err = cpty.Read(bytes)
  92. cpty.Close()
  93. readErr <- err
  94. }()
  95. select {
  96. case err := <-readErr:
  97. c.Assert(err, check.IsNil)
  98. case <-time.After(2 * time.Second):
  99. c.Fatal("timeout waiting for attach read")
  100. }
  101. err = cmd.Wait()
  102. c.Assert(err, checker.IsNil)
  103. c.Assert(string(bytes[:nBytes]), checker.Contains, "/ #")
  104. }
  105. // TestAttachDetach checks that attach in tty mode can be detached using the long container ID
  106. func (s *DockerSuite) TestAttachDetach(c *check.C) {
  107. out, _ := dockerCmd(c, "run", "-itd", "busybox", "cat")
  108. id := strings.TrimSpace(out)
  109. c.Assert(waitRun(id), check.IsNil)
  110. cpty, tty, err := pty.Open()
  111. c.Assert(err, check.IsNil)
  112. defer cpty.Close()
  113. cmd := exec.Command(dockerBinary, "attach", id)
  114. cmd.Stdin = tty
  115. stdout, err := cmd.StdoutPipe()
  116. c.Assert(err, check.IsNil)
  117. defer stdout.Close()
  118. err = cmd.Start()
  119. c.Assert(err, check.IsNil)
  120. c.Assert(waitRun(id), check.IsNil)
  121. _, err = cpty.Write([]byte("hello\n"))
  122. c.Assert(err, check.IsNil)
  123. out, err = bufio.NewReader(stdout).ReadString('\n')
  124. c.Assert(err, check.IsNil)
  125. c.Assert(strings.TrimSpace(out), checker.Equals, "hello", check.Commentf("expected 'hello', got %q", out))
  126. // escape sequence
  127. _, err = cpty.Write([]byte{16})
  128. c.Assert(err, checker.IsNil)
  129. time.Sleep(100 * time.Millisecond)
  130. _, err = cpty.Write([]byte{17})
  131. c.Assert(err, checker.IsNil)
  132. ch := make(chan struct{})
  133. go func() {
  134. cmd.Wait()
  135. ch <- struct{}{}
  136. }()
  137. running := inspectField(c, id, "State.Running")
  138. c.Assert(running, checker.Equals, "true", check.Commentf("expected container to still be running"))
  139. go func() {
  140. dockerCmd(c, "kill", id)
  141. }()
  142. select {
  143. case <-ch:
  144. case <-time.After(10 * time.Millisecond):
  145. c.Fatal("timed out waiting for container to exit")
  146. }
  147. }
  148. // TestAttachDetachTruncatedID checks that attach in tty mode can be detached
  149. func (s *DockerSuite) TestAttachDetachTruncatedID(c *check.C) {
  150. out, _ := dockerCmd(c, "run", "-itd", "busybox", "cat")
  151. id := stringid.TruncateID(strings.TrimSpace(out))
  152. c.Assert(waitRun(id), check.IsNil)
  153. cpty, tty, err := pty.Open()
  154. c.Assert(err, checker.IsNil)
  155. defer cpty.Close()
  156. cmd := exec.Command(dockerBinary, "attach", id)
  157. cmd.Stdin = tty
  158. stdout, err := cmd.StdoutPipe()
  159. c.Assert(err, checker.IsNil)
  160. defer stdout.Close()
  161. err = cmd.Start()
  162. c.Assert(err, checker.IsNil)
  163. _, err = cpty.Write([]byte("hello\n"))
  164. c.Assert(err, checker.IsNil)
  165. out, err = bufio.NewReader(stdout).ReadString('\n')
  166. c.Assert(err, checker.IsNil)
  167. c.Assert(strings.TrimSpace(out), checker.Equals, "hello", check.Commentf("expected 'hello', got %q", out))
  168. // escape sequence
  169. _, err = cpty.Write([]byte{16})
  170. c.Assert(err, checker.IsNil)
  171. time.Sleep(100 * time.Millisecond)
  172. _, err = cpty.Write([]byte{17})
  173. c.Assert(err, checker.IsNil)
  174. ch := make(chan struct{})
  175. go func() {
  176. cmd.Wait()
  177. ch <- struct{}{}
  178. }()
  179. running := inspectField(c, id, "State.Running")
  180. c.Assert(running, checker.Equals, "true", check.Commentf("expected container to still be running"))
  181. go func() {
  182. dockerCmd(c, "kill", id)
  183. }()
  184. select {
  185. case <-ch:
  186. case <-time.After(10 * time.Millisecond):
  187. c.Fatal("timed out waiting for container to exit")
  188. }
  189. }