docker_api_attach_test.go 5.4 KB

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