github.com/noxiouz/docker@v0.7.3-0.20160629055221-3d231c78e8c5/integration-cli/docker_api_containers_test.go (about) 1 package main 2 3 import ( 4 "archive/tar" 5 "bytes" 6 "encoding/json" 7 "fmt" 8 "io" 9 "net/http" 10 "net/http/httputil" 11 "net/url" 12 "os" 13 "regexp" 14 "strconv" 15 "strings" 16 "time" 17 18 "github.com/docker/docker/pkg/integration" 19 "github.com/docker/docker/pkg/integration/checker" 20 "github.com/docker/docker/pkg/stringid" 21 "github.com/docker/engine-api/types" 22 containertypes "github.com/docker/engine-api/types/container" 23 networktypes "github.com/docker/engine-api/types/network" 24 "github.com/go-check/check" 25 ) 26 27 func (s *DockerSuite) TestContainerApiGetAll(c *check.C) { 28 startCount, err := getContainerCount() 29 c.Assert(err, checker.IsNil, check.Commentf("Cannot query container count")) 30 31 name := "getall" 32 dockerCmd(c, "run", "--name", name, "busybox", "true") 33 34 status, body, err := sockRequest("GET", "/containers/json?all=1", nil) 35 c.Assert(err, checker.IsNil) 36 c.Assert(status, checker.Equals, http.StatusOK) 37 38 var inspectJSON []struct { 39 Names []string 40 } 41 err = json.Unmarshal(body, &inspectJSON) 42 c.Assert(err, checker.IsNil, check.Commentf("unable to unmarshal response body")) 43 44 c.Assert(inspectJSON, checker.HasLen, startCount+1) 45 46 actual := inspectJSON[0].Names[0] 47 c.Assert(actual, checker.Equals, "/"+name) 48 } 49 50 // regression test for empty json field being omitted #13691 51 func (s *DockerSuite) TestContainerApiGetJSONNoFieldsOmitted(c *check.C) { 52 dockerCmd(c, "run", "busybox", "true") 53 54 status, body, err := sockRequest("GET", "/containers/json?all=1", nil) 55 c.Assert(err, checker.IsNil) 56 c.Assert(status, checker.Equals, http.StatusOK) 57 58 // empty Labels field triggered this bug, make sense to check for everything 59 // cause even Ports for instance can trigger this bug 60 // better safe than sorry.. 61 fields := []string{ 62 "Id", 63 "Names", 64 "Image", 65 "Command", 66 "Created", 67 "Ports", 68 "Labels", 69 "Status", 70 "NetworkSettings", 71 } 72 73 // decoding into types.Container do not work since it eventually unmarshal 74 // and empty field to an empty go map, so we just check for a string 75 for _, f := range fields { 76 if !strings.Contains(string(body), f) { 77 c.Fatalf("Field %s is missing and it shouldn't", f) 78 } 79 } 80 } 81 82 type containerPs struct { 83 Names []string 84 Ports []map[string]interface{} 85 } 86 87 // regression test for non-empty fields from #13901 88 func (s *DockerSuite) TestContainerApiPsOmitFields(c *check.C) { 89 // Problematic for Windows porting due to networking not yet being passed back 90 testRequires(c, DaemonIsLinux) 91 name := "pstest" 92 port := 80 93 runSleepingContainer(c, "--name", name, "--expose", strconv.Itoa(port)) 94 95 status, body, err := sockRequest("GET", "/containers/json?all=1", nil) 96 c.Assert(err, checker.IsNil) 97 c.Assert(status, checker.Equals, http.StatusOK) 98 99 var resp []containerPs 100 err = json.Unmarshal(body, &resp) 101 c.Assert(err, checker.IsNil) 102 103 var foundContainer *containerPs 104 for _, container := range resp { 105 for _, testName := range container.Names { 106 if "/"+name == testName { 107 foundContainer = &container 108 break 109 } 110 } 111 } 112 113 c.Assert(foundContainer.Ports, checker.HasLen, 1) 114 c.Assert(foundContainer.Ports[0]["PrivatePort"], checker.Equals, float64(port)) 115 _, ok := foundContainer.Ports[0]["PublicPort"] 116 c.Assert(ok, checker.Not(checker.Equals), true) 117 _, ok = foundContainer.Ports[0]["IP"] 118 c.Assert(ok, checker.Not(checker.Equals), true) 119 } 120 121 func (s *DockerSuite) TestContainerApiGetExport(c *check.C) { 122 // TODO: Investigate why this fails on Windows to Windows CI 123 testRequires(c, DaemonIsLinux) 124 name := "exportcontainer" 125 dockerCmd(c, "run", "--name", name, "busybox", "touch", "/test") 126 127 status, body, err := sockRequest("GET", "/containers/"+name+"/export", nil) 128 c.Assert(err, checker.IsNil) 129 c.Assert(status, checker.Equals, http.StatusOK) 130 131 found := false 132 for tarReader := tar.NewReader(bytes.NewReader(body)); ; { 133 h, err := tarReader.Next() 134 if err != nil && err == io.EOF { 135 break 136 } 137 if h.Name == "test" { 138 found = true 139 break 140 } 141 } 142 c.Assert(found, checker.True, check.Commentf("The created test file has not been found in the exported image")) 143 } 144 145 func (s *DockerSuite) TestContainerApiGetChanges(c *check.C) { 146 // Not supported on Windows as Windows does not support docker diff (/containers/name/changes) 147 testRequires(c, DaemonIsLinux) 148 name := "changescontainer" 149 dockerCmd(c, "run", "--name", name, "busybox", "rm", "/etc/passwd") 150 151 status, body, err := sockRequest("GET", "/containers/"+name+"/changes", nil) 152 c.Assert(err, checker.IsNil) 153 c.Assert(status, checker.Equals, http.StatusOK) 154 155 changes := []struct { 156 Kind int 157 Path string 158 }{} 159 c.Assert(json.Unmarshal(body, &changes), checker.IsNil, check.Commentf("unable to unmarshal response body")) 160 161 // Check the changelog for removal of /etc/passwd 162 success := false 163 for _, elem := range changes { 164 if elem.Path == "/etc/passwd" && elem.Kind == 2 { 165 success = true 166 } 167 } 168 c.Assert(success, checker.True, check.Commentf("/etc/passwd has been removed but is not present in the diff")) 169 } 170 171 func (s *DockerSuite) TestGetContainerStats(c *check.C) { 172 // Problematic on Windows as Windows does not support stats 173 testRequires(c, DaemonIsLinux) 174 var ( 175 name = "statscontainer" 176 ) 177 dockerCmd(c, "run", "-d", "--name", name, "busybox", "top") 178 179 type b struct { 180 status int 181 body []byte 182 err error 183 } 184 bc := make(chan b, 1) 185 go func() { 186 status, body, err := sockRequest("GET", "/containers/"+name+"/stats", nil) 187 bc <- b{status, body, err} 188 }() 189 190 // allow some time to stream the stats from the container 191 time.Sleep(4 * time.Second) 192 dockerCmd(c, "rm", "-f", name) 193 194 // collect the results from the stats stream or timeout and fail 195 // if the stream was not disconnected. 196 select { 197 case <-time.After(2 * time.Second): 198 c.Fatal("stream was not closed after container was removed") 199 case sr := <-bc: 200 c.Assert(sr.err, checker.IsNil) 201 c.Assert(sr.status, checker.Equals, http.StatusOK) 202 203 dec := json.NewDecoder(bytes.NewBuffer(sr.body)) 204 var s *types.Stats 205 // decode only one object from the stream 206 c.Assert(dec.Decode(&s), checker.IsNil) 207 } 208 } 209 210 func (s *DockerSuite) TestGetContainerStatsRmRunning(c *check.C) { 211 // Problematic on Windows as Windows does not support stats 212 testRequires(c, DaemonIsLinux) 213 out, _ := dockerCmd(c, "run", "-d", "busybox", "top") 214 id := strings.TrimSpace(out) 215 216 buf := &integration.ChannelBuffer{make(chan []byte, 1)} 217 defer buf.Close() 218 chErr := make(chan error, 1) 219 go func() { 220 _, body, err := sockRequestRaw("GET", "/containers/"+id+"/stats?stream=1", nil, "application/json") 221 if err != nil { 222 chErr <- err 223 } 224 defer body.Close() 225 _, err = io.Copy(buf, body) 226 chErr <- err 227 }() 228 defer func() { 229 select { 230 case err := <-chErr: 231 c.Assert(err, checker.IsNil) 232 default: 233 return 234 } 235 }() 236 237 b := make([]byte, 32) 238 // make sure we've got some stats 239 _, err := buf.ReadTimeout(b, 2*time.Second) 240 c.Assert(err, checker.IsNil) 241 242 // Now remove without `-f` and make sure we are still pulling stats 243 _, _, err = dockerCmdWithError("rm", id) 244 c.Assert(err, checker.Not(checker.IsNil), check.Commentf("rm should have failed but didn't")) 245 _, err = buf.ReadTimeout(b, 2*time.Second) 246 c.Assert(err, checker.IsNil) 247 248 dockerCmd(c, "kill", id) 249 } 250 251 // regression test for gh13421 252 // previous test was just checking one stat entry so it didn't fail (stats with 253 // stream false always return one stat) 254 func (s *DockerSuite) TestGetContainerStatsStream(c *check.C) { 255 // Problematic on Windows as Windows does not support stats 256 testRequires(c, DaemonIsLinux) 257 name := "statscontainer" 258 dockerCmd(c, "run", "-d", "--name", name, "busybox", "top") 259 260 type b struct { 261 status int 262 body []byte 263 err error 264 } 265 bc := make(chan b, 1) 266 go func() { 267 status, body, err := sockRequest("GET", "/containers/"+name+"/stats", nil) 268 bc <- b{status, body, err} 269 }() 270 271 // allow some time to stream the stats from the container 272 time.Sleep(4 * time.Second) 273 dockerCmd(c, "rm", "-f", name) 274 275 // collect the results from the stats stream or timeout and fail 276 // if the stream was not disconnected. 277 select { 278 case <-time.After(2 * time.Second): 279 c.Fatal("stream was not closed after container was removed") 280 case sr := <-bc: 281 c.Assert(sr.err, checker.IsNil) 282 c.Assert(sr.status, checker.Equals, http.StatusOK) 283 284 s := string(sr.body) 285 // count occurrences of "read" of types.Stats 286 if l := strings.Count(s, "read"); l < 2 { 287 c.Fatalf("Expected more than one stat streamed, got %d", l) 288 } 289 } 290 } 291 292 func (s *DockerSuite) TestGetContainerStatsNoStream(c *check.C) { 293 // Problematic on Windows as Windows does not support stats 294 testRequires(c, DaemonIsLinux) 295 name := "statscontainer" 296 dockerCmd(c, "run", "-d", "--name", name, "busybox", "top") 297 298 type b struct { 299 status int 300 body []byte 301 err error 302 } 303 bc := make(chan b, 1) 304 go func() { 305 status, body, err := sockRequest("GET", "/containers/"+name+"/stats?stream=0", nil) 306 bc <- b{status, body, err} 307 }() 308 309 // allow some time to stream the stats from the container 310 time.Sleep(4 * time.Second) 311 dockerCmd(c, "rm", "-f", name) 312 313 // collect the results from the stats stream or timeout and fail 314 // if the stream was not disconnected. 315 select { 316 case <-time.After(2 * time.Second): 317 c.Fatal("stream was not closed after container was removed") 318 case sr := <-bc: 319 c.Assert(sr.err, checker.IsNil) 320 c.Assert(sr.status, checker.Equals, http.StatusOK) 321 322 s := string(sr.body) 323 // count occurrences of "read" of types.Stats 324 c.Assert(strings.Count(s, "read"), checker.Equals, 1, check.Commentf("Expected only one stat streamed, got %d", strings.Count(s, "read"))) 325 } 326 } 327 328 func (s *DockerSuite) TestGetStoppedContainerStats(c *check.C) { 329 // Problematic on Windows as Windows does not support stats 330 testRequires(c, DaemonIsLinux) 331 name := "statscontainer" 332 dockerCmd(c, "create", "--name", name, "busybox", "top") 333 334 type stats struct { 335 status int 336 err error 337 } 338 chResp := make(chan stats) 339 340 // We expect an immediate response, but if it's not immediate, the test would hang, so put it in a goroutine 341 // below we'll check this on a timeout. 342 go func() { 343 resp, body, err := sockRequestRaw("GET", "/containers/"+name+"/stats", nil, "") 344 body.Close() 345 chResp <- stats{resp.StatusCode, err} 346 }() 347 348 select { 349 case r := <-chResp: 350 c.Assert(r.err, checker.IsNil) 351 c.Assert(r.status, checker.Equals, http.StatusOK) 352 case <-time.After(10 * time.Second): 353 c.Fatal("timeout waiting for stats response for stopped container") 354 } 355 } 356 357 func (s *DockerSuite) TestContainerApiPause(c *check.C) { 358 // Problematic on Windows as Windows does not support pause 359 testRequires(c, DaemonIsLinux) 360 defer unpauseAllContainers() 361 out, _ := dockerCmd(c, "run", "-d", "busybox", "sleep", "30") 362 ContainerID := strings.TrimSpace(out) 363 364 status, _, err := sockRequest("POST", "/containers/"+ContainerID+"/pause", nil) 365 c.Assert(err, checker.IsNil) 366 c.Assert(status, checker.Equals, http.StatusNoContent) 367 368 pausedContainers, err := getSliceOfPausedContainers() 369 c.Assert(err, checker.IsNil, check.Commentf("error thrown while checking if containers were paused")) 370 371 if len(pausedContainers) != 1 || stringid.TruncateID(ContainerID) != pausedContainers[0] { 372 c.Fatalf("there should be one paused container and not %d", len(pausedContainers)) 373 } 374 375 status, _, err = sockRequest("POST", "/containers/"+ContainerID+"/unpause", nil) 376 c.Assert(err, checker.IsNil) 377 c.Assert(status, checker.Equals, http.StatusNoContent) 378 379 pausedContainers, err = getSliceOfPausedContainers() 380 c.Assert(err, checker.IsNil, check.Commentf("error thrown while checking if containers were paused")) 381 c.Assert(pausedContainers, checker.IsNil, check.Commentf("There should be no paused container.")) 382 } 383 384 func (s *DockerSuite) TestContainerApiTop(c *check.C) { 385 // Problematic on Windows as Windows does not support top 386 testRequires(c, DaemonIsLinux) 387 out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "top") 388 id := strings.TrimSpace(string(out)) 389 c.Assert(waitRun(id), checker.IsNil) 390 391 type topResp struct { 392 Titles []string 393 Processes [][]string 394 } 395 var top topResp 396 status, b, err := sockRequest("GET", "/containers/"+id+"/top?ps_args=aux", nil) 397 c.Assert(err, checker.IsNil) 398 c.Assert(status, checker.Equals, http.StatusOK) 399 c.Assert(json.Unmarshal(b, &top), checker.IsNil) 400 c.Assert(top.Titles, checker.HasLen, 11, check.Commentf("expected 11 titles, found %d: %v", len(top.Titles), top.Titles)) 401 402 if top.Titles[0] != "USER" || top.Titles[10] != "COMMAND" { 403 c.Fatalf("expected `USER` at `Titles[0]` and `COMMAND` at Titles[10]: %v", top.Titles) 404 } 405 c.Assert(top.Processes, checker.HasLen, 2, check.Commentf("expected 2 processes, found %d: %v", len(top.Processes), top.Processes)) 406 c.Assert(top.Processes[0][10], checker.Equals, "/bin/sh -c top") 407 c.Assert(top.Processes[1][10], checker.Equals, "top") 408 } 409 410 func (s *DockerSuite) TestContainerApiCommit(c *check.C) { 411 cName := "testapicommit" 412 dockerCmd(c, "run", "--name="+cName, "busybox", "/bin/sh", "-c", "touch /test") 413 414 name := "testcontainerapicommit" 415 status, b, err := sockRequest("POST", "/commit?repo="+name+"&testtag=tag&container="+cName, nil) 416 c.Assert(err, checker.IsNil) 417 c.Assert(status, checker.Equals, http.StatusCreated) 418 419 type resp struct { 420 ID string 421 } 422 var img resp 423 c.Assert(json.Unmarshal(b, &img), checker.IsNil) 424 425 cmd := inspectField(c, img.ID, "Config.Cmd") 426 c.Assert(cmd, checker.Equals, "[/bin/sh -c touch /test]", check.Commentf("got wrong Cmd from commit: %q", cmd)) 427 428 // sanity check, make sure the image is what we think it is 429 dockerCmd(c, "run", img.ID, "ls", "/test") 430 } 431 432 func (s *DockerSuite) TestContainerApiCommitWithLabelInConfig(c *check.C) { 433 cName := "testapicommitwithconfig" 434 dockerCmd(c, "run", "--name="+cName, "busybox", "/bin/sh", "-c", "touch /test") 435 436 config := map[string]interface{}{ 437 "Labels": map[string]string{"key1": "value1", "key2": "value2"}, 438 } 439 440 name := "testcontainerapicommitwithconfig" 441 status, b, err := sockRequest("POST", "/commit?repo="+name+"&container="+cName, config) 442 c.Assert(err, checker.IsNil) 443 c.Assert(status, checker.Equals, http.StatusCreated) 444 445 type resp struct { 446 ID string 447 } 448 var img resp 449 c.Assert(json.Unmarshal(b, &img), checker.IsNil) 450 451 label1 := inspectFieldMap(c, img.ID, "Config.Labels", "key1") 452 c.Assert(label1, checker.Equals, "value1") 453 454 label2 := inspectFieldMap(c, img.ID, "Config.Labels", "key2") 455 c.Assert(label2, checker.Equals, "value2") 456 457 cmd := inspectField(c, img.ID, "Config.Cmd") 458 c.Assert(cmd, checker.Equals, "[/bin/sh -c touch /test]", check.Commentf("got wrong Cmd from commit: %q", cmd)) 459 460 // sanity check, make sure the image is what we think it is 461 dockerCmd(c, "run", img.ID, "ls", "/test") 462 } 463 464 func (s *DockerSuite) TestContainerApiBadPort(c *check.C) { 465 // TODO Windows to Windows CI - Port this test 466 testRequires(c, DaemonIsLinux) 467 config := map[string]interface{}{ 468 "Image": "busybox", 469 "Cmd": []string{"/bin/sh", "-c", "echo test"}, 470 "PortBindings": map[string]interface{}{ 471 "8080/tcp": []map[string]interface{}{ 472 { 473 "HostIP": "", 474 "HostPort": "aa80", 475 }, 476 }, 477 }, 478 } 479 480 jsonData := bytes.NewBuffer(nil) 481 json.NewEncoder(jsonData).Encode(config) 482 483 status, body, err := sockRequest("POST", "/containers/create", config) 484 c.Assert(err, checker.IsNil) 485 c.Assert(status, checker.Equals, http.StatusInternalServerError) 486 c.Assert(getErrorMessage(c, body), checker.Equals, `Invalid port specification: "aa80"`, check.Commentf("Incorrect error msg: %s", body)) 487 } 488 489 func (s *DockerSuite) TestContainerApiCreate(c *check.C) { 490 config := map[string]interface{}{ 491 "Image": "busybox", 492 "Cmd": []string{"/bin/sh", "-c", "touch /test && ls /test"}, 493 } 494 495 status, b, err := sockRequest("POST", "/containers/create", config) 496 c.Assert(err, checker.IsNil) 497 c.Assert(status, checker.Equals, http.StatusCreated) 498 499 type createResp struct { 500 ID string 501 } 502 var container createResp 503 c.Assert(json.Unmarshal(b, &container), checker.IsNil) 504 505 out, _ := dockerCmd(c, "start", "-a", container.ID) 506 c.Assert(strings.TrimSpace(out), checker.Equals, "/test") 507 } 508 509 func (s *DockerSuite) TestContainerApiCreateEmptyConfig(c *check.C) { 510 config := map[string]interface{}{} 511 512 status, body, err := sockRequest("POST", "/containers/create", config) 513 c.Assert(err, checker.IsNil) 514 c.Assert(status, checker.Equals, http.StatusInternalServerError) 515 516 expected := "Config cannot be empty in order to create a container" 517 c.Assert(getErrorMessage(c, body), checker.Equals, expected) 518 } 519 520 func (s *DockerSuite) TestContainerApiCreateMultipleNetworksConfig(c *check.C) { 521 // Container creation must fail if client specified configurations for more than one network 522 config := map[string]interface{}{ 523 "Image": "busybox", 524 "NetworkingConfig": networktypes.NetworkingConfig{ 525 EndpointsConfig: map[string]*networktypes.EndpointSettings{ 526 "net1": {}, 527 "net2": {}, 528 "net3": {}, 529 }, 530 }, 531 } 532 533 status, body, err := sockRequest("POST", "/containers/create", config) 534 c.Assert(err, checker.IsNil) 535 c.Assert(status, checker.Equals, http.StatusBadRequest) 536 msg := getErrorMessage(c, body) 537 // network name order in error message is not deterministic 538 c.Assert(msg, checker.Contains, "Container cannot be connected to network endpoints") 539 c.Assert(msg, checker.Contains, "net1") 540 c.Assert(msg, checker.Contains, "net2") 541 c.Assert(msg, checker.Contains, "net3") 542 } 543 544 func (s *DockerSuite) TestContainerApiCreateWithHostName(c *check.C) { 545 // TODO Windows: Port this test once hostname is supported 546 testRequires(c, DaemonIsLinux) 547 hostName := "test-host" 548 config := map[string]interface{}{ 549 "Image": "busybox", 550 "Hostname": hostName, 551 } 552 553 status, body, err := sockRequest("POST", "/containers/create", config) 554 c.Assert(err, checker.IsNil) 555 c.Assert(status, checker.Equals, http.StatusCreated) 556 557 var container types.ContainerCreateResponse 558 c.Assert(json.Unmarshal(body, &container), checker.IsNil) 559 560 status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil) 561 c.Assert(err, checker.IsNil) 562 c.Assert(status, checker.Equals, http.StatusOK) 563 564 var containerJSON types.ContainerJSON 565 c.Assert(json.Unmarshal(body, &containerJSON), checker.IsNil) 566 c.Assert(containerJSON.Config.Hostname, checker.Equals, hostName, check.Commentf("Mismatched Hostname")) 567 } 568 569 func (s *DockerSuite) TestContainerApiCreateWithDomainName(c *check.C) { 570 // TODO Windows: Port this test once domain name is supported 571 testRequires(c, DaemonIsLinux) 572 domainName := "test-domain" 573 config := map[string]interface{}{ 574 "Image": "busybox", 575 "Domainname": domainName, 576 } 577 578 status, body, err := sockRequest("POST", "/containers/create", config) 579 c.Assert(err, checker.IsNil) 580 c.Assert(status, checker.Equals, http.StatusCreated) 581 582 var container types.ContainerCreateResponse 583 c.Assert(json.Unmarshal(body, &container), checker.IsNil) 584 585 status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil) 586 c.Assert(err, checker.IsNil) 587 c.Assert(status, checker.Equals, http.StatusOK) 588 589 var containerJSON types.ContainerJSON 590 c.Assert(json.Unmarshal(body, &containerJSON), checker.IsNil) 591 c.Assert(containerJSON.Config.Domainname, checker.Equals, domainName, check.Commentf("Mismatched Domainname")) 592 } 593 594 func (s *DockerSuite) TestContainerApiCreateBridgeNetworkMode(c *check.C) { 595 // Windows does not support bridge 596 testRequires(c, DaemonIsLinux) 597 UtilCreateNetworkMode(c, "bridge") 598 } 599 600 func (s *DockerSuite) TestContainerApiCreateOtherNetworkModes(c *check.C) { 601 // Windows does not support these network modes 602 testRequires(c, DaemonIsLinux, NotUserNamespace) 603 UtilCreateNetworkMode(c, "host") 604 UtilCreateNetworkMode(c, "container:web1") 605 } 606 607 func UtilCreateNetworkMode(c *check.C, networkMode string) { 608 config := map[string]interface{}{ 609 "Image": "busybox", 610 "HostConfig": map[string]interface{}{"NetworkMode": networkMode}, 611 } 612 613 status, body, err := sockRequest("POST", "/containers/create", config) 614 c.Assert(err, checker.IsNil) 615 c.Assert(status, checker.Equals, http.StatusCreated) 616 617 var container types.ContainerCreateResponse 618 c.Assert(json.Unmarshal(body, &container), checker.IsNil) 619 620 status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil) 621 c.Assert(err, checker.IsNil) 622 c.Assert(status, checker.Equals, http.StatusOK) 623 624 var containerJSON types.ContainerJSON 625 c.Assert(json.Unmarshal(body, &containerJSON), checker.IsNil) 626 c.Assert(containerJSON.HostConfig.NetworkMode, checker.Equals, containertypes.NetworkMode(networkMode), check.Commentf("Mismatched NetworkMode")) 627 } 628 629 func (s *DockerSuite) TestContainerApiCreateWithCpuSharesCpuset(c *check.C) { 630 // TODO Windows to Windows CI. The CpuShares part could be ported. 631 testRequires(c, DaemonIsLinux) 632 config := map[string]interface{}{ 633 "Image": "busybox", 634 "CpuShares": 512, 635 "CpusetCpus": "0", 636 } 637 638 status, body, err := sockRequest("POST", "/containers/create", config) 639 c.Assert(err, checker.IsNil) 640 c.Assert(status, checker.Equals, http.StatusCreated) 641 642 var container types.ContainerCreateResponse 643 c.Assert(json.Unmarshal(body, &container), checker.IsNil) 644 645 status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil) 646 c.Assert(err, checker.IsNil) 647 c.Assert(status, checker.Equals, http.StatusOK) 648 649 var containerJSON types.ContainerJSON 650 651 c.Assert(json.Unmarshal(body, &containerJSON), checker.IsNil) 652 653 out := inspectField(c, containerJSON.ID, "HostConfig.CpuShares") 654 c.Assert(out, checker.Equals, "512") 655 656 outCpuset := inspectField(c, containerJSON.ID, "HostConfig.CpusetCpus") 657 c.Assert(outCpuset, checker.Equals, "0") 658 } 659 660 func (s *DockerSuite) TestContainerApiVerifyHeader(c *check.C) { 661 config := map[string]interface{}{ 662 "Image": "busybox", 663 } 664 665 create := func(ct string) (*http.Response, io.ReadCloser, error) { 666 jsonData := bytes.NewBuffer(nil) 667 c.Assert(json.NewEncoder(jsonData).Encode(config), checker.IsNil) 668 return sockRequestRaw("POST", "/containers/create", jsonData, ct) 669 } 670 671 // Try with no content-type 672 res, body, err := create("") 673 c.Assert(err, checker.IsNil) 674 c.Assert(res.StatusCode, checker.Equals, http.StatusInternalServerError) 675 body.Close() 676 677 // Try with wrong content-type 678 res, body, err = create("application/xml") 679 c.Assert(err, checker.IsNil) 680 c.Assert(res.StatusCode, checker.Equals, http.StatusInternalServerError) 681 body.Close() 682 683 // now application/json 684 res, body, err = create("application/json") 685 c.Assert(err, checker.IsNil) 686 c.Assert(res.StatusCode, checker.Equals, http.StatusCreated) 687 body.Close() 688 } 689 690 //Issue 14230. daemon should return 500 for invalid port syntax 691 func (s *DockerSuite) TestContainerApiInvalidPortSyntax(c *check.C) { 692 config := `{ 693 "Image": "busybox", 694 "HostConfig": { 695 "NetworkMode": "default", 696 "PortBindings": { 697 "19039;1230": [ 698 {} 699 ] 700 } 701 } 702 }` 703 704 res, body, err := sockRequestRaw("POST", "/containers/create", strings.NewReader(config), "application/json") 705 c.Assert(err, checker.IsNil) 706 c.Assert(res.StatusCode, checker.Equals, http.StatusInternalServerError) 707 708 b, err := readBody(body) 709 c.Assert(err, checker.IsNil) 710 c.Assert(string(b[:]), checker.Contains, "Invalid port") 711 } 712 713 // Issue 7941 - test to make sure a "null" in JSON is just ignored. 714 // W/o this fix a null in JSON would be parsed into a string var as "null" 715 func (s *DockerSuite) TestContainerApiPostCreateNull(c *check.C) { 716 // TODO Windows to Windows CI. Bit of this with alternate fields checked 717 // can probably be ported. 718 testRequires(c, DaemonIsLinux) 719 config := `{ 720 "Hostname":"", 721 "Domainname":"", 722 "Memory":0, 723 "MemorySwap":0, 724 "CpuShares":0, 725 "Cpuset":null, 726 "AttachStdin":true, 727 "AttachStdout":true, 728 "AttachStderr":true, 729 "ExposedPorts":{}, 730 "Tty":true, 731 "OpenStdin":true, 732 "StdinOnce":true, 733 "Env":[], 734 "Cmd":"ls", 735 "Image":"busybox", 736 "Volumes":{}, 737 "WorkingDir":"", 738 "Entrypoint":null, 739 "NetworkDisabled":false, 740 "OnBuild":null}` 741 742 res, body, err := sockRequestRaw("POST", "/containers/create", strings.NewReader(config), "application/json") 743 c.Assert(err, checker.IsNil) 744 c.Assert(res.StatusCode, checker.Equals, http.StatusCreated) 745 746 b, err := readBody(body) 747 c.Assert(err, checker.IsNil) 748 type createResp struct { 749 ID string 750 } 751 var container createResp 752 c.Assert(json.Unmarshal(b, &container), checker.IsNil) 753 out := inspectField(c, container.ID, "HostConfig.CpusetCpus") 754 c.Assert(out, checker.Equals, "") 755 756 outMemory := inspectField(c, container.ID, "HostConfig.Memory") 757 c.Assert(outMemory, checker.Equals, "0") 758 outMemorySwap := inspectField(c, container.ID, "HostConfig.MemorySwap") 759 c.Assert(outMemorySwap, checker.Equals, "0") 760 } 761 762 func (s *DockerSuite) TestCreateWithTooLowMemoryLimit(c *check.C) { 763 // TODO Windows: Port once memory is supported 764 testRequires(c, DaemonIsLinux) 765 config := `{ 766 "Image": "busybox", 767 "Cmd": "ls", 768 "OpenStdin": true, 769 "CpuShares": 100, 770 "Memory": 524287 771 }` 772 773 res, body, err := sockRequestRaw("POST", "/containers/create", strings.NewReader(config), "application/json") 774 c.Assert(err, checker.IsNil) 775 b, err2 := readBody(body) 776 c.Assert(err2, checker.IsNil) 777 778 c.Assert(res.StatusCode, checker.Equals, http.StatusInternalServerError) 779 c.Assert(string(b), checker.Contains, "Minimum memory limit allowed is 4MB") 780 } 781 782 func (s *DockerSuite) TestContainerApiRename(c *check.C) { 783 // TODO Windows: Debug why this sometimes fails on TP5. For now, leave disabled 784 testRequires(c, DaemonIsLinux) 785 out, _ := dockerCmd(c, "run", "--name", "TestContainerApiRename", "-d", "busybox", "sh") 786 787 containerID := strings.TrimSpace(out) 788 newName := "TestContainerApiRenameNew" 789 statusCode, _, err := sockRequest("POST", "/containers/"+containerID+"/rename?name="+newName, nil) 790 c.Assert(err, checker.IsNil) 791 // 204 No Content is expected, not 200 792 c.Assert(statusCode, checker.Equals, http.StatusNoContent) 793 794 name := inspectField(c, containerID, "Name") 795 c.Assert(name, checker.Equals, "/"+newName, check.Commentf("Failed to rename container")) 796 } 797 798 func (s *DockerSuite) TestContainerApiKill(c *check.C) { 799 name := "test-api-kill" 800 runSleepingContainer(c, "-i", "--name", name) 801 802 status, _, err := sockRequest("POST", "/containers/"+name+"/kill", nil) 803 c.Assert(err, checker.IsNil) 804 c.Assert(status, checker.Equals, http.StatusNoContent) 805 806 state := inspectField(c, name, "State.Running") 807 c.Assert(state, checker.Equals, "false", check.Commentf("got wrong State from container %s: %q", name, state)) 808 } 809 810 func (s *DockerSuite) TestContainerApiRestart(c *check.C) { 811 // TODO Windows to Windows CI. This is flaky due to the timing 812 testRequires(c, DaemonIsLinux) 813 name := "test-api-restart" 814 dockerCmd(c, "run", "-di", "--name", name, "busybox", "top") 815 816 status, _, err := sockRequest("POST", "/containers/"+name+"/restart?t=1", nil) 817 c.Assert(err, checker.IsNil) 818 c.Assert(status, checker.Equals, http.StatusNoContent) 819 c.Assert(waitInspect(name, "{{ .State.Restarting }} {{ .State.Running }}", "false true", 5*time.Second), checker.IsNil) 820 } 821 822 func (s *DockerSuite) TestContainerApiRestartNotimeoutParam(c *check.C) { 823 // TODO Windows to Windows CI. This is flaky due to the timing 824 testRequires(c, DaemonIsLinux) 825 name := "test-api-restart-no-timeout-param" 826 out, _ := dockerCmd(c, "run", "-di", "--name", name, "busybox", "top") 827 id := strings.TrimSpace(out) 828 c.Assert(waitRun(id), checker.IsNil) 829 830 status, _, err := sockRequest("POST", "/containers/"+name+"/restart", nil) 831 c.Assert(err, checker.IsNil) 832 c.Assert(status, checker.Equals, http.StatusNoContent) 833 c.Assert(waitInspect(name, "{{ .State.Restarting }} {{ .State.Running }}", "false true", 5*time.Second), checker.IsNil) 834 } 835 836 func (s *DockerSuite) TestContainerApiStart(c *check.C) { 837 name := "testing-start" 838 config := map[string]interface{}{ 839 "Image": "busybox", 840 "Cmd": append([]string{"/bin/sh", "-c"}, defaultSleepCommand...), 841 "OpenStdin": true, 842 } 843 844 status, _, err := sockRequest("POST", "/containers/create?name="+name, config) 845 c.Assert(err, checker.IsNil) 846 c.Assert(status, checker.Equals, http.StatusCreated) 847 848 status, _, err = sockRequest("POST", "/containers/"+name+"/start", nil) 849 c.Assert(err, checker.IsNil) 850 c.Assert(status, checker.Equals, http.StatusNoContent) 851 852 // second call to start should give 304 853 status, _, err = sockRequest("POST", "/containers/"+name+"/start", nil) 854 c.Assert(err, checker.IsNil) 855 856 // TODO(tibor): figure out why this doesn't work on windows 857 if isLocalDaemon { 858 c.Assert(status, checker.Equals, http.StatusNotModified) 859 } 860 } 861 862 func (s *DockerSuite) TestContainerApiStop(c *check.C) { 863 name := "test-api-stop" 864 runSleepingContainer(c, "-i", "--name", name) 865 866 status, _, err := sockRequest("POST", "/containers/"+name+"/stop?t=30", nil) 867 c.Assert(err, checker.IsNil) 868 c.Assert(status, checker.Equals, http.StatusNoContent) 869 c.Assert(waitInspect(name, "{{ .State.Running }}", "false", 60*time.Second), checker.IsNil) 870 871 // second call to start should give 304 872 status, _, err = sockRequest("POST", "/containers/"+name+"/stop?t=30", nil) 873 c.Assert(err, checker.IsNil) 874 c.Assert(status, checker.Equals, http.StatusNotModified) 875 } 876 877 func (s *DockerSuite) TestContainerApiWait(c *check.C) { 878 name := "test-api-wait" 879 880 sleepCmd := "/bin/sleep" 881 if daemonPlatform == "windows" { 882 sleepCmd = "sleep" 883 } 884 dockerCmd(c, "run", "--name", name, "busybox", sleepCmd, "5") 885 886 status, body, err := sockRequest("POST", "/containers/"+name+"/wait", nil) 887 c.Assert(err, checker.IsNil) 888 c.Assert(status, checker.Equals, http.StatusOK) 889 c.Assert(waitInspect(name, "{{ .State.Running }}", "false", 60*time.Second), checker.IsNil) 890 891 var waitres types.ContainerWaitResponse 892 c.Assert(json.Unmarshal(body, &waitres), checker.IsNil) 893 c.Assert(waitres.StatusCode, checker.Equals, 0) 894 } 895 896 func (s *DockerSuite) TestContainerApiCopyNotExistsAnyMore(c *check.C) { 897 // TODO Windows to Windows CI. This can be ported. 898 testRequires(c, DaemonIsLinux) 899 name := "test-container-api-copy" 900 dockerCmd(c, "run", "--name", name, "busybox", "touch", "/test.txt") 901 902 postData := types.CopyConfig{ 903 Resource: "/test.txt", 904 } 905 906 status, _, err := sockRequest("POST", "/containers/"+name+"/copy", postData) 907 c.Assert(err, checker.IsNil) 908 c.Assert(status, checker.Equals, http.StatusNotFound) 909 } 910 911 func (s *DockerSuite) TestContainerApiCopyPre124(c *check.C) { 912 // TODO Windows to Windows CI. This can be ported. 913 testRequires(c, DaemonIsLinux) 914 name := "test-container-api-copy" 915 dockerCmd(c, "run", "--name", name, "busybox", "touch", "/test.txt") 916 917 postData := types.CopyConfig{ 918 Resource: "/test.txt", 919 } 920 921 status, body, err := sockRequest("POST", "/v1.23/containers/"+name+"/copy", postData) 922 c.Assert(err, checker.IsNil) 923 c.Assert(status, checker.Equals, http.StatusOK) 924 925 found := false 926 for tarReader := tar.NewReader(bytes.NewReader(body)); ; { 927 h, err := tarReader.Next() 928 if err != nil { 929 if err == io.EOF { 930 break 931 } 932 c.Fatal(err) 933 } 934 if h.Name == "test.txt" { 935 found = true 936 break 937 } 938 } 939 c.Assert(found, checker.True) 940 } 941 942 func (s *DockerSuite) TestContainerApiCopyResourcePathEmptyPr124(c *check.C) { 943 // TODO Windows to Windows CI. This can be ported. 944 testRequires(c, DaemonIsLinux) 945 name := "test-container-api-copy-resource-empty" 946 dockerCmd(c, "run", "--name", name, "busybox", "touch", "/test.txt") 947 948 postData := types.CopyConfig{ 949 Resource: "", 950 } 951 952 status, body, err := sockRequest("POST", "/v1.23/containers/"+name+"/copy", postData) 953 c.Assert(err, checker.IsNil) 954 c.Assert(status, checker.Equals, http.StatusInternalServerError) 955 c.Assert(string(body), checker.Matches, "Path cannot be empty\n") 956 } 957 958 func (s *DockerSuite) TestContainerApiCopyResourcePathNotFoundPre124(c *check.C) { 959 // TODO Windows to Windows CI. This can be ported. 960 testRequires(c, DaemonIsLinux) 961 name := "test-container-api-copy-resource-not-found" 962 dockerCmd(c, "run", "--name", name, "busybox") 963 964 postData := types.CopyConfig{ 965 Resource: "/notexist", 966 } 967 968 status, body, err := sockRequest("POST", "/v1.23/containers/"+name+"/copy", postData) 969 c.Assert(err, checker.IsNil) 970 c.Assert(status, checker.Equals, http.StatusInternalServerError) 971 c.Assert(string(body), checker.Matches, "Could not find the file /notexist in container "+name+"\n") 972 } 973 974 func (s *DockerSuite) TestContainerApiCopyContainerNotFoundPr124(c *check.C) { 975 postData := types.CopyConfig{ 976 Resource: "/something", 977 } 978 979 status, _, err := sockRequest("POST", "/v1.23/containers/notexists/copy", postData) 980 c.Assert(err, checker.IsNil) 981 c.Assert(status, checker.Equals, http.StatusNotFound) 982 } 983 984 func (s *DockerSuite) TestContainerApiDelete(c *check.C) { 985 out, _ := runSleepingContainer(c) 986 987 id := strings.TrimSpace(out) 988 c.Assert(waitRun(id), checker.IsNil) 989 990 dockerCmd(c, "stop", id) 991 992 status, _, err := sockRequest("DELETE", "/containers/"+id, nil) 993 c.Assert(err, checker.IsNil) 994 c.Assert(status, checker.Equals, http.StatusNoContent) 995 } 996 997 func (s *DockerSuite) TestContainerApiDeleteNotExist(c *check.C) { 998 status, body, err := sockRequest("DELETE", "/containers/doesnotexist", nil) 999 c.Assert(err, checker.IsNil) 1000 c.Assert(status, checker.Equals, http.StatusNotFound) 1001 c.Assert(getErrorMessage(c, body), checker.Matches, "No such container: doesnotexist") 1002 } 1003 1004 func (s *DockerSuite) TestContainerApiDeleteForce(c *check.C) { 1005 out, _ := runSleepingContainer(c) 1006 1007 id := strings.TrimSpace(out) 1008 c.Assert(waitRun(id), checker.IsNil) 1009 1010 status, _, err := sockRequest("DELETE", "/containers/"+id+"?force=1", nil) 1011 c.Assert(err, checker.IsNil) 1012 c.Assert(status, checker.Equals, http.StatusNoContent) 1013 } 1014 1015 func (s *DockerSuite) TestContainerApiDeleteRemoveLinks(c *check.C) { 1016 // Windows does not support links 1017 testRequires(c, DaemonIsLinux) 1018 out, _ := dockerCmd(c, "run", "-d", "--name", "tlink1", "busybox", "top") 1019 1020 id := strings.TrimSpace(out) 1021 c.Assert(waitRun(id), checker.IsNil) 1022 1023 out, _ = dockerCmd(c, "run", "--link", "tlink1:tlink1", "--name", "tlink2", "-d", "busybox", "top") 1024 1025 id2 := strings.TrimSpace(out) 1026 c.Assert(waitRun(id2), checker.IsNil) 1027 1028 links := inspectFieldJSON(c, id2, "HostConfig.Links") 1029 c.Assert(links, checker.Equals, "[\"/tlink1:/tlink2/tlink1\"]", check.Commentf("expected to have links between containers")) 1030 1031 status, b, err := sockRequest("DELETE", "/containers/tlink2/tlink1?link=1", nil) 1032 c.Assert(err, check.IsNil) 1033 c.Assert(status, check.Equals, http.StatusNoContent, check.Commentf(string(b))) 1034 1035 linksPostRm := inspectFieldJSON(c, id2, "HostConfig.Links") 1036 c.Assert(linksPostRm, checker.Equals, "null", check.Commentf("call to api deleteContainer links should have removed the specified links")) 1037 } 1038 1039 func (s *DockerSuite) TestContainerApiDeleteConflict(c *check.C) { 1040 out, _ := runSleepingContainer(c) 1041 1042 id := strings.TrimSpace(out) 1043 c.Assert(waitRun(id), checker.IsNil) 1044 1045 status, _, err := sockRequest("DELETE", "/containers/"+id, nil) 1046 c.Assert(err, checker.IsNil) 1047 c.Assert(status, checker.Equals, http.StatusConflict) 1048 } 1049 1050 func (s *DockerSuite) TestContainerApiDeleteRemoveVolume(c *check.C) { 1051 testRequires(c, SameHostDaemon) 1052 1053 vol := "/testvolume" 1054 if daemonPlatform == "windows" { 1055 vol = `c:\testvolume` 1056 } 1057 1058 out, _ := runSleepingContainer(c, "-v", vol) 1059 1060 id := strings.TrimSpace(out) 1061 c.Assert(waitRun(id), checker.IsNil) 1062 1063 source, err := inspectMountSourceField(id, vol) 1064 _, err = os.Stat(source) 1065 c.Assert(err, checker.IsNil) 1066 1067 status, _, err := sockRequest("DELETE", "/containers/"+id+"?v=1&force=1", nil) 1068 c.Assert(err, checker.IsNil) 1069 c.Assert(status, checker.Equals, http.StatusNoContent) 1070 _, err = os.Stat(source) 1071 c.Assert(os.IsNotExist(err), checker.True, check.Commentf("expected to get ErrNotExist error, got %v", err)) 1072 } 1073 1074 // Regression test for https://github.com/docker/docker/issues/6231 1075 func (s *DockerSuite) TestContainerApiChunkedEncoding(c *check.C) { 1076 // TODO Windows CI: This can be ported 1077 testRequires(c, DaemonIsLinux) 1078 1079 conn, err := sockConn(time.Duration(10*time.Second), "") 1080 c.Assert(err, checker.IsNil) 1081 client := httputil.NewClientConn(conn, nil) 1082 defer client.Close() 1083 1084 config := map[string]interface{}{ 1085 "Image": "busybox", 1086 "Cmd": append([]string{"/bin/sh", "-c"}, defaultSleepCommand...), 1087 "OpenStdin": true, 1088 } 1089 b, err := json.Marshal(config) 1090 c.Assert(err, checker.IsNil) 1091 1092 req, err := http.NewRequest("POST", "/containers/create", bytes.NewBuffer(b)) 1093 c.Assert(err, checker.IsNil) 1094 req.Header.Set("Content-Type", "application/json") 1095 // This is a cheat to make the http request do chunked encoding 1096 // Otherwise (just setting the Content-Encoding to chunked) net/http will overwrite 1097 // https://golang.org/src/pkg/net/http/request.go?s=11980:12172 1098 req.ContentLength = -1 1099 1100 resp, err := client.Do(req) 1101 c.Assert(err, checker.IsNil, check.Commentf("error creating container with chunked encoding")) 1102 resp.Body.Close() 1103 c.Assert(resp.StatusCode, checker.Equals, http.StatusCreated) 1104 } 1105 1106 func (s *DockerSuite) TestContainerApiPostContainerStop(c *check.C) { 1107 out, _ := runSleepingContainer(c) 1108 1109 containerID := strings.TrimSpace(out) 1110 c.Assert(waitRun(containerID), checker.IsNil) 1111 1112 statusCode, _, err := sockRequest("POST", "/containers/"+containerID+"/stop", nil) 1113 c.Assert(err, checker.IsNil) 1114 // 204 No Content is expected, not 200 1115 c.Assert(statusCode, checker.Equals, http.StatusNoContent) 1116 c.Assert(waitInspect(containerID, "{{ .State.Running }}", "false", 60*time.Second), checker.IsNil) 1117 } 1118 1119 // #14170 1120 func (s *DockerSuite) TestPostContainerApiCreateWithStringOrSliceEntrypoint(c *check.C) { 1121 config := struct { 1122 Image string 1123 Entrypoint string 1124 Cmd []string 1125 }{"busybox", "echo", []string{"hello", "world"}} 1126 _, _, err := sockRequest("POST", "/containers/create?name=echotest", config) 1127 c.Assert(err, checker.IsNil) 1128 out, _ := dockerCmd(c, "start", "-a", "echotest") 1129 c.Assert(strings.TrimSpace(out), checker.Equals, "hello world") 1130 1131 config2 := struct { 1132 Image string 1133 Entrypoint []string 1134 Cmd []string 1135 }{"busybox", []string{"echo"}, []string{"hello", "world"}} 1136 _, _, err = sockRequest("POST", "/containers/create?name=echotest2", config2) 1137 c.Assert(err, checker.IsNil) 1138 out, _ = dockerCmd(c, "start", "-a", "echotest2") 1139 c.Assert(strings.TrimSpace(out), checker.Equals, "hello world") 1140 } 1141 1142 // #14170 1143 func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceCmd(c *check.C) { 1144 config := struct { 1145 Image string 1146 Entrypoint string 1147 Cmd string 1148 }{"busybox", "echo", "hello world"} 1149 _, _, err := sockRequest("POST", "/containers/create?name=echotest", config) 1150 c.Assert(err, checker.IsNil) 1151 out, _ := dockerCmd(c, "start", "-a", "echotest") 1152 c.Assert(strings.TrimSpace(out), checker.Equals, "hello world") 1153 1154 config2 := struct { 1155 Image string 1156 Cmd []string 1157 }{"busybox", []string{"echo", "hello", "world"}} 1158 _, _, err = sockRequest("POST", "/containers/create?name=echotest2", config2) 1159 c.Assert(err, checker.IsNil) 1160 out, _ = dockerCmd(c, "start", "-a", "echotest2") 1161 c.Assert(strings.TrimSpace(out), checker.Equals, "hello world") 1162 } 1163 1164 // regression #14318 1165 func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceCapAddDrop(c *check.C) { 1166 // Windows doesn't support CapAdd/CapDrop 1167 testRequires(c, DaemonIsLinux) 1168 config := struct { 1169 Image string 1170 CapAdd string 1171 CapDrop string 1172 }{"busybox", "NET_ADMIN", "SYS_ADMIN"} 1173 status, _, err := sockRequest("POST", "/containers/create?name=capaddtest0", config) 1174 c.Assert(err, checker.IsNil) 1175 c.Assert(status, checker.Equals, http.StatusCreated) 1176 1177 config2 := struct { 1178 Image string 1179 CapAdd []string 1180 CapDrop []string 1181 }{"busybox", []string{"NET_ADMIN", "SYS_ADMIN"}, []string{"SETGID"}} 1182 status, _, err = sockRequest("POST", "/containers/create?name=capaddtest1", config2) 1183 c.Assert(err, checker.IsNil) 1184 c.Assert(status, checker.Equals, http.StatusCreated) 1185 } 1186 1187 // #14915 1188 func (s *DockerSuite) TestContainerApiCreateNoHostConfig118(c *check.C) { 1189 config := struct { 1190 Image string 1191 }{"busybox"} 1192 status, _, err := sockRequest("POST", "/v1.18/containers/create", config) 1193 c.Assert(err, checker.IsNil) 1194 c.Assert(status, checker.Equals, http.StatusCreated) 1195 } 1196 1197 // Ensure an error occurs when you have a container read-only rootfs but you 1198 // extract an archive to a symlink in a writable volume which points to a 1199 // directory outside of the volume. 1200 func (s *DockerSuite) TestPutContainerArchiveErrSymlinkInVolumeToReadOnlyRootfs(c *check.C) { 1201 // Windows does not support read-only rootfs 1202 // Requires local volume mount bind. 1203 // --read-only + userns has remount issues 1204 testRequires(c, SameHostDaemon, NotUserNamespace, DaemonIsLinux) 1205 1206 testVol := getTestDir(c, "test-put-container-archive-err-symlink-in-volume-to-read-only-rootfs-") 1207 defer os.RemoveAll(testVol) 1208 1209 makeTestContentInDir(c, testVol) 1210 1211 cID := makeTestContainer(c, testContainerOptions{ 1212 readOnly: true, 1213 volumes: defaultVolumes(testVol), // Our bind mount is at /vol2 1214 }) 1215 defer deleteContainer(cID) 1216 1217 // Attempt to extract to a symlink in the volume which points to a 1218 // directory outside the volume. This should cause an error because the 1219 // rootfs is read-only. 1220 query := make(url.Values, 1) 1221 query.Set("path", "/vol2/symlinkToAbsDir") 1222 urlPath := fmt.Sprintf("/v1.20/containers/%s/archive?%s", cID, query.Encode()) 1223 1224 statusCode, body, err := sockRequest("PUT", urlPath, nil) 1225 c.Assert(err, checker.IsNil) 1226 1227 if !isCpCannotCopyReadOnly(fmt.Errorf(string(body))) { 1228 c.Fatalf("expected ErrContainerRootfsReadonly error, but got %d: %s", statusCode, string(body)) 1229 } 1230 } 1231 1232 func (s *DockerSuite) TestContainerApiGetContainersJSONEmpty(c *check.C) { 1233 status, body, err := sockRequest("GET", "/containers/json?all=1", nil) 1234 c.Assert(err, checker.IsNil) 1235 c.Assert(status, checker.Equals, http.StatusOK) 1236 c.Assert(string(body), checker.Equals, "[]\n") 1237 } 1238 1239 func (s *DockerSuite) TestPostContainersCreateWithWrongCpusetValues(c *check.C) { 1240 // Not supported on Windows 1241 testRequires(c, DaemonIsLinux) 1242 1243 c1 := struct { 1244 Image string 1245 CpusetCpus string 1246 }{"busybox", "1-42,,"} 1247 name := "wrong-cpuset-cpus" 1248 status, body, err := sockRequest("POST", "/containers/create?name="+name, c1) 1249 c.Assert(err, checker.IsNil) 1250 c.Assert(status, checker.Equals, http.StatusInternalServerError) 1251 expected := "Invalid value 1-42,, for cpuset cpus" 1252 c.Assert(getErrorMessage(c, body), checker.Equals, expected) 1253 1254 c2 := struct { 1255 Image string 1256 CpusetMems string 1257 }{"busybox", "42-3,1--"} 1258 name = "wrong-cpuset-mems" 1259 status, body, err = sockRequest("POST", "/containers/create?name="+name, c2) 1260 c.Assert(err, checker.IsNil) 1261 c.Assert(status, checker.Equals, http.StatusInternalServerError) 1262 expected = "Invalid value 42-3,1-- for cpuset mems" 1263 c.Assert(getErrorMessage(c, body), checker.Equals, expected) 1264 } 1265 1266 func (s *DockerSuite) TestPostContainersCreateShmSizeNegative(c *check.C) { 1267 // ShmSize is not supported on Windows 1268 testRequires(c, DaemonIsLinux) 1269 config := map[string]interface{}{ 1270 "Image": "busybox", 1271 "HostConfig": map[string]interface{}{"ShmSize": -1}, 1272 } 1273 1274 status, body, err := sockRequest("POST", "/containers/create", config) 1275 c.Assert(err, check.IsNil) 1276 c.Assert(status, check.Equals, http.StatusInternalServerError) 1277 c.Assert(getErrorMessage(c, body), checker.Contains, "SHM size must be greater than 0") 1278 } 1279 1280 func (s *DockerSuite) TestPostContainersCreateShmSizeHostConfigOmitted(c *check.C) { 1281 // ShmSize is not supported on Windows 1282 testRequires(c, DaemonIsLinux) 1283 var defaultSHMSize int64 = 67108864 1284 config := map[string]interface{}{ 1285 "Image": "busybox", 1286 "Cmd": "mount", 1287 } 1288 1289 status, body, err := sockRequest("POST", "/containers/create", config) 1290 c.Assert(err, check.IsNil) 1291 c.Assert(status, check.Equals, http.StatusCreated) 1292 1293 var container types.ContainerCreateResponse 1294 c.Assert(json.Unmarshal(body, &container), check.IsNil) 1295 1296 status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil) 1297 c.Assert(err, check.IsNil) 1298 c.Assert(status, check.Equals, http.StatusOK) 1299 1300 var containerJSON types.ContainerJSON 1301 c.Assert(json.Unmarshal(body, &containerJSON), check.IsNil) 1302 1303 c.Assert(containerJSON.HostConfig.ShmSize, check.Equals, defaultSHMSize) 1304 1305 out, _ := dockerCmd(c, "start", "-i", containerJSON.ID) 1306 shmRegexp := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=65536k`) 1307 if !shmRegexp.MatchString(out) { 1308 c.Fatalf("Expected shm of 64MB in mount command, got %v", out) 1309 } 1310 } 1311 1312 func (s *DockerSuite) TestPostContainersCreateShmSizeOmitted(c *check.C) { 1313 // ShmSize is not supported on Windows 1314 testRequires(c, DaemonIsLinux) 1315 config := map[string]interface{}{ 1316 "Image": "busybox", 1317 "HostConfig": map[string]interface{}{}, 1318 "Cmd": "mount", 1319 } 1320 1321 status, body, err := sockRequest("POST", "/containers/create", config) 1322 c.Assert(err, check.IsNil) 1323 c.Assert(status, check.Equals, http.StatusCreated) 1324 1325 var container types.ContainerCreateResponse 1326 c.Assert(json.Unmarshal(body, &container), check.IsNil) 1327 1328 status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil) 1329 c.Assert(err, check.IsNil) 1330 c.Assert(status, check.Equals, http.StatusOK) 1331 1332 var containerJSON types.ContainerJSON 1333 c.Assert(json.Unmarshal(body, &containerJSON), check.IsNil) 1334 1335 c.Assert(containerJSON.HostConfig.ShmSize, check.Equals, int64(67108864)) 1336 1337 out, _ := dockerCmd(c, "start", "-i", containerJSON.ID) 1338 shmRegexp := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=65536k`) 1339 if !shmRegexp.MatchString(out) { 1340 c.Fatalf("Expected shm of 64MB in mount command, got %v", out) 1341 } 1342 } 1343 1344 func (s *DockerSuite) TestPostContainersCreateWithShmSize(c *check.C) { 1345 // ShmSize is not supported on Windows 1346 testRequires(c, DaemonIsLinux) 1347 config := map[string]interface{}{ 1348 "Image": "busybox", 1349 "Cmd": "mount", 1350 "HostConfig": map[string]interface{}{"ShmSize": 1073741824}, 1351 } 1352 1353 status, body, err := sockRequest("POST", "/containers/create", config) 1354 c.Assert(err, check.IsNil) 1355 c.Assert(status, check.Equals, http.StatusCreated) 1356 1357 var container types.ContainerCreateResponse 1358 c.Assert(json.Unmarshal(body, &container), check.IsNil) 1359 1360 status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil) 1361 c.Assert(err, check.IsNil) 1362 c.Assert(status, check.Equals, http.StatusOK) 1363 1364 var containerJSON types.ContainerJSON 1365 c.Assert(json.Unmarshal(body, &containerJSON), check.IsNil) 1366 1367 c.Assert(containerJSON.HostConfig.ShmSize, check.Equals, int64(1073741824)) 1368 1369 out, _ := dockerCmd(c, "start", "-i", containerJSON.ID) 1370 shmRegex := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=1048576k`) 1371 if !shmRegex.MatchString(out) { 1372 c.Fatalf("Expected shm of 1GB in mount command, got %v", out) 1373 } 1374 } 1375 1376 func (s *DockerSuite) TestPostContainersCreateMemorySwappinessHostConfigOmitted(c *check.C) { 1377 // Swappiness is not supported on Windows 1378 testRequires(c, DaemonIsLinux) 1379 config := map[string]interface{}{ 1380 "Image": "busybox", 1381 } 1382 1383 status, body, err := sockRequest("POST", "/containers/create", config) 1384 c.Assert(err, check.IsNil) 1385 c.Assert(status, check.Equals, http.StatusCreated) 1386 1387 var container types.ContainerCreateResponse 1388 c.Assert(json.Unmarshal(body, &container), check.IsNil) 1389 1390 status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil) 1391 c.Assert(err, check.IsNil) 1392 c.Assert(status, check.Equals, http.StatusOK) 1393 1394 var containerJSON types.ContainerJSON 1395 c.Assert(json.Unmarshal(body, &containerJSON), check.IsNil) 1396 1397 c.Assert(*containerJSON.HostConfig.MemorySwappiness, check.Equals, int64(-1)) 1398 } 1399 1400 // check validation is done daemon side and not only in cli 1401 func (s *DockerSuite) TestPostContainersCreateWithOomScoreAdjInvalidRange(c *check.C) { 1402 // OomScoreAdj is not supported on Windows 1403 testRequires(c, DaemonIsLinux) 1404 1405 config := struct { 1406 Image string 1407 OomScoreAdj int 1408 }{"busybox", 1001} 1409 name := "oomscoreadj-over" 1410 status, b, err := sockRequest("POST", "/containers/create?name="+name, config) 1411 c.Assert(err, check.IsNil) 1412 c.Assert(status, check.Equals, http.StatusInternalServerError) 1413 1414 expected := "Invalid value 1001, range for oom score adj is [-1000, 1000]" 1415 msg := getErrorMessage(c, b) 1416 if !strings.Contains(msg, expected) { 1417 c.Fatalf("Expected output to contain %q, got %q", expected, msg) 1418 } 1419 1420 config = struct { 1421 Image string 1422 OomScoreAdj int 1423 }{"busybox", -1001} 1424 name = "oomscoreadj-low" 1425 status, b, err = sockRequest("POST", "/containers/create?name="+name, config) 1426 c.Assert(err, check.IsNil) 1427 c.Assert(status, check.Equals, http.StatusInternalServerError) 1428 expected = "Invalid value -1001, range for oom score adj is [-1000, 1000]" 1429 msg = getErrorMessage(c, b) 1430 if !strings.Contains(msg, expected) { 1431 c.Fatalf("Expected output to contain %q, got %q", expected, msg) 1432 } 1433 } 1434 1435 // test case for #22210 where an emtpy container name caused panic. 1436 func (s *DockerSuite) TestContainerApiDeleteWithEmptyName(c *check.C) { 1437 status, out, err := sockRequest("DELETE", "/containers/", nil) 1438 c.Assert(err, checker.IsNil) 1439 c.Assert(status, checker.Equals, http.StatusBadRequest) 1440 c.Assert(string(out), checker.Contains, "No container name or ID supplied") 1441 }