docker_api_exec_test.go 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. // +build !test_no_exec
  2. package main
  3. import (
  4. "bytes"
  5. "encoding/json"
  6. "fmt"
  7. "io/ioutil"
  8. "net/http"
  9. "time"
  10. "github.com/docker/docker/api/types"
  11. "github.com/docker/docker/client"
  12. "github.com/docker/docker/integration-cli/checker"
  13. "github.com/docker/docker/integration-cli/request"
  14. "github.com/go-check/check"
  15. "golang.org/x/net/context"
  16. )
  17. // Regression test for #9414
  18. func (s *DockerSuite) TestExecAPICreateNoCmd(c *check.C) {
  19. name := "exec_test"
  20. dockerCmd(c, "run", "-d", "-t", "--name", name, "busybox", "/bin/sh")
  21. res, body, err := request.Post(fmt.Sprintf("/containers/%s/exec", name), request.JSONBody(map[string]interface{}{"Cmd": nil}))
  22. c.Assert(err, checker.IsNil)
  23. c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest)
  24. b, err := request.ReadBody(body)
  25. c.Assert(err, checker.IsNil)
  26. comment := check.Commentf("Expected message when creating exec command with no Cmd specified")
  27. c.Assert(getErrorMessage(c, b), checker.Contains, "No exec command specified", comment)
  28. }
  29. func (s *DockerSuite) TestExecAPICreateNoValidContentType(c *check.C) {
  30. name := "exec_test"
  31. dockerCmd(c, "run", "-d", "-t", "--name", name, "busybox", "/bin/sh")
  32. jsonData := bytes.NewBuffer(nil)
  33. if err := json.NewEncoder(jsonData).Encode(map[string]interface{}{"Cmd": nil}); err != nil {
  34. c.Fatalf("Can not encode data to json %s", err)
  35. }
  36. res, body, err := request.Post(fmt.Sprintf("/containers/%s/exec", name), request.RawContent(ioutil.NopCloser(jsonData)), request.ContentType("test/plain"))
  37. c.Assert(err, checker.IsNil)
  38. c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest)
  39. b, err := request.ReadBody(body)
  40. c.Assert(err, checker.IsNil)
  41. comment := check.Commentf("Expected message when creating exec command with invalid Content-Type specified")
  42. c.Assert(getErrorMessage(c, b), checker.Contains, "Content-Type specified", comment)
  43. }
  44. func (s *DockerSuite) TestExecAPICreateContainerPaused(c *check.C) {
  45. // Not relevant on Windows as Windows containers cannot be paused
  46. testRequires(c, DaemonIsLinux)
  47. name := "exec_create_test"
  48. dockerCmd(c, "run", "-d", "-t", "--name", name, "busybox", "/bin/sh")
  49. dockerCmd(c, "pause", name)
  50. cli, err := client.NewEnvClient()
  51. c.Assert(err, checker.IsNil)
  52. defer cli.Close()
  53. config := types.ExecConfig{
  54. Cmd: []string{"true"},
  55. }
  56. _, err = cli.ContainerExecCreate(context.Background(), name, config)
  57. comment := check.Commentf("Expected message when creating exec command with Container %s is paused", name)
  58. c.Assert(err.Error(), checker.Contains, "Container "+name+" is paused, unpause the container before exec", comment)
  59. }
  60. func (s *DockerSuite) TestExecAPIStart(c *check.C) {
  61. testRequires(c, DaemonIsLinux) // Uses pause/unpause but bits may be salvageable to Windows to Windows CI
  62. dockerCmd(c, "run", "-d", "--name", "test", "busybox", "top")
  63. id := createExec(c, "test")
  64. startExec(c, id, http.StatusOK)
  65. var execJSON struct{ PID int }
  66. inspectExec(c, id, &execJSON)
  67. c.Assert(execJSON.PID, checker.GreaterThan, 1)
  68. id = createExec(c, "test")
  69. dockerCmd(c, "stop", "test")
  70. startExec(c, id, http.StatusNotFound)
  71. dockerCmd(c, "start", "test")
  72. startExec(c, id, http.StatusNotFound)
  73. // make sure exec is created before pausing
  74. id = createExec(c, "test")
  75. dockerCmd(c, "pause", "test")
  76. startExec(c, id, http.StatusConflict)
  77. dockerCmd(c, "unpause", "test")
  78. startExec(c, id, http.StatusOK)
  79. }
  80. func (s *DockerSuite) TestExecAPIStartEnsureHeaders(c *check.C) {
  81. testRequires(c, DaemonIsLinux)
  82. dockerCmd(c, "run", "-d", "--name", "test", "busybox", "top")
  83. id := createExec(c, "test")
  84. resp, _, err := request.Post(fmt.Sprintf("/exec/%s/start", id), request.RawString(`{"Detach": true}`), request.JSON)
  85. c.Assert(err, checker.IsNil)
  86. c.Assert(resp.Header.Get("Server"), checker.Not(checker.Equals), "")
  87. }
  88. func (s *DockerSuite) TestExecAPIStartBackwardsCompatible(c *check.C) {
  89. testRequires(c, DaemonIsLinux) // Windows only supports 1.25 or later
  90. runSleepingContainer(c, "-d", "--name", "test")
  91. id := createExec(c, "test")
  92. resp, body, err := request.Post(fmt.Sprintf("/v1.20/exec/%s/start", id), request.RawString(`{"Detach": true}`), request.ContentType("text/plain"))
  93. c.Assert(err, checker.IsNil)
  94. b, err := request.ReadBody(body)
  95. comment := check.Commentf("response body: %s", b)
  96. c.Assert(err, checker.IsNil, comment)
  97. c.Assert(resp.StatusCode, checker.Equals, http.StatusOK, comment)
  98. }
  99. // #19362
  100. func (s *DockerSuite) TestExecAPIStartMultipleTimesError(c *check.C) {
  101. runSleepingContainer(c, "-d", "--name", "test")
  102. execID := createExec(c, "test")
  103. startExec(c, execID, http.StatusOK)
  104. waitForExec(c, execID)
  105. startExec(c, execID, http.StatusConflict)
  106. }
  107. // #20638
  108. func (s *DockerSuite) TestExecAPIStartWithDetach(c *check.C) {
  109. name := "foo"
  110. runSleepingContainer(c, "-d", "-t", "--name", name)
  111. config := types.ExecConfig{
  112. Cmd: []string{"true"},
  113. AttachStderr: true,
  114. }
  115. cli, err := client.NewEnvClient()
  116. c.Assert(err, checker.IsNil)
  117. defer cli.Close()
  118. createResp, err := cli.ContainerExecCreate(context.Background(), name, config)
  119. c.Assert(err, checker.IsNil)
  120. _, body, err := request.Post(fmt.Sprintf("/exec/%s/start", createResp.ID), request.RawString(`{"Detach": true}`), request.JSON)
  121. c.Assert(err, checker.IsNil)
  122. b, err := request.ReadBody(body)
  123. comment := check.Commentf("response body: %s", b)
  124. c.Assert(err, checker.IsNil, comment)
  125. resp, _, err := request.Get("/_ping")
  126. c.Assert(err, checker.IsNil)
  127. if resp.StatusCode != http.StatusOK {
  128. c.Fatal("daemon is down, it should alive")
  129. }
  130. }
  131. // #30311
  132. func (s *DockerSuite) TestExecAPIStartValidCommand(c *check.C) {
  133. name := "exec_test"
  134. dockerCmd(c, "run", "-d", "-t", "--name", name, "busybox", "/bin/sh")
  135. id := createExecCmd(c, name, "true")
  136. startExec(c, id, http.StatusOK)
  137. waitForExec(c, id)
  138. var inspectJSON struct{ ExecIDs []string }
  139. inspectContainer(c, name, &inspectJSON)
  140. c.Assert(inspectJSON.ExecIDs, checker.IsNil)
  141. }
  142. // #30311
  143. func (s *DockerSuite) TestExecAPIStartInvalidCommand(c *check.C) {
  144. name := "exec_test"
  145. dockerCmd(c, "run", "-d", "-t", "--name", name, "busybox", "/bin/sh")
  146. id := createExecCmd(c, name, "invalid")
  147. startExec(c, id, http.StatusBadRequest)
  148. waitForExec(c, id)
  149. var inspectJSON struct{ ExecIDs []string }
  150. inspectContainer(c, name, &inspectJSON)
  151. c.Assert(inspectJSON.ExecIDs, checker.IsNil)
  152. }
  153. func createExec(c *check.C, name string) string {
  154. return createExecCmd(c, name, "true")
  155. }
  156. func createExecCmd(c *check.C, name string, cmd string) string {
  157. _, reader, err := request.Post(fmt.Sprintf("/containers/%s/exec", name), request.JSONBody(map[string]interface{}{"Cmd": []string{cmd}}))
  158. c.Assert(err, checker.IsNil)
  159. b, err := ioutil.ReadAll(reader)
  160. c.Assert(err, checker.IsNil)
  161. defer reader.Close()
  162. createResp := struct {
  163. ID string `json:"Id"`
  164. }{}
  165. c.Assert(json.Unmarshal(b, &createResp), checker.IsNil, check.Commentf(string(b)))
  166. return createResp.ID
  167. }
  168. func startExec(c *check.C, id string, code int) {
  169. resp, body, err := request.Post(fmt.Sprintf("/exec/%s/start", id), request.RawString(`{"Detach": true}`), request.JSON)
  170. c.Assert(err, checker.IsNil)
  171. b, err := request.ReadBody(body)
  172. comment := check.Commentf("response body: %s", b)
  173. c.Assert(err, checker.IsNil, comment)
  174. c.Assert(resp.StatusCode, checker.Equals, code, comment)
  175. }
  176. func inspectExec(c *check.C, id string, out interface{}) {
  177. resp, body, err := request.Get(fmt.Sprintf("/exec/%s/json", id))
  178. c.Assert(err, checker.IsNil)
  179. defer body.Close()
  180. c.Assert(resp.StatusCode, checker.Equals, http.StatusOK)
  181. err = json.NewDecoder(body).Decode(out)
  182. c.Assert(err, checker.IsNil)
  183. }
  184. func waitForExec(c *check.C, id string) {
  185. timeout := time.After(60 * time.Second)
  186. var execJSON struct{ Running bool }
  187. for {
  188. select {
  189. case <-timeout:
  190. c.Fatal("timeout waiting for exec to start")
  191. default:
  192. }
  193. inspectExec(c, id, &execJSON)
  194. if !execJSON.Running {
  195. break
  196. }
  197. }
  198. }
  199. func inspectContainer(c *check.C, id string, out interface{}) {
  200. resp, body, err := request.Get(fmt.Sprintf("/containers/%s/json", id))
  201. c.Assert(err, checker.IsNil)
  202. defer body.Close()
  203. c.Assert(resp.StatusCode, checker.Equals, http.StatusOK)
  204. err = json.NewDecoder(body).Decode(out)
  205. c.Assert(err, checker.IsNil)
  206. }