github.com/pritambaral/docker@v1.4.2-0.20150120174542-b2fe1b3dd952/integration-cli/docker_cli_run_test.go (about) 1 package main 2 3 import ( 4 "bufio" 5 "bytes" 6 "fmt" 7 "io/ioutil" 8 "net" 9 "os" 10 "os/exec" 11 "path" 12 "path/filepath" 13 "reflect" 14 "regexp" 15 "sort" 16 "strconv" 17 "strings" 18 "sync" 19 "testing" 20 "time" 21 22 "github.com/docker/docker/nat" 23 "github.com/docker/docker/pkg/networkfs/resolvconf" 24 ) 25 26 // "test123" should be printed by docker run 27 func TestRunEchoStdout(t *testing.T) { 28 runCmd := exec.Command(dockerBinary, "run", "busybox", "echo", "test123") 29 out, _, _, err := runCommandWithStdoutStderr(runCmd) 30 if err != nil { 31 t.Fatalf("failed to run container: %v, output: %q", err, out) 32 } 33 34 if out != "test123\n" { 35 t.Errorf("container should've printed 'test123'") 36 } 37 38 deleteAllContainers() 39 40 logDone("run - echo test123") 41 } 42 43 // "test" should be printed 44 func TestRunEchoStdoutWithMemoryLimit(t *testing.T) { 45 runCmd := exec.Command(dockerBinary, "run", "-m", "16m", "busybox", "echo", "test") 46 out, _, _, err := runCommandWithStdoutStderr(runCmd) 47 if err != nil { 48 t.Fatalf("failed to run container: %v, output: %q", err, out) 49 } 50 51 out = strings.Trim(out, "\r\n") 52 53 if expected := "test"; out != expected { 54 t.Errorf("container should've printed %q but printed %q", expected, out) 55 56 } 57 58 deleteAllContainers() 59 60 logDone("run - echo with memory limit") 61 } 62 63 // "test" should be printed 64 func TestRunEchoStdoutWitCPULimit(t *testing.T) { 65 runCmd := exec.Command(dockerBinary, "run", "-c", "1000", "busybox", "echo", "test") 66 out, _, _, err := runCommandWithStdoutStderr(runCmd) 67 if err != nil { 68 t.Fatalf("failed to run container: %v, output: %q", err, out) 69 } 70 71 if out != "test\n" { 72 t.Errorf("container should've printed 'test'") 73 } 74 75 deleteAllContainers() 76 77 logDone("run - echo with CPU limit") 78 } 79 80 // "test" should be printed 81 func TestRunEchoStdoutWithCPUAndMemoryLimit(t *testing.T) { 82 runCmd := exec.Command(dockerBinary, "run", "-c", "1000", "-m", "16m", "busybox", "echo", "test") 83 out, _, _, err := runCommandWithStdoutStderr(runCmd) 84 if err != nil { 85 t.Fatalf("failed to run container: %v, output: %q", err, out) 86 } 87 88 if out != "test\n" { 89 t.Errorf("container should've printed 'test', got %q instead", out) 90 } 91 92 deleteAllContainers() 93 94 logDone("run - echo with CPU and memory limit") 95 } 96 97 // "test" should be printed 98 func TestRunEchoNamedContainer(t *testing.T) { 99 runCmd := exec.Command(dockerBinary, "run", "--name", "testfoonamedcontainer", "busybox", "echo", "test") 100 out, _, _, err := runCommandWithStdoutStderr(runCmd) 101 if err != nil { 102 t.Fatalf("failed to run container: %v, output: %q", err, out) 103 } 104 105 if out != "test\n" { 106 t.Errorf("container should've printed 'test'") 107 } 108 109 if err := deleteContainer("testfoonamedcontainer"); err != nil { 110 t.Errorf("failed to remove the named container: %v", err) 111 } 112 113 deleteAllContainers() 114 115 logDone("run - echo with named container") 116 } 117 118 // docker run should not leak file descriptors 119 func TestRunLeakyFileDescriptors(t *testing.T) { 120 runCmd := exec.Command(dockerBinary, "run", "busybox", "ls", "-C", "/proc/self/fd") 121 out, _, _, err := runCommandWithStdoutStderr(runCmd) 122 if err != nil { 123 t.Fatalf("failed to run container: %v, output: %q", err, out) 124 } 125 126 // normally, we should only get 0, 1, and 2, but 3 gets created by "ls" when it does "opendir" on the "fd" directory 127 if out != "0 1 2 3\n" { 128 t.Errorf("container should've printed '0 1 2 3', not: %s", out) 129 } 130 131 deleteAllContainers() 132 133 logDone("run - check file descriptor leakage") 134 } 135 136 // it should be possible to ping Google DNS resolver 137 // this will fail when Internet access is unavailable 138 func TestRunPingGoogle(t *testing.T) { 139 runCmd := exec.Command(dockerBinary, "run", "busybox", "ping", "-c", "1", "8.8.8.8") 140 out, _, _, err := runCommandWithStdoutStderr(runCmd) 141 if err != nil { 142 t.Fatalf("failed to run container: %v, output: %q", err, out) 143 } 144 145 deleteAllContainers() 146 147 logDone("run - ping 8.8.8.8") 148 } 149 150 // the exit code should be 0 151 // some versions of lxc might make this test fail 152 func TestRunExitCodeZero(t *testing.T) { 153 runCmd := exec.Command(dockerBinary, "run", "busybox", "true") 154 if out, _, err := runCommandWithOutput(runCmd); err != nil { 155 t.Errorf("container should've exited with exit code 0: %s, %v", out, err) 156 } 157 158 deleteAllContainers() 159 160 logDone("run - exit with 0") 161 } 162 163 // the exit code should be 1 164 // some versions of lxc might make this test fail 165 func TestRunExitCodeOne(t *testing.T) { 166 runCmd := exec.Command(dockerBinary, "run", "busybox", "false") 167 exitCode, err := runCommand(runCmd) 168 if err != nil && !strings.Contains("exit status 1", fmt.Sprintf("%s", err)) { 169 t.Fatal(err) 170 } 171 if exitCode != 1 { 172 t.Errorf("container should've exited with exit code 1") 173 } 174 175 deleteAllContainers() 176 177 logDone("run - exit with 1") 178 } 179 180 // it should be possible to pipe in data via stdin to a process running in a container 181 // some versions of lxc might make this test fail 182 func TestRunStdinPipe(t *testing.T) { 183 runCmd := exec.Command("bash", "-c", `echo "blahblah" | docker run -i -a stdin busybox cat`) 184 out, _, _, err := runCommandWithStdoutStderr(runCmd) 185 if err != nil { 186 t.Fatalf("failed to run container: %v, output: %q", err, out) 187 } 188 189 out = stripTrailingCharacters(out) 190 191 inspectCmd := exec.Command(dockerBinary, "inspect", out) 192 if out, _, err := runCommandWithOutput(inspectCmd); err != nil { 193 t.Fatalf("out should've been a container id: %s %v", out, err) 194 } 195 196 waitCmd := exec.Command(dockerBinary, "wait", out) 197 if waitOut, _, err := runCommandWithOutput(waitCmd); err != nil { 198 t.Fatalf("error thrown while waiting for container: %s, %v", waitOut, err) 199 } 200 201 logsCmd := exec.Command(dockerBinary, "logs", out) 202 logsOut, _, err := runCommandWithOutput(logsCmd) 203 if err != nil { 204 t.Fatalf("error thrown while trying to get container logs: %s, %v", logsOut, err) 205 } 206 207 containerLogs := stripTrailingCharacters(logsOut) 208 209 if containerLogs != "blahblah" { 210 t.Errorf("logs didn't print the container's logs %s", containerLogs) 211 } 212 213 rmCmd := exec.Command(dockerBinary, "rm", out) 214 if out, _, err = runCommandWithOutput(rmCmd); err != nil { 215 t.Fatalf("rm failed to remove container: %s, %v", out, err) 216 } 217 218 deleteAllContainers() 219 220 logDone("run - pipe in with -i -a stdin") 221 } 222 223 // the container's ID should be printed when starting a container in detached mode 224 func TestRunDetachedContainerIDPrinting(t *testing.T) { 225 runCmd := exec.Command(dockerBinary, "run", "-d", "busybox", "true") 226 out, _, _, err := runCommandWithStdoutStderr(runCmd) 227 if err != nil { 228 t.Fatalf("failed to run container: %v, output: %q", err, out) 229 } 230 231 out = stripTrailingCharacters(out) 232 233 inspectCmd := exec.Command(dockerBinary, "inspect", out) 234 if inspectOut, _, err := runCommandWithOutput(inspectCmd); err != nil { 235 t.Fatalf("out should've been a container id: %s %v", inspectOut, err) 236 } 237 238 waitCmd := exec.Command(dockerBinary, "wait", out) 239 if waitOut, _, err := runCommandWithOutput(waitCmd); err != nil { 240 t.Fatalf("error thrown while waiting for container: %s, %v", waitOut, err) 241 } 242 243 rmCmd := exec.Command(dockerBinary, "rm", out) 244 rmOut, _, err := runCommandWithOutput(rmCmd) 245 if err != nil { 246 t.Fatalf("rm failed to remove container: %s, %v", rmOut, err) 247 } 248 249 rmOut = stripTrailingCharacters(rmOut) 250 if rmOut != out { 251 t.Errorf("rm didn't print the container ID %s %s", out, rmOut) 252 } 253 254 deleteAllContainers() 255 256 logDone("run - print container ID in detached mode") 257 } 258 259 // the working directory should be set correctly 260 func TestRunWorkingDirectory(t *testing.T) { 261 runCmd := exec.Command(dockerBinary, "run", "-w", "/root", "busybox", "pwd") 262 out, _, _, err := runCommandWithStdoutStderr(runCmd) 263 if err != nil { 264 t.Fatalf("failed to run container: %v, output: %q", err, out) 265 } 266 267 out = stripTrailingCharacters(out) 268 269 if out != "/root" { 270 t.Errorf("-w failed to set working directory") 271 } 272 273 runCmd = exec.Command(dockerBinary, "run", "--workdir", "/root", "busybox", "pwd") 274 out, _, _, err = runCommandWithStdoutStderr(runCmd) 275 if err != nil { 276 t.Fatal(out, err) 277 } 278 279 out = stripTrailingCharacters(out) 280 281 if out != "/root" { 282 t.Errorf("--workdir failed to set working directory") 283 } 284 285 deleteAllContainers() 286 287 logDone("run - run with working directory set by -w") 288 logDone("run - run with working directory set by --workdir") 289 } 290 291 // pinging Google's DNS resolver should fail when we disable the networking 292 func TestRunWithoutNetworking(t *testing.T) { 293 runCmd := exec.Command(dockerBinary, "run", "--net=none", "busybox", "ping", "-c", "1", "8.8.8.8") 294 out, _, exitCode, err := runCommandWithStdoutStderr(runCmd) 295 if err != nil && exitCode != 1 { 296 t.Fatal(out, err) 297 } 298 if exitCode != 1 { 299 t.Errorf("--net=none should've disabled the network; the container shouldn't have been able to ping 8.8.8.8") 300 } 301 302 runCmd = exec.Command(dockerBinary, "run", "-n=false", "busybox", "ping", "-c", "1", "8.8.8.8") 303 out, _, exitCode, err = runCommandWithStdoutStderr(runCmd) 304 if err != nil && exitCode != 1 { 305 t.Fatal(out, err) 306 } 307 if exitCode != 1 { 308 t.Errorf("-n=false should've disabled the network; the container shouldn't have been able to ping 8.8.8.8") 309 } 310 311 deleteAllContainers() 312 313 logDone("run - disable networking with --net=none") 314 logDone("run - disable networking with -n=false") 315 } 316 317 //test --link use container name to link target 318 func TestRunLinksContainerWithContainerName(t *testing.T) { 319 cmd := exec.Command(dockerBinary, "run", "-i", "-t", "-d", "--name", "parent", "busybox") 320 out, _, _, err := runCommandWithStdoutStderr(cmd) 321 if err != nil { 322 t.Fatalf("failed to run container: %v, output: %q", err, out) 323 } 324 cmd = exec.Command(dockerBinary, "inspect", "-f", "{{.NetworkSettings.IPAddress}}", "parent") 325 ip, _, _, err := runCommandWithStdoutStderr(cmd) 326 if err != nil { 327 t.Fatalf("failed to inspect container: %v, output: %q", err, ip) 328 } 329 ip = strings.TrimSpace(ip) 330 cmd = exec.Command(dockerBinary, "run", "--link", "parent:test", "busybox", "/bin/cat", "/etc/hosts") 331 out, _, err = runCommandWithOutput(cmd) 332 if err != nil { 333 t.Fatalf("failed to run container: %v, output: %q", err, out) 334 } 335 if !strings.Contains(out, ip+" test") { 336 t.Fatalf("use a container name to link target failed") 337 } 338 deleteAllContainers() 339 340 logDone("run - use a container name to link target work") 341 } 342 343 //test --link use container id to link target 344 func TestRunLinksContainerWithContainerId(t *testing.T) { 345 cmd := exec.Command(dockerBinary, "run", "-i", "-t", "-d", "busybox") 346 cID, _, _, err := runCommandWithStdoutStderr(cmd) 347 if err != nil { 348 t.Fatalf("failed to run container: %v, output: %q", err, cID) 349 } 350 cID = strings.TrimSpace(cID) 351 cmd = exec.Command(dockerBinary, "inspect", "-f", "{{.NetworkSettings.IPAddress}}", cID) 352 ip, _, _, err := runCommandWithStdoutStderr(cmd) 353 if err != nil { 354 t.Fatalf("faild to inspect container: %v, output: %q", err, ip) 355 } 356 ip = strings.TrimSpace(ip) 357 cmd = exec.Command(dockerBinary, "run", "--link", cID+":test", "busybox", "/bin/cat", "/etc/hosts") 358 out, _, err := runCommandWithOutput(cmd) 359 if err != nil { 360 t.Fatalf("failed to run container: %v, output: %q", err, out) 361 } 362 if !strings.Contains(out, ip+" test") { 363 t.Fatalf("use a container id to link target failed") 364 } 365 366 deleteAllContainers() 367 368 logDone("run - use a container id to link target work") 369 } 370 371 // Regression test for #4741 372 func TestRunWithVolumesAsFiles(t *testing.T) { 373 runCmd := exec.Command(dockerBinary, "run", "--name", "test-data", "--volume", "/etc/hosts:/target-file", "busybox", "true") 374 out, stderr, exitCode, err := runCommandWithStdoutStderr(runCmd) 375 if err != nil && exitCode != 0 { 376 t.Fatal("1", out, stderr, err) 377 } 378 379 runCmd = exec.Command(dockerBinary, "run", "--volumes-from", "test-data", "busybox", "cat", "/target-file") 380 out, stderr, exitCode, err = runCommandWithStdoutStderr(runCmd) 381 if err != nil && exitCode != 0 { 382 t.Fatal("2", out, stderr, err) 383 } 384 deleteAllContainers() 385 386 logDone("run - regression test for #4741 - volumes from as files") 387 } 388 389 // Regression test for #4979 390 func TestRunWithVolumesFromExited(t *testing.T) { 391 runCmd := exec.Command(dockerBinary, "run", "--name", "test-data", "--volume", "/some/dir", "busybox", "touch", "/some/dir/file") 392 out, stderr, exitCode, err := runCommandWithStdoutStderr(runCmd) 393 if err != nil && exitCode != 0 { 394 t.Fatal("1", out, stderr, err) 395 } 396 397 runCmd = exec.Command(dockerBinary, "run", "--volumes-from", "test-data", "busybox", "cat", "/some/dir/file") 398 out, stderr, exitCode, err = runCommandWithStdoutStderr(runCmd) 399 if err != nil && exitCode != 0 { 400 t.Fatal("2", out, stderr, err) 401 } 402 deleteAllContainers() 403 404 logDone("run - regression test for #4979 - volumes-from on exited container") 405 } 406 407 // Regression test for #4830 408 func TestRunWithRelativePath(t *testing.T) { 409 runCmd := exec.Command(dockerBinary, "run", "-v", "tmp:/other-tmp", "busybox", "true") 410 if _, _, _, err := runCommandWithStdoutStderr(runCmd); err == nil { 411 t.Fatalf("relative path should result in an error") 412 } 413 414 deleteAllContainers() 415 416 logDone("run - volume with relative path") 417 } 418 419 func TestRunVolumesMountedAsReadonly(t *testing.T) { 420 cmd := exec.Command(dockerBinary, "run", "-v", "/test:/test:ro", "busybox", "touch", "/test/somefile") 421 if code, err := runCommand(cmd); err == nil || code == 0 { 422 t.Fatalf("run should fail because volume is ro: exit code %d", code) 423 } 424 425 deleteAllContainers() 426 427 logDone("run - volumes as readonly mount") 428 } 429 430 func TestRunVolumesFromInReadonlyMode(t *testing.T) { 431 defer deleteAllContainers() 432 cmd := exec.Command(dockerBinary, "run", "--name", "parent", "-v", "/test", "busybox", "true") 433 if _, err := runCommand(cmd); err != nil { 434 t.Fatal(err) 435 } 436 437 cmd = exec.Command(dockerBinary, "run", "--volumes-from", "parent:ro", "busybox", "touch", "/test/file") 438 if code, err := runCommand(cmd); err == nil || code == 0 { 439 t.Fatalf("run should fail because volume is ro: exit code %d", code) 440 } 441 442 logDone("run - volumes from as readonly mount") 443 } 444 445 // Regression test for #1201 446 func TestRunVolumesFromInReadWriteMode(t *testing.T) { 447 defer deleteAllContainers() 448 cmd := exec.Command(dockerBinary, "run", "--name", "parent", "-v", "/test", "busybox", "true") 449 if _, err := runCommand(cmd); err != nil { 450 t.Fatal(err) 451 } 452 453 cmd = exec.Command(dockerBinary, "run", "--volumes-from", "parent:rw", "busybox", "touch", "/test/file") 454 if out, _, err := runCommandWithOutput(cmd); err != nil { 455 t.Fatalf("running --volumes-from parent:rw failed with output: %q\nerror: %v", out, err) 456 } 457 458 cmd = exec.Command(dockerBinary, "run", "--volumes-from", "parent:bar", "busybox", "touch", "/test/file") 459 if out, _, err := runCommandWithOutput(cmd); err == nil || !strings.Contains(out, "invalid mode for volumes-from: bar") { 460 t.Fatalf("running --volumes-from foo:bar should have failed with invalid mount mode: %q", out) 461 } 462 463 cmd = exec.Command(dockerBinary, "run", "--volumes-from", "parent", "busybox", "touch", "/test/file") 464 if out, _, err := runCommandWithOutput(cmd); err != nil { 465 t.Fatalf("running --volumes-from parent failed with output: %q\nerror: %v", out, err) 466 } 467 468 logDone("run - volumes from as read write mount") 469 } 470 471 func TestVolumesFromGetsProperMode(t *testing.T) { 472 defer deleteAllContainers() 473 cmd := exec.Command(dockerBinary, "run", "--name", "parent", "-v", "/test:/test:ro", "busybox", "true") 474 if _, err := runCommand(cmd); err != nil { 475 t.Fatal(err) 476 } 477 // Expect this "rw" mode to be be ignored since the inheritted volume is "ro" 478 cmd = exec.Command(dockerBinary, "run", "--volumes-from", "parent:rw", "busybox", "touch", "/test/file") 479 if _, err := runCommand(cmd); err == nil { 480 t.Fatal("Expected volumes-from to inherit read-only volume even when passing in `rw`") 481 } 482 483 cmd = exec.Command(dockerBinary, "run", "--name", "parent2", "-v", "/test:/test:ro", "busybox", "true") 484 if _, err := runCommand(cmd); err != nil { 485 t.Fatal(err) 486 } 487 // Expect this to be read-only since both are "ro" 488 cmd = exec.Command(dockerBinary, "run", "--volumes-from", "parent2:ro", "busybox", "touch", "/test/file") 489 if _, err := runCommand(cmd); err == nil { 490 t.Fatal("Expected volumes-from to inherit read-only volume even when passing in `ro`") 491 } 492 493 logDone("run - volumes from ignores `rw` if inherrited volume is `ro`") 494 } 495 496 // Test for #1351 497 func TestRunApplyVolumesFromBeforeVolumes(t *testing.T) { 498 cmd := exec.Command(dockerBinary, "run", "--name", "parent", "-v", "/test", "busybox", "touch", "/test/foo") 499 if _, err := runCommand(cmd); err != nil { 500 t.Fatal(err) 501 } 502 503 cmd = exec.Command(dockerBinary, "run", "--volumes-from", "parent", "-v", "/test", "busybox", "cat", "/test/foo") 504 if out, _, err := runCommandWithOutput(cmd); err != nil { 505 t.Fatal(out, err) 506 } 507 508 deleteAllContainers() 509 510 logDone("run - volumes from mounted first") 511 } 512 513 func TestRunMultipleVolumesFrom(t *testing.T) { 514 cmd := exec.Command(dockerBinary, "run", "--name", "parent1", "-v", "/test", "busybox", "touch", "/test/foo") 515 if _, err := runCommand(cmd); err != nil { 516 t.Fatal(err) 517 } 518 519 cmd = exec.Command(dockerBinary, "run", "--name", "parent2", "-v", "/other", "busybox", "touch", "/other/bar") 520 if _, err := runCommand(cmd); err != nil { 521 t.Fatal(err) 522 } 523 524 cmd = exec.Command(dockerBinary, "run", "--volumes-from", "parent1", "--volumes-from", "parent2", 525 "busybox", "sh", "-c", "cat /test/foo && cat /other/bar") 526 if _, err := runCommand(cmd); err != nil { 527 t.Fatal(err) 528 } 529 530 deleteAllContainers() 531 532 logDone("run - multiple volumes from") 533 } 534 535 // this tests verifies the ID format for the container 536 func TestRunVerifyContainerID(t *testing.T) { 537 cmd := exec.Command(dockerBinary, "run", "-d", "busybox", "true") 538 out, exit, err := runCommandWithOutput(cmd) 539 if err != nil { 540 t.Fatal(err) 541 } 542 if exit != 0 { 543 t.Fatalf("expected exit code 0 received %d", exit) 544 } 545 match, err := regexp.MatchString("^[0-9a-f]{64}$", strings.TrimSuffix(out, "\n")) 546 if err != nil { 547 t.Fatal(err) 548 } 549 if !match { 550 t.Fatalf("Invalid container ID: %s", out) 551 } 552 553 deleteAllContainers() 554 555 logDone("run - verify container ID") 556 } 557 558 // Test that creating a container with a volume doesn't crash. Regression test for #995. 559 func TestRunCreateVolume(t *testing.T) { 560 cmd := exec.Command(dockerBinary, "run", "-v", "/var/lib/data", "busybox", "true") 561 if _, err := runCommand(cmd); err != nil { 562 t.Fatal(err) 563 } 564 565 deleteAllContainers() 566 567 logDone("run - create docker managed volume") 568 } 569 570 // Test that creating a volume with a symlink in its path works correctly. Test for #5152. 571 // Note that this bug happens only with symlinks with a target that starts with '/'. 572 func TestRunCreateVolumeWithSymlink(t *testing.T) { 573 buildCmd := exec.Command(dockerBinary, "build", "-t", "docker-test-createvolumewithsymlink", "-") 574 buildCmd.Stdin = strings.NewReader(`FROM busybox 575 RUN mkdir /foo && ln -s /foo /bar`) 576 buildCmd.Dir = workingDirectory 577 err := buildCmd.Run() 578 if err != nil { 579 t.Fatalf("could not build 'docker-test-createvolumewithsymlink': %v", err) 580 } 581 582 cmd := exec.Command(dockerBinary, "run", "-v", "/bar/foo", "--name", "test-createvolumewithsymlink", "docker-test-createvolumewithsymlink", "sh", "-c", "mount | grep -q /foo/foo") 583 exitCode, err := runCommand(cmd) 584 if err != nil || exitCode != 0 { 585 t.Fatalf("[run] err: %v, exitcode: %d", err, exitCode) 586 } 587 588 var volPath string 589 cmd = exec.Command(dockerBinary, "inspect", "-f", "{{range .Volumes}}{{.}}{{end}}", "test-createvolumewithsymlink") 590 volPath, exitCode, err = runCommandWithOutput(cmd) 591 if err != nil || exitCode != 0 { 592 t.Fatalf("[inspect] err: %v, exitcode: %d", err, exitCode) 593 } 594 595 cmd = exec.Command(dockerBinary, "rm", "-v", "test-createvolumewithsymlink") 596 exitCode, err = runCommand(cmd) 597 if err != nil || exitCode != 0 { 598 t.Fatalf("[rm] err: %v, exitcode: %d", err, exitCode) 599 } 600 601 f, err := os.Open(volPath) 602 defer f.Close() 603 if !os.IsNotExist(err) { 604 t.Fatalf("[open] (expecting 'file does not exist' error) err: %v, volPath: %s", err, volPath) 605 } 606 607 deleteImages("docker-test-createvolumewithsymlink") 608 deleteAllContainers() 609 610 logDone("run - create volume with symlink") 611 } 612 613 // Tests that a volume path that has a symlink exists in a container mounting it with `--volumes-from`. 614 func TestRunVolumesFromSymlinkPath(t *testing.T) { 615 name := "docker-test-volumesfromsymlinkpath" 616 buildCmd := exec.Command(dockerBinary, "build", "-t", name, "-") 617 buildCmd.Stdin = strings.NewReader(`FROM busybox 618 RUN mkdir /baz && ln -s /baz /foo 619 VOLUME ["/foo/bar"]`) 620 buildCmd.Dir = workingDirectory 621 err := buildCmd.Run() 622 if err != nil { 623 t.Fatalf("could not build 'docker-test-volumesfromsymlinkpath': %v", err) 624 } 625 626 cmd := exec.Command(dockerBinary, "run", "--name", "test-volumesfromsymlinkpath", name) 627 exitCode, err := runCommand(cmd) 628 if err != nil || exitCode != 0 { 629 t.Fatalf("[run] (volume) err: %v, exitcode: %d", err, exitCode) 630 } 631 632 cmd = exec.Command(dockerBinary, "run", "--volumes-from", "test-volumesfromsymlinkpath", "busybox", "sh", "-c", "ls /foo | grep -q bar") 633 exitCode, err = runCommand(cmd) 634 if err != nil || exitCode != 0 { 635 t.Fatalf("[run] err: %v, exitcode: %d", err, exitCode) 636 } 637 638 deleteAllContainers() 639 deleteImages(name) 640 641 logDone("run - volumes-from symlink path") 642 } 643 644 func TestRunExitCode(t *testing.T) { 645 cmd := exec.Command(dockerBinary, "run", "busybox", "/bin/sh", "-c", "exit 72") 646 647 exit, err := runCommand(cmd) 648 if err == nil { 649 t.Fatal("should not have a non nil error") 650 } 651 if exit != 72 { 652 t.Fatalf("expected exit code 72 received %d", exit) 653 } 654 655 deleteAllContainers() 656 657 logDone("run - correct exit code") 658 } 659 660 func TestRunUserDefaultsToRoot(t *testing.T) { 661 cmd := exec.Command(dockerBinary, "run", "busybox", "id") 662 663 out, _, err := runCommandWithOutput(cmd) 664 if err != nil { 665 t.Fatal(err, out) 666 } 667 if !strings.Contains(out, "uid=0(root) gid=0(root)") { 668 t.Fatalf("expected root user got %s", out) 669 } 670 deleteAllContainers() 671 672 logDone("run - default user") 673 } 674 675 func TestRunUserByName(t *testing.T) { 676 cmd := exec.Command(dockerBinary, "run", "-u", "root", "busybox", "id") 677 678 out, _, err := runCommandWithOutput(cmd) 679 if err != nil { 680 t.Fatal(err, out) 681 } 682 if !strings.Contains(out, "uid=0(root) gid=0(root)") { 683 t.Fatalf("expected root user got %s", out) 684 } 685 deleteAllContainers() 686 687 logDone("run - user by name") 688 } 689 690 func TestRunUserByID(t *testing.T) { 691 cmd := exec.Command(dockerBinary, "run", "-u", "1", "busybox", "id") 692 693 out, _, err := runCommandWithOutput(cmd) 694 if err != nil { 695 t.Fatal(err, out) 696 } 697 if !strings.Contains(out, "uid=1(daemon) gid=1(daemon)") { 698 t.Fatalf("expected daemon user got %s", out) 699 } 700 deleteAllContainers() 701 702 logDone("run - user by id") 703 } 704 705 func TestRunUserByIDBig(t *testing.T) { 706 cmd := exec.Command(dockerBinary, "run", "-u", "2147483648", "busybox", "id") 707 708 out, _, err := runCommandWithOutput(cmd) 709 if err == nil { 710 t.Fatal("No error, but must be.", out) 711 } 712 if !strings.Contains(out, "Uids and gids must be in range") { 713 t.Fatalf("expected error about uids range, got %s", out) 714 } 715 deleteAllContainers() 716 717 logDone("run - user by id, id too big") 718 } 719 720 func TestRunUserByIDNegative(t *testing.T) { 721 cmd := exec.Command(dockerBinary, "run", "-u", "-1", "busybox", "id") 722 723 out, _, err := runCommandWithOutput(cmd) 724 if err == nil { 725 t.Fatal("No error, but must be.", out) 726 } 727 if !strings.Contains(out, "Uids and gids must be in range") { 728 t.Fatalf("expected error about uids range, got %s", out) 729 } 730 deleteAllContainers() 731 732 logDone("run - user by id, id negative") 733 } 734 735 func TestRunUserByIDZero(t *testing.T) { 736 cmd := exec.Command(dockerBinary, "run", "-u", "0", "busybox", "id") 737 738 out, _, err := runCommandWithOutput(cmd) 739 if err != nil { 740 t.Fatal(err, out) 741 } 742 if !strings.Contains(out, "uid=0(root) gid=0(root) groups=10(wheel)") { 743 t.Fatalf("expected daemon user got %s", out) 744 } 745 deleteAllContainers() 746 747 logDone("run - user by id, zero uid") 748 } 749 750 func TestRunUserNotFound(t *testing.T) { 751 cmd := exec.Command(dockerBinary, "run", "-u", "notme", "busybox", "id") 752 753 _, err := runCommand(cmd) 754 if err == nil { 755 t.Fatal("unknown user should cause container to fail") 756 } 757 deleteAllContainers() 758 759 logDone("run - user not found") 760 } 761 762 func TestRunTwoConcurrentContainers(t *testing.T) { 763 group := sync.WaitGroup{} 764 group.Add(2) 765 766 for i := 0; i < 2; i++ { 767 go func() { 768 defer group.Done() 769 cmd := exec.Command(dockerBinary, "run", "busybox", "sleep", "2") 770 if _, err := runCommand(cmd); err != nil { 771 t.Fatal(err) 772 } 773 }() 774 } 775 776 group.Wait() 777 778 deleteAllContainers() 779 780 logDone("run - two concurrent containers") 781 } 782 783 func TestRunEnvironment(t *testing.T) { 784 cmd := exec.Command(dockerBinary, "run", "-h", "testing", "-e=FALSE=true", "-e=TRUE", "-e=TRICKY", "-e=HOME=", "busybox", "env") 785 cmd.Env = append(os.Environ(), 786 "TRUE=false", 787 "TRICKY=tri\ncky\n", 788 ) 789 790 out, _, err := runCommandWithOutput(cmd) 791 if err != nil { 792 t.Fatal(err, out) 793 } 794 795 actualEnv := strings.Split(out, "\n") 796 if actualEnv[len(actualEnv)-1] == "" { 797 actualEnv = actualEnv[:len(actualEnv)-1] 798 } 799 sort.Strings(actualEnv) 800 801 goodEnv := []string{ 802 "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 803 "HOSTNAME=testing", 804 "FALSE=true", 805 "TRUE=false", 806 "TRICKY=tri", 807 "cky", 808 "", 809 "HOME=/root", 810 } 811 sort.Strings(goodEnv) 812 if len(goodEnv) != len(actualEnv) { 813 t.Fatalf("Wrong environment: should be %d variables, not: %q\n", len(goodEnv), strings.Join(actualEnv, ", ")) 814 } 815 for i := range goodEnv { 816 if actualEnv[i] != goodEnv[i] { 817 t.Fatalf("Wrong environment variable: should be %s, not %s", goodEnv[i], actualEnv[i]) 818 } 819 } 820 821 deleteAllContainers() 822 823 logDone("run - verify environment") 824 } 825 826 func TestRunContainerNetwork(t *testing.T) { 827 cmd := exec.Command(dockerBinary, "run", "busybox", "ping", "-c", "1", "127.0.0.1") 828 if _, err := runCommand(cmd); err != nil { 829 t.Fatal(err) 830 } 831 832 deleteAllContainers() 833 834 logDone("run - test container network via ping") 835 } 836 837 // Issue #4681 838 func TestRunLoopbackWhenNetworkDisabled(t *testing.T) { 839 cmd := exec.Command(dockerBinary, "run", "--net=none", "busybox", "ping", "-c", "1", "127.0.0.1") 840 if _, err := runCommand(cmd); err != nil { 841 t.Fatal(err) 842 } 843 844 deleteAllContainers() 845 846 logDone("run - test container loopback when networking disabled") 847 } 848 849 func TestRunNetHostNotAllowedWithLinks(t *testing.T) { 850 _, _, err := dockerCmd(t, "run", "--name", "linked", "busybox", "true") 851 852 cmd := exec.Command(dockerBinary, "run", "--net=host", "--link", "linked:linked", "busybox", "true") 853 _, _, err = runCommandWithOutput(cmd) 854 if err == nil { 855 t.Fatal("Expected error") 856 } 857 858 deleteAllContainers() 859 860 logDone("run - don't allow --net=host to be used with links") 861 } 862 863 func TestRunLoopbackOnlyExistsWhenNetworkingDisabled(t *testing.T) { 864 cmd := exec.Command(dockerBinary, "run", "--net=none", "busybox", "ip", "-o", "-4", "a", "show", "up") 865 out, _, err := runCommandWithOutput(cmd) 866 if err != nil { 867 t.Fatal(err, out) 868 } 869 870 var ( 871 count = 0 872 parts = strings.Split(out, "\n") 873 ) 874 875 for _, l := range parts { 876 if l != "" { 877 count++ 878 } 879 } 880 881 if count != 1 { 882 t.Fatalf("Wrong interface count in container %d", count) 883 } 884 885 if !strings.HasPrefix(out, "1: lo") { 886 t.Fatalf("Wrong interface in test container: expected [1: lo], got %s", out) 887 } 888 889 deleteAllContainers() 890 891 logDone("run - test loopback only exists when networking disabled") 892 } 893 894 // #7851 hostname outside container shows FQDN, inside only shortname 895 // For testing purposes it is not required to set host's hostname directly 896 // and use "--net=host" (as the original issue submitter did), as the same 897 // codepath is executed with "docker run -h <hostname>". Both were manually 898 // tested, but this testcase takes the simpler path of using "run -h .." 899 func TestRunFullHostnameSet(t *testing.T) { 900 cmd := exec.Command(dockerBinary, "run", "-h", "foo.bar.baz", "busybox", "hostname") 901 out, _, err := runCommandWithOutput(cmd) 902 if err != nil { 903 t.Fatal(err, out) 904 } 905 906 if actual := strings.Trim(out, "\r\n"); actual != "foo.bar.baz" { 907 t.Fatalf("expected hostname 'foo.bar.baz', received %s", actual) 908 } 909 deleteAllContainers() 910 911 logDone("run - test fully qualified hostname set with -h") 912 } 913 914 func TestRunPrivilegedCanMknod(t *testing.T) { 915 cmd := exec.Command(dockerBinary, "run", "--privileged", "busybox", "sh", "-c", "mknod /tmp/sda b 8 0 && echo ok") 916 out, _, err := runCommandWithOutput(cmd) 917 if err != nil { 918 t.Fatal(err) 919 } 920 921 if actual := strings.Trim(out, "\r\n"); actual != "ok" { 922 t.Fatalf("expected output ok received %s", actual) 923 } 924 deleteAllContainers() 925 926 logDone("run - test privileged can mknod") 927 } 928 929 func TestRunUnPrivilegedCanMknod(t *testing.T) { 930 cmd := exec.Command(dockerBinary, "run", "busybox", "sh", "-c", "mknod /tmp/sda b 8 0 && echo ok") 931 out, _, err := runCommandWithOutput(cmd) 932 if err != nil { 933 t.Fatal(err) 934 } 935 936 if actual := strings.Trim(out, "\r\n"); actual != "ok" { 937 t.Fatalf("expected output ok received %s", actual) 938 } 939 deleteAllContainers() 940 941 logDone("run - test un-privileged can mknod") 942 } 943 944 func TestRunCapDropInvalid(t *testing.T) { 945 defer deleteAllContainers() 946 cmd := exec.Command(dockerBinary, "run", "--cap-drop=CHPASS", "busybox", "ls") 947 out, _, err := runCommandWithOutput(cmd) 948 if err == nil { 949 t.Fatal(err, out) 950 } 951 952 logDone("run - test --cap-drop=CHPASS invalid") 953 } 954 955 func TestRunCapDropCannotMknod(t *testing.T) { 956 cmd := exec.Command(dockerBinary, "run", "--cap-drop=MKNOD", "busybox", "sh", "-c", "mknod /tmp/sda b 8 0 && echo ok") 957 out, _, err := runCommandWithOutput(cmd) 958 if err == nil { 959 t.Fatal(err, out) 960 } 961 962 if actual := strings.Trim(out, "\r\n"); actual == "ok" { 963 t.Fatalf("expected output not ok received %s", actual) 964 } 965 deleteAllContainers() 966 967 logDone("run - test --cap-drop=MKNOD cannot mknod") 968 } 969 970 func TestRunCapDropCannotMknodLowerCase(t *testing.T) { 971 cmd := exec.Command(dockerBinary, "run", "--cap-drop=mknod", "busybox", "sh", "-c", "mknod /tmp/sda b 8 0 && echo ok") 972 out, _, err := runCommandWithOutput(cmd) 973 if err == nil { 974 t.Fatal(err, out) 975 } 976 977 if actual := strings.Trim(out, "\r\n"); actual == "ok" { 978 t.Fatalf("expected output not ok received %s", actual) 979 } 980 deleteAllContainers() 981 982 logDone("run - test --cap-drop=mknod cannot mknod lowercase") 983 } 984 985 func TestRunCapDropALLCannotMknod(t *testing.T) { 986 cmd := exec.Command(dockerBinary, "run", "--cap-drop=ALL", "--cap-add=SETGID", "busybox", "sh", "-c", "mknod /tmp/sda b 8 0 && echo ok") 987 out, _, err := runCommandWithOutput(cmd) 988 if err == nil { 989 t.Fatal(err, out) 990 } 991 992 if actual := strings.Trim(out, "\r\n"); actual == "ok" { 993 t.Fatalf("expected output not ok received %s", actual) 994 } 995 deleteAllContainers() 996 997 logDone("run - test --cap-drop=ALL cannot mknod") 998 } 999 1000 func TestRunCapDropALLAddMknodCanMknod(t *testing.T) { 1001 cmd := exec.Command(dockerBinary, "run", "--cap-drop=ALL", "--cap-add=MKNOD", "--cap-add=SETGID", "busybox", "sh", "-c", "mknod /tmp/sda b 8 0 && echo ok") 1002 out, _, err := runCommandWithOutput(cmd) 1003 if err != nil { 1004 t.Fatal(err, out) 1005 } 1006 1007 if actual := strings.Trim(out, "\r\n"); actual != "ok" { 1008 t.Fatalf("expected output ok received %s", actual) 1009 } 1010 deleteAllContainers() 1011 1012 logDone("run - test --cap-drop=ALL --cap-add=MKNOD can mknod") 1013 } 1014 1015 func TestRunCapAddInvalid(t *testing.T) { 1016 defer deleteAllContainers() 1017 1018 cmd := exec.Command(dockerBinary, "run", "--cap-add=CHPASS", "busybox", "ls") 1019 out, _, err := runCommandWithOutput(cmd) 1020 if err == nil { 1021 t.Fatal(err, out) 1022 } 1023 1024 logDone("run - test --cap-add=CHPASS invalid") 1025 } 1026 1027 func TestRunCapAddCanDownInterface(t *testing.T) { 1028 cmd := exec.Command(dockerBinary, "run", "--cap-add=NET_ADMIN", "busybox", "sh", "-c", "ip link set eth0 down && echo ok") 1029 out, _, err := runCommandWithOutput(cmd) 1030 if err != nil { 1031 t.Fatal(err, out) 1032 } 1033 1034 if actual := strings.Trim(out, "\r\n"); actual != "ok" { 1035 t.Fatalf("expected output ok received %s", actual) 1036 } 1037 deleteAllContainers() 1038 1039 logDone("run - test --cap-add=NET_ADMIN can set eth0 down") 1040 } 1041 1042 func TestRunCapAddALLCanDownInterface(t *testing.T) { 1043 cmd := exec.Command(dockerBinary, "run", "--cap-add=ALL", "busybox", "sh", "-c", "ip link set eth0 down && echo ok") 1044 out, _, err := runCommandWithOutput(cmd) 1045 if err != nil { 1046 t.Fatal(err, out) 1047 } 1048 1049 if actual := strings.Trim(out, "\r\n"); actual != "ok" { 1050 t.Fatalf("expected output ok received %s", actual) 1051 } 1052 deleteAllContainers() 1053 1054 logDone("run - test --cap-add=ALL can set eth0 down") 1055 } 1056 1057 func TestRunCapAddALLDropNetAdminCanDownInterface(t *testing.T) { 1058 cmd := exec.Command(dockerBinary, "run", "--cap-add=ALL", "--cap-drop=NET_ADMIN", "busybox", "sh", "-c", "ip link set eth0 down && echo ok") 1059 out, _, err := runCommandWithOutput(cmd) 1060 if err == nil { 1061 t.Fatal(err, out) 1062 } 1063 1064 if actual := strings.Trim(out, "\r\n"); actual == "ok" { 1065 t.Fatalf("expected output not ok received %s", actual) 1066 } 1067 deleteAllContainers() 1068 1069 logDone("run - test --cap-add=ALL --cap-drop=NET_ADMIN cannot set eth0 down") 1070 } 1071 1072 func TestRunPrivilegedCanMount(t *testing.T) { 1073 cmd := exec.Command(dockerBinary, "run", "--privileged", "busybox", "sh", "-c", "mount -t tmpfs none /tmp && echo ok") 1074 1075 out, _, err := runCommandWithOutput(cmd) 1076 if err != nil { 1077 t.Fatal(err) 1078 } 1079 1080 if actual := strings.Trim(out, "\r\n"); actual != "ok" { 1081 t.Fatalf("expected output ok received %s", actual) 1082 } 1083 deleteAllContainers() 1084 1085 logDone("run - test privileged can mount") 1086 } 1087 1088 func TestRunUnPrivilegedCannotMount(t *testing.T) { 1089 cmd := exec.Command(dockerBinary, "run", "busybox", "sh", "-c", "mount -t tmpfs none /tmp && echo ok") 1090 1091 out, _, err := runCommandWithOutput(cmd) 1092 if err == nil { 1093 t.Fatal(err, out) 1094 } 1095 1096 if actual := strings.Trim(out, "\r\n"); actual == "ok" { 1097 t.Fatalf("expected output not ok received %s", actual) 1098 } 1099 deleteAllContainers() 1100 1101 logDone("run - test un-privileged cannot mount") 1102 } 1103 1104 func TestRunSysNotWritableInNonPrivilegedContainers(t *testing.T) { 1105 cmd := exec.Command(dockerBinary, "run", "busybox", "touch", "/sys/kernel/profiling") 1106 if code, err := runCommand(cmd); err == nil || code == 0 { 1107 t.Fatal("sys should not be writable in a non privileged container") 1108 } 1109 1110 deleteAllContainers() 1111 1112 logDone("run - sys not writable in non privileged container") 1113 } 1114 1115 func TestRunSysWritableInPrivilegedContainers(t *testing.T) { 1116 cmd := exec.Command(dockerBinary, "run", "--privileged", "busybox", "touch", "/sys/kernel/profiling") 1117 if code, err := runCommand(cmd); err != nil || code != 0 { 1118 t.Fatalf("sys should be writable in privileged container") 1119 } 1120 1121 deleteAllContainers() 1122 1123 logDone("run - sys writable in privileged container") 1124 } 1125 1126 func TestRunProcNotWritableInNonPrivilegedContainers(t *testing.T) { 1127 cmd := exec.Command(dockerBinary, "run", "busybox", "touch", "/proc/sysrq-trigger") 1128 if code, err := runCommand(cmd); err == nil || code == 0 { 1129 t.Fatal("proc should not be writable in a non privileged container") 1130 } 1131 1132 deleteAllContainers() 1133 1134 logDone("run - proc not writable in non privileged container") 1135 } 1136 1137 func TestRunProcWritableInPrivilegedContainers(t *testing.T) { 1138 cmd := exec.Command(dockerBinary, "run", "--privileged", "busybox", "touch", "/proc/sysrq-trigger") 1139 if code, err := runCommand(cmd); err != nil || code != 0 { 1140 t.Fatalf("proc should be writable in privileged container") 1141 } 1142 1143 deleteAllContainers() 1144 1145 logDone("run - proc writable in privileged container") 1146 } 1147 1148 func TestRunWithCpuset(t *testing.T) { 1149 cmd := exec.Command(dockerBinary, "run", "--cpuset", "0", "busybox", "true") 1150 if code, err := runCommand(cmd); err != nil || code != 0 { 1151 t.Fatalf("container should run successfuly with cpuset of 0: %s", err) 1152 } 1153 1154 deleteAllContainers() 1155 1156 logDone("run - cpuset 0") 1157 } 1158 1159 func TestRunDeviceNumbers(t *testing.T) { 1160 cmd := exec.Command(dockerBinary, "run", "busybox", "sh", "-c", "ls -l /dev/null") 1161 1162 out, _, err := runCommandWithOutput(cmd) 1163 if err != nil { 1164 t.Fatal(err, out) 1165 } 1166 deviceLineFields := strings.Fields(out) 1167 deviceLineFields[6] = "" 1168 deviceLineFields[7] = "" 1169 deviceLineFields[8] = "" 1170 expected := []string{"crw-rw-rw-", "1", "root", "root", "1,", "3", "", "", "", "/dev/null"} 1171 1172 if !(reflect.DeepEqual(deviceLineFields, expected)) { 1173 t.Fatalf("expected output\ncrw-rw-rw- 1 root root 1, 3 May 24 13:29 /dev/null\n received\n %s\n", out) 1174 } 1175 deleteAllContainers() 1176 1177 logDone("run - test device numbers") 1178 } 1179 1180 func TestRunThatCharacterDevicesActLikeCharacterDevices(t *testing.T) { 1181 cmd := exec.Command(dockerBinary, "run", "busybox", "sh", "-c", "dd if=/dev/zero of=/zero bs=1k count=5 2> /dev/null ; du -h /zero") 1182 1183 out, _, err := runCommandWithOutput(cmd) 1184 if err != nil { 1185 t.Fatal(err, out) 1186 } 1187 1188 if actual := strings.Trim(out, "\r\n"); actual[0] == '0' { 1189 t.Fatalf("expected a new file called /zero to be create that is greater than 0 bytes long, but du says: %s", actual) 1190 } 1191 deleteAllContainers() 1192 1193 logDone("run - test that character devices work.") 1194 } 1195 1196 func TestRunUnprivilegedWithChroot(t *testing.T) { 1197 cmd := exec.Command(dockerBinary, "run", "busybox", "chroot", "/", "true") 1198 1199 if _, err := runCommand(cmd); err != nil { 1200 t.Fatal(err) 1201 } 1202 1203 deleteAllContainers() 1204 1205 logDone("run - unprivileged with chroot") 1206 } 1207 1208 func TestRunAddingOptionalDevices(t *testing.T) { 1209 cmd := exec.Command(dockerBinary, "run", "--device", "/dev/zero:/dev/nulo", "busybox", "sh", "-c", "ls /dev/nulo") 1210 1211 out, _, err := runCommandWithOutput(cmd) 1212 if err != nil { 1213 t.Fatal(err, out) 1214 } 1215 1216 if actual := strings.Trim(out, "\r\n"); actual != "/dev/nulo" { 1217 t.Fatalf("expected output /dev/nulo, received %s", actual) 1218 } 1219 deleteAllContainers() 1220 1221 logDone("run - test --device argument") 1222 } 1223 1224 func TestRunModeHostname(t *testing.T) { 1225 cmd := exec.Command(dockerBinary, "run", "-h=testhostname", "busybox", "cat", "/etc/hostname") 1226 1227 out, _, err := runCommandWithOutput(cmd) 1228 if err != nil { 1229 t.Fatal(err, out) 1230 } 1231 1232 if actual := strings.Trim(out, "\r\n"); actual != "testhostname" { 1233 t.Fatalf("expected 'testhostname', but says: %q", actual) 1234 } 1235 1236 cmd = exec.Command(dockerBinary, "run", "--net=host", "busybox", "cat", "/etc/hostname") 1237 1238 out, _, err = runCommandWithOutput(cmd) 1239 if err != nil { 1240 t.Fatal(err, out) 1241 } 1242 hostname, err := os.Hostname() 1243 if err != nil { 1244 t.Fatal(err) 1245 } 1246 if actual := strings.Trim(out, "\r\n"); actual != hostname { 1247 t.Fatalf("expected %q, but says: %q", hostname, actual) 1248 } 1249 1250 deleteAllContainers() 1251 1252 logDone("run - hostname and several network modes") 1253 } 1254 1255 func TestRunRootWorkdir(t *testing.T) { 1256 s, _, err := dockerCmd(t, "run", "--workdir", "/", "busybox", "pwd") 1257 if err != nil { 1258 t.Fatal(s, err) 1259 } 1260 if s != "/\n" { 1261 t.Fatalf("pwd returned %q (expected /\\n)", s) 1262 } 1263 1264 deleteAllContainers() 1265 1266 logDone("run - workdir /") 1267 } 1268 1269 func TestRunAllowBindMountingRoot(t *testing.T) { 1270 s, _, err := dockerCmd(t, "run", "-v", "/:/host", "busybox", "ls", "/host") 1271 if err != nil { 1272 t.Fatal(s, err) 1273 } 1274 1275 deleteAllContainers() 1276 1277 logDone("run - bind mount / as volume") 1278 } 1279 1280 func TestRunDisallowBindMountingRootToRoot(t *testing.T) { 1281 cmd := exec.Command(dockerBinary, "run", "-v", "/:/", "busybox", "ls", "/host") 1282 out, _, err := runCommandWithOutput(cmd) 1283 if err == nil { 1284 t.Fatal(out, err) 1285 } 1286 1287 deleteAllContainers() 1288 1289 logDone("run - bind mount /:/ as volume should fail") 1290 } 1291 1292 func TestRunDnsDefaultOptions(t *testing.T) { 1293 // ci server has default resolv.conf 1294 // so rewrite it for the test 1295 origResolvConf, err := ioutil.ReadFile("/etc/resolv.conf") 1296 if os.IsNotExist(err) { 1297 t.Fatalf("/etc/resolv.conf does not exist") 1298 } 1299 1300 // test with file 1301 tmpResolvConf := []byte("nameserver 127.0.0.1") 1302 if err := ioutil.WriteFile("/etc/resolv.conf", tmpResolvConf, 0644); err != nil { 1303 t.Fatal(err) 1304 } 1305 // put the old resolvconf back 1306 defer func() { 1307 if err := ioutil.WriteFile("/etc/resolv.conf", origResolvConf, 0644); err != nil { 1308 t.Fatal(err) 1309 } 1310 }() 1311 1312 cmd := exec.Command(dockerBinary, "run", "busybox", "cat", "/etc/resolv.conf") 1313 1314 actual, _, err := runCommandWithOutput(cmd) 1315 if err != nil { 1316 t.Error(err, actual) 1317 return 1318 } 1319 1320 // check that the actual defaults are there 1321 // if we ever change the defaults from google dns, this will break 1322 expected := "\nnameserver 8.8.8.8\nnameserver 8.8.4.4" 1323 if actual != expected { 1324 t.Errorf("expected resolv.conf be: %q, but was: %q", expected, actual) 1325 return 1326 } 1327 1328 deleteAllContainers() 1329 1330 logDone("run - dns default options") 1331 } 1332 1333 func TestRunDnsOptions(t *testing.T) { 1334 cmd := exec.Command(dockerBinary, "run", "--dns=127.0.0.1", "--dns-search=mydomain", "busybox", "cat", "/etc/resolv.conf") 1335 1336 out, _, err := runCommandWithOutput(cmd) 1337 if err != nil { 1338 t.Fatal(err, out) 1339 } 1340 1341 actual := strings.Replace(strings.Trim(out, "\r\n"), "\n", " ", -1) 1342 if actual != "nameserver 127.0.0.1 search mydomain" { 1343 t.Fatalf("expected 'nameserver 127.0.0.1 search mydomain', but says: %q", actual) 1344 } 1345 1346 cmd = exec.Command(dockerBinary, "run", "--dns=127.0.0.1", "--dns-search=.", "busybox", "cat", "/etc/resolv.conf") 1347 1348 out, _, err = runCommandWithOutput(cmd) 1349 if err != nil { 1350 t.Fatal(err, out) 1351 } 1352 1353 actual = strings.Replace(strings.Trim(strings.Trim(out, "\r\n"), " "), "\n", " ", -1) 1354 if actual != "nameserver 127.0.0.1" { 1355 t.Fatalf("expected 'nameserver 127.0.0.1', but says: %q", actual) 1356 } 1357 1358 logDone("run - dns options") 1359 } 1360 1361 func TestRunDnsOptionsBasedOnHostResolvConf(t *testing.T) { 1362 var out string 1363 1364 origResolvConf, err := ioutil.ReadFile("/etc/resolv.conf") 1365 if os.IsNotExist(err) { 1366 t.Fatalf("/etc/resolv.conf does not exist") 1367 } 1368 1369 hostNamservers := resolvconf.GetNameservers(origResolvConf) 1370 hostSearch := resolvconf.GetSearchDomains(origResolvConf) 1371 1372 cmd := exec.Command(dockerBinary, "run", "--dns=127.0.0.1", "busybox", "cat", "/etc/resolv.conf") 1373 1374 if out, _, err = runCommandWithOutput(cmd); err != nil { 1375 t.Fatal(err, out) 1376 } 1377 1378 if actualNameservers := resolvconf.GetNameservers([]byte(out)); string(actualNameservers[0]) != "127.0.0.1" { 1379 t.Fatalf("expected '127.0.0.1', but says: %q", string(actualNameservers[0])) 1380 } 1381 1382 actualSearch := resolvconf.GetSearchDomains([]byte(out)) 1383 if len(actualSearch) != len(hostSearch) { 1384 t.Fatalf("expected %q search domain(s), but it has: %q", len(hostSearch), len(actualSearch)) 1385 } 1386 for i := range actualSearch { 1387 if actualSearch[i] != hostSearch[i] { 1388 t.Fatalf("expected %q domain, but says: %q", actualSearch[i], hostSearch[i]) 1389 } 1390 } 1391 1392 cmd = exec.Command(dockerBinary, "run", "--dns-search=mydomain", "busybox", "cat", "/etc/resolv.conf") 1393 1394 if out, _, err = runCommandWithOutput(cmd); err != nil { 1395 t.Fatal(err, out) 1396 } 1397 1398 actualNameservers := resolvconf.GetNameservers([]byte(out)) 1399 if len(actualNameservers) != len(hostNamservers) { 1400 t.Fatalf("expected %q nameserver(s), but it has: %q", len(hostNamservers), len(actualNameservers)) 1401 } 1402 for i := range actualNameservers { 1403 if actualNameservers[i] != hostNamservers[i] { 1404 t.Fatalf("expected %q nameserver, but says: %q", actualNameservers[i], hostNamservers[i]) 1405 } 1406 } 1407 1408 if actualSearch = resolvconf.GetSearchDomains([]byte(out)); string(actualSearch[0]) != "mydomain" { 1409 t.Fatalf("expected 'mydomain', but says: %q", string(actualSearch[0])) 1410 } 1411 1412 // test with file 1413 tmpResolvConf := []byte("search example.com\nnameserver 12.34.56.78\nnameserver 127.0.0.1") 1414 if err := ioutil.WriteFile("/etc/resolv.conf", tmpResolvConf, 0644); err != nil { 1415 t.Fatal(err) 1416 } 1417 // put the old resolvconf back 1418 defer func() { 1419 if err := ioutil.WriteFile("/etc/resolv.conf", origResolvConf, 0644); err != nil { 1420 t.Fatal(err) 1421 } 1422 }() 1423 1424 resolvConf, err := ioutil.ReadFile("/etc/resolv.conf") 1425 if os.IsNotExist(err) { 1426 t.Fatalf("/etc/resolv.conf does not exist") 1427 } 1428 1429 hostNamservers = resolvconf.GetNameservers(resolvConf) 1430 hostSearch = resolvconf.GetSearchDomains(resolvConf) 1431 1432 cmd = exec.Command(dockerBinary, "run", "busybox", "cat", "/etc/resolv.conf") 1433 1434 if out, _, err = runCommandWithOutput(cmd); err != nil { 1435 t.Fatal(err, out) 1436 } 1437 1438 if actualNameservers = resolvconf.GetNameservers([]byte(out)); string(actualNameservers[0]) != "12.34.56.78" || len(actualNameservers) != 1 { 1439 t.Fatalf("expected '12.34.56.78', but has: %v", actualNameservers) 1440 } 1441 1442 actualSearch = resolvconf.GetSearchDomains([]byte(out)) 1443 if len(actualSearch) != len(hostSearch) { 1444 t.Fatalf("expected %q search domain(s), but it has: %q", len(hostSearch), len(actualSearch)) 1445 } 1446 for i := range actualSearch { 1447 if actualSearch[i] != hostSearch[i] { 1448 t.Fatalf("expected %q domain, but says: %q", actualSearch[i], hostSearch[i]) 1449 } 1450 } 1451 1452 deleteAllContainers() 1453 1454 logDone("run - dns options based on host resolv.conf") 1455 } 1456 1457 // Test the file watch notifier on docker host's /etc/resolv.conf 1458 // A go-routine is responsible for auto-updating containers which are 1459 // stopped and have an unmodified copy of resolv.conf, as well as 1460 // marking running containers as requiring an update on next restart 1461 func TestRunResolvconfUpdater(t *testing.T) { 1462 1463 tmpResolvConf := []byte("search pommesfrites.fr\nnameserver 12.34.56.78") 1464 tmpLocalhostResolvConf := []byte("nameserver 127.0.0.1") 1465 1466 //take a copy of resolv.conf for restoring after test completes 1467 resolvConfSystem, err := ioutil.ReadFile("/etc/resolv.conf") 1468 if err != nil { 1469 t.Fatal(err) 1470 } 1471 1472 //cleanup 1473 defer func() { 1474 deleteAllContainers() 1475 if err := ioutil.WriteFile("/etc/resolv.conf", resolvConfSystem, 0644); err != nil { 1476 t.Fatal(err) 1477 } 1478 }() 1479 1480 //1. test that a non-running container gets an updated resolv.conf 1481 cmd := exec.Command(dockerBinary, "run", "--name='first'", "busybox", "true") 1482 if _, err := runCommand(cmd); err != nil { 1483 t.Fatal(err) 1484 } 1485 containerID1, err := getIDByName("first") 1486 if err != nil { 1487 t.Fatal(err) 1488 } 1489 1490 // replace resolv.conf with our temporary copy 1491 bytesResolvConf := []byte(tmpResolvConf) 1492 if err := ioutil.WriteFile("/etc/resolv.conf", bytesResolvConf, 0644); err != nil { 1493 t.Fatal(err) 1494 } 1495 1496 time.Sleep(time.Second / 2) 1497 // check for update in container 1498 containerResolv, err := readContainerFile(containerID1, "resolv.conf") 1499 if err != nil { 1500 t.Fatal(err) 1501 } 1502 if !bytes.Equal(containerResolv, bytesResolvConf) { 1503 t.Fatalf("Stopped container does not have updated resolv.conf; expected %q, got %q", tmpResolvConf, string(containerResolv)) 1504 } 1505 1506 //2. test that a non-running container does not receive resolv.conf updates 1507 // if it modified the container copy of the starting point resolv.conf 1508 cmd = exec.Command(dockerBinary, "run", "--name='second'", "busybox", "sh", "-c", "echo 'search mylittlepony.com' >>/etc/resolv.conf") 1509 if _, err = runCommand(cmd); err != nil { 1510 t.Fatal(err) 1511 } 1512 containerID2, err := getIDByName("second") 1513 if err != nil { 1514 t.Fatal(err) 1515 } 1516 containerResolvHashBefore, err := readContainerFile(containerID2, "resolv.conf.hash") 1517 if err != nil { 1518 t.Fatal(err) 1519 } 1520 1521 //make a change to resolv.conf (in this case replacing our tmp copy with orig copy) 1522 if err := ioutil.WriteFile("/etc/resolv.conf", resolvConfSystem, 0644); err != nil { 1523 t.Fatal(err) 1524 } 1525 1526 time.Sleep(time.Second / 2) 1527 containerResolvHashAfter, err := readContainerFile(containerID2, "resolv.conf.hash") 1528 if err != nil { 1529 t.Fatal(err) 1530 } 1531 1532 if !bytes.Equal(containerResolvHashBefore, containerResolvHashAfter) { 1533 t.Fatalf("Stopped container with modified resolv.conf should not have been updated; expected hash: %v, new hash: %v", containerResolvHashBefore, containerResolvHashAfter) 1534 } 1535 1536 //3. test that a running container's resolv.conf is not modified while running 1537 cmd = exec.Command(dockerBinary, "run", "-d", "busybox", "top") 1538 out, _, err := runCommandWithOutput(cmd) 1539 if err != nil { 1540 t.Fatal(err) 1541 } 1542 runningContainerID := strings.TrimSpace(out) 1543 1544 containerResolvHashBefore, err = readContainerFile(runningContainerID, "resolv.conf.hash") 1545 if err != nil { 1546 t.Fatal(err) 1547 } 1548 1549 // replace resolv.conf 1550 if err := ioutil.WriteFile("/etc/resolv.conf", bytesResolvConf, 0644); err != nil { 1551 t.Fatal(err) 1552 } 1553 1554 // make sure the updater has time to run to validate we really aren't 1555 // getting updated 1556 time.Sleep(time.Second / 2) 1557 containerResolvHashAfter, err = readContainerFile(runningContainerID, "resolv.conf.hash") 1558 if err != nil { 1559 t.Fatal(err) 1560 } 1561 1562 if !bytes.Equal(containerResolvHashBefore, containerResolvHashAfter) { 1563 t.Fatalf("Running container's resolv.conf should not be updated; expected hash: %v, new hash: %v", containerResolvHashBefore, containerResolvHashAfter) 1564 } 1565 1566 //4. test that a running container's resolv.conf is updated upon restart 1567 // (the above container is still running..) 1568 cmd = exec.Command(dockerBinary, "restart", runningContainerID) 1569 if _, err = runCommand(cmd); err != nil { 1570 t.Fatal(err) 1571 } 1572 1573 // check for update in container 1574 containerResolv, err = readContainerFile(runningContainerID, "resolv.conf") 1575 if err != nil { 1576 t.Fatal(err) 1577 } 1578 if !bytes.Equal(containerResolv, bytesResolvConf) { 1579 t.Fatalf("Restarted container should have updated resolv.conf; expected %q, got %q", tmpResolvConf, string(containerResolv)) 1580 } 1581 1582 //5. test that additions of a localhost resolver are cleaned from 1583 // host resolv.conf before updating container's resolv.conf copies 1584 1585 // replace resolv.conf with a localhost-only nameserver copy 1586 bytesResolvConf = []byte(tmpLocalhostResolvConf) 1587 if err = ioutil.WriteFile("/etc/resolv.conf", bytesResolvConf, 0644); err != nil { 1588 t.Fatal(err) 1589 } 1590 1591 time.Sleep(time.Second / 2) 1592 // our first exited container ID should have been updated, but with default DNS 1593 // after the cleanup of resolv.conf found only a localhost nameserver: 1594 containerResolv, err = readContainerFile(containerID1, "resolv.conf") 1595 if err != nil { 1596 t.Fatal(err) 1597 } 1598 1599 expected := "\nnameserver 8.8.8.8\nnameserver 8.8.4.4" 1600 if !bytes.Equal(containerResolv, []byte(expected)) { 1601 t.Fatalf("Container does not have cleaned/replaced DNS in resolv.conf; expected %q, got %q", expected, string(containerResolv)) 1602 } 1603 1604 //cleanup, restore original resolv.conf happens in defer func() 1605 logDone("run - resolv.conf updater") 1606 } 1607 1608 func TestRunAddHost(t *testing.T) { 1609 defer deleteAllContainers() 1610 cmd := exec.Command(dockerBinary, "run", "--add-host=extra:86.75.30.9", "busybox", "grep", "extra", "/etc/hosts") 1611 1612 out, _, err := runCommandWithOutput(cmd) 1613 if err != nil { 1614 t.Fatal(err, out) 1615 } 1616 1617 actual := strings.Trim(out, "\r\n") 1618 if actual != "86.75.30.9\textra" { 1619 t.Fatalf("expected '86.75.30.9\textra', but says: %q", actual) 1620 } 1621 1622 logDone("run - add-host option") 1623 } 1624 1625 // Regression test for #6983 1626 func TestRunAttachStdErrOnlyTTYMode(t *testing.T) { 1627 cmd := exec.Command(dockerBinary, "run", "-t", "-a", "stderr", "busybox", "true") 1628 1629 exitCode, err := runCommand(cmd) 1630 if err != nil { 1631 t.Fatal(err) 1632 } else if exitCode != 0 { 1633 t.Fatalf("Container should have exited with error code 0") 1634 } 1635 1636 deleteAllContainers() 1637 1638 logDone("run - Attach stderr only with -t") 1639 } 1640 1641 // Regression test for #6983 1642 func TestRunAttachStdOutOnlyTTYMode(t *testing.T) { 1643 cmd := exec.Command(dockerBinary, "run", "-t", "-a", "stdout", "busybox", "true") 1644 1645 exitCode, err := runCommand(cmd) 1646 if err != nil { 1647 t.Fatal(err) 1648 } else if exitCode != 0 { 1649 t.Fatalf("Container should have exited with error code 0") 1650 } 1651 1652 deleteAllContainers() 1653 1654 logDone("run - Attach stdout only with -t") 1655 } 1656 1657 // Regression test for #6983 1658 func TestRunAttachStdOutAndErrTTYMode(t *testing.T) { 1659 cmd := exec.Command(dockerBinary, "run", "-t", "-a", "stdout", "-a", "stderr", "busybox", "true") 1660 1661 exitCode, err := runCommand(cmd) 1662 if err != nil { 1663 t.Fatal(err) 1664 } else if exitCode != 0 { 1665 t.Fatalf("Container should have exited with error code 0") 1666 } 1667 1668 deleteAllContainers() 1669 1670 logDone("run - Attach stderr and stdout with -t") 1671 } 1672 1673 func TestRunState(t *testing.T) { 1674 defer deleteAllContainers() 1675 cmd := exec.Command(dockerBinary, "run", "-d", "busybox", "top") 1676 1677 out, _, err := runCommandWithOutput(cmd) 1678 if err != nil { 1679 t.Fatal(err, out) 1680 } 1681 id := strings.TrimSpace(out) 1682 state, err := inspectField(id, "State.Running") 1683 if err != nil { 1684 t.Fatal(err) 1685 } 1686 if state != "true" { 1687 t.Fatal("Container state is 'not running'") 1688 } 1689 pid1, err := inspectField(id, "State.Pid") 1690 if err != nil { 1691 t.Fatal(err) 1692 } 1693 if pid1 == "0" { 1694 t.Fatal("Container state Pid 0") 1695 } 1696 1697 cmd = exec.Command(dockerBinary, "stop", id) 1698 out, _, err = runCommandWithOutput(cmd) 1699 if err != nil { 1700 t.Fatal(err, out) 1701 } 1702 state, err = inspectField(id, "State.Running") 1703 if err != nil { 1704 t.Fatal(err) 1705 } 1706 if state != "false" { 1707 t.Fatal("Container state is 'running'") 1708 } 1709 pid2, err := inspectField(id, "State.Pid") 1710 if err != nil { 1711 t.Fatal(err) 1712 } 1713 if pid2 == pid1 { 1714 t.Fatalf("Container state Pid %s, but expected %s", pid2, pid1) 1715 } 1716 1717 cmd = exec.Command(dockerBinary, "start", id) 1718 out, _, err = runCommandWithOutput(cmd) 1719 if err != nil { 1720 t.Fatal(err, out) 1721 } 1722 state, err = inspectField(id, "State.Running") 1723 if err != nil { 1724 t.Fatal(err) 1725 } 1726 if state != "true" { 1727 t.Fatal("Container state is 'not running'") 1728 } 1729 pid3, err := inspectField(id, "State.Pid") 1730 if err != nil { 1731 t.Fatal(err) 1732 } 1733 if pid3 == pid1 { 1734 t.Fatalf("Container state Pid %s, but expected %s", pid2, pid1) 1735 } 1736 logDone("run - test container state.") 1737 } 1738 1739 // Test for #1737 1740 func TestRunCopyVolumeUidGid(t *testing.T) { 1741 name := "testrunvolumesuidgid" 1742 defer deleteImages(name) 1743 defer deleteAllContainers() 1744 _, err := buildImage(name, 1745 `FROM busybox 1746 RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd 1747 RUN echo 'dockerio:x:1001:' >> /etc/group 1748 RUN mkdir -p /hello && touch /hello/test && chown dockerio.dockerio /hello`, 1749 true) 1750 if err != nil { 1751 t.Fatal(err) 1752 } 1753 1754 // Test that the uid and gid is copied from the image to the volume 1755 cmd := exec.Command(dockerBinary, "run", "--rm", "-v", "/hello", name, "sh", "-c", "ls -l / | grep hello | awk '{print $3\":\"$4}'") 1756 out, _, err := runCommandWithOutput(cmd) 1757 if err != nil { 1758 t.Fatal(err, out) 1759 } 1760 out = strings.TrimSpace(out) 1761 if out != "dockerio:dockerio" { 1762 t.Fatalf("Wrong /hello ownership: %s, expected dockerio:dockerio", out) 1763 } 1764 1765 logDone("run - copy uid/gid for volume") 1766 } 1767 1768 // Test for #1582 1769 func TestRunCopyVolumeContent(t *testing.T) { 1770 name := "testruncopyvolumecontent" 1771 defer deleteImages(name) 1772 defer deleteAllContainers() 1773 _, err := buildImage(name, 1774 `FROM busybox 1775 RUN mkdir -p /hello/local && echo hello > /hello/local/world`, 1776 true) 1777 if err != nil { 1778 t.Fatal(err) 1779 } 1780 1781 // Test that the content is copied from the image to the volume 1782 cmd := exec.Command(dockerBinary, "run", "--rm", "-v", "/hello", name, "find", "/hello") 1783 out, _, err := runCommandWithOutput(cmd) 1784 if err != nil { 1785 t.Fatal(err, out) 1786 } 1787 if !(strings.Contains(out, "/hello/local/world") && strings.Contains(out, "/hello/local")) { 1788 t.Fatal("Container failed to transfer content to volume") 1789 } 1790 logDone("run - copy volume content") 1791 } 1792 1793 func TestRunCleanupCmdOnEntrypoint(t *testing.T) { 1794 name := "testrunmdcleanuponentrypoint" 1795 defer deleteImages(name) 1796 defer deleteAllContainers() 1797 if _, err := buildImage(name, 1798 `FROM busybox 1799 ENTRYPOINT ["echo"] 1800 CMD ["testingpoint"]`, 1801 true); err != nil { 1802 t.Fatal(err) 1803 } 1804 runCmd := exec.Command(dockerBinary, "run", "--entrypoint", "whoami", name) 1805 out, exit, err := runCommandWithOutput(runCmd) 1806 if err != nil { 1807 t.Fatalf("Error: %v, out: %q", err, out) 1808 } 1809 if exit != 0 { 1810 t.Fatalf("expected exit code 0 received %d, out: %q", exit, out) 1811 } 1812 out = strings.TrimSpace(out) 1813 if out != "root" { 1814 t.Fatalf("Expected output root, got %q", out) 1815 } 1816 logDone("run - cleanup cmd on --entrypoint") 1817 } 1818 1819 // TestRunWorkdirExistsAndIsFile checks that if 'docker run -w' with existing file can be detected 1820 func TestRunWorkdirExistsAndIsFile(t *testing.T) { 1821 defer deleteAllContainers() 1822 runCmd := exec.Command(dockerBinary, "run", "-w", "/bin/cat", "busybox") 1823 out, exit, err := runCommandWithOutput(runCmd) 1824 if !(err != nil && exit == 1 && strings.Contains(out, "Cannot mkdir: /bin/cat is not a directory")) { 1825 t.Fatalf("Docker must complains about making dir, but we got out: %s, exit: %d, err: %s", out, exit, err) 1826 } 1827 logDone("run - error on existing file for workdir") 1828 } 1829 1830 func TestRunExitOnStdinClose(t *testing.T) { 1831 name := "testrunexitonstdinclose" 1832 defer deleteAllContainers() 1833 runCmd := exec.Command(dockerBinary, "run", "--name", name, "-i", "busybox", "/bin/cat") 1834 1835 stdin, err := runCmd.StdinPipe() 1836 if err != nil { 1837 t.Fatal(err) 1838 } 1839 stdout, err := runCmd.StdoutPipe() 1840 if err != nil { 1841 t.Fatal(err) 1842 } 1843 1844 if err := runCmd.Start(); err != nil { 1845 t.Fatal(err) 1846 } 1847 if _, err := stdin.Write([]byte("hello\n")); err != nil { 1848 t.Fatal(err) 1849 } 1850 1851 r := bufio.NewReader(stdout) 1852 line, err := r.ReadString('\n') 1853 if err != nil { 1854 t.Fatal(err) 1855 } 1856 line = strings.TrimSpace(line) 1857 if line != "hello" { 1858 t.Fatalf("Output should be 'hello', got '%q'", line) 1859 } 1860 if err := stdin.Close(); err != nil { 1861 t.Fatal(err) 1862 } 1863 finish := make(chan struct{}) 1864 go func() { 1865 if err := runCmd.Wait(); err != nil { 1866 t.Fatal(err) 1867 } 1868 close(finish) 1869 }() 1870 select { 1871 case <-finish: 1872 case <-time.After(1 * time.Second): 1873 t.Fatal("docker run failed to exit on stdin close") 1874 } 1875 state, err := inspectField(name, "State.Running") 1876 if err != nil { 1877 t.Fatal(err) 1878 } 1879 if state != "false" { 1880 t.Fatal("Container must be stopped after stdin closing") 1881 } 1882 logDone("run - exit on stdin closing") 1883 } 1884 1885 // Test for #2267 1886 func TestRunWriteHostsFileAndNotCommit(t *testing.T) { 1887 defer deleteAllContainers() 1888 1889 name := "writehosts" 1890 cmd := exec.Command(dockerBinary, "run", "--name", name, "busybox", "sh", "-c", "echo test2267 >> /etc/hosts && cat /etc/hosts") 1891 out, _, err := runCommandWithOutput(cmd) 1892 if err != nil { 1893 t.Fatal(err, out) 1894 } 1895 if !strings.Contains(out, "test2267") { 1896 t.Fatal("/etc/hosts should contain 'test2267'") 1897 } 1898 1899 cmd = exec.Command(dockerBinary, "diff", name) 1900 if err != nil { 1901 t.Fatal(err, out) 1902 } 1903 out, _, err = runCommandWithOutput(cmd) 1904 if err != nil { 1905 t.Fatal(err, out) 1906 } 1907 if len(strings.Trim(out, "\r\n")) != 0 { 1908 t.Fatal("diff should be empty") 1909 } 1910 1911 logDone("run - write to /etc/hosts and not commited") 1912 } 1913 1914 // Test for #2267 1915 func TestRunWriteHostnameFileAndNotCommit(t *testing.T) { 1916 defer deleteAllContainers() 1917 1918 name := "writehostname" 1919 cmd := exec.Command(dockerBinary, "run", "--name", name, "busybox", "sh", "-c", "echo test2267 >> /etc/hostname && cat /etc/hostname") 1920 out, _, err := runCommandWithOutput(cmd) 1921 if err != nil { 1922 t.Fatal(err, out) 1923 } 1924 if !strings.Contains(out, "test2267") { 1925 t.Fatal("/etc/hostname should contain 'test2267'") 1926 } 1927 1928 cmd = exec.Command(dockerBinary, "diff", name) 1929 if err != nil { 1930 t.Fatal(err, out) 1931 } 1932 out, _, err = runCommandWithOutput(cmd) 1933 if err != nil { 1934 t.Fatal(err, out) 1935 } 1936 if len(strings.Trim(out, "\r\n")) != 0 { 1937 t.Fatal("diff should be empty") 1938 } 1939 1940 logDone("run - write to /etc/hostname and not commited") 1941 } 1942 1943 // Test for #2267 1944 func TestRunWriteResolvFileAndNotCommit(t *testing.T) { 1945 defer deleteAllContainers() 1946 1947 name := "writeresolv" 1948 cmd := exec.Command(dockerBinary, "run", "--name", name, "busybox", "sh", "-c", "echo test2267 >> /etc/resolv.conf && cat /etc/resolv.conf") 1949 out, _, err := runCommandWithOutput(cmd) 1950 if err != nil { 1951 t.Fatal(err, out) 1952 } 1953 if !strings.Contains(out, "test2267") { 1954 t.Fatal("/etc/resolv.conf should contain 'test2267'") 1955 } 1956 1957 cmd = exec.Command(dockerBinary, "diff", name) 1958 if err != nil { 1959 t.Fatal(err, out) 1960 } 1961 out, _, err = runCommandWithOutput(cmd) 1962 if err != nil { 1963 t.Fatal(err, out) 1964 } 1965 if len(strings.Trim(out, "\r\n")) != 0 { 1966 t.Fatal("diff should be empty") 1967 } 1968 1969 logDone("run - write to /etc/resolv.conf and not commited") 1970 } 1971 1972 func TestRunWithBadDevice(t *testing.T) { 1973 defer deleteAllContainers() 1974 1975 name := "baddevice" 1976 cmd := exec.Command(dockerBinary, "run", "--name", name, "--device", "/etc", "busybox", "true") 1977 out, _, err := runCommandWithOutput(cmd) 1978 if err == nil { 1979 t.Fatal("Run should fail with bad device") 1980 } 1981 expected := `\"/etc\": not a device node` 1982 if !strings.Contains(out, expected) { 1983 t.Fatalf("Output should contain %q, actual out: %q", expected, out) 1984 } 1985 logDone("run - error with bad device") 1986 } 1987 1988 func TestRunEntrypoint(t *testing.T) { 1989 defer deleteAllContainers() 1990 1991 name := "entrypoint" 1992 cmd := exec.Command(dockerBinary, "run", "--name", name, "--entrypoint", "/bin/echo", "busybox", "-n", "foobar") 1993 out, _, err := runCommandWithOutput(cmd) 1994 if err != nil { 1995 t.Fatal(err, out) 1996 } 1997 expected := "foobar" 1998 if out != expected { 1999 t.Fatalf("Output should be %q, actual out: %q", expected, out) 2000 } 2001 logDone("run - entrypoint") 2002 } 2003 2004 func TestRunBindMounts(t *testing.T) { 2005 defer deleteAllContainers() 2006 2007 tmpDir, err := ioutil.TempDir("", "docker-test-container") 2008 if err != nil { 2009 t.Fatal(err) 2010 } 2011 2012 defer os.RemoveAll(tmpDir) 2013 writeFile(path.Join(tmpDir, "touch-me"), "", t) 2014 2015 // Test reading from a read-only bind mount 2016 cmd := exec.Command(dockerBinary, "run", "-v", fmt.Sprintf("%s:/tmp:ro", tmpDir), "busybox", "ls", "/tmp") 2017 out, _, err := runCommandWithOutput(cmd) 2018 if err != nil { 2019 t.Fatal(err, out) 2020 } 2021 if !strings.Contains(out, "touch-me") { 2022 t.Fatal("Container failed to read from bind mount") 2023 } 2024 2025 // test writing to bind mount 2026 cmd = exec.Command(dockerBinary, "run", "-v", fmt.Sprintf("%s:/tmp:rw", tmpDir), "busybox", "touch", "/tmp/holla") 2027 out, _, err = runCommandWithOutput(cmd) 2028 if err != nil { 2029 t.Fatal(err, out) 2030 } 2031 readFile(path.Join(tmpDir, "holla"), t) // Will fail if the file doesn't exist 2032 2033 // test mounting to an illegal destination directory 2034 cmd = exec.Command(dockerBinary, "run", "-v", fmt.Sprintf("%s:.", tmpDir), "busybox", "ls", ".") 2035 _, err = runCommand(cmd) 2036 if err == nil { 2037 t.Fatal("Container bind mounted illegal directory") 2038 } 2039 2040 // test mount a file 2041 cmd = exec.Command(dockerBinary, "run", "-v", fmt.Sprintf("%s/holla:/tmp/holla:rw", tmpDir), "busybox", "sh", "-c", "echo -n 'yotta' > /tmp/holla") 2042 _, err = runCommand(cmd) 2043 if err != nil { 2044 t.Fatal(err, out) 2045 } 2046 content := readFile(path.Join(tmpDir, "holla"), t) // Will fail if the file doesn't exist 2047 expected := "yotta" 2048 if content != expected { 2049 t.Fatalf("Output should be %q, actual out: %q", expected, content) 2050 } 2051 2052 logDone("run - bind mounts") 2053 } 2054 2055 func TestRunMutableNetworkFiles(t *testing.T) { 2056 defer deleteAllContainers() 2057 2058 for _, fn := range []string{"resolv.conf", "hosts"} { 2059 deleteAllContainers() 2060 2061 content, err := runCommandAndReadContainerFile(fn, exec.Command(dockerBinary, "run", "-d", "--name", "c1", "busybox", "sh", "-c", fmt.Sprintf("echo success >/etc/%s && top", fn))) 2062 if err != nil { 2063 t.Fatal(err) 2064 } 2065 2066 if strings.TrimSpace(string(content)) != "success" { 2067 t.Fatal("Content was not what was modified in the container", string(content)) 2068 } 2069 2070 out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "-d", "--name", "c2", "busybox", "top")) 2071 if err != nil { 2072 t.Fatal(err) 2073 } 2074 2075 contID := strings.TrimSpace(out) 2076 2077 netFilePath := containerStorageFile(contID, fn) 2078 2079 f, err := os.OpenFile(netFilePath, os.O_WRONLY|os.O_SYNC|os.O_APPEND, 0644) 2080 if err != nil { 2081 t.Fatal(err) 2082 } 2083 2084 if _, err := f.Seek(0, 0); err != nil { 2085 f.Close() 2086 t.Fatal(err) 2087 } 2088 2089 if err := f.Truncate(0); err != nil { 2090 f.Close() 2091 t.Fatal(err) 2092 } 2093 2094 if _, err := f.Write([]byte("success2\n")); err != nil { 2095 f.Close() 2096 t.Fatal(err) 2097 } 2098 f.Close() 2099 2100 res, err := exec.Command(dockerBinary, "exec", contID, "cat", "/etc/"+fn).CombinedOutput() 2101 if err != nil { 2102 t.Fatalf("Output: %s, error: %s", res, err) 2103 } 2104 if string(res) != "success2\n" { 2105 t.Fatalf("Expected content of %s: %q, got: %q", fn, "success2\n", res) 2106 } 2107 } 2108 logDone("run - mutable network files") 2109 } 2110 2111 // Ensure that CIDFile gets deleted if it's empty 2112 // Perform this test by making `docker run` fail 2113 func TestRunCidFileCleanupIfEmpty(t *testing.T) { 2114 tmpDir, err := ioutil.TempDir("", "TestRunCidFile") 2115 if err != nil { 2116 t.Fatal(err) 2117 } 2118 defer os.RemoveAll(tmpDir) 2119 tmpCidFile := path.Join(tmpDir, "cid") 2120 cmd := exec.Command(dockerBinary, "run", "--cidfile", tmpCidFile, "scratch") 2121 out, _, err := runCommandWithOutput(cmd) 2122 t.Log(out) 2123 if err == nil { 2124 t.Fatal("Run without command must fail") 2125 } 2126 2127 if _, err := os.Stat(tmpCidFile); err == nil { 2128 t.Fatalf("empty CIDFile %q should've been deleted", tmpCidFile) 2129 } 2130 deleteAllContainers() 2131 logDone("run - cleanup empty cidfile on fail") 2132 } 2133 2134 // #2098 - Docker cidFiles only contain short version of the containerId 2135 //sudo docker run --cidfile /tmp/docker_test.cid ubuntu echo "test" 2136 // TestRunCidFile tests that run --cidfile returns the longid 2137 func TestRunCidFileCheckIDLength(t *testing.T) { 2138 tmpDir, err := ioutil.TempDir("", "TestRunCidFile") 2139 if err != nil { 2140 t.Fatal(err) 2141 } 2142 tmpCidFile := path.Join(tmpDir, "cid") 2143 defer os.RemoveAll(tmpDir) 2144 cmd := exec.Command(dockerBinary, "run", "-d", "--cidfile", tmpCidFile, "busybox", "true") 2145 out, _, err := runCommandWithOutput(cmd) 2146 if err != nil { 2147 t.Fatal(err) 2148 } 2149 id := strings.TrimSpace(out) 2150 buffer, err := ioutil.ReadFile(tmpCidFile) 2151 if err != nil { 2152 t.Fatal(err) 2153 } 2154 cid := string(buffer) 2155 if len(cid) != 64 { 2156 t.Fatalf("--cidfile should be a long id, not %q", id) 2157 } 2158 if cid != id { 2159 t.Fatalf("cid must be equal to %s, got %s", id, cid) 2160 } 2161 deleteAllContainers() 2162 logDone("run - cidfile contains long id") 2163 } 2164 2165 func TestRunNetworkNotInitializedNoneMode(t *testing.T) { 2166 cmd := exec.Command(dockerBinary, "run", "-d", "--net=none", "busybox", "top") 2167 out, _, err := runCommandWithOutput(cmd) 2168 if err != nil { 2169 t.Fatal(err) 2170 } 2171 id := strings.TrimSpace(out) 2172 res, err := inspectField(id, "NetworkSettings.IPAddress") 2173 if err != nil { 2174 t.Fatal(err) 2175 } 2176 if res != "" { 2177 t.Fatalf("For 'none' mode network must not be initialized, but container got IP: %s", res) 2178 } 2179 deleteAllContainers() 2180 logDone("run - network must not be initialized in 'none' mode") 2181 } 2182 2183 func TestRunSetMacAddress(t *testing.T) { 2184 mac := "12:34:56:78:9a:bc" 2185 cmd := exec.Command("/bin/bash", "-c", dockerBinary+` run -i --rm --mac-address=`+mac+` busybox /bin/sh -c "ip link show eth0 | tail -1 | awk '{ print \$2 }'"`) 2186 out, _, err := runCommandWithOutput(cmd) 2187 if err != nil { 2188 t.Fatal(err) 2189 } 2190 actualMac := strings.TrimSpace(out) 2191 if actualMac != mac { 2192 t.Fatalf("Set MAC address with --mac-address failed. The container has an incorrect MAC address: %q, expected: %q", actualMac, mac) 2193 } 2194 2195 deleteAllContainers() 2196 logDone("run - setting MAC address with --mac-address") 2197 } 2198 2199 func TestRunInspectMacAddress(t *testing.T) { 2200 mac := "12:34:56:78:9a:bc" 2201 cmd := exec.Command(dockerBinary, "run", "-d", "--mac-address="+mac, "busybox", "top") 2202 out, _, err := runCommandWithOutput(cmd) 2203 if err != nil { 2204 t.Fatal(err) 2205 } 2206 id := strings.TrimSpace(out) 2207 inspectedMac, err := inspectField(id, "NetworkSettings.MacAddress") 2208 if err != nil { 2209 t.Fatal(err) 2210 } 2211 if inspectedMac != mac { 2212 t.Fatalf("docker inspect outputs wrong MAC address: %q, should be: %q", inspectedMac, mac) 2213 } 2214 deleteAllContainers() 2215 logDone("run - inspecting MAC address") 2216 } 2217 2218 func TestRunDeallocatePortOnMissingIptablesRule(t *testing.T) { 2219 cmd := exec.Command(dockerBinary, "run", "-d", "-p", "23:23", "busybox", "top") 2220 out, _, err := runCommandWithOutput(cmd) 2221 if err != nil { 2222 t.Fatal(err) 2223 } 2224 id := strings.TrimSpace(out) 2225 ip, err := inspectField(id, "NetworkSettings.IPAddress") 2226 if err != nil { 2227 t.Fatal(err) 2228 } 2229 iptCmd := exec.Command("iptables", "-D", "DOCKER", "-d", fmt.Sprintf("%s/32", ip), 2230 "!", "-i", "docker0", "-o", "docker0", "-p", "tcp", "-m", "tcp", "--dport", "23", "-j", "ACCEPT") 2231 out, _, err = runCommandWithOutput(iptCmd) 2232 if err != nil { 2233 t.Fatal(err, out) 2234 } 2235 if err := deleteContainer(id); err != nil { 2236 t.Fatal(err) 2237 } 2238 cmd = exec.Command(dockerBinary, "run", "-d", "-p", "23:23", "busybox", "top") 2239 out, _, err = runCommandWithOutput(cmd) 2240 if err != nil { 2241 t.Fatal(err, out) 2242 } 2243 deleteAllContainers() 2244 logDone("run - port should be deallocated even on iptables error") 2245 } 2246 2247 func TestRunPortInUse(t *testing.T) { 2248 port := "1234" 2249 l, err := net.Listen("tcp", ":"+port) 2250 if err != nil { 2251 t.Fatal(err) 2252 } 2253 defer l.Close() 2254 cmd := exec.Command(dockerBinary, "run", "-d", "-p", port+":80", "busybox", "top") 2255 out, _, err := runCommandWithOutput(cmd) 2256 if err == nil { 2257 t.Fatalf("Binding on used port must fail") 2258 } 2259 if !strings.Contains(out, "address already in use") { 2260 t.Fatalf("Out must be about \"address already in use\", got %s", out) 2261 } 2262 2263 deleteAllContainers() 2264 logDone("run - fail if port already in use") 2265 } 2266 2267 // https://github.com/docker/docker/issues/8428 2268 func TestRunPortProxy(t *testing.T) { 2269 defer deleteAllContainers() 2270 2271 port := "12345" 2272 cmd := exec.Command(dockerBinary, "run", "-d", "-p", port+":80", "busybox", "top") 2273 2274 out, _, err := runCommandWithOutput(cmd) 2275 if err != nil { 2276 t.Fatalf("Failed to run and bind port %s, output: %s, error: %s", port, out, err) 2277 } 2278 2279 // connect for 10 times here. This will trigger 10 EPIPES in the child 2280 // process and kill it when it writes to a closed stdout/stderr 2281 for i := 0; i < 10; i++ { 2282 net.Dial("tcp", fmt.Sprintf("0.0.0.0:%s", port)) 2283 } 2284 2285 listPs := exec.Command("sh", "-c", "ps ax | grep docker") 2286 out, _, err = runCommandWithOutput(listPs) 2287 if err != nil { 2288 t.Errorf("list docker process failed with output %s, error %s", out, err) 2289 } 2290 if strings.Contains(out, "docker <defunct>") { 2291 t.Errorf("Unexpected defunct docker process") 2292 } 2293 if !strings.Contains(out, "docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 12345") { 2294 t.Errorf("Failed to find docker-proxy process, got %s", out) 2295 } 2296 2297 logDone("run - proxy should work with unavailable port") 2298 } 2299 2300 // Regression test for #7792 2301 func TestRunMountOrdering(t *testing.T) { 2302 tmpDir, err := ioutil.TempDir("", "docker_nested_mount_test") 2303 if err != nil { 2304 t.Fatal(err) 2305 } 2306 defer os.RemoveAll(tmpDir) 2307 2308 tmpDir2, err := ioutil.TempDir("", "docker_nested_mount_test2") 2309 if err != nil { 2310 t.Fatal(err) 2311 } 2312 defer os.RemoveAll(tmpDir2) 2313 2314 // Create a temporary tmpfs mount. 2315 fooDir := filepath.Join(tmpDir, "foo") 2316 if err := os.MkdirAll(filepath.Join(tmpDir, "foo"), 0755); err != nil { 2317 t.Fatalf("failed to mkdir at %s - %s", fooDir, err) 2318 } 2319 2320 if err := ioutil.WriteFile(fmt.Sprintf("%s/touch-me", fooDir), []byte{}, 0644); err != nil { 2321 t.Fatal(err) 2322 } 2323 2324 if err := ioutil.WriteFile(fmt.Sprintf("%s/touch-me", tmpDir), []byte{}, 0644); err != nil { 2325 t.Fatal(err) 2326 } 2327 2328 if err := ioutil.WriteFile(fmt.Sprintf("%s/touch-me", tmpDir2), []byte{}, 0644); err != nil { 2329 t.Fatal(err) 2330 } 2331 2332 cmd := exec.Command(dockerBinary, "run", "-v", fmt.Sprintf("%s:/tmp", tmpDir), "-v", fmt.Sprintf("%s:/tmp/foo", fooDir), "-v", fmt.Sprintf("%s:/tmp/tmp2", tmpDir2), "-v", fmt.Sprintf("%s:/tmp/tmp2/foo", fooDir), "busybox:latest", "sh", "-c", "ls /tmp/touch-me && ls /tmp/foo/touch-me && ls /tmp/tmp2/touch-me && ls /tmp/tmp2/foo/touch-me") 2333 out, _, err := runCommandWithOutput(cmd) 2334 if err != nil { 2335 t.Fatal(out, err) 2336 } 2337 2338 deleteAllContainers() 2339 logDone("run - volumes are mounted in the correct order") 2340 } 2341 2342 func TestRunExecDir(t *testing.T) { 2343 cmd := exec.Command(dockerBinary, "run", "-d", "busybox", "top") 2344 out, _, err := runCommandWithOutput(cmd) 2345 if err != nil { 2346 t.Fatal(err, out) 2347 } 2348 id := strings.TrimSpace(out) 2349 execDir := filepath.Join(execDriverPath, id) 2350 stateFile := filepath.Join(execDir, "state.json") 2351 contFile := filepath.Join(execDir, "container.json") 2352 2353 { 2354 fi, err := os.Stat(execDir) 2355 if err != nil { 2356 t.Fatal(err) 2357 } 2358 if !fi.IsDir() { 2359 t.Fatalf("%q must be a directory", execDir) 2360 } 2361 fi, err = os.Stat(stateFile) 2362 if err != nil { 2363 t.Fatal(err) 2364 } 2365 fi, err = os.Stat(contFile) 2366 if err != nil { 2367 t.Fatal(err) 2368 } 2369 } 2370 2371 stopCmd := exec.Command(dockerBinary, "stop", id) 2372 out, _, err = runCommandWithOutput(stopCmd) 2373 if err != nil { 2374 t.Fatal(err, out) 2375 } 2376 { 2377 fi, err := os.Stat(execDir) 2378 if err != nil { 2379 t.Fatal(err) 2380 } 2381 if !fi.IsDir() { 2382 t.Fatalf("%q must be a directory", execDir) 2383 } 2384 fi, err = os.Stat(stateFile) 2385 if err == nil { 2386 t.Fatalf("Statefile %q is exists for stopped container!", stateFile) 2387 } 2388 if !os.IsNotExist(err) { 2389 t.Fatalf("Error should be about non-existing, got %s", err) 2390 } 2391 fi, err = os.Stat(contFile) 2392 if err == nil { 2393 t.Fatalf("Container file %q is exists for stopped container!", contFile) 2394 } 2395 if !os.IsNotExist(err) { 2396 t.Fatalf("Error should be about non-existing, got %s", err) 2397 } 2398 } 2399 startCmd := exec.Command(dockerBinary, "start", id) 2400 out, _, err = runCommandWithOutput(startCmd) 2401 if err != nil { 2402 t.Fatal(err, out) 2403 } 2404 { 2405 fi, err := os.Stat(execDir) 2406 if err != nil { 2407 t.Fatal(err) 2408 } 2409 if !fi.IsDir() { 2410 t.Fatalf("%q must be a directory", execDir) 2411 } 2412 fi, err = os.Stat(stateFile) 2413 if err != nil { 2414 t.Fatal(err) 2415 } 2416 fi, err = os.Stat(contFile) 2417 if err != nil { 2418 t.Fatal(err) 2419 } 2420 } 2421 rmCmd := exec.Command(dockerBinary, "rm", "-f", id) 2422 out, _, err = runCommandWithOutput(rmCmd) 2423 if err != nil { 2424 t.Fatal(err, out) 2425 } 2426 { 2427 _, err := os.Stat(execDir) 2428 if err == nil { 2429 t.Fatal(err) 2430 } 2431 if err == nil { 2432 t.Fatalf("Exec directory %q is exists for removed container!", execDir) 2433 } 2434 if !os.IsNotExist(err) { 2435 t.Fatalf("Error should be about non-existing, got %s", err) 2436 } 2437 } 2438 2439 logDone("run - check execdriver dir behavior") 2440 } 2441 2442 // Regression test for https://github.com/docker/docker/issues/8259 2443 func TestRunReuseBindVolumeThatIsSymlink(t *testing.T) { 2444 tmpDir, err := ioutil.TempDir(os.TempDir(), "testlink") 2445 if err != nil { 2446 t.Fatal(err) 2447 } 2448 defer os.RemoveAll(tmpDir) 2449 2450 linkPath := os.TempDir() + "/testlink2" 2451 if err := os.Symlink(tmpDir, linkPath); err != nil { 2452 t.Fatal(err) 2453 } 2454 defer os.RemoveAll(linkPath) 2455 2456 // Create first container 2457 cmd := exec.Command(dockerBinary, "run", "-v", fmt.Sprintf("%s:/tmp/test", linkPath), "busybox", "ls", "-lh", "/tmp/test") 2458 if _, err := runCommand(cmd); err != nil { 2459 t.Fatal(err) 2460 } 2461 2462 // Create second container with same symlinked path 2463 // This will fail if the referenced issue is hit with a "Volume exists" error 2464 cmd = exec.Command(dockerBinary, "run", "-v", fmt.Sprintf("%s:/tmp/test", linkPath), "busybox", "ls", "-lh", "/tmp/test") 2465 if out, _, err := runCommandWithOutput(cmd); err != nil { 2466 t.Fatal(err, out) 2467 } 2468 2469 deleteAllContainers() 2470 logDone("run - can remount old bindmount volume") 2471 } 2472 2473 func TestVolumesNoCopyData(t *testing.T) { 2474 defer deleteImages("dataimage") 2475 defer deleteAllContainers() 2476 if _, err := buildImage("dataimage", 2477 `FROM busybox 2478 RUN mkdir -p /foo 2479 RUN touch /foo/bar`, 2480 true); err != nil { 2481 t.Fatal(err) 2482 } 2483 2484 cmd := exec.Command(dockerBinary, "run", "--name", "test", "-v", "/foo", "busybox") 2485 if _, err := runCommand(cmd); err != nil { 2486 t.Fatal(err) 2487 } 2488 2489 cmd = exec.Command(dockerBinary, "run", "--volumes-from", "test", "dataimage", "ls", "-lh", "/foo/bar") 2490 if out, _, err := runCommandWithOutput(cmd); err == nil || !strings.Contains(out, "No such file or directory") { 2491 t.Fatalf("Data was copied on volumes-from but shouldn't be:\n%q", out) 2492 } 2493 2494 tmpDir, err := ioutil.TempDir("", "docker_test_bind_mount_copy_data") 2495 if err != nil { 2496 t.Fatal(err) 2497 } 2498 2499 defer os.RemoveAll(tmpDir) 2500 2501 cmd = exec.Command(dockerBinary, "run", "-v", tmpDir+":/foo", "dataimage", "ls", "-lh", "/foo/bar") 2502 if out, _, err := runCommandWithOutput(cmd); err == nil || !strings.Contains(out, "No such file or directory") { 2503 t.Fatalf("Data was copied on bind-mount but shouldn't be:\n%q", out) 2504 } 2505 2506 logDone("run - volumes do not copy data for volumes-from and bindmounts") 2507 } 2508 2509 func TestRunVolumesNotRecreatedOnStart(t *testing.T) { 2510 // Clear out any remnants from other tests 2511 deleteAllContainers() 2512 info, err := ioutil.ReadDir(volumesConfigPath) 2513 if err != nil { 2514 t.Fatal(err) 2515 } 2516 if len(info) > 0 { 2517 for _, f := range info { 2518 if err := os.RemoveAll(volumesConfigPath + "/" + f.Name()); err != nil { 2519 t.Fatal(err) 2520 } 2521 } 2522 } 2523 2524 defer deleteAllContainers() 2525 cmd := exec.Command(dockerBinary, "run", "-v", "/foo", "--name", "lone_starr", "busybox") 2526 if _, err := runCommand(cmd); err != nil { 2527 t.Fatal(err) 2528 } 2529 2530 cmd = exec.Command(dockerBinary, "start", "lone_starr") 2531 if _, err := runCommand(cmd); err != nil { 2532 t.Fatal(err) 2533 } 2534 2535 info, err = ioutil.ReadDir(volumesConfigPath) 2536 if err != nil { 2537 t.Fatal(err) 2538 } 2539 if len(info) != 1 { 2540 t.Fatalf("Expected only 1 volume have %v", len(info)) 2541 } 2542 2543 logDone("run - volumes not recreated on start") 2544 } 2545 2546 func TestRunNoOutputFromPullInStdout(t *testing.T) { 2547 defer deleteAllContainers() 2548 // just run with unknown image 2549 cmd := exec.Command(dockerBinary, "run", "asdfsg") 2550 stdout := bytes.NewBuffer(nil) 2551 cmd.Stdout = stdout 2552 if err := cmd.Run(); err == nil { 2553 t.Fatal("Run with unknown image should fail") 2554 } 2555 if stdout.Len() != 0 { 2556 t.Fatalf("Stdout contains output from pull: %s", stdout) 2557 } 2558 logDone("run - no output from pull in stdout") 2559 } 2560 2561 func TestRunVolumesCleanPaths(t *testing.T) { 2562 if _, err := buildImage("run_volumes_clean_paths", 2563 `FROM busybox 2564 VOLUME /foo/`, 2565 true); err != nil { 2566 t.Fatal(err) 2567 } 2568 defer deleteImages("run_volumes_clean_paths") 2569 defer deleteAllContainers() 2570 2571 cmd := exec.Command(dockerBinary, "run", "-v", "/foo", "-v", "/bar/", "--name", "dark_helmet", "run_volumes_clean_paths") 2572 if out, _, err := runCommandWithOutput(cmd); err != nil { 2573 t.Fatal(err, out) 2574 } 2575 2576 out, err := inspectFieldMap("dark_helmet", "Volumes", "/foo/") 2577 if err != nil { 2578 t.Fatal(err) 2579 } 2580 if out != "<no value>" { 2581 t.Fatalf("Found unexpected volume entry for '/foo/' in volumes\n%q", out) 2582 } 2583 2584 out, err = inspectFieldMap("dark_helmet", "Volumes", "/foo") 2585 if err != nil { 2586 t.Fatal(err) 2587 } 2588 if !strings.Contains(out, volumesStoragePath) { 2589 t.Fatalf("Volume was not defined for /foo\n%q", out) 2590 } 2591 2592 out, err = inspectFieldMap("dark_helmet", "Volumes", "/bar/") 2593 if err != nil { 2594 t.Fatal(err) 2595 } 2596 if out != "<no value>" { 2597 t.Fatalf("Found unexpected volume entry for '/bar/' in volumes\n%q", out) 2598 } 2599 out, err = inspectFieldMap("dark_helmet", "Volumes", "/bar") 2600 if err != nil { 2601 t.Fatal(err) 2602 } 2603 if !strings.Contains(out, volumesStoragePath) { 2604 t.Fatalf("Volume was not defined for /bar\n%q", out) 2605 } 2606 2607 logDone("run - volume paths are cleaned") 2608 } 2609 2610 // Regression test for #3631 2611 func TestRunSlowStdoutConsumer(t *testing.T) { 2612 defer deleteAllContainers() 2613 2614 c := exec.Command("/bin/bash", "-c", dockerBinary+` run --rm -i busybox /bin/sh -c "dd if=/dev/zero of=/foo bs=1024 count=2000 &>/dev/null; catv /foo"`) 2615 2616 stdout, err := c.StdoutPipe() 2617 if err != nil { 2618 t.Fatal(err) 2619 } 2620 2621 if err := c.Start(); err != nil { 2622 t.Fatal(err) 2623 } 2624 n, err := consumeWithSpeed(stdout, 10000, 5*time.Millisecond, nil) 2625 if err != nil { 2626 t.Fatal(err) 2627 } 2628 2629 expected := 2 * 1024 * 2000 2630 if n != expected { 2631 t.Fatalf("Expected %d, got %d", expected, n) 2632 } 2633 2634 logDone("run - slow consumer") 2635 } 2636 2637 func TestRunAllowPortRangeThroughExpose(t *testing.T) { 2638 cmd := exec.Command(dockerBinary, "run", "-d", "--expose", "3000-3003", "-P", "busybox", "top") 2639 out, _, err := runCommandWithOutput(cmd) 2640 if err != nil { 2641 t.Fatal(err) 2642 } 2643 id := strings.TrimSpace(out) 2644 portstr, err := inspectFieldJSON(id, "NetworkSettings.Ports") 2645 if err != nil { 2646 t.Fatal(err) 2647 } 2648 var ports nat.PortMap 2649 err = unmarshalJSON([]byte(portstr), &ports) 2650 for port, binding := range ports { 2651 portnum, _ := strconv.Atoi(strings.Split(string(port), "/")[0]) 2652 if portnum < 3000 || portnum > 3003 { 2653 t.Fatalf("Port is out of range ", portnum, binding, out) 2654 } 2655 if binding == nil || len(binding) != 1 || len(binding[0].HostPort) == 0 { 2656 t.Fatal("Port is not mapped for the port "+port, out) 2657 } 2658 } 2659 if err := deleteContainer(id); err != nil { 2660 t.Fatal(err) 2661 } 2662 logDone("run - allow port range through --expose flag") 2663 } 2664 2665 func TestRunUnknownCommand(t *testing.T) { 2666 defer deleteAllContainers() 2667 runCmd := exec.Command(dockerBinary, "create", "busybox", "/bin/nada") 2668 cID, _, _, err := runCommandWithStdoutStderr(runCmd) 2669 if err != nil { 2670 t.Fatalf("Failed to create container: %v, output: %q", err, cID) 2671 } 2672 cID = strings.TrimSpace(cID) 2673 2674 runCmd = exec.Command(dockerBinary, "start", cID) 2675 _, _, _, err = runCommandWithStdoutStderr(runCmd) 2676 if err == nil { 2677 t.Fatalf("Container should not have been able to start!") 2678 } 2679 2680 runCmd = exec.Command(dockerBinary, "inspect", "--format={{.State.ExitCode}}", cID) 2681 rc, _, _, err2 := runCommandWithStdoutStderr(runCmd) 2682 rc = strings.TrimSpace(rc) 2683 2684 if err2 != nil { 2685 t.Fatalf("Error getting status of container: %v", err2) 2686 } 2687 2688 if rc != "-1" { 2689 t.Fatalf("ExitCode(%v) was supposed to be -1", rc) 2690 } 2691 2692 logDone("run - Unknown Command") 2693 } 2694 2695 func TestRunModeIpcHost(t *testing.T) { 2696 hostIpc, err := os.Readlink("/proc/1/ns/ipc") 2697 if err != nil { 2698 t.Fatal(err) 2699 } 2700 2701 cmd := exec.Command(dockerBinary, "run", "--ipc=host", "busybox", "readlink", "/proc/self/ns/ipc") 2702 out2, _, err := runCommandWithOutput(cmd) 2703 if err != nil { 2704 t.Fatal(err, out2) 2705 } 2706 2707 out2 = strings.Trim(out2, "\n") 2708 if hostIpc != out2 { 2709 t.Fatalf("IPC different with --ipc=host %s != %s\n", hostIpc, out2) 2710 } 2711 2712 cmd = exec.Command(dockerBinary, "run", "busybox", "readlink", "/proc/self/ns/ipc") 2713 out2, _, err = runCommandWithOutput(cmd) 2714 if err != nil { 2715 t.Fatal(err, out2) 2716 } 2717 2718 out2 = strings.Trim(out2, "\n") 2719 if hostIpc == out2 { 2720 t.Fatalf("IPC should be different without --ipc=host %s == %s\n", hostIpc, out2) 2721 } 2722 deleteAllContainers() 2723 2724 logDone("run - ipc host mode") 2725 } 2726 2727 func TestRunModeIpcContainer(t *testing.T) { 2728 cmd := exec.Command(dockerBinary, "run", "-d", "busybox", "top") 2729 out, _, err := runCommandWithOutput(cmd) 2730 if err != nil { 2731 t.Fatal(err, out) 2732 } 2733 id := strings.TrimSpace(out) 2734 state, err := inspectField(id, "State.Running") 2735 if err != nil { 2736 t.Fatal(err) 2737 } 2738 if state != "true" { 2739 t.Fatal("Container state is 'not running'") 2740 } 2741 pid1, err := inspectField(id, "State.Pid") 2742 if err != nil { 2743 t.Fatal(err) 2744 } 2745 2746 parentContainerIpc, err := os.Readlink(fmt.Sprintf("/proc/%s/ns/ipc", pid1)) 2747 if err != nil { 2748 t.Fatal(err) 2749 } 2750 cmd = exec.Command(dockerBinary, "run", fmt.Sprintf("--ipc=container:%s", id), "busybox", "readlink", "/proc/self/ns/ipc") 2751 out2, _, err := runCommandWithOutput(cmd) 2752 if err != nil { 2753 t.Fatal(err, out2) 2754 } 2755 2756 out2 = strings.Trim(out2, "\n") 2757 if parentContainerIpc != out2 { 2758 t.Fatalf("IPC different with --ipc=container:%s %s != %s\n", id, parentContainerIpc, out2) 2759 } 2760 deleteAllContainers() 2761 2762 logDone("run - ipc container mode") 2763 } 2764 2765 func TestContainerNetworkMode(t *testing.T) { 2766 cmd := exec.Command(dockerBinary, "run", "-d", "busybox", "top") 2767 out, _, err := runCommandWithOutput(cmd) 2768 if err != nil { 2769 t.Fatal(err, out) 2770 } 2771 id := strings.TrimSpace(out) 2772 if err := waitRun(id); err != nil { 2773 t.Fatal(err) 2774 } 2775 pid1, err := inspectField(id, "State.Pid") 2776 if err != nil { 2777 t.Fatal(err) 2778 } 2779 2780 parentContainerNet, err := os.Readlink(fmt.Sprintf("/proc/%s/ns/net", pid1)) 2781 if err != nil { 2782 t.Fatal(err) 2783 } 2784 cmd = exec.Command(dockerBinary, "run", fmt.Sprintf("--net=container:%s", id), "busybox", "readlink", "/proc/self/ns/net") 2785 out2, _, err := runCommandWithOutput(cmd) 2786 if err != nil { 2787 t.Fatal(err, out2) 2788 } 2789 2790 out2 = strings.Trim(out2, "\n") 2791 if parentContainerNet != out2 { 2792 t.Fatalf("NET different with --net=container:%s %s != %s\n", id, parentContainerNet, out2) 2793 } 2794 deleteAllContainers() 2795 2796 logDone("run - container shared network namespace") 2797 } 2798 2799 func TestRunModePidHost(t *testing.T) { 2800 hostPid, err := os.Readlink("/proc/1/ns/pid") 2801 if err != nil { 2802 t.Fatal(err) 2803 } 2804 2805 cmd := exec.Command(dockerBinary, "run", "--pid=host", "busybox", "readlink", "/proc/self/ns/pid") 2806 out2, _, err := runCommandWithOutput(cmd) 2807 if err != nil { 2808 t.Fatal(err, out2) 2809 } 2810 2811 out2 = strings.Trim(out2, "\n") 2812 if hostPid != out2 { 2813 t.Fatalf("PID different with --pid=host %s != %s\n", hostPid, out2) 2814 } 2815 2816 cmd = exec.Command(dockerBinary, "run", "busybox", "readlink", "/proc/self/ns/pid") 2817 out2, _, err = runCommandWithOutput(cmd) 2818 if err != nil { 2819 t.Fatal(err, out2) 2820 } 2821 2822 out2 = strings.Trim(out2, "\n") 2823 if hostPid == out2 { 2824 t.Fatalf("PID should be different without --pid=host %s == %s\n", hostPid, out2) 2825 } 2826 deleteAllContainers() 2827 2828 logDone("run - pid host mode") 2829 } 2830 2831 func TestRunTLSverify(t *testing.T) { 2832 cmd := exec.Command(dockerBinary, "ps") 2833 out, ec, err := runCommandWithOutput(cmd) 2834 if err != nil || ec != 0 { 2835 t.Fatalf("Should have worked: %v:\n%v", err, out) 2836 } 2837 2838 // Regardless of whether we specify true or false we need to 2839 // test to make sure tls is turned on if --tlsverify is specified at all 2840 2841 cmd = exec.Command(dockerBinary, "--tlsverify=false", "ps") 2842 out, ec, err = runCommandWithOutput(cmd) 2843 if err == nil || ec == 0 || !strings.Contains(out, "trying to connect") { 2844 t.Fatalf("Should have failed: \nec:%v\nout:%v\nerr:%v", ec, out, err) 2845 } 2846 2847 cmd = exec.Command(dockerBinary, "--tlsverify=true", "ps") 2848 out, ec, err = runCommandWithOutput(cmd) 2849 if err == nil || ec == 0 || !strings.Contains(out, "cert") { 2850 t.Fatalf("Should have failed: \nec:%v\nout:%v\nerr:%v", ec, out, err) 2851 } 2852 2853 logDone("run - verify tls is set for --tlsverify") 2854 } 2855 2856 func TestRunPortFromDockerRangeInUse(t *testing.T) { 2857 defer deleteAllContainers() 2858 // first find allocator current position 2859 cmd := exec.Command(dockerBinary, "run", "-d", "-p", ":80", "busybox", "top") 2860 out, _, err := runCommandWithOutput(cmd) 2861 if err != nil { 2862 t.Fatal(out, err) 2863 } 2864 id := strings.TrimSpace(out) 2865 cmd = exec.Command(dockerBinary, "port", id) 2866 out, _, err = runCommandWithOutput(cmd) 2867 if err != nil { 2868 t.Fatal(out, err) 2869 } 2870 out = strings.TrimSpace(out) 2871 out = strings.Split(out, ":")[1] 2872 lastPort, err := strconv.Atoi(out) 2873 if err != nil { 2874 t.Fatal(err) 2875 } 2876 port := lastPort + 1 2877 l, err := net.Listen("tcp", ":"+strconv.Itoa(port)) 2878 if err != nil { 2879 t.Fatal(err) 2880 } 2881 defer l.Close() 2882 cmd = exec.Command(dockerBinary, "run", "-d", "-p", ":80", "busybox", "top") 2883 out, _, err = runCommandWithOutput(cmd) 2884 if err != nil { 2885 t.Fatalf(out, err) 2886 } 2887 id = strings.TrimSpace(out) 2888 cmd = exec.Command(dockerBinary, "port", id) 2889 out, _, err = runCommandWithOutput(cmd) 2890 if err != nil { 2891 t.Fatal(out, err) 2892 } 2893 2894 logDone("run - find another port if port from autorange already bound") 2895 } 2896 2897 func TestRunTtyWithPipe(t *testing.T) { 2898 defer deleteAllContainers() 2899 2900 done := make(chan struct{}) 2901 go func() { 2902 defer close(done) 2903 2904 cmd := exec.Command(dockerBinary, "run", "-ti", "busybox", "true") 2905 if _, err := cmd.StdinPipe(); err != nil { 2906 t.Fatal(err) 2907 } 2908 2909 expected := "cannot enable tty mode" 2910 if out, _, err := runCommandWithOutput(cmd); err == nil { 2911 t.Fatal("run should have failed") 2912 } else if !strings.Contains(out, expected) { 2913 t.Fatalf("run failed with error %q: expected %q", out, expected) 2914 } 2915 }() 2916 2917 select { 2918 case <-done: 2919 case <-time.After(3 * time.Second): 2920 t.Fatal("container is running but should have failed") 2921 } 2922 2923 logDone("run - forbid piped stdin with tty") 2924 } 2925 2926 func TestRunNonLocalMacAddress(t *testing.T) { 2927 defer deleteAllContainers() 2928 addr := "00:16:3E:08:00:50" 2929 2930 cmd := exec.Command(dockerBinary, "run", "--mac-address", addr, "busybox", "ifconfig") 2931 if out, _, err := runCommandWithOutput(cmd); err != nil || !strings.Contains(out, addr) { 2932 t.Fatalf("Output should have contained %q: %s, %v", addr, out, err) 2933 } 2934 2935 logDone("run - use non-local mac-address") 2936 } 2937 2938 func TestRunNetHost(t *testing.T) { 2939 defer deleteAllContainers() 2940 hostNet, err := os.Readlink("/proc/1/ns/net") 2941 if err != nil { 2942 t.Fatal(err) 2943 } 2944 2945 cmd := exec.Command(dockerBinary, "run", "--net=host", "busybox", "readlink", "/proc/self/ns/net") 2946 out2, _, err := runCommandWithOutput(cmd) 2947 if err != nil { 2948 t.Fatal(err, out2) 2949 } 2950 2951 out2 = strings.Trim(out2, "\n") 2952 if hostNet != out2 { 2953 t.Fatalf("Net namespace different with --net=host %s != %s\n", hostNet, out2) 2954 } 2955 2956 cmd = exec.Command(dockerBinary, "run", "busybox", "readlink", "/proc/self/ns/net") 2957 out2, _, err = runCommandWithOutput(cmd) 2958 if err != nil { 2959 t.Fatal(err, out2) 2960 } 2961 2962 out2 = strings.Trim(out2, "\n") 2963 if hostNet == out2 { 2964 t.Fatalf("Net namespace should be different without --net=host %s == %s\n", hostNet, out2) 2965 } 2966 2967 logDone("run - net host mode") 2968 } 2969 2970 func TestRunAllowPortRangeThroughPublish(t *testing.T) { 2971 cmd := exec.Command(dockerBinary, "run", "-d", "--expose", "3000-3003", "-p", "3000-3003", "busybox", "top") 2972 out, _, err := runCommandWithOutput(cmd) 2973 defer deleteAllContainers() 2974 2975 id := strings.TrimSpace(out) 2976 portstr, err := inspectFieldJSON(id, "NetworkSettings.Ports") 2977 if err != nil { 2978 t.Fatal(err) 2979 } 2980 var ports nat.PortMap 2981 err = unmarshalJSON([]byte(portstr), &ports) 2982 for port, binding := range ports { 2983 portnum, _ := strconv.Atoi(strings.Split(string(port), "/")[0]) 2984 if portnum < 3000 || portnum > 3003 { 2985 t.Fatalf("Port is out of range ", portnum, binding, out) 2986 } 2987 if binding == nil || len(binding) != 1 || len(binding[0].HostPort) == 0 { 2988 t.Fatal("Port is not mapped for the port "+port, out) 2989 } 2990 } 2991 logDone("run - allow port range through --expose flag") 2992 } 2993 2994 func TestRunOOMExitCode(t *testing.T) { 2995 defer deleteAllContainers() 2996 2997 done := make(chan struct{}) 2998 go func() { 2999 defer close(done) 3000 3001 runCmd := exec.Command(dockerBinary, "run", "-m", "4MB", "busybox", "sh", "-c", "x=a; while true; do x=$x$x; done") 3002 out, exitCode, _ := runCommandWithOutput(runCmd) 3003 if expected := 137; exitCode != expected { 3004 t.Fatalf("wrong exit code for OOM container: expected %d, got %d (output: %q)", expected, exitCode, out) 3005 } 3006 }() 3007 3008 select { 3009 case <-done: 3010 case <-time.After(3 * time.Second): 3011 t.Fatal("Timeout waiting for container to die on OOM") 3012 } 3013 3014 logDone("run - exit code on oom") 3015 } 3016 3017 func TestRunRestartMaxRetries(t *testing.T) { 3018 defer deleteAllContainers() 3019 out, err := exec.Command(dockerBinary, "run", "-d", "--restart=on-failure:3", "busybox", "false").CombinedOutput() 3020 if err != nil { 3021 t.Fatal(string(out), err) 3022 } 3023 id := strings.TrimSpace(string(out)) 3024 if err := waitInspect(id, "{{ .State.Restarting }} {{ .State.Running }}", "false false", 5); err != nil { 3025 t.Fatal(err) 3026 } 3027 count, err := inspectField(id, "RestartCount") 3028 if err != nil { 3029 t.Fatal(err) 3030 } 3031 if count != "3" { 3032 t.Fatalf("Container was restarted %s times, expected %d", count, 3) 3033 } 3034 logDone("run - test max-retries for --restart") 3035 } 3036 3037 func TestRunContainerWithWritableRootfs(t *testing.T) { 3038 defer deleteAllContainers() 3039 out, err := exec.Command(dockerBinary, "run", "--rm", "busybox", "touch", "/file").CombinedOutput() 3040 if err != nil { 3041 t.Fatal(string(out), err) 3042 } 3043 logDone("run - writable rootfs") 3044 } 3045 3046 func TestRunContainerWithReadonlyRootfs(t *testing.T) { 3047 defer deleteAllContainers() 3048 out, err := exec.Command(dockerBinary, "run", "--read-only", "--rm", "busybox", "touch", "/file").CombinedOutput() 3049 if err == nil { 3050 t.Fatal("expected container to error on run with read only error") 3051 } 3052 expected := "Read-only file system" 3053 if !strings.Contains(string(out), expected) { 3054 t.Fatalf("expected output from failure to contain %s but contains %s", expected, out) 3055 } 3056 logDone("run - read only rootfs") 3057 } 3058 3059 func TestRunVolumesFromRestartAfterRemoved(t *testing.T) { 3060 defer deleteAllContainers() 3061 3062 out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "-d", "--name", "voltest", "-v", "/foo", "busybox")) 3063 if err != nil { 3064 t.Fatal(out, err) 3065 } 3066 3067 out, _, err = runCommandWithOutput(exec.Command(dockerBinary, "run", "-d", "--name", "restarter", "--volumes-from", "voltest", "busybox", "top")) 3068 if err != nil { 3069 t.Fatal(out, err) 3070 } 3071 3072 // Remove the main volume container and restart the consuming container 3073 out, _, err = runCommandWithOutput(exec.Command(dockerBinary, "rm", "-f", "voltest")) 3074 if err != nil { 3075 t.Fatal(out, err) 3076 } 3077 3078 // This should not fail since the volumes-from were already applied 3079 out, _, err = runCommandWithOutput(exec.Command(dockerBinary, "restart", "restarter")) 3080 if err != nil { 3081 t.Fatalf("expected container to restart successfully: %v\n%s", err, out) 3082 } 3083 3084 logDone("run - can restart a volumes-from container after producer is removed") 3085 }