docker_api_attach_test.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. package main
  2. import (
  3. "bytes"
  4. "io"
  5. "net/http"
  6. "net/http/httputil"
  7. "os/exec"
  8. "strings"
  9. "time"
  10. "github.com/go-check/check"
  11. "golang.org/x/net/websocket"
  12. )
  13. func (s *DockerSuite) TestGetContainersAttachWebsocket(c *check.C) {
  14. runCmd := exec.Command(dockerBinary, "run", "-dit", "busybox", "cat")
  15. out, _, err := runCommandWithOutput(runCmd)
  16. if err != nil {
  17. c.Fatalf(out, err)
  18. }
  19. rwc, err := sockConn(time.Duration(10 * time.Second))
  20. if err != nil {
  21. c.Fatal(err)
  22. }
  23. cleanedContainerID := strings.TrimSpace(out)
  24. config, err := websocket.NewConfig(
  25. "/containers/"+cleanedContainerID+"/attach/ws?stream=1&stdin=1&stdout=1&stderr=1",
  26. "http://localhost",
  27. )
  28. if err != nil {
  29. c.Fatal(err)
  30. }
  31. ws, err := websocket.NewClient(config, rwc)
  32. if err != nil {
  33. c.Fatal(err)
  34. }
  35. defer ws.Close()
  36. expected := []byte("hello")
  37. actual := make([]byte, len(expected))
  38. outChan := make(chan error)
  39. go func() {
  40. _, err := ws.Read(actual)
  41. outChan <- err
  42. close(outChan)
  43. }()
  44. inChan := make(chan error)
  45. go func() {
  46. _, err := ws.Write(expected)
  47. inChan <- err
  48. close(inChan)
  49. }()
  50. select {
  51. case err := <-inChan:
  52. if err != nil {
  53. c.Fatal(err)
  54. }
  55. case <-time.After(5 * time.Second):
  56. c.Fatal("Timeout writing to ws")
  57. }
  58. select {
  59. case err := <-outChan:
  60. if err != nil {
  61. c.Fatal(err)
  62. }
  63. case <-time.After(5 * time.Second):
  64. c.Fatal("Timeout reading from ws")
  65. }
  66. if !bytes.Equal(expected, actual) {
  67. c.Fatal("Expected output on websocket to match input")
  68. }
  69. }
  70. // regression gh14320
  71. func (s *DockerSuite) TestPostContainersAttachContainerNotFound(c *check.C) {
  72. status, body, err := sockRequest("POST", "/containers/doesnotexist/attach", nil)
  73. c.Assert(status, check.Equals, http.StatusNotFound)
  74. c.Assert(err, check.IsNil)
  75. expected := "no such id: doesnotexist\n"
  76. if !strings.Contains(string(body), expected) {
  77. c.Fatalf("Expected response body to contain %q", expected)
  78. }
  79. }
  80. func (s *DockerSuite) TestGetContainersWsAttachContainerNotFound(c *check.C) {
  81. status, body, err := sockRequest("GET", "/containers/doesnotexist/attach/ws", nil)
  82. c.Assert(status, check.Equals, http.StatusNotFound)
  83. c.Assert(err, check.IsNil)
  84. expected := "no such id: doesnotexist\n"
  85. if !strings.Contains(string(body), expected) {
  86. c.Fatalf("Expected response body to contain %q", expected)
  87. }
  88. }
  89. func (s *DockerSuite) TestPostContainersAttach(c *check.C) {
  90. runCmd := exec.Command(dockerBinary, "run", "-dit", "busybox", "cat")
  91. out, _, err := runCommandWithOutput(runCmd)
  92. c.Assert(err, check.IsNil)
  93. r, w := io.Pipe()
  94. defer r.Close()
  95. defer w.Close()
  96. conn, err := sockConn(time.Duration(10 * time.Second))
  97. c.Assert(err, check.IsNil)
  98. containerID := strings.TrimSpace(out)
  99. req, err := http.NewRequest("POST", "/containers/"+containerID+"/attach?stream=1&stdin=1&stdout=1&stderr=1", bytes.NewReader([]byte{}))
  100. c.Assert(err, check.IsNil)
  101. client := httputil.NewClientConn(conn, nil)
  102. defer client.Close()
  103. // Do POST attach request
  104. resp, err := client.Do(req)
  105. c.Assert(resp.StatusCode, check.Equals, http.StatusOK)
  106. // If we check the err, we get a ErrPersistEOF = &http.ProtocolError{ErrorString: "persistent connection closed"}
  107. // This means that the remote requested this be the last request serviced, is this okay?
  108. // Test read and write to the attached container
  109. expected := []byte("hello")
  110. actual := make([]byte, len(expected))
  111. outChan := make(chan error)
  112. go func() {
  113. _, err := r.Read(actual)
  114. outChan <- err
  115. close(outChan)
  116. }()
  117. inChan := make(chan error)
  118. go func() {
  119. _, err := w.Write(expected)
  120. inChan <- err
  121. close(inChan)
  122. }()
  123. select {
  124. case err := <-inChan:
  125. c.Assert(err, check.IsNil)
  126. case <-time.After(5 * time.Second):
  127. c.Fatal("Timeout writing to stdout")
  128. }
  129. select {
  130. case err := <-outChan:
  131. c.Assert(err, check.IsNil)
  132. case <-time.After(5 * time.Second):
  133. c.Fatal("Timeout reading from stdin")
  134. }
  135. if !bytes.Equal(expected, actual) {
  136. c.Fatal("Expected output to match input")
  137. }
  138. resp.Body.Close()
  139. }
  140. func (s *DockerSuite) TestPostContainersAttachStderr(c *check.C) {
  141. runCmd := exec.Command(dockerBinary, "run", "-dit", "busybox", "/bin/sh", "-c", "cat >&2")
  142. out, _, err := runCommandWithOutput(runCmd)
  143. c.Assert(err, check.IsNil)
  144. r, w := io.Pipe()
  145. defer r.Close()
  146. defer w.Close()
  147. conn, err := sockConn(time.Duration(10 * time.Second))
  148. c.Assert(err, check.IsNil)
  149. containerID := strings.TrimSpace(out)
  150. req, err := http.NewRequest("POST", "/containers/"+containerID+"/attach?stream=1&stdin=1&stdout=1&stderr=1", bytes.NewReader([]byte{}))
  151. c.Assert(err, check.IsNil)
  152. client := httputil.NewClientConn(conn, nil)
  153. defer client.Close()
  154. // Do POST attach request
  155. resp, err := client.Do(req)
  156. c.Assert(resp.StatusCode, check.Equals, http.StatusOK)
  157. // If we check the err, we get a ErrPersistEOF = &http.ProtocolError{ErrorString: "persistent connection closed"}
  158. // This means that the remote requested this be the last request serviced, is this okay?
  159. // Test read and write to the attached container
  160. expected := []byte("hello")
  161. actual := make([]byte, len(expected))
  162. outChan := make(chan error)
  163. go func() {
  164. _, err := r.Read(actual)
  165. outChan <- err
  166. close(outChan)
  167. }()
  168. inChan := make(chan error)
  169. go func() {
  170. _, err := w.Write(expected)
  171. inChan <- err
  172. close(inChan)
  173. }()
  174. select {
  175. case err := <-inChan:
  176. c.Assert(err, check.IsNil)
  177. case <-time.After(5 * time.Second):
  178. c.Fatal("Timeout writing to stdout")
  179. }
  180. select {
  181. case err := <-outChan:
  182. c.Assert(err, check.IsNil)
  183. case <-time.After(5 * time.Second):
  184. c.Fatal("Timeout reading from stdin")
  185. }
  186. if !bytes.Equal(expected, actual) {
  187. c.Fatal("Expected output to match input")
  188. }
  189. resp.Body.Close()
  190. }