docker_api_exec_test.go 9.1 KB

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