docker_cli_attach_unix_test.go 5.5 KB

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