docker_api_attach_test.go 5.3 KB

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