github.com/rita33cool1/iot-system-gateway@v0.0.0-20200911033302-e65bde238cc5/docker-engine/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 "io/ioutil" 10 "net/http" 11 "os" 12 "path/filepath" 13 "regexp" 14 "runtime" 15 "strconv" 16 "strings" 17 "time" 18 19 "github.com/docker/docker/api/types" 20 containertypes "github.com/docker/docker/api/types/container" 21 mounttypes "github.com/docker/docker/api/types/mount" 22 networktypes "github.com/docker/docker/api/types/network" 23 "github.com/docker/docker/client" 24 "github.com/docker/docker/integration-cli/checker" 25 "github.com/docker/docker/integration-cli/cli" 26 "github.com/docker/docker/integration-cli/cli/build" 27 "github.com/docker/docker/integration-cli/request" 28 "github.com/docker/docker/pkg/ioutils" 29 "github.com/docker/docker/pkg/mount" 30 "github.com/docker/docker/pkg/stringid" 31 "github.com/docker/docker/volume" 32 "github.com/docker/go-connections/nat" 33 "github.com/go-check/check" 34 "github.com/gotestyourself/gotestyourself/assert" 35 is "github.com/gotestyourself/gotestyourself/assert/cmp" 36 "github.com/gotestyourself/gotestyourself/poll" 37 "golang.org/x/net/context" 38 ) 39 40 func (s *DockerSuite) TestContainerAPIGetAll(c *check.C) { 41 startCount := getContainerCount(c) 42 name := "getall" 43 dockerCmd(c, "run", "--name", name, "busybox", "true") 44 45 cli, err := client.NewEnvClient() 46 c.Assert(err, checker.IsNil) 47 defer cli.Close() 48 49 options := types.ContainerListOptions{ 50 All: true, 51 } 52 containers, err := cli.ContainerList(context.Background(), options) 53 c.Assert(err, checker.IsNil) 54 c.Assert(containers, checker.HasLen, startCount+1) 55 actual := containers[0].Names[0] 56 c.Assert(actual, checker.Equals, "/"+name) 57 } 58 59 // regression test for empty json field being omitted #13691 60 func (s *DockerSuite) TestContainerAPIGetJSONNoFieldsOmitted(c *check.C) { 61 startCount := getContainerCount(c) 62 dockerCmd(c, "run", "busybox", "true") 63 64 cli, err := client.NewEnvClient() 65 c.Assert(err, checker.IsNil) 66 defer cli.Close() 67 68 options := types.ContainerListOptions{ 69 All: true, 70 } 71 containers, err := cli.ContainerList(context.Background(), options) 72 c.Assert(err, checker.IsNil) 73 c.Assert(containers, checker.HasLen, startCount+1) 74 actual := fmt.Sprintf("%+v", containers[0]) 75 76 // empty Labels field triggered this bug, make sense to check for everything 77 // cause even Ports for instance can trigger this bug 78 // better safe than sorry.. 79 fields := []string{ 80 "ID", 81 "Names", 82 "Image", 83 "Command", 84 "Created", 85 "Ports", 86 "Labels", 87 "Status", 88 "NetworkSettings", 89 } 90 91 // decoding into types.Container do not work since it eventually unmarshal 92 // and empty field to an empty go map, so we just check for a string 93 for _, f := range fields { 94 if !strings.Contains(actual, f) { 95 c.Fatalf("Field %s is missing and it shouldn't", f) 96 } 97 } 98 } 99 100 type containerPs struct { 101 Names []string 102 Ports []types.Port 103 } 104 105 // regression test for non-empty fields from #13901 106 func (s *DockerSuite) TestContainerAPIPsOmitFields(c *check.C) { 107 // Problematic for Windows porting due to networking not yet being passed back 108 testRequires(c, DaemonIsLinux) 109 name := "pstest" 110 port := 80 111 runSleepingContainer(c, "--name", name, "--expose", strconv.Itoa(port)) 112 113 cli, err := client.NewEnvClient() 114 c.Assert(err, checker.IsNil) 115 defer cli.Close() 116 117 options := types.ContainerListOptions{ 118 All: true, 119 } 120 containers, err := cli.ContainerList(context.Background(), options) 121 c.Assert(err, checker.IsNil) 122 var foundContainer containerPs 123 for _, c := range containers { 124 for _, testName := range c.Names { 125 if "/"+name == testName { 126 foundContainer.Names = c.Names 127 foundContainer.Ports = c.Ports 128 break 129 } 130 } 131 } 132 133 c.Assert(foundContainer.Ports, checker.HasLen, 1) 134 c.Assert(foundContainer.Ports[0].PrivatePort, checker.Equals, uint16(port)) 135 c.Assert(foundContainer.Ports[0].PublicPort, checker.NotNil) 136 c.Assert(foundContainer.Ports[0].IP, checker.NotNil) 137 } 138 139 func (s *DockerSuite) TestContainerAPIGetExport(c *check.C) { 140 // Not supported on Windows as Windows does not support docker export 141 testRequires(c, DaemonIsLinux) 142 name := "exportcontainer" 143 dockerCmd(c, "run", "--name", name, "busybox", "touch", "/test") 144 145 cli, err := client.NewEnvClient() 146 c.Assert(err, checker.IsNil) 147 defer cli.Close() 148 149 body, err := cli.ContainerExport(context.Background(), name) 150 c.Assert(err, checker.IsNil) 151 defer body.Close() 152 found := false 153 for tarReader := tar.NewReader(body); ; { 154 h, err := tarReader.Next() 155 if err != nil && err == io.EOF { 156 break 157 } 158 if h.Name == "test" { 159 found = true 160 break 161 } 162 } 163 c.Assert(found, checker.True, check.Commentf("The created test file has not been found in the exported image")) 164 } 165 166 func (s *DockerSuite) TestContainerAPIGetChanges(c *check.C) { 167 // Not supported on Windows as Windows does not support docker diff (/containers/name/changes) 168 testRequires(c, DaemonIsLinux) 169 name := "changescontainer" 170 dockerCmd(c, "run", "--name", name, "busybox", "rm", "/etc/passwd") 171 172 cli, err := client.NewEnvClient() 173 c.Assert(err, checker.IsNil) 174 defer cli.Close() 175 176 changes, err := cli.ContainerDiff(context.Background(), name) 177 c.Assert(err, checker.IsNil) 178 179 // Check the changelog for removal of /etc/passwd 180 success := false 181 for _, elem := range changes { 182 if elem.Path == "/etc/passwd" && elem.Kind == 2 { 183 success = true 184 } 185 } 186 c.Assert(success, checker.True, check.Commentf("/etc/passwd has been removed but is not present in the diff")) 187 } 188 189 func (s *DockerSuite) TestGetContainerStats(c *check.C) { 190 var ( 191 name = "statscontainer" 192 ) 193 runSleepingContainer(c, "--name", name) 194 195 type b struct { 196 stats types.ContainerStats 197 err error 198 } 199 200 bc := make(chan b, 1) 201 go func() { 202 cli, err := client.NewEnvClient() 203 c.Assert(err, checker.IsNil) 204 defer cli.Close() 205 206 stats, err := cli.ContainerStats(context.Background(), name, true) 207 c.Assert(err, checker.IsNil) 208 bc <- b{stats, err} 209 }() 210 211 // allow some time to stream the stats from the container 212 time.Sleep(4 * time.Second) 213 dockerCmd(c, "rm", "-f", name) 214 215 // collect the results from the stats stream or timeout and fail 216 // if the stream was not disconnected. 217 select { 218 case <-time.After(2 * time.Second): 219 c.Fatal("stream was not closed after container was removed") 220 case sr := <-bc: 221 dec := json.NewDecoder(sr.stats.Body) 222 defer sr.stats.Body.Close() 223 var s *types.Stats 224 // decode only one object from the stream 225 c.Assert(dec.Decode(&s), checker.IsNil) 226 } 227 } 228 229 func (s *DockerSuite) TestGetContainerStatsRmRunning(c *check.C) { 230 out := runSleepingContainer(c) 231 id := strings.TrimSpace(out) 232 233 buf := &ChannelBuffer{C: make(chan []byte, 1)} 234 defer buf.Close() 235 236 cli, err := client.NewEnvClient() 237 c.Assert(err, checker.IsNil) 238 defer cli.Close() 239 240 stats, err := cli.ContainerStats(context.Background(), id, true) 241 c.Assert(err, checker.IsNil) 242 defer stats.Body.Close() 243 244 chErr := make(chan error, 1) 245 go func() { 246 _, err = io.Copy(buf, stats.Body) 247 chErr <- err 248 }() 249 250 b := make([]byte, 32) 251 // make sure we've got some stats 252 _, err = buf.ReadTimeout(b, 2*time.Second) 253 c.Assert(err, checker.IsNil) 254 255 // Now remove without `-f` and make sure we are still pulling stats 256 _, _, err = dockerCmdWithError("rm", id) 257 c.Assert(err, checker.Not(checker.IsNil), check.Commentf("rm should have failed but didn't")) 258 _, err = buf.ReadTimeout(b, 2*time.Second) 259 c.Assert(err, checker.IsNil) 260 261 dockerCmd(c, "rm", "-f", id) 262 c.Assert(<-chErr, checker.IsNil) 263 } 264 265 // ChannelBuffer holds a chan of byte array that can be populate in a goroutine. 266 type ChannelBuffer struct { 267 C chan []byte 268 } 269 270 // Write implements Writer. 271 func (c *ChannelBuffer) Write(b []byte) (int, error) { 272 c.C <- b 273 return len(b), nil 274 } 275 276 // Close closes the go channel. 277 func (c *ChannelBuffer) Close() error { 278 close(c.C) 279 return nil 280 } 281 282 // ReadTimeout reads the content of the channel in the specified byte array with 283 // the specified duration as timeout. 284 func (c *ChannelBuffer) ReadTimeout(p []byte, n time.Duration) (int, error) { 285 select { 286 case b := <-c.C: 287 return copy(p[0:], b), nil 288 case <-time.After(n): 289 return -1, fmt.Errorf("timeout reading from channel") 290 } 291 } 292 293 // regression test for gh13421 294 // previous test was just checking one stat entry so it didn't fail (stats with 295 // stream false always return one stat) 296 func (s *DockerSuite) TestGetContainerStatsStream(c *check.C) { 297 name := "statscontainer" 298 runSleepingContainer(c, "--name", name) 299 300 type b struct { 301 stats types.ContainerStats 302 err error 303 } 304 305 bc := make(chan b, 1) 306 go func() { 307 cli, err := client.NewEnvClient() 308 c.Assert(err, checker.IsNil) 309 defer cli.Close() 310 311 stats, err := cli.ContainerStats(context.Background(), name, true) 312 c.Assert(err, checker.IsNil) 313 bc <- b{stats, err} 314 }() 315 316 // allow some time to stream the stats from the container 317 time.Sleep(4 * time.Second) 318 dockerCmd(c, "rm", "-f", name) 319 320 // collect the results from the stats stream or timeout and fail 321 // if the stream was not disconnected. 322 select { 323 case <-time.After(2 * time.Second): 324 c.Fatal("stream was not closed after container was removed") 325 case sr := <-bc: 326 b, err := ioutil.ReadAll(sr.stats.Body) 327 defer sr.stats.Body.Close() 328 c.Assert(err, checker.IsNil) 329 s := string(b) 330 // count occurrences of "read" of types.Stats 331 if l := strings.Count(s, "read"); l < 2 { 332 c.Fatalf("Expected more than one stat streamed, got %d", l) 333 } 334 } 335 } 336 337 func (s *DockerSuite) TestGetContainerStatsNoStream(c *check.C) { 338 name := "statscontainer" 339 runSleepingContainer(c, "--name", name) 340 341 type b struct { 342 stats types.ContainerStats 343 err error 344 } 345 346 bc := make(chan b, 1) 347 348 go func() { 349 cli, err := client.NewEnvClient() 350 c.Assert(err, checker.IsNil) 351 defer cli.Close() 352 353 stats, err := cli.ContainerStats(context.Background(), name, false) 354 c.Assert(err, checker.IsNil) 355 bc <- b{stats, err} 356 }() 357 358 // allow some time to stream the stats from the container 359 time.Sleep(4 * time.Second) 360 dockerCmd(c, "rm", "-f", name) 361 362 // collect the results from the stats stream or timeout and fail 363 // if the stream was not disconnected. 364 select { 365 case <-time.After(2 * time.Second): 366 c.Fatal("stream was not closed after container was removed") 367 case sr := <-bc: 368 b, err := ioutil.ReadAll(sr.stats.Body) 369 defer sr.stats.Body.Close() 370 c.Assert(err, checker.IsNil) 371 s := string(b) 372 // count occurrences of `"read"` of types.Stats 373 c.Assert(strings.Count(s, `"read"`), checker.Equals, 1, check.Commentf("Expected only one stat streamed, got %d", strings.Count(s, `"read"`))) 374 } 375 } 376 377 func (s *DockerSuite) TestGetStoppedContainerStats(c *check.C) { 378 name := "statscontainer" 379 dockerCmd(c, "create", "--name", name, "busybox", "ps") 380 381 chResp := make(chan error) 382 383 // We expect an immediate response, but if it's not immediate, the test would hang, so put it in a goroutine 384 // below we'll check this on a timeout. 385 go func() { 386 cli, err := client.NewEnvClient() 387 c.Assert(err, checker.IsNil) 388 defer cli.Close() 389 390 resp, err := cli.ContainerStats(context.Background(), name, false) 391 defer resp.Body.Close() 392 chResp <- err 393 }() 394 395 select { 396 case err := <-chResp: 397 c.Assert(err, checker.IsNil) 398 case <-time.After(10 * time.Second): 399 c.Fatal("timeout waiting for stats response for stopped container") 400 } 401 } 402 403 func (s *DockerSuite) TestContainerAPIPause(c *check.C) { 404 // Problematic on Windows as Windows does not support pause 405 testRequires(c, DaemonIsLinux) 406 407 getPaused := func(c *check.C) []string { 408 return strings.Fields(cli.DockerCmd(c, "ps", "-f", "status=paused", "-q", "-a").Combined()) 409 } 410 411 out := cli.DockerCmd(c, "run", "-d", "busybox", "sleep", "30").Combined() 412 ContainerID := strings.TrimSpace(out) 413 414 cli, err := client.NewEnvClient() 415 c.Assert(err, checker.IsNil) 416 defer cli.Close() 417 418 err = cli.ContainerPause(context.Background(), ContainerID) 419 c.Assert(err, checker.IsNil) 420 421 pausedContainers := getPaused(c) 422 423 if len(pausedContainers) != 1 || stringid.TruncateID(ContainerID) != pausedContainers[0] { 424 c.Fatalf("there should be one paused container and not %d", len(pausedContainers)) 425 } 426 427 err = cli.ContainerUnpause(context.Background(), ContainerID) 428 c.Assert(err, checker.IsNil) 429 430 pausedContainers = getPaused(c) 431 c.Assert(pausedContainers, checker.HasLen, 0, check.Commentf("There should be no paused container.")) 432 } 433 434 func (s *DockerSuite) TestContainerAPITop(c *check.C) { 435 testRequires(c, DaemonIsLinux) 436 out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "top") 437 id := strings.TrimSpace(string(out)) 438 c.Assert(waitRun(id), checker.IsNil) 439 440 cli, err := client.NewEnvClient() 441 c.Assert(err, checker.IsNil) 442 defer cli.Close() 443 444 top, err := cli.ContainerTop(context.Background(), id, []string{"aux"}) 445 c.Assert(err, checker.IsNil) 446 c.Assert(top.Titles, checker.HasLen, 11, check.Commentf("expected 11 titles, found %d: %v", len(top.Titles), top.Titles)) 447 448 if top.Titles[0] != "USER" || top.Titles[10] != "COMMAND" { 449 c.Fatalf("expected `USER` at `Titles[0]` and `COMMAND` at Titles[10]: %v", top.Titles) 450 } 451 c.Assert(top.Processes, checker.HasLen, 2, check.Commentf("expected 2 processes, found %d: %v", len(top.Processes), top.Processes)) 452 c.Assert(top.Processes[0][10], checker.Equals, "/bin/sh -c top") 453 c.Assert(top.Processes[1][10], checker.Equals, "top") 454 } 455 456 func (s *DockerSuite) TestContainerAPITopWindows(c *check.C) { 457 testRequires(c, DaemonIsWindows) 458 out := runSleepingContainer(c, "-d") 459 id := strings.TrimSpace(string(out)) 460 c.Assert(waitRun(id), checker.IsNil) 461 462 cli, err := client.NewEnvClient() 463 c.Assert(err, checker.IsNil) 464 defer cli.Close() 465 466 top, err := cli.ContainerTop(context.Background(), id, nil) 467 c.Assert(err, checker.IsNil) 468 c.Assert(top.Titles, checker.HasLen, 4, check.Commentf("expected 4 titles, found %d: %v", len(top.Titles), top.Titles)) 469 470 if top.Titles[0] != "Name" || top.Titles[3] != "Private Working Set" { 471 c.Fatalf("expected `Name` at `Titles[0]` and `Private Working Set` at Titles[3]: %v", top.Titles) 472 } 473 c.Assert(len(top.Processes), checker.GreaterOrEqualThan, 2, check.Commentf("expected at least 2 processes, found %d: %v", len(top.Processes), top.Processes)) 474 475 foundProcess := false 476 expectedProcess := "busybox.exe" 477 for _, process := range top.Processes { 478 if process[0] == expectedProcess { 479 foundProcess = true 480 break 481 } 482 } 483 484 c.Assert(foundProcess, checker.Equals, true, check.Commentf("expected to find %s: %v", expectedProcess, top.Processes)) 485 } 486 487 func (s *DockerSuite) TestContainerAPICommit(c *check.C) { 488 cName := "testapicommit" 489 dockerCmd(c, "run", "--name="+cName, "busybox", "/bin/sh", "-c", "touch /test") 490 491 cli, err := client.NewEnvClient() 492 c.Assert(err, checker.IsNil) 493 defer cli.Close() 494 495 options := types.ContainerCommitOptions{ 496 Reference: "testcontainerapicommit:testtag", 497 } 498 499 img, err := cli.ContainerCommit(context.Background(), cName, options) 500 c.Assert(err, checker.IsNil) 501 502 cmd := inspectField(c, img.ID, "Config.Cmd") 503 c.Assert(cmd, checker.Equals, "[/bin/sh -c touch /test]", check.Commentf("got wrong Cmd from commit: %q", cmd)) 504 505 // sanity check, make sure the image is what we think it is 506 dockerCmd(c, "run", img.ID, "ls", "/test") 507 } 508 509 func (s *DockerSuite) TestContainerAPICommitWithLabelInConfig(c *check.C) { 510 cName := "testapicommitwithconfig" 511 dockerCmd(c, "run", "--name="+cName, "busybox", "/bin/sh", "-c", "touch /test") 512 513 cli, err := client.NewEnvClient() 514 c.Assert(err, checker.IsNil) 515 defer cli.Close() 516 517 config := containertypes.Config{ 518 Labels: map[string]string{"key1": "value1", "key2": "value2"}} 519 520 options := types.ContainerCommitOptions{ 521 Reference: "testcontainerapicommitwithconfig", 522 Config: &config, 523 } 524 525 img, err := cli.ContainerCommit(context.Background(), cName, options) 526 c.Assert(err, checker.IsNil) 527 528 label1 := inspectFieldMap(c, img.ID, "Config.Labels", "key1") 529 c.Assert(label1, checker.Equals, "value1") 530 531 label2 := inspectFieldMap(c, img.ID, "Config.Labels", "key2") 532 c.Assert(label2, checker.Equals, "value2") 533 534 cmd := inspectField(c, img.ID, "Config.Cmd") 535 c.Assert(cmd, checker.Equals, "[/bin/sh -c touch /test]", check.Commentf("got wrong Cmd from commit: %q", cmd)) 536 537 // sanity check, make sure the image is what we think it is 538 dockerCmd(c, "run", img.ID, "ls", "/test") 539 } 540 541 func (s *DockerSuite) TestContainerAPIBadPort(c *check.C) { 542 // TODO Windows to Windows CI - Port this test 543 testRequires(c, DaemonIsLinux) 544 545 config := containertypes.Config{ 546 Image: "busybox", 547 Cmd: []string{"/bin/sh", "-c", "echo test"}, 548 } 549 550 hostConfig := containertypes.HostConfig{ 551 PortBindings: nat.PortMap{ 552 "8080/tcp": []nat.PortBinding{ 553 { 554 HostIP: "", 555 HostPort: "aa80"}, 556 }, 557 }, 558 } 559 560 cli, err := client.NewEnvClient() 561 c.Assert(err, checker.IsNil) 562 defer cli.Close() 563 564 _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, "") 565 c.Assert(err.Error(), checker.Contains, `invalid port specification: "aa80"`) 566 } 567 568 func (s *DockerSuite) TestContainerAPICreate(c *check.C) { 569 config := containertypes.Config{ 570 Image: "busybox", 571 Cmd: []string{"/bin/sh", "-c", "touch /test && ls /test"}, 572 } 573 574 cli, err := client.NewEnvClient() 575 c.Assert(err, checker.IsNil) 576 defer cli.Close() 577 578 container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "") 579 c.Assert(err, checker.IsNil) 580 581 out, _ := dockerCmd(c, "start", "-a", container.ID) 582 c.Assert(strings.TrimSpace(out), checker.Equals, "/test") 583 } 584 585 func (s *DockerSuite) TestContainerAPICreateEmptyConfig(c *check.C) { 586 587 cli, err := client.NewEnvClient() 588 c.Assert(err, checker.IsNil) 589 defer cli.Close() 590 591 _, err = cli.ContainerCreate(context.Background(), &containertypes.Config{}, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "") 592 593 expected := "No command specified" 594 c.Assert(err.Error(), checker.Contains, expected) 595 } 596 597 func (s *DockerSuite) TestContainerAPICreateMultipleNetworksConfig(c *check.C) { 598 // Container creation must fail if client specified configurations for more than one network 599 config := containertypes.Config{ 600 Image: "busybox", 601 } 602 603 networkingConfig := networktypes.NetworkingConfig{ 604 EndpointsConfig: map[string]*networktypes.EndpointSettings{ 605 "net1": {}, 606 "net2": {}, 607 "net3": {}, 608 }, 609 } 610 611 cli, err := client.NewEnvClient() 612 c.Assert(err, checker.IsNil) 613 defer cli.Close() 614 615 _, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networkingConfig, "") 616 msg := err.Error() 617 // network name order in error message is not deterministic 618 c.Assert(msg, checker.Contains, "Container cannot be connected to network endpoints") 619 c.Assert(msg, checker.Contains, "net1") 620 c.Assert(msg, checker.Contains, "net2") 621 c.Assert(msg, checker.Contains, "net3") 622 } 623 624 func (s *DockerSuite) TestContainerAPICreateWithHostName(c *check.C) { 625 domainName := "test-domain" 626 hostName := "test-hostname" 627 config := containertypes.Config{ 628 Image: "busybox", 629 Hostname: hostName, 630 Domainname: domainName, 631 } 632 633 cli, err := client.NewEnvClient() 634 c.Assert(err, checker.IsNil) 635 defer cli.Close() 636 637 container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "") 638 c.Assert(err, checker.IsNil) 639 640 containerJSON, err := cli.ContainerInspect(context.Background(), container.ID) 641 c.Assert(err, checker.IsNil) 642 643 c.Assert(containerJSON.Config.Hostname, checker.Equals, hostName, check.Commentf("Mismatched Hostname")) 644 c.Assert(containerJSON.Config.Domainname, checker.Equals, domainName, check.Commentf("Mismatched Domainname")) 645 } 646 647 func (s *DockerSuite) TestContainerAPICreateBridgeNetworkMode(c *check.C) { 648 // Windows does not support bridge 649 testRequires(c, DaemonIsLinux) 650 UtilCreateNetworkMode(c, "bridge") 651 } 652 653 func (s *DockerSuite) TestContainerAPICreateOtherNetworkModes(c *check.C) { 654 // Windows does not support these network modes 655 testRequires(c, DaemonIsLinux, NotUserNamespace) 656 UtilCreateNetworkMode(c, "host") 657 UtilCreateNetworkMode(c, "container:web1") 658 } 659 660 func UtilCreateNetworkMode(c *check.C, networkMode containertypes.NetworkMode) { 661 config := containertypes.Config{ 662 Image: "busybox", 663 } 664 665 hostConfig := containertypes.HostConfig{ 666 NetworkMode: networkMode, 667 } 668 669 cli, err := client.NewEnvClient() 670 c.Assert(err, checker.IsNil) 671 defer cli.Close() 672 673 container, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, "") 674 c.Assert(err, checker.IsNil) 675 676 containerJSON, err := cli.ContainerInspect(context.Background(), container.ID) 677 c.Assert(err, checker.IsNil) 678 679 c.Assert(containerJSON.HostConfig.NetworkMode, checker.Equals, containertypes.NetworkMode(networkMode), check.Commentf("Mismatched NetworkMode")) 680 } 681 682 func (s *DockerSuite) TestContainerAPICreateWithCpuSharesCpuset(c *check.C) { 683 // TODO Windows to Windows CI. The CpuShares part could be ported. 684 testRequires(c, DaemonIsLinux) 685 config := containertypes.Config{ 686 Image: "busybox", 687 } 688 689 hostConfig := containertypes.HostConfig{ 690 Resources: containertypes.Resources{ 691 CPUShares: 512, 692 CpusetCpus: "0", 693 }, 694 } 695 696 cli, err := client.NewEnvClient() 697 c.Assert(err, checker.IsNil) 698 defer cli.Close() 699 700 container, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, "") 701 c.Assert(err, checker.IsNil) 702 703 containerJSON, err := cli.ContainerInspect(context.Background(), container.ID) 704 c.Assert(err, checker.IsNil) 705 706 out := inspectField(c, containerJSON.ID, "HostConfig.CpuShares") 707 c.Assert(out, checker.Equals, "512") 708 709 outCpuset := inspectField(c, containerJSON.ID, "HostConfig.CpusetCpus") 710 c.Assert(outCpuset, checker.Equals, "0") 711 } 712 713 func (s *DockerSuite) TestContainerAPIVerifyHeader(c *check.C) { 714 config := map[string]interface{}{ 715 "Image": "busybox", 716 } 717 718 create := func(ct string) (*http.Response, io.ReadCloser, error) { 719 jsonData := bytes.NewBuffer(nil) 720 c.Assert(json.NewEncoder(jsonData).Encode(config), checker.IsNil) 721 return request.Post("/containers/create", request.RawContent(ioutil.NopCloser(jsonData)), request.ContentType(ct)) 722 } 723 724 // Try with no content-type 725 res, body, err := create("") 726 c.Assert(err, checker.IsNil) 727 c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest) 728 body.Close() 729 730 // Try with wrong content-type 731 res, body, err = create("application/xml") 732 c.Assert(err, checker.IsNil) 733 c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest) 734 body.Close() 735 736 // now application/json 737 res, body, err = create("application/json") 738 c.Assert(err, checker.IsNil) 739 c.Assert(res.StatusCode, checker.Equals, http.StatusCreated) 740 body.Close() 741 } 742 743 //Issue 14230. daemon should return 500 for invalid port syntax 744 func (s *DockerSuite) TestContainerAPIInvalidPortSyntax(c *check.C) { 745 config := `{ 746 "Image": "busybox", 747 "HostConfig": { 748 "NetworkMode": "default", 749 "PortBindings": { 750 "19039;1230": [ 751 {} 752 ] 753 } 754 } 755 }` 756 757 res, body, err := request.Post("/containers/create", request.RawString(config), request.JSON) 758 c.Assert(err, checker.IsNil) 759 c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest) 760 761 b, err := request.ReadBody(body) 762 c.Assert(err, checker.IsNil) 763 c.Assert(string(b[:]), checker.Contains, "invalid port") 764 } 765 766 func (s *DockerSuite) TestContainerAPIRestartPolicyInvalidPolicyName(c *check.C) { 767 config := `{ 768 "Image": "busybox", 769 "HostConfig": { 770 "RestartPolicy": { 771 "Name": "something", 772 "MaximumRetryCount": 0 773 } 774 } 775 }` 776 777 res, body, err := request.Post("/containers/create", request.RawString(config), request.JSON) 778 c.Assert(err, checker.IsNil) 779 c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest) 780 781 b, err := request.ReadBody(body) 782 c.Assert(err, checker.IsNil) 783 c.Assert(string(b[:]), checker.Contains, "invalid restart policy") 784 } 785 786 func (s *DockerSuite) TestContainerAPIRestartPolicyRetryMismatch(c *check.C) { 787 config := `{ 788 "Image": "busybox", 789 "HostConfig": { 790 "RestartPolicy": { 791 "Name": "always", 792 "MaximumRetryCount": 2 793 } 794 } 795 }` 796 797 res, body, err := request.Post("/containers/create", request.RawString(config), request.JSON) 798 c.Assert(err, checker.IsNil) 799 c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest) 800 801 b, err := request.ReadBody(body) 802 c.Assert(err, checker.IsNil) 803 c.Assert(string(b[:]), checker.Contains, "maximum retry count cannot be used with restart policy") 804 } 805 806 func (s *DockerSuite) TestContainerAPIRestartPolicyNegativeRetryCount(c *check.C) { 807 config := `{ 808 "Image": "busybox", 809 "HostConfig": { 810 "RestartPolicy": { 811 "Name": "on-failure", 812 "MaximumRetryCount": -2 813 } 814 } 815 }` 816 817 res, body, err := request.Post("/containers/create", request.RawString(config), request.JSON) 818 c.Assert(err, checker.IsNil) 819 c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest) 820 821 b, err := request.ReadBody(body) 822 c.Assert(err, checker.IsNil) 823 c.Assert(string(b[:]), checker.Contains, "maximum retry count cannot be negative") 824 } 825 826 func (s *DockerSuite) TestContainerAPIRestartPolicyDefaultRetryCount(c *check.C) { 827 config := `{ 828 "Image": "busybox", 829 "HostConfig": { 830 "RestartPolicy": { 831 "Name": "on-failure", 832 "MaximumRetryCount": 0 833 } 834 } 835 }` 836 837 res, _, err := request.Post("/containers/create", request.RawString(config), request.JSON) 838 c.Assert(err, checker.IsNil) 839 c.Assert(res.StatusCode, checker.Equals, http.StatusCreated) 840 } 841 842 // Issue 7941 - test to make sure a "null" in JSON is just ignored. 843 // W/o this fix a null in JSON would be parsed into a string var as "null" 844 func (s *DockerSuite) TestContainerAPIPostCreateNull(c *check.C) { 845 config := `{ 846 "Hostname":"", 847 "Domainname":"", 848 "Memory":0, 849 "MemorySwap":0, 850 "CpuShares":0, 851 "Cpuset":null, 852 "AttachStdin":true, 853 "AttachStdout":true, 854 "AttachStderr":true, 855 "ExposedPorts":{}, 856 "Tty":true, 857 "OpenStdin":true, 858 "StdinOnce":true, 859 "Env":[], 860 "Cmd":"ls", 861 "Image":"busybox", 862 "Volumes":{}, 863 "WorkingDir":"", 864 "Entrypoint":null, 865 "NetworkDisabled":false, 866 "OnBuild":null}` 867 868 res, body, err := request.Post("/containers/create", request.RawString(config), request.JSON) 869 c.Assert(err, checker.IsNil) 870 c.Assert(res.StatusCode, checker.Equals, http.StatusCreated) 871 872 b, err := request.ReadBody(body) 873 c.Assert(err, checker.IsNil) 874 type createResp struct { 875 ID string 876 } 877 var container createResp 878 c.Assert(json.Unmarshal(b, &container), checker.IsNil) 879 out := inspectField(c, container.ID, "HostConfig.CpusetCpus") 880 c.Assert(out, checker.Equals, "") 881 882 outMemory := inspectField(c, container.ID, "HostConfig.Memory") 883 c.Assert(outMemory, checker.Equals, "0") 884 outMemorySwap := inspectField(c, container.ID, "HostConfig.MemorySwap") 885 c.Assert(outMemorySwap, checker.Equals, "0") 886 } 887 888 func (s *DockerSuite) TestCreateWithTooLowMemoryLimit(c *check.C) { 889 // TODO Windows: Port once memory is supported 890 testRequires(c, DaemonIsLinux) 891 config := `{ 892 "Image": "busybox", 893 "Cmd": "ls", 894 "OpenStdin": true, 895 "CpuShares": 100, 896 "Memory": 524287 897 }` 898 899 res, body, err := request.Post("/containers/create", request.RawString(config), request.JSON) 900 c.Assert(err, checker.IsNil) 901 b, err2 := request.ReadBody(body) 902 c.Assert(err2, checker.IsNil) 903 904 c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest) 905 c.Assert(string(b), checker.Contains, "Minimum memory limit allowed is 4MB") 906 } 907 908 func (s *DockerSuite) TestContainerAPIRename(c *check.C) { 909 out, _ := dockerCmd(c, "run", "--name", "TestContainerAPIRename", "-d", "busybox", "sh") 910 911 containerID := strings.TrimSpace(out) 912 newName := "TestContainerAPIRenameNew" 913 914 cli, err := client.NewEnvClient() 915 c.Assert(err, checker.IsNil) 916 defer cli.Close() 917 918 err = cli.ContainerRename(context.Background(), containerID, newName) 919 c.Assert(err, checker.IsNil) 920 921 name := inspectField(c, containerID, "Name") 922 c.Assert(name, checker.Equals, "/"+newName, check.Commentf("Failed to rename container")) 923 } 924 925 func (s *DockerSuite) TestContainerAPIKill(c *check.C) { 926 name := "test-api-kill" 927 runSleepingContainer(c, "-i", "--name", name) 928 929 cli, err := client.NewEnvClient() 930 c.Assert(err, checker.IsNil) 931 defer cli.Close() 932 933 err = cli.ContainerKill(context.Background(), name, "SIGKILL") 934 c.Assert(err, checker.IsNil) 935 936 state := inspectField(c, name, "State.Running") 937 c.Assert(state, checker.Equals, "false", check.Commentf("got wrong State from container %s: %q", name, state)) 938 } 939 940 func (s *DockerSuite) TestContainerAPIRestart(c *check.C) { 941 name := "test-api-restart" 942 runSleepingContainer(c, "-di", "--name", name) 943 cli, err := client.NewEnvClient() 944 c.Assert(err, checker.IsNil) 945 defer cli.Close() 946 947 timeout := 1 * time.Second 948 err = cli.ContainerRestart(context.Background(), name, &timeout) 949 c.Assert(err, checker.IsNil) 950 951 c.Assert(waitInspect(name, "{{ .State.Restarting }} {{ .State.Running }}", "false true", 15*time.Second), checker.IsNil) 952 } 953 954 func (s *DockerSuite) TestContainerAPIRestartNotimeoutParam(c *check.C) { 955 name := "test-api-restart-no-timeout-param" 956 out := runSleepingContainer(c, "-di", "--name", name) 957 id := strings.TrimSpace(out) 958 c.Assert(waitRun(id), checker.IsNil) 959 960 cli, err := client.NewEnvClient() 961 c.Assert(err, checker.IsNil) 962 defer cli.Close() 963 964 err = cli.ContainerRestart(context.Background(), name, nil) 965 c.Assert(err, checker.IsNil) 966 967 c.Assert(waitInspect(name, "{{ .State.Restarting }} {{ .State.Running }}", "false true", 15*time.Second), checker.IsNil) 968 } 969 970 func (s *DockerSuite) TestContainerAPIStart(c *check.C) { 971 name := "testing-start" 972 config := containertypes.Config{ 973 Image: "busybox", 974 Cmd: append([]string{"/bin/sh", "-c"}, sleepCommandForDaemonPlatform()...), 975 OpenStdin: true, 976 } 977 978 cli, err := client.NewEnvClient() 979 c.Assert(err, checker.IsNil) 980 defer cli.Close() 981 982 _, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, name) 983 c.Assert(err, checker.IsNil) 984 985 err = cli.ContainerStart(context.Background(), name, types.ContainerStartOptions{}) 986 c.Assert(err, checker.IsNil) 987 988 // second call to start should give 304 989 // maybe add ContainerStartWithRaw to test it 990 err = cli.ContainerStart(context.Background(), name, types.ContainerStartOptions{}) 991 c.Assert(err, checker.IsNil) 992 993 // TODO(tibor): figure out why this doesn't work on windows 994 } 995 996 func (s *DockerSuite) TestContainerAPIStop(c *check.C) { 997 name := "test-api-stop" 998 runSleepingContainer(c, "-i", "--name", name) 999 timeout := 30 * time.Second 1000 1001 cli, err := client.NewEnvClient() 1002 c.Assert(err, checker.IsNil) 1003 defer cli.Close() 1004 1005 err = cli.ContainerStop(context.Background(), name, &timeout) 1006 c.Assert(err, checker.IsNil) 1007 c.Assert(waitInspect(name, "{{ .State.Running }}", "false", 60*time.Second), checker.IsNil) 1008 1009 // second call to start should give 304 1010 // maybe add ContainerStartWithRaw to test it 1011 err = cli.ContainerStop(context.Background(), name, &timeout) 1012 c.Assert(err, checker.IsNil) 1013 } 1014 1015 func (s *DockerSuite) TestContainerAPIWait(c *check.C) { 1016 name := "test-api-wait" 1017 1018 sleepCmd := "/bin/sleep" 1019 if testEnv.OSType == "windows" { 1020 sleepCmd = "sleep" 1021 } 1022 dockerCmd(c, "run", "--name", name, "busybox", sleepCmd, "2") 1023 1024 cli, err := client.NewEnvClient() 1025 c.Assert(err, checker.IsNil) 1026 defer cli.Close() 1027 1028 waitresC, errC := cli.ContainerWait(context.Background(), name, "") 1029 1030 select { 1031 case err = <-errC: 1032 c.Assert(err, checker.IsNil) 1033 case waitres := <-waitresC: 1034 c.Assert(waitres.StatusCode, checker.Equals, int64(0)) 1035 } 1036 } 1037 1038 func (s *DockerSuite) TestContainerAPICopyNotExistsAnyMore(c *check.C) { 1039 name := "test-container-api-copy" 1040 dockerCmd(c, "run", "--name", name, "busybox", "touch", "/test.txt") 1041 1042 postData := types.CopyConfig{ 1043 Resource: "/test.txt", 1044 } 1045 // no copy in client/ 1046 res, _, err := request.Post("/containers/"+name+"/copy", request.JSONBody(postData)) 1047 c.Assert(err, checker.IsNil) 1048 c.Assert(res.StatusCode, checker.Equals, http.StatusNotFound) 1049 } 1050 1051 func (s *DockerSuite) TestContainerAPICopyPre124(c *check.C) { 1052 testRequires(c, DaemonIsLinux) // Windows only supports 1.25 or later 1053 name := "test-container-api-copy" 1054 dockerCmd(c, "run", "--name", name, "busybox", "touch", "/test.txt") 1055 1056 postData := types.CopyConfig{ 1057 Resource: "/test.txt", 1058 } 1059 1060 res, body, err := request.Post("/v1.23/containers/"+name+"/copy", request.JSONBody(postData)) 1061 c.Assert(err, checker.IsNil) 1062 c.Assert(res.StatusCode, checker.Equals, http.StatusOK) 1063 1064 found := false 1065 for tarReader := tar.NewReader(body); ; { 1066 h, err := tarReader.Next() 1067 if err != nil { 1068 if err == io.EOF { 1069 break 1070 } 1071 c.Fatal(err) 1072 } 1073 if h.Name == "test.txt" { 1074 found = true 1075 break 1076 } 1077 } 1078 c.Assert(found, checker.True) 1079 } 1080 1081 func (s *DockerSuite) TestContainerAPICopyResourcePathEmptyPre124(c *check.C) { 1082 testRequires(c, DaemonIsLinux) // Windows only supports 1.25 or later 1083 name := "test-container-api-copy-resource-empty" 1084 dockerCmd(c, "run", "--name", name, "busybox", "touch", "/test.txt") 1085 1086 postData := types.CopyConfig{ 1087 Resource: "", 1088 } 1089 1090 res, body, err := request.Post("/v1.23/containers/"+name+"/copy", request.JSONBody(postData)) 1091 c.Assert(err, checker.IsNil) 1092 c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest) 1093 b, err := request.ReadBody(body) 1094 c.Assert(err, checker.IsNil) 1095 c.Assert(string(b), checker.Matches, "Path cannot be empty\n") 1096 } 1097 1098 func (s *DockerSuite) TestContainerAPICopyResourcePathNotFoundPre124(c *check.C) { 1099 testRequires(c, DaemonIsLinux) // Windows only supports 1.25 or later 1100 name := "test-container-api-copy-resource-not-found" 1101 dockerCmd(c, "run", "--name", name, "busybox") 1102 1103 postData := types.CopyConfig{ 1104 Resource: "/notexist", 1105 } 1106 1107 res, body, err := request.Post("/v1.23/containers/"+name+"/copy", request.JSONBody(postData)) 1108 c.Assert(err, checker.IsNil) 1109 c.Assert(res.StatusCode, checker.Equals, http.StatusNotFound) 1110 1111 b, err := request.ReadBody(body) 1112 c.Assert(err, checker.IsNil) 1113 c.Assert(string(b), checker.Matches, "Could not find the file /notexist in container "+name+"\n") 1114 } 1115 1116 func (s *DockerSuite) TestContainerAPICopyContainerNotFoundPr124(c *check.C) { 1117 testRequires(c, DaemonIsLinux) // Windows only supports 1.25 or later 1118 postData := types.CopyConfig{ 1119 Resource: "/something", 1120 } 1121 1122 res, _, err := request.Post("/v1.23/containers/notexists/copy", request.JSONBody(postData)) 1123 c.Assert(err, checker.IsNil) 1124 c.Assert(res.StatusCode, checker.Equals, http.StatusNotFound) 1125 } 1126 1127 func (s *DockerSuite) TestContainerAPIDelete(c *check.C) { 1128 out := runSleepingContainer(c) 1129 1130 id := strings.TrimSpace(out) 1131 c.Assert(waitRun(id), checker.IsNil) 1132 1133 dockerCmd(c, "stop", id) 1134 1135 cli, err := client.NewEnvClient() 1136 c.Assert(err, checker.IsNil) 1137 defer cli.Close() 1138 1139 err = cli.ContainerRemove(context.Background(), id, types.ContainerRemoveOptions{}) 1140 c.Assert(err, checker.IsNil) 1141 } 1142 1143 func (s *DockerSuite) TestContainerAPIDeleteNotExist(c *check.C) { 1144 cli, err := client.NewEnvClient() 1145 c.Assert(err, checker.IsNil) 1146 defer cli.Close() 1147 1148 err = cli.ContainerRemove(context.Background(), "doesnotexist", types.ContainerRemoveOptions{}) 1149 c.Assert(err.Error(), checker.Contains, "No such container: doesnotexist") 1150 } 1151 1152 func (s *DockerSuite) TestContainerAPIDeleteForce(c *check.C) { 1153 out := runSleepingContainer(c) 1154 id := strings.TrimSpace(out) 1155 c.Assert(waitRun(id), checker.IsNil) 1156 1157 removeOptions := types.ContainerRemoveOptions{ 1158 Force: true, 1159 } 1160 1161 cli, err := client.NewEnvClient() 1162 c.Assert(err, checker.IsNil) 1163 defer cli.Close() 1164 1165 err = cli.ContainerRemove(context.Background(), id, removeOptions) 1166 c.Assert(err, checker.IsNil) 1167 } 1168 1169 func (s *DockerSuite) TestContainerAPIDeleteRemoveLinks(c *check.C) { 1170 // Windows does not support links 1171 testRequires(c, DaemonIsLinux) 1172 out, _ := dockerCmd(c, "run", "-d", "--name", "tlink1", "busybox", "top") 1173 1174 id := strings.TrimSpace(out) 1175 c.Assert(waitRun(id), checker.IsNil) 1176 1177 out, _ = dockerCmd(c, "run", "--link", "tlink1:tlink1", "--name", "tlink2", "-d", "busybox", "top") 1178 1179 id2 := strings.TrimSpace(out) 1180 c.Assert(waitRun(id2), checker.IsNil) 1181 1182 links := inspectFieldJSON(c, id2, "HostConfig.Links") 1183 c.Assert(links, checker.Equals, "[\"/tlink1:/tlink2/tlink1\"]", check.Commentf("expected to have links between containers")) 1184 1185 removeOptions := types.ContainerRemoveOptions{ 1186 RemoveLinks: true, 1187 } 1188 1189 cli, err := client.NewEnvClient() 1190 c.Assert(err, checker.IsNil) 1191 defer cli.Close() 1192 1193 err = cli.ContainerRemove(context.Background(), "tlink2/tlink1", removeOptions) 1194 c.Assert(err, check.IsNil) 1195 1196 linksPostRm := inspectFieldJSON(c, id2, "HostConfig.Links") 1197 c.Assert(linksPostRm, checker.Equals, "null", check.Commentf("call to api deleteContainer links should have removed the specified links")) 1198 } 1199 1200 func (s *DockerSuite) TestContainerAPIDeleteConflict(c *check.C) { 1201 out := runSleepingContainer(c) 1202 1203 id := strings.TrimSpace(out) 1204 c.Assert(waitRun(id), checker.IsNil) 1205 1206 cli, err := client.NewEnvClient() 1207 c.Assert(err, checker.IsNil) 1208 defer cli.Close() 1209 1210 err = cli.ContainerRemove(context.Background(), id, types.ContainerRemoveOptions{}) 1211 expected := "cannot remove a running container" 1212 c.Assert(err.Error(), checker.Contains, expected) 1213 } 1214 1215 func (s *DockerSuite) TestContainerAPIDeleteRemoveVolume(c *check.C) { 1216 testRequires(c, SameHostDaemon) 1217 1218 vol := "/testvolume" 1219 if testEnv.OSType == "windows" { 1220 vol = `c:\testvolume` 1221 } 1222 1223 out := runSleepingContainer(c, "-v", vol) 1224 1225 id := strings.TrimSpace(out) 1226 c.Assert(waitRun(id), checker.IsNil) 1227 1228 source, err := inspectMountSourceField(id, vol) 1229 _, err = os.Stat(source) 1230 c.Assert(err, checker.IsNil) 1231 1232 removeOptions := types.ContainerRemoveOptions{ 1233 Force: true, 1234 RemoveVolumes: true, 1235 } 1236 1237 cli, err := client.NewEnvClient() 1238 c.Assert(err, checker.IsNil) 1239 defer cli.Close() 1240 1241 err = cli.ContainerRemove(context.Background(), id, removeOptions) 1242 c.Assert(err, check.IsNil) 1243 1244 _, err = os.Stat(source) 1245 c.Assert(os.IsNotExist(err), checker.True, check.Commentf("expected to get ErrNotExist error, got %v", err)) 1246 } 1247 1248 // Regression test for https://github.com/docker/docker/issues/6231 1249 func (s *DockerSuite) TestContainerAPIChunkedEncoding(c *check.C) { 1250 1251 config := map[string]interface{}{ 1252 "Image": "busybox", 1253 "Cmd": append([]string{"/bin/sh", "-c"}, sleepCommandForDaemonPlatform()...), 1254 "OpenStdin": true, 1255 } 1256 1257 resp, _, err := request.Post("/containers/create", request.JSONBody(config), func(req *http.Request) error { 1258 // This is a cheat to make the http request do chunked encoding 1259 // Otherwise (just setting the Content-Encoding to chunked) net/http will overwrite 1260 // https://golang.org/src/pkg/net/http/request.go?s=11980:12172 1261 req.ContentLength = -1 1262 return nil 1263 }) 1264 c.Assert(err, checker.IsNil, check.Commentf("error creating container with chunked encoding")) 1265 defer resp.Body.Close() 1266 c.Assert(resp.StatusCode, checker.Equals, http.StatusCreated) 1267 } 1268 1269 func (s *DockerSuite) TestContainerAPIPostContainerStop(c *check.C) { 1270 out := runSleepingContainer(c) 1271 1272 containerID := strings.TrimSpace(out) 1273 c.Assert(waitRun(containerID), checker.IsNil) 1274 1275 cli, err := client.NewEnvClient() 1276 c.Assert(err, checker.IsNil) 1277 defer cli.Close() 1278 1279 err = cli.ContainerStop(context.Background(), containerID, nil) 1280 c.Assert(err, checker.IsNil) 1281 c.Assert(waitInspect(containerID, "{{ .State.Running }}", "false", 60*time.Second), checker.IsNil) 1282 } 1283 1284 // #14170 1285 func (s *DockerSuite) TestPostContainerAPICreateWithStringOrSliceEntrypoint(c *check.C) { 1286 config := containertypes.Config{ 1287 Image: "busybox", 1288 Entrypoint: []string{"echo"}, 1289 Cmd: []string{"hello", "world"}, 1290 } 1291 1292 cli, err := client.NewEnvClient() 1293 c.Assert(err, checker.IsNil) 1294 defer cli.Close() 1295 1296 _, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "echotest") 1297 c.Assert(err, checker.IsNil) 1298 out, _ := dockerCmd(c, "start", "-a", "echotest") 1299 c.Assert(strings.TrimSpace(out), checker.Equals, "hello world") 1300 1301 config2 := struct { 1302 Image string 1303 Entrypoint string 1304 Cmd []string 1305 }{"busybox", "echo", []string{"hello", "world"}} 1306 _, _, err = request.Post("/containers/create?name=echotest2", request.JSONBody(config2)) 1307 c.Assert(err, checker.IsNil) 1308 out, _ = dockerCmd(c, "start", "-a", "echotest2") 1309 c.Assert(strings.TrimSpace(out), checker.Equals, "hello world") 1310 } 1311 1312 // #14170 1313 func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceCmd(c *check.C) { 1314 config := containertypes.Config{ 1315 Image: "busybox", 1316 Cmd: []string{"echo", "hello", "world"}, 1317 } 1318 1319 cli, err := client.NewEnvClient() 1320 c.Assert(err, checker.IsNil) 1321 defer cli.Close() 1322 1323 _, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "echotest") 1324 c.Assert(err, checker.IsNil) 1325 out, _ := dockerCmd(c, "start", "-a", "echotest") 1326 c.Assert(strings.TrimSpace(out), checker.Equals, "hello world") 1327 1328 config2 := struct { 1329 Image string 1330 Entrypoint string 1331 Cmd string 1332 }{"busybox", "echo", "hello world"} 1333 _, _, err = request.Post("/containers/create?name=echotest2", request.JSONBody(config2)) 1334 c.Assert(err, checker.IsNil) 1335 out, _ = dockerCmd(c, "start", "-a", "echotest2") 1336 c.Assert(strings.TrimSpace(out), checker.Equals, "hello world") 1337 } 1338 1339 // regression #14318 1340 func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceCapAddDrop(c *check.C) { 1341 // Windows doesn't support CapAdd/CapDrop 1342 testRequires(c, DaemonIsLinux) 1343 config := struct { 1344 Image string 1345 CapAdd string 1346 CapDrop string 1347 }{"busybox", "NET_ADMIN", "SYS_ADMIN"} 1348 res, _, err := request.Post("/containers/create?name=capaddtest0", request.JSONBody(config)) 1349 c.Assert(err, checker.IsNil) 1350 c.Assert(res.StatusCode, checker.Equals, http.StatusCreated) 1351 1352 config2 := containertypes.Config{ 1353 Image: "busybox", 1354 } 1355 hostConfig := containertypes.HostConfig{ 1356 CapAdd: []string{"NET_ADMIN", "SYS_ADMIN"}, 1357 CapDrop: []string{"SETGID"}, 1358 } 1359 1360 cli, err := client.NewEnvClient() 1361 c.Assert(err, checker.IsNil) 1362 defer cli.Close() 1363 1364 _, err = cli.ContainerCreate(context.Background(), &config2, &hostConfig, &networktypes.NetworkingConfig{}, "capaddtest1") 1365 c.Assert(err, checker.IsNil) 1366 } 1367 1368 // #14915 1369 func (s *DockerSuite) TestContainerAPICreateNoHostConfig118(c *check.C) { 1370 testRequires(c, DaemonIsLinux) // Windows only support 1.25 or later 1371 config := containertypes.Config{ 1372 Image: "busybox", 1373 } 1374 1375 cli, err := client.NewClientWithOpts(client.FromEnv, client.WithVersion("v1.18")) 1376 c.Assert(err, checker.IsNil) 1377 1378 _, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "") 1379 c.Assert(err, checker.IsNil) 1380 } 1381 1382 // Ensure an error occurs when you have a container read-only rootfs but you 1383 // extract an archive to a symlink in a writable volume which points to a 1384 // directory outside of the volume. 1385 func (s *DockerSuite) TestPutContainerArchiveErrSymlinkInVolumeToReadOnlyRootfs(c *check.C) { 1386 // Windows does not support read-only rootfs 1387 // Requires local volume mount bind. 1388 // --read-only + userns has remount issues 1389 testRequires(c, SameHostDaemon, NotUserNamespace, DaemonIsLinux) 1390 1391 testVol := getTestDir(c, "test-put-container-archive-err-symlink-in-volume-to-read-only-rootfs-") 1392 defer os.RemoveAll(testVol) 1393 1394 makeTestContentInDir(c, testVol) 1395 1396 cID := makeTestContainer(c, testContainerOptions{ 1397 readOnly: true, 1398 volumes: defaultVolumes(testVol), // Our bind mount is at /vol2 1399 }) 1400 1401 // Attempt to extract to a symlink in the volume which points to a 1402 // directory outside the volume. This should cause an error because the 1403 // rootfs is read-only. 1404 var httpClient *http.Client 1405 cli, err := client.NewClient(daemonHost(), "v1.20", httpClient, map[string]string{}) 1406 c.Assert(err, checker.IsNil) 1407 1408 err = cli.CopyToContainer(context.Background(), cID, "/vol2/symlinkToAbsDir", nil, types.CopyToContainerOptions{}) 1409 c.Assert(err.Error(), checker.Contains, "container rootfs is marked read-only") 1410 } 1411 1412 func (s *DockerSuite) TestPostContainersCreateWithWrongCpusetValues(c *check.C) { 1413 // Not supported on Windows 1414 testRequires(c, DaemonIsLinux) 1415 1416 cli, err := client.NewEnvClient() 1417 c.Assert(err, checker.IsNil) 1418 defer cli.Close() 1419 1420 config := containertypes.Config{ 1421 Image: "busybox", 1422 } 1423 hostConfig1 := containertypes.HostConfig{ 1424 Resources: containertypes.Resources{ 1425 CpusetCpus: "1-42,,", 1426 }, 1427 } 1428 name := "wrong-cpuset-cpus" 1429 1430 _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig1, &networktypes.NetworkingConfig{}, name) 1431 expected := "Invalid value 1-42,, for cpuset cpus" 1432 c.Assert(err.Error(), checker.Contains, expected) 1433 1434 hostConfig2 := containertypes.HostConfig{ 1435 Resources: containertypes.Resources{ 1436 CpusetMems: "42-3,1--", 1437 }, 1438 } 1439 name = "wrong-cpuset-mems" 1440 _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig2, &networktypes.NetworkingConfig{}, name) 1441 expected = "Invalid value 42-3,1-- for cpuset mems" 1442 c.Assert(err.Error(), checker.Contains, expected) 1443 } 1444 1445 func (s *DockerSuite) TestPostContainersCreateShmSizeNegative(c *check.C) { 1446 // ShmSize is not supported on Windows 1447 testRequires(c, DaemonIsLinux) 1448 config := containertypes.Config{ 1449 Image: "busybox", 1450 } 1451 hostConfig := containertypes.HostConfig{ 1452 ShmSize: -1, 1453 } 1454 1455 cli, err := client.NewEnvClient() 1456 c.Assert(err, checker.IsNil) 1457 defer cli.Close() 1458 1459 _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, "") 1460 c.Assert(err.Error(), checker.Contains, "SHM size can not be less than 0") 1461 } 1462 1463 func (s *DockerSuite) TestPostContainersCreateShmSizeHostConfigOmitted(c *check.C) { 1464 // ShmSize is not supported on Windows 1465 testRequires(c, DaemonIsLinux) 1466 var defaultSHMSize int64 = 67108864 1467 config := containertypes.Config{ 1468 Image: "busybox", 1469 Cmd: []string{"mount"}, 1470 } 1471 1472 cli, err := client.NewEnvClient() 1473 c.Assert(err, checker.IsNil) 1474 defer cli.Close() 1475 1476 container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "") 1477 c.Assert(err, check.IsNil) 1478 1479 containerJSON, err := cli.ContainerInspect(context.Background(), container.ID) 1480 c.Assert(err, check.IsNil) 1481 1482 c.Assert(containerJSON.HostConfig.ShmSize, check.Equals, defaultSHMSize) 1483 1484 out, _ := dockerCmd(c, "start", "-i", containerJSON.ID) 1485 shmRegexp := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=65536k`) 1486 if !shmRegexp.MatchString(out) { 1487 c.Fatalf("Expected shm of 64MB in mount command, got %v", out) 1488 } 1489 } 1490 1491 func (s *DockerSuite) TestPostContainersCreateShmSizeOmitted(c *check.C) { 1492 // ShmSize is not supported on Windows 1493 testRequires(c, DaemonIsLinux) 1494 config := containertypes.Config{ 1495 Image: "busybox", 1496 Cmd: []string{"mount"}, 1497 } 1498 1499 cli, err := client.NewEnvClient() 1500 c.Assert(err, checker.IsNil) 1501 defer cli.Close() 1502 1503 container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "") 1504 c.Assert(err, check.IsNil) 1505 1506 containerJSON, err := cli.ContainerInspect(context.Background(), container.ID) 1507 c.Assert(err, check.IsNil) 1508 1509 c.Assert(containerJSON.HostConfig.ShmSize, check.Equals, int64(67108864)) 1510 1511 out, _ := dockerCmd(c, "start", "-i", containerJSON.ID) 1512 shmRegexp := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=65536k`) 1513 if !shmRegexp.MatchString(out) { 1514 c.Fatalf("Expected shm of 64MB in mount command, got %v", out) 1515 } 1516 } 1517 1518 func (s *DockerSuite) TestPostContainersCreateWithShmSize(c *check.C) { 1519 // ShmSize is not supported on Windows 1520 testRequires(c, DaemonIsLinux) 1521 config := containertypes.Config{ 1522 Image: "busybox", 1523 Cmd: []string{"mount"}, 1524 } 1525 1526 hostConfig := containertypes.HostConfig{ 1527 ShmSize: 1073741824, 1528 } 1529 1530 cli, err := client.NewEnvClient() 1531 c.Assert(err, checker.IsNil) 1532 defer cli.Close() 1533 1534 container, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, "") 1535 c.Assert(err, check.IsNil) 1536 1537 containerJSON, err := cli.ContainerInspect(context.Background(), container.ID) 1538 c.Assert(err, check.IsNil) 1539 1540 c.Assert(containerJSON.HostConfig.ShmSize, check.Equals, int64(1073741824)) 1541 1542 out, _ := dockerCmd(c, "start", "-i", containerJSON.ID) 1543 shmRegex := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=1048576k`) 1544 if !shmRegex.MatchString(out) { 1545 c.Fatalf("Expected shm of 1GB in mount command, got %v", out) 1546 } 1547 } 1548 1549 func (s *DockerSuite) TestPostContainersCreateMemorySwappinessHostConfigOmitted(c *check.C) { 1550 // Swappiness is not supported on Windows 1551 testRequires(c, DaemonIsLinux) 1552 config := containertypes.Config{ 1553 Image: "busybox", 1554 } 1555 1556 cli, err := client.NewEnvClient() 1557 c.Assert(err, checker.IsNil) 1558 defer cli.Close() 1559 1560 container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "") 1561 c.Assert(err, check.IsNil) 1562 1563 containerJSON, err := cli.ContainerInspect(context.Background(), container.ID) 1564 c.Assert(err, check.IsNil) 1565 1566 c.Assert(containerJSON.HostConfig.MemorySwappiness, check.IsNil) 1567 } 1568 1569 // check validation is done daemon side and not only in cli 1570 func (s *DockerSuite) TestPostContainersCreateWithOomScoreAdjInvalidRange(c *check.C) { 1571 // OomScoreAdj is not supported on Windows 1572 testRequires(c, DaemonIsLinux) 1573 1574 config := containertypes.Config{ 1575 Image: "busybox", 1576 } 1577 1578 hostConfig := containertypes.HostConfig{ 1579 OomScoreAdj: 1001, 1580 } 1581 1582 cli, err := client.NewEnvClient() 1583 c.Assert(err, checker.IsNil) 1584 defer cli.Close() 1585 1586 name := "oomscoreadj-over" 1587 _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, name) 1588 1589 expected := "Invalid value 1001, range for oom score adj is [-1000, 1000]" 1590 c.Assert(err.Error(), checker.Contains, expected) 1591 1592 hostConfig = containertypes.HostConfig{ 1593 OomScoreAdj: -1001, 1594 } 1595 1596 name = "oomscoreadj-low" 1597 _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, name) 1598 1599 expected = "Invalid value -1001, range for oom score adj is [-1000, 1000]" 1600 c.Assert(err.Error(), checker.Contains, expected) 1601 } 1602 1603 // test case for #22210 where an empty container name caused panic. 1604 func (s *DockerSuite) TestContainerAPIDeleteWithEmptyName(c *check.C) { 1605 cli, err := client.NewEnvClient() 1606 c.Assert(err, checker.IsNil) 1607 defer cli.Close() 1608 1609 err = cli.ContainerRemove(context.Background(), "", types.ContainerRemoveOptions{}) 1610 c.Assert(err.Error(), checker.Contains, "No such container") 1611 } 1612 1613 func (s *DockerSuite) TestContainerAPIStatsWithNetworkDisabled(c *check.C) { 1614 // Problematic on Windows as Windows does not support stats 1615 testRequires(c, DaemonIsLinux) 1616 1617 name := "testing-network-disabled" 1618 1619 config := containertypes.Config{ 1620 Image: "busybox", 1621 Cmd: []string{"top"}, 1622 NetworkDisabled: true, 1623 } 1624 1625 cli, err := client.NewEnvClient() 1626 c.Assert(err, checker.IsNil) 1627 defer cli.Close() 1628 1629 _, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, name) 1630 c.Assert(err, checker.IsNil) 1631 1632 err = cli.ContainerStart(context.Background(), name, types.ContainerStartOptions{}) 1633 c.Assert(err, checker.IsNil) 1634 1635 c.Assert(waitRun(name), check.IsNil) 1636 1637 type b struct { 1638 stats types.ContainerStats 1639 err error 1640 } 1641 bc := make(chan b, 1) 1642 go func() { 1643 stats, err := cli.ContainerStats(context.Background(), name, false) 1644 bc <- b{stats, err} 1645 }() 1646 1647 // allow some time to stream the stats from the container 1648 time.Sleep(4 * time.Second) 1649 dockerCmd(c, "rm", "-f", name) 1650 1651 // collect the results from the stats stream or timeout and fail 1652 // if the stream was not disconnected. 1653 select { 1654 case <-time.After(2 * time.Second): 1655 c.Fatal("stream was not closed after container was removed") 1656 case sr := <-bc: 1657 c.Assert(sr.err, checker.IsNil) 1658 sr.stats.Body.Close() 1659 } 1660 } 1661 1662 func (s *DockerSuite) TestContainersAPICreateMountsValidation(c *check.C) { 1663 type testCase struct { 1664 config containertypes.Config 1665 hostConfig containertypes.HostConfig 1666 msg string 1667 } 1668 1669 prefix, slash := getPrefixAndSlashFromDaemonPlatform() 1670 destPath := prefix + slash + "foo" 1671 notExistPath := prefix + slash + "notexist" 1672 1673 cases := []testCase{ 1674 { 1675 config: containertypes.Config{ 1676 Image: "busybox", 1677 }, 1678 hostConfig: containertypes.HostConfig{ 1679 Mounts: []mounttypes.Mount{{ 1680 Type: "notreal", 1681 Target: destPath, 1682 }, 1683 }, 1684 }, 1685 1686 msg: "mount type unknown", 1687 }, 1688 { 1689 config: containertypes.Config{ 1690 Image: "busybox", 1691 }, 1692 hostConfig: containertypes.HostConfig{ 1693 Mounts: []mounttypes.Mount{{ 1694 Type: "bind"}}}, 1695 msg: "Target must not be empty", 1696 }, 1697 { 1698 config: containertypes.Config{ 1699 Image: "busybox", 1700 }, 1701 hostConfig: containertypes.HostConfig{ 1702 Mounts: []mounttypes.Mount{{ 1703 Type: "bind", 1704 Target: destPath}}}, 1705 msg: "Source must not be empty", 1706 }, 1707 { 1708 config: containertypes.Config{ 1709 Image: "busybox", 1710 }, 1711 hostConfig: containertypes.HostConfig{ 1712 Mounts: []mounttypes.Mount{{ 1713 Type: "bind", 1714 Source: notExistPath, 1715 Target: destPath}}}, 1716 msg: "source path does not exist", 1717 // FIXME(vdemeester) fails into e2e, migrate to integration/container anyway 1718 // msg: "bind mount source path does not exist: " + notExistPath, 1719 }, 1720 { 1721 config: containertypes.Config{ 1722 Image: "busybox", 1723 }, 1724 hostConfig: containertypes.HostConfig{ 1725 Mounts: []mounttypes.Mount{{ 1726 Type: "volume"}}}, 1727 msg: "Target must not be empty", 1728 }, 1729 { 1730 config: containertypes.Config{ 1731 Image: "busybox", 1732 }, 1733 hostConfig: containertypes.HostConfig{ 1734 Mounts: []mounttypes.Mount{{ 1735 Type: "volume", 1736 Source: "hello", 1737 Target: destPath}}}, 1738 msg: "", 1739 }, 1740 { 1741 config: containertypes.Config{ 1742 Image: "busybox", 1743 }, 1744 hostConfig: containertypes.HostConfig{ 1745 Mounts: []mounttypes.Mount{{ 1746 Type: "volume", 1747 Source: "hello2", 1748 Target: destPath, 1749 VolumeOptions: &mounttypes.VolumeOptions{ 1750 DriverConfig: &mounttypes.Driver{ 1751 Name: "local"}}}}}, 1752 msg: "", 1753 }, 1754 } 1755 1756 if SameHostDaemon() { 1757 tmpDir, err := ioutils.TempDir("", "test-mounts-api") 1758 c.Assert(err, checker.IsNil) 1759 defer os.RemoveAll(tmpDir) 1760 cases = append(cases, []testCase{ 1761 { 1762 config: containertypes.Config{ 1763 Image: "busybox", 1764 }, 1765 hostConfig: containertypes.HostConfig{ 1766 Mounts: []mounttypes.Mount{{ 1767 Type: "bind", 1768 Source: tmpDir, 1769 Target: destPath}}}, 1770 msg: "", 1771 }, 1772 { 1773 config: containertypes.Config{ 1774 Image: "busybox", 1775 }, 1776 hostConfig: containertypes.HostConfig{ 1777 Mounts: []mounttypes.Mount{{ 1778 Type: "bind", 1779 Source: tmpDir, 1780 Target: destPath, 1781 VolumeOptions: &mounttypes.VolumeOptions{}}}}, 1782 msg: "VolumeOptions must not be specified", 1783 }, 1784 }...) 1785 } 1786 1787 if DaemonIsLinux() { 1788 cases = append(cases, []testCase{ 1789 { 1790 config: containertypes.Config{ 1791 Image: "busybox", 1792 }, 1793 hostConfig: containertypes.HostConfig{ 1794 Mounts: []mounttypes.Mount{{ 1795 Type: "volume", 1796 Source: "hello3", 1797 Target: destPath, 1798 VolumeOptions: &mounttypes.VolumeOptions{ 1799 DriverConfig: &mounttypes.Driver{ 1800 Name: "local", 1801 Options: map[string]string{"o": "size=1"}}}}}}, 1802 msg: "", 1803 }, 1804 { 1805 config: containertypes.Config{ 1806 Image: "busybox", 1807 }, 1808 hostConfig: containertypes.HostConfig{ 1809 Mounts: []mounttypes.Mount{{ 1810 Type: "tmpfs", 1811 Target: destPath}}}, 1812 msg: "", 1813 }, 1814 { 1815 config: containertypes.Config{ 1816 Image: "busybox", 1817 }, 1818 hostConfig: containertypes.HostConfig{ 1819 Mounts: []mounttypes.Mount{{ 1820 Type: "tmpfs", 1821 Target: destPath, 1822 TmpfsOptions: &mounttypes.TmpfsOptions{ 1823 SizeBytes: 4096 * 1024, 1824 Mode: 0700, 1825 }}}}, 1826 msg: "", 1827 }, 1828 1829 { 1830 config: containertypes.Config{ 1831 Image: "busybox", 1832 }, 1833 hostConfig: containertypes.HostConfig{ 1834 Mounts: []mounttypes.Mount{{ 1835 Type: "tmpfs", 1836 Source: "/shouldnotbespecified", 1837 Target: destPath}}}, 1838 msg: "Source must not be specified", 1839 }, 1840 }...) 1841 1842 } 1843 cli, err := client.NewEnvClient() 1844 c.Assert(err, checker.IsNil) 1845 defer cli.Close() 1846 1847 for i, x := range cases { 1848 c.Logf("case %d", i) 1849 _, err = cli.ContainerCreate(context.Background(), &x.config, &x.hostConfig, &networktypes.NetworkingConfig{}, "") 1850 if len(x.msg) > 0 { 1851 c.Assert(err.Error(), checker.Contains, x.msg, check.Commentf("%v", cases[i].config)) 1852 } else { 1853 c.Assert(err, checker.IsNil) 1854 } 1855 } 1856 } 1857 1858 func (s *DockerSuite) TestContainerAPICreateMountsBindRead(c *check.C) { 1859 testRequires(c, NotUserNamespace, SameHostDaemon) 1860 // also with data in the host side 1861 prefix, slash := getPrefixAndSlashFromDaemonPlatform() 1862 destPath := prefix + slash + "foo" 1863 tmpDir, err := ioutil.TempDir("", "test-mounts-api-bind") 1864 c.Assert(err, checker.IsNil) 1865 defer os.RemoveAll(tmpDir) 1866 err = ioutil.WriteFile(filepath.Join(tmpDir, "bar"), []byte("hello"), 666) 1867 c.Assert(err, checker.IsNil) 1868 config := containertypes.Config{ 1869 Image: "busybox", 1870 Cmd: []string{"/bin/sh", "-c", "cat /foo/bar"}, 1871 } 1872 hostConfig := containertypes.HostConfig{ 1873 Mounts: []mounttypes.Mount{ 1874 {Type: "bind", Source: tmpDir, Target: destPath}, 1875 }, 1876 } 1877 cli, err := client.NewEnvClient() 1878 c.Assert(err, checker.IsNil) 1879 defer cli.Close() 1880 1881 _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, "test") 1882 c.Assert(err, checker.IsNil) 1883 1884 out, _ := dockerCmd(c, "start", "-a", "test") 1885 c.Assert(out, checker.Equals, "hello") 1886 } 1887 1888 // Test Mounts comes out as expected for the MountPoint 1889 func (s *DockerSuite) TestContainersAPICreateMountsCreate(c *check.C) { 1890 prefix, slash := getPrefixAndSlashFromDaemonPlatform() 1891 destPath := prefix + slash + "foo" 1892 1893 var ( 1894 testImg string 1895 ) 1896 if testEnv.OSType != "windows" { 1897 testImg = "test-mount-config" 1898 buildImageSuccessfully(c, testImg, build.WithDockerfile(` 1899 FROM busybox 1900 RUN mkdir `+destPath+` && touch `+destPath+slash+`bar 1901 CMD cat `+destPath+slash+`bar 1902 `)) 1903 } else { 1904 testImg = "busybox" 1905 } 1906 1907 type testCase struct { 1908 spec mounttypes.Mount 1909 expected types.MountPoint 1910 } 1911 1912 var selinuxSharedLabel string 1913 if runtime.GOOS == "linux" { 1914 selinuxSharedLabel = "z" 1915 } 1916 1917 cases := []testCase{ 1918 // use literal strings here for `Type` instead of the defined constants in the volume package to keep this honest 1919 // Validation of the actual `Mount` struct is done in another test is not needed here 1920 { 1921 spec: mounttypes.Mount{Type: "volume", Target: destPath}, 1922 expected: types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath, Mode: selinuxSharedLabel}, 1923 }, 1924 { 1925 spec: mounttypes.Mount{Type: "volume", Target: destPath + slash}, 1926 expected: types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath, Mode: selinuxSharedLabel}, 1927 }, 1928 { 1929 spec: mounttypes.Mount{Type: "volume", Target: destPath, Source: "test1"}, 1930 expected: types.MountPoint{Type: "volume", Name: "test1", RW: true, Destination: destPath, Mode: selinuxSharedLabel}, 1931 }, 1932 { 1933 spec: mounttypes.Mount{Type: "volume", Target: destPath, ReadOnly: true, Source: "test2"}, 1934 expected: types.MountPoint{Type: "volume", Name: "test2", RW: false, Destination: destPath, Mode: selinuxSharedLabel}, 1935 }, 1936 { 1937 spec: mounttypes.Mount{Type: "volume", Target: destPath, Source: "test3", VolumeOptions: &mounttypes.VolumeOptions{DriverConfig: &mounttypes.Driver{Name: volume.DefaultDriverName}}}, 1938 expected: types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", Name: "test3", RW: true, Destination: destPath, Mode: selinuxSharedLabel}, 1939 }, 1940 } 1941 1942 if SameHostDaemon() { 1943 // setup temp dir for testing binds 1944 tmpDir1, err := ioutil.TempDir("", "test-mounts-api-1") 1945 c.Assert(err, checker.IsNil) 1946 defer os.RemoveAll(tmpDir1) 1947 cases = append(cases, []testCase{ 1948 { 1949 spec: mounttypes.Mount{ 1950 Type: "bind", 1951 Source: tmpDir1, 1952 Target: destPath, 1953 }, 1954 expected: types.MountPoint{ 1955 Type: "bind", 1956 RW: true, 1957 Destination: destPath, 1958 Source: tmpDir1, 1959 }, 1960 }, 1961 { 1962 spec: mounttypes.Mount{Type: "bind", Source: tmpDir1, Target: destPath, ReadOnly: true}, 1963 expected: types.MountPoint{Type: "bind", RW: false, Destination: destPath, Source: tmpDir1}, 1964 }, 1965 }...) 1966 1967 // for modes only supported on Linux 1968 if DaemonIsLinux() { 1969 tmpDir3, err := ioutils.TempDir("", "test-mounts-api-3") 1970 c.Assert(err, checker.IsNil) 1971 defer os.RemoveAll(tmpDir3) 1972 1973 c.Assert(mount.Mount(tmpDir3, tmpDir3, "none", "bind,rw"), checker.IsNil) 1974 c.Assert(mount.ForceMount("", tmpDir3, "none", "shared"), checker.IsNil) 1975 1976 cases = append(cases, []testCase{ 1977 { 1978 spec: mounttypes.Mount{Type: "bind", Source: tmpDir3, Target: destPath}, 1979 expected: types.MountPoint{Type: "bind", RW: true, Destination: destPath, Source: tmpDir3}, 1980 }, 1981 { 1982 spec: mounttypes.Mount{Type: "bind", Source: tmpDir3, Target: destPath, ReadOnly: true}, 1983 expected: types.MountPoint{Type: "bind", RW: false, Destination: destPath, Source: tmpDir3}, 1984 }, 1985 { 1986 spec: mounttypes.Mount{Type: "bind", Source: tmpDir3, Target: destPath, ReadOnly: true, BindOptions: &mounttypes.BindOptions{Propagation: "shared"}}, 1987 expected: types.MountPoint{Type: "bind", RW: false, Destination: destPath, Source: tmpDir3, Propagation: "shared"}, 1988 }, 1989 }...) 1990 } 1991 } 1992 1993 if testEnv.OSType != "windows" { // Windows does not support volume populate 1994 cases = append(cases, []testCase{ 1995 { 1996 spec: mounttypes.Mount{Type: "volume", Target: destPath, VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}}, 1997 expected: types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath, Mode: selinuxSharedLabel}, 1998 }, 1999 { 2000 spec: mounttypes.Mount{Type: "volume", Target: destPath + slash, VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}}, 2001 expected: types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath, Mode: selinuxSharedLabel}, 2002 }, 2003 { 2004 spec: mounttypes.Mount{Type: "volume", Target: destPath, Source: "test4", VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}}, 2005 expected: types.MountPoint{Type: "volume", Name: "test4", RW: true, Destination: destPath, Mode: selinuxSharedLabel}, 2006 }, 2007 { 2008 spec: mounttypes.Mount{Type: "volume", Target: destPath, Source: "test5", ReadOnly: true, VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}}, 2009 expected: types.MountPoint{Type: "volume", Name: "test5", RW: false, Destination: destPath, Mode: selinuxSharedLabel}, 2010 }, 2011 }...) 2012 } 2013 2014 type wrapper struct { 2015 containertypes.Config 2016 HostConfig containertypes.HostConfig 2017 } 2018 type createResp struct { 2019 ID string `json:"Id"` 2020 } 2021 2022 ctx := context.Background() 2023 apiclient := testEnv.APIClient() 2024 for i, x := range cases { 2025 c.Logf("case %d - config: %v", i, x.spec) 2026 container, err := apiclient.ContainerCreate( 2027 ctx, 2028 &containertypes.Config{Image: testImg}, 2029 &containertypes.HostConfig{Mounts: []mounttypes.Mount{x.spec}}, 2030 &networktypes.NetworkingConfig{}, 2031 "") 2032 assert.NilError(c, err) 2033 2034 containerInspect, err := apiclient.ContainerInspect(ctx, container.ID) 2035 assert.NilError(c, err) 2036 mps := containerInspect.Mounts 2037 assert.Assert(c, is.Len(mps, 1)) 2038 mountPoint := mps[0] 2039 2040 if x.expected.Source != "" { 2041 assert.Check(c, is.Equal(x.expected.Source, mountPoint.Source)) 2042 } 2043 if x.expected.Name != "" { 2044 assert.Check(c, is.Equal(x.expected.Name, mountPoint.Name)) 2045 } 2046 if x.expected.Driver != "" { 2047 assert.Check(c, is.Equal(x.expected.Driver, mountPoint.Driver)) 2048 } 2049 if x.expected.Propagation != "" { 2050 assert.Check(c, is.Equal(x.expected.Propagation, mountPoint.Propagation)) 2051 } 2052 assert.Check(c, is.Equal(x.expected.RW, mountPoint.RW)) 2053 assert.Check(c, is.Equal(x.expected.Type, mountPoint.Type)) 2054 assert.Check(c, is.Equal(x.expected.Mode, mountPoint.Mode)) 2055 assert.Check(c, is.Equal(x.expected.Destination, mountPoint.Destination)) 2056 2057 err = apiclient.ContainerStart(ctx, container.ID, types.ContainerStartOptions{}) 2058 assert.NilError(c, err) 2059 poll.WaitOn(c, containerExit(apiclient, container.ID), poll.WithDelay(time.Second)) 2060 2061 err = apiclient.ContainerRemove(ctx, container.ID, types.ContainerRemoveOptions{ 2062 RemoveVolumes: true, 2063 Force: true, 2064 }) 2065 assert.NilError(c, err) 2066 2067 switch { 2068 2069 // Named volumes still exist after the container is removed 2070 case x.spec.Type == "volume" && len(x.spec.Source) > 0: 2071 _, err := apiclient.VolumeInspect(ctx, mountPoint.Name) 2072 assert.NilError(c, err) 2073 2074 // Bind mounts are never removed with the container 2075 case x.spec.Type == "bind": 2076 2077 // anonymous volumes are removed 2078 default: 2079 _, err := apiclient.VolumeInspect(ctx, mountPoint.Name) 2080 assert.Check(c, client.IsErrNotFound(err)) 2081 } 2082 } 2083 } 2084 2085 func containerExit(apiclient client.APIClient, name string) func(poll.LogT) poll.Result { 2086 return func(logT poll.LogT) poll.Result { 2087 container, err := apiclient.ContainerInspect(context.Background(), name) 2088 if err != nil { 2089 return poll.Error(err) 2090 } 2091 switch container.State.Status { 2092 case "created", "running": 2093 return poll.Continue("container %s is %s, waiting for exit", name, container.State.Status) 2094 } 2095 return poll.Success() 2096 } 2097 } 2098 2099 func (s *DockerSuite) TestContainersAPICreateMountsTmpfs(c *check.C) { 2100 testRequires(c, DaemonIsLinux) 2101 type testCase struct { 2102 cfg mounttypes.Mount 2103 expectedOptions []string 2104 } 2105 target := "/foo" 2106 cases := []testCase{ 2107 { 2108 cfg: mounttypes.Mount{ 2109 Type: "tmpfs", 2110 Target: target}, 2111 expectedOptions: []string{"rw", "nosuid", "nodev", "noexec", "relatime"}, 2112 }, 2113 { 2114 cfg: mounttypes.Mount{ 2115 Type: "tmpfs", 2116 Target: target, 2117 TmpfsOptions: &mounttypes.TmpfsOptions{ 2118 SizeBytes: 4096 * 1024, Mode: 0700}}, 2119 expectedOptions: []string{"rw", "nosuid", "nodev", "noexec", "relatime", "size=4096k", "mode=700"}, 2120 }, 2121 } 2122 2123 cli, err := client.NewEnvClient() 2124 c.Assert(err, checker.IsNil) 2125 defer cli.Close() 2126 2127 config := containertypes.Config{ 2128 Image: "busybox", 2129 Cmd: []string{"/bin/sh", "-c", fmt.Sprintf("mount | grep 'tmpfs on %s'", target)}, 2130 } 2131 for i, x := range cases { 2132 cName := fmt.Sprintf("test-tmpfs-%d", i) 2133 hostConfig := containertypes.HostConfig{ 2134 Mounts: []mounttypes.Mount{x.cfg}, 2135 } 2136 2137 _, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, cName) 2138 c.Assert(err, checker.IsNil) 2139 out, _ := dockerCmd(c, "start", "-a", cName) 2140 for _, option := range x.expectedOptions { 2141 c.Assert(out, checker.Contains, option) 2142 } 2143 } 2144 } 2145 2146 // Regression test for #33334 2147 // Makes sure that when a container which has a custom stop signal + restart=always 2148 // gets killed (with SIGKILL) by the kill API, that the restart policy is cancelled. 2149 func (s *DockerSuite) TestContainerKillCustomStopSignal(c *check.C) { 2150 id := strings.TrimSpace(runSleepingContainer(c, "--stop-signal=SIGTERM", "--restart=always")) 2151 res, _, err := request.Post("/containers/" + id + "/kill") 2152 c.Assert(err, checker.IsNil) 2153 defer res.Body.Close() 2154 2155 b, err := ioutil.ReadAll(res.Body) 2156 c.Assert(res.StatusCode, checker.Equals, http.StatusNoContent, check.Commentf(string(b))) 2157 err = waitInspect(id, "{{.State.Running}} {{.State.Restarting}}", "false false", 30*time.Second) 2158 c.Assert(err, checker.IsNil) 2159 }