docker_cli_attach_unix_test.go 5.4 KB

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