docker_cli_attach_unix_test.go 5.6 KB

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