docker_cli_attach_unix_test.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. // +build !windows
  2. package main
  3. import (
  4. "bufio"
  5. "fmt"
  6. "io/ioutil"
  7. "os/exec"
  8. "strings"
  9. "testing"
  10. "time"
  11. "github.com/creack/pty"
  12. "gotest.tools/assert"
  13. )
  14. // #9860 Make sure attach ends when container ends (with no errors)
  15. func (s *DockerSuite) TestAttachClosedOnContainerStop(c *testing.T) {
  16. testRequires(c, testEnv.IsLocalDaemon)
  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. assert.NilError(c, waitRun(id))
  20. pty, tty, err := pty.Open()
  21. assert.NilError(c, err)
  22. attachCmd := exec.Command(dockerBinary, "attach", id)
  23. attachCmd.Stdin = tty
  24. attachCmd.Stdout = tty
  25. attachCmd.Stderr = tty
  26. err = attachCmd.Start()
  27. assert.NilError(c, err)
  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. assert.Assert(c, err == nil, fmt.Sprintf("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 *testing.T) {
  50. name := "detachtest"
  51. cpty, tty, err := pty.Open()
  52. assert.NilError(c, err, "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. cmdExit := make(chan error)
  58. go func() {
  59. cmdExit <- cmd.Run()
  60. close(cmdExit)
  61. }()
  62. assert.Assert(c, waitRun(name) == nil)
  63. cpty.Write([]byte{16})
  64. time.Sleep(100 * time.Millisecond)
  65. cpty.Write([]byte{17})
  66. select {
  67. case <-cmdExit:
  68. case <-time.After(5 * time.Second):
  69. c.Fatal("timeout while detaching")
  70. }
  71. cpty, tty, err = pty.Open()
  72. assert.NilError(c, err, "Could not open pty: %v", err)
  73. cmd = exec.Command(dockerBinary, "attach", name)
  74. cmd.Stdin = tty
  75. cmd.Stdout = tty
  76. cmd.Stderr = tty
  77. err = cmd.Start()
  78. assert.NilError(c, err)
  79. defer cmd.Process.Kill()
  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. assert.NilError(c, err)
  94. case <-time.After(2 * time.Second):
  95. c.Fatal("timeout waiting for attach read")
  96. }
  97. assert.Assert(c, strings.Contains(string(bytes[:nBytes]), "/ #"))
  98. }
  99. // TestAttachDetach checks that attach in tty mode can be detached using the long container ID
  100. func (s *DockerSuite) TestAttachDetach(c *testing.T) {
  101. out, _ := dockerCmd(c, "run", "-itd", "busybox", "cat")
  102. id := strings.TrimSpace(out)
  103. assert.NilError(c, waitRun(id))
  104. cpty, tty, err := pty.Open()
  105. assert.NilError(c, err)
  106. defer cpty.Close()
  107. cmd := exec.Command(dockerBinary, "attach", id)
  108. cmd.Stdin = tty
  109. stdout, err := cmd.StdoutPipe()
  110. assert.NilError(c, err)
  111. defer stdout.Close()
  112. err = cmd.Start()
  113. assert.NilError(c, err)
  114. assert.NilError(c, waitRun(id))
  115. _, err = cpty.Write([]byte("hello\n"))
  116. assert.NilError(c, err)
  117. out, err = bufio.NewReader(stdout).ReadString('\n')
  118. assert.NilError(c, err)
  119. assert.Equal(c, strings.TrimSpace(out), "hello")
  120. // escape sequence
  121. _, err = cpty.Write([]byte{16})
  122. assert.NilError(c, err)
  123. time.Sleep(100 * time.Millisecond)
  124. _, err = cpty.Write([]byte{17})
  125. assert.NilError(c, err)
  126. ch := make(chan struct{})
  127. go func() {
  128. cmd.Wait()
  129. close(ch)
  130. }()
  131. select {
  132. case <-ch:
  133. case <-time.After(1 * time.Second):
  134. c.Fatal("timed out waiting for container to exit")
  135. }
  136. running := inspectField(c, id, "State.Running")
  137. assert.Equal(c, running, "true") // container should be running
  138. }