docker_cli_attach_unix_test.go 5.2 KB

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