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