github.com/ctmnz/docker@v1.6.0-rc3/integration-cli/docker_cli_exec_test.go (about) 1 // +build !test_no_exec 2 3 package main 4 5 import ( 6 "bufio" 7 "fmt" 8 "os" 9 "os/exec" 10 "path/filepath" 11 "reflect" 12 "sort" 13 "strings" 14 "sync" 15 "testing" 16 "time" 17 ) 18 19 func TestExec(t *testing.T) { 20 defer deleteAllContainers() 21 22 runCmd := exec.Command(dockerBinary, "run", "-d", "--name", "testing", "busybox", "sh", "-c", "echo test > /tmp/file && sleep 100") 23 if out, _, _, err := runCommandWithStdoutStderr(runCmd); err != nil { 24 t.Fatal(out, err) 25 } 26 27 execCmd := exec.Command(dockerBinary, "exec", "testing", "cat", "/tmp/file") 28 out, _, err := runCommandWithOutput(execCmd) 29 if err != nil { 30 t.Fatal(out, err) 31 } 32 33 out = strings.Trim(out, "\r\n") 34 35 if expected := "test"; out != expected { 36 t.Errorf("container exec should've printed %q but printed %q", expected, out) 37 } 38 39 logDone("exec - basic test") 40 } 41 42 func TestExecInteractiveStdinClose(t *testing.T) { 43 defer deleteAllContainers() 44 out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "-itd", "busybox", "/bin/cat")) 45 if err != nil { 46 t.Fatal(err) 47 } 48 49 contId := strings.TrimSpace(out) 50 51 returnchan := make(chan struct{}) 52 53 go func() { 54 var err error 55 cmd := exec.Command(dockerBinary, "exec", "-i", contId, "/bin/ls", "/") 56 cmd.Stdin = os.Stdin 57 if err != nil { 58 t.Fatal(err) 59 } 60 61 out, err := cmd.CombinedOutput() 62 if err != nil { 63 t.Fatal(err, string(out)) 64 } 65 66 if string(out) == "" { 67 t.Fatalf("Output was empty, likely blocked by standard input") 68 } 69 70 returnchan <- struct{}{} 71 }() 72 73 select { 74 case <-returnchan: 75 case <-time.After(10 * time.Second): 76 t.Fatal("timed out running docker exec") 77 } 78 79 logDone("exec - interactive mode closes stdin after execution") 80 } 81 82 func TestExecInteractive(t *testing.T) { 83 defer deleteAllContainers() 84 85 runCmd := exec.Command(dockerBinary, "run", "-d", "--name", "testing", "busybox", "sh", "-c", "echo test > /tmp/file && sleep 100") 86 if out, _, _, err := runCommandWithStdoutStderr(runCmd); err != nil { 87 t.Fatal(out, err) 88 } 89 90 execCmd := exec.Command(dockerBinary, "exec", "-i", "testing", "sh") 91 stdin, err := execCmd.StdinPipe() 92 if err != nil { 93 t.Fatal(err) 94 } 95 stdout, err := execCmd.StdoutPipe() 96 if err != nil { 97 t.Fatal(err) 98 } 99 100 if err := execCmd.Start(); err != nil { 101 t.Fatal(err) 102 } 103 if _, err := stdin.Write([]byte("cat /tmp/file\n")); err != nil { 104 t.Fatal(err) 105 } 106 107 r := bufio.NewReader(stdout) 108 line, err := r.ReadString('\n') 109 if err != nil { 110 t.Fatal(err) 111 } 112 line = strings.TrimSpace(line) 113 if line != "test" { 114 t.Fatalf("Output should be 'test', got '%q'", line) 115 } 116 if err := stdin.Close(); err != nil { 117 t.Fatal(err) 118 } 119 finish := make(chan struct{}) 120 go func() { 121 if err := execCmd.Wait(); err != nil { 122 t.Fatal(err) 123 } 124 close(finish) 125 }() 126 select { 127 case <-finish: 128 case <-time.After(1 * time.Second): 129 t.Fatal("docker exec failed to exit on stdin close") 130 } 131 132 logDone("exec - Interactive test") 133 } 134 135 func TestExecAfterContainerRestart(t *testing.T) { 136 defer deleteAllContainers() 137 138 runCmd := exec.Command(dockerBinary, "run", "-d", "busybox", "top") 139 out, _, err := runCommandWithOutput(runCmd) 140 if err != nil { 141 t.Fatal(out, err) 142 } 143 144 cleanedContainerID := stripTrailingCharacters(out) 145 146 runCmd = exec.Command(dockerBinary, "restart", cleanedContainerID) 147 if out, _, err = runCommandWithOutput(runCmd); err != nil { 148 t.Fatal(out, err) 149 } 150 151 runCmd = exec.Command(dockerBinary, "exec", cleanedContainerID, "echo", "hello") 152 out, _, err = runCommandWithOutput(runCmd) 153 if err != nil { 154 t.Fatal(out, err) 155 } 156 157 outStr := strings.TrimSpace(out) 158 if outStr != "hello" { 159 t.Errorf("container should've printed hello, instead printed %q", outStr) 160 } 161 162 logDone("exec - exec running container after container restart") 163 } 164 165 func TestExecAfterDaemonRestart(t *testing.T) { 166 testRequires(t, SameHostDaemon) 167 defer deleteAllContainers() 168 169 d := NewDaemon(t) 170 if err := d.StartWithBusybox(); err != nil { 171 t.Fatalf("Could not start daemon with busybox: %v", err) 172 } 173 defer d.Stop() 174 175 if out, err := d.Cmd("run", "-d", "--name", "top", "-p", "80", "busybox:latest", "top"); err != nil { 176 t.Fatalf("Could not run top: err=%v\n%s", err, out) 177 } 178 179 if err := d.Restart(); err != nil { 180 t.Fatalf("Could not restart daemon: %v", err) 181 } 182 183 if out, err := d.Cmd("start", "top"); err != nil { 184 t.Fatalf("Could not start top after daemon restart: err=%v\n%s", err, out) 185 } 186 187 out, err := d.Cmd("exec", "top", "echo", "hello") 188 if err != nil { 189 t.Fatalf("Could not exec on container top: err=%v\n%s", err, out) 190 } 191 192 outStr := strings.TrimSpace(string(out)) 193 if outStr != "hello" { 194 t.Errorf("container should've printed hello, instead printed %q", outStr) 195 } 196 197 logDone("exec - exec running container after daemon restart") 198 } 199 200 // Regression test for #9155, #9044 201 func TestExecEnv(t *testing.T) { 202 defer deleteAllContainers() 203 204 runCmd := exec.Command(dockerBinary, "run", 205 "-e", "LALA=value1", 206 "-e", "LALA=value2", 207 "-d", "--name", "testing", "busybox", "top") 208 if out, _, _, err := runCommandWithStdoutStderr(runCmd); err != nil { 209 t.Fatal(out, err) 210 } 211 212 execCmd := exec.Command(dockerBinary, "exec", "testing", "env") 213 out, _, err := runCommandWithOutput(execCmd) 214 if err != nil { 215 t.Fatal(out, err) 216 } 217 218 if strings.Contains(out, "LALA=value1") || 219 !strings.Contains(out, "LALA=value2") || 220 !strings.Contains(out, "HOME=/root") { 221 t.Errorf("exec env(%q), expect %q, %q", out, "LALA=value2", "HOME=/root") 222 } 223 224 logDone("exec - exec inherits correct env") 225 } 226 227 func TestExecExitStatus(t *testing.T) { 228 defer deleteAllContainers() 229 230 runCmd := exec.Command(dockerBinary, "run", "-d", "--name", "top", "busybox", "top") 231 if out, _, _, err := runCommandWithStdoutStderr(runCmd); err != nil { 232 t.Fatal(out, err) 233 } 234 235 // Test normal (non-detached) case first 236 cmd := exec.Command(dockerBinary, "exec", "top", "sh", "-c", "exit 23") 237 ec, _ := runCommand(cmd) 238 239 if ec != 23 { 240 t.Fatalf("Should have had an ExitCode of 23, not: %d", ec) 241 } 242 243 logDone("exec - exec non-zero ExitStatus") 244 } 245 246 func TestExecPausedContainer(t *testing.T) { 247 defer deleteAllContainers() 248 defer unpauseAllContainers() 249 250 runCmd := exec.Command(dockerBinary, "run", "-d", "--name", "testing", "busybox", "top") 251 out, _, err := runCommandWithOutput(runCmd) 252 if err != nil { 253 t.Fatal(out, err) 254 } 255 256 ContainerID := stripTrailingCharacters(out) 257 258 pausedCmd := exec.Command(dockerBinary, "pause", "testing") 259 out, _, _, err = runCommandWithStdoutStderr(pausedCmd) 260 if err != nil { 261 t.Fatal(out, err) 262 } 263 264 execCmd := exec.Command(dockerBinary, "exec", "-i", "-t", ContainerID, "echo", "hello") 265 out, _, err = runCommandWithOutput(execCmd) 266 if err == nil { 267 t.Fatal("container should fail to exec new command if it is paused") 268 } 269 270 expected := ContainerID + " is paused, unpause the container before exec" 271 if !strings.Contains(out, expected) { 272 t.Fatal("container should not exec new command if it is paused") 273 } 274 275 logDone("exec - exec should not exec a pause container") 276 } 277 278 // regression test for #9476 279 func TestExecTtyCloseStdin(t *testing.T) { 280 defer deleteAllContainers() 281 282 cmd := exec.Command(dockerBinary, "run", "-d", "-it", "--name", "exec_tty_stdin", "busybox") 283 if out, _, err := runCommandWithOutput(cmd); err != nil { 284 t.Fatal(out, err) 285 } 286 287 cmd = exec.Command(dockerBinary, "exec", "-i", "exec_tty_stdin", "cat") 288 stdinRw, err := cmd.StdinPipe() 289 if err != nil { 290 t.Fatal(err) 291 } 292 293 stdinRw.Write([]byte("test")) 294 stdinRw.Close() 295 296 if out, _, err := runCommandWithOutput(cmd); err != nil { 297 t.Fatal(out, err) 298 } 299 300 cmd = exec.Command(dockerBinary, "top", "exec_tty_stdin") 301 out, _, err := runCommandWithOutput(cmd) 302 if err != nil { 303 t.Fatal(out, err) 304 } 305 306 outArr := strings.Split(out, "\n") 307 if len(outArr) > 3 || strings.Contains(out, "nsenter-exec") { 308 // This is the really bad part 309 if out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "rm", "-f", "exec_tty_stdin")); err != nil { 310 t.Fatal(out, err) 311 } 312 313 t.Fatalf("exec process left running\n\t %s", out) 314 } 315 316 logDone("exec - stdin is closed properly with tty enabled") 317 } 318 319 func TestExecTtyWithoutStdin(t *testing.T) { 320 defer deleteAllContainers() 321 322 cmd := exec.Command(dockerBinary, "run", "-d", "-ti", "busybox") 323 out, _, err := runCommandWithOutput(cmd) 324 if err != nil { 325 t.Fatalf("failed to start container: %v (%v)", out, err) 326 } 327 328 id := strings.TrimSpace(out) 329 if err := waitRun(id); err != nil { 330 t.Fatal(err) 331 } 332 333 defer func() { 334 cmd := exec.Command(dockerBinary, "kill", id) 335 if out, _, err := runCommandWithOutput(cmd); err != nil { 336 t.Fatalf("failed to kill container: %v (%v)", out, err) 337 } 338 }() 339 340 done := make(chan struct{}) 341 go func() { 342 defer close(done) 343 344 cmd := exec.Command(dockerBinary, "exec", "-ti", id, "true") 345 if _, err := cmd.StdinPipe(); err != nil { 346 t.Fatal(err) 347 } 348 349 expected := "cannot enable tty mode" 350 if out, _, err := runCommandWithOutput(cmd); err == nil { 351 t.Fatal("exec should have failed") 352 } else if !strings.Contains(out, expected) { 353 t.Fatalf("exec failed with error %q: expected %q", out, expected) 354 } 355 }() 356 357 select { 358 case <-done: 359 case <-time.After(3 * time.Second): 360 t.Fatal("exec is running but should have failed") 361 } 362 363 logDone("exec - forbid piped stdin to tty enabled container") 364 } 365 366 func TestExecParseError(t *testing.T) { 367 defer deleteAllContainers() 368 369 runCmd := exec.Command(dockerBinary, "run", "-d", "--name", "top", "busybox", "top") 370 if out, _, err := runCommandWithOutput(runCmd); err != nil { 371 t.Fatal(out, err) 372 } 373 374 // Test normal (non-detached) case first 375 cmd := exec.Command(dockerBinary, "exec", "top") 376 if _, stderr, code, err := runCommandWithStdoutStderr(cmd); err == nil || !strings.Contains(stderr, "See '"+dockerBinary+" exec --help'") || code == 0 { 377 t.Fatalf("Should have thrown error & point to help: %s", stderr) 378 } 379 logDone("exec - error on parseExec should point to help") 380 } 381 382 func TestExecStopNotHanging(t *testing.T) { 383 defer deleteAllContainers() 384 if out, err := exec.Command(dockerBinary, "run", "-d", "--name", "testing", "busybox", "top").CombinedOutput(); err != nil { 385 t.Fatal(out, err) 386 } 387 388 if err := exec.Command(dockerBinary, "exec", "testing", "top").Start(); err != nil { 389 t.Fatal(err) 390 } 391 392 wait := make(chan struct{}) 393 go func() { 394 if out, err := exec.Command(dockerBinary, "stop", "testing").CombinedOutput(); err != nil { 395 t.Fatal(out, err) 396 } 397 close(wait) 398 }() 399 select { 400 case <-time.After(3 * time.Second): 401 t.Fatal("Container stop timed out") 402 case <-wait: 403 } 404 logDone("exec - container with exec not hanging on stop") 405 } 406 407 func TestExecCgroup(t *testing.T) { 408 defer deleteAllContainers() 409 var cmd *exec.Cmd 410 411 cmd = exec.Command(dockerBinary, "run", "-d", "--name", "testing", "busybox", "top") 412 _, err := runCommand(cmd) 413 if err != nil { 414 t.Fatal(err) 415 } 416 417 cmd = exec.Command(dockerBinary, "exec", "testing", "cat", "/proc/1/cgroup") 418 out, _, err := runCommandWithOutput(cmd) 419 if err != nil { 420 t.Fatal(out, err) 421 } 422 containerCgroups := sort.StringSlice(strings.Split(string(out), "\n")) 423 424 var wg sync.WaitGroup 425 var s sync.Mutex 426 execCgroups := []sort.StringSlice{} 427 // exec a few times concurrently to get consistent failure 428 for i := 0; i < 5; i++ { 429 wg.Add(1) 430 go func() { 431 cmd := exec.Command(dockerBinary, "exec", "testing", "cat", "/proc/self/cgroup") 432 out, _, err := runCommandWithOutput(cmd) 433 if err != nil { 434 t.Fatal(out, err) 435 } 436 cg := sort.StringSlice(strings.Split(string(out), "\n")) 437 438 s.Lock() 439 execCgroups = append(execCgroups, cg) 440 s.Unlock() 441 wg.Done() 442 }() 443 } 444 wg.Wait() 445 446 for _, cg := range execCgroups { 447 if !reflect.DeepEqual(cg, containerCgroups) { 448 fmt.Println("exec cgroups:") 449 for _, name := range cg { 450 fmt.Printf(" %s\n", name) 451 } 452 453 fmt.Println("container cgroups:") 454 for _, name := range containerCgroups { 455 fmt.Printf(" %s\n", name) 456 } 457 t.Fatal("cgroups mismatched") 458 } 459 } 460 461 logDone("exec - exec has the container cgroups") 462 } 463 464 func TestInspectExecID(t *testing.T) { 465 defer deleteAllContainers() 466 467 out, exitCode, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "-d", "busybox", "top")) 468 if exitCode != 0 || err != nil { 469 t.Fatalf("failed to run container: %s, %v", out, err) 470 } 471 id := strings.TrimSuffix(out, "\n") 472 473 out, err = inspectField(id, "ExecIDs") 474 if err != nil { 475 t.Fatalf("failed to inspect container: %s, %v", out, err) 476 } 477 if out != "<no value>" { 478 t.Fatalf("ExecIDs should be empty, got: %s", out) 479 } 480 481 exitCode, err = runCommand(exec.Command(dockerBinary, "exec", "-d", id, "ls", "/")) 482 if exitCode != 0 || err != nil { 483 t.Fatalf("failed to exec in container: %s, %v", out, err) 484 } 485 486 out, err = inspectField(id, "ExecIDs") 487 if err != nil { 488 t.Fatalf("failed to inspect container: %s, %v", out, err) 489 } 490 491 out = strings.TrimSuffix(out, "\n") 492 if out == "[]" || out == "<no value>" { 493 t.Fatalf("ExecIDs should not be empty, got: %s", out) 494 } 495 496 logDone("inspect - inspect a container with ExecIDs") 497 } 498 499 func TestLinksPingLinkedContainersOnRename(t *testing.T) { 500 defer deleteAllContainers() 501 502 var out string 503 out, _, _ = dockerCmd(t, "run", "-d", "--name", "container1", "busybox", "sleep", "10") 504 idA := stripTrailingCharacters(out) 505 if idA == "" { 506 t.Fatal(out, "id should not be nil") 507 } 508 out, _, _ = dockerCmd(t, "run", "-d", "--link", "container1:alias1", "--name", "container2", "busybox", "sleep", "10") 509 idB := stripTrailingCharacters(out) 510 if idB == "" { 511 t.Fatal(out, "id should not be nil") 512 } 513 514 execCmd := exec.Command(dockerBinary, "exec", "container2", "ping", "-c", "1", "alias1", "-W", "1") 515 out, _, err := runCommandWithOutput(execCmd) 516 if err != nil { 517 t.Fatal(out, err) 518 } 519 520 dockerCmd(t, "rename", "container1", "container_new") 521 522 execCmd = exec.Command(dockerBinary, "exec", "container2", "ping", "-c", "1", "alias1", "-W", "1") 523 out, _, err = runCommandWithOutput(execCmd) 524 if err != nil { 525 t.Fatal(out, err) 526 } 527 528 logDone("links - ping linked container upon rename") 529 } 530 531 func TestRunExecDir(t *testing.T) { 532 testRequires(t, SameHostDaemon) 533 cmd := exec.Command(dockerBinary, "run", "-d", "busybox", "top") 534 out, _, err := runCommandWithOutput(cmd) 535 if err != nil { 536 t.Fatal(err, out) 537 } 538 id := strings.TrimSpace(out) 539 execDir := filepath.Join(execDriverPath, id) 540 stateFile := filepath.Join(execDir, "state.json") 541 542 { 543 fi, err := os.Stat(execDir) 544 if err != nil { 545 t.Fatal(err) 546 } 547 if !fi.IsDir() { 548 t.Fatalf("%q must be a directory", execDir) 549 } 550 fi, err = os.Stat(stateFile) 551 if err != nil { 552 t.Fatal(err) 553 } 554 } 555 556 stopCmd := exec.Command(dockerBinary, "stop", id) 557 out, _, err = runCommandWithOutput(stopCmd) 558 if err != nil { 559 t.Fatal(err, out) 560 } 561 { 562 _, err := os.Stat(execDir) 563 if err == nil { 564 t.Fatal(err) 565 } 566 if err == nil { 567 t.Fatalf("Exec directory %q exists for removed container!", execDir) 568 } 569 if !os.IsNotExist(err) { 570 t.Fatalf("Error should be about non-existing, got %s", err) 571 } 572 } 573 startCmd := exec.Command(dockerBinary, "start", id) 574 out, _, err = runCommandWithOutput(startCmd) 575 if err != nil { 576 t.Fatal(err, out) 577 } 578 { 579 fi, err := os.Stat(execDir) 580 if err != nil { 581 t.Fatal(err) 582 } 583 if !fi.IsDir() { 584 t.Fatalf("%q must be a directory", execDir) 585 } 586 fi, err = os.Stat(stateFile) 587 if err != nil { 588 t.Fatal(err) 589 } 590 } 591 rmCmd := exec.Command(dockerBinary, "rm", "-f", id) 592 out, _, err = runCommandWithOutput(rmCmd) 593 if err != nil { 594 t.Fatal(err, out) 595 } 596 { 597 _, err := os.Stat(execDir) 598 if err == nil { 599 t.Fatal(err) 600 } 601 if err == nil { 602 t.Fatalf("Exec directory %q is exists for removed container!", execDir) 603 } 604 if !os.IsNotExist(err) { 605 t.Fatalf("Error should be about non-existing, got %s", err) 606 } 607 } 608 609 logDone("run - check execdriver dir behavior") 610 } 611 612 func TestRunMutableNetworkFiles(t *testing.T) { 613 testRequires(t, SameHostDaemon) 614 defer deleteAllContainers() 615 616 for _, fn := range []string{"resolv.conf", "hosts"} { 617 deleteAllContainers() 618 619 content, err := runCommandAndReadContainerFile(fn, exec.Command(dockerBinary, "run", "-d", "--name", "c1", "busybox", "sh", "-c", fmt.Sprintf("echo success >/etc/%s && top", fn))) 620 if err != nil { 621 t.Fatal(err) 622 } 623 624 if strings.TrimSpace(string(content)) != "success" { 625 t.Fatal("Content was not what was modified in the container", string(content)) 626 } 627 628 out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "-d", "--name", "c2", "busybox", "top")) 629 if err != nil { 630 t.Fatal(err) 631 } 632 633 contID := strings.TrimSpace(out) 634 635 netFilePath := containerStorageFile(contID, fn) 636 637 f, err := os.OpenFile(netFilePath, os.O_WRONLY|os.O_SYNC|os.O_APPEND, 0644) 638 if err != nil { 639 t.Fatal(err) 640 } 641 642 if _, err := f.Seek(0, 0); err != nil { 643 f.Close() 644 t.Fatal(err) 645 } 646 647 if err := f.Truncate(0); err != nil { 648 f.Close() 649 t.Fatal(err) 650 } 651 652 if _, err := f.Write([]byte("success2\n")); err != nil { 653 f.Close() 654 t.Fatal(err) 655 } 656 f.Close() 657 658 res, err := exec.Command(dockerBinary, "exec", contID, "cat", "/etc/"+fn).CombinedOutput() 659 if err != nil { 660 t.Fatalf("Output: %s, error: %s", res, err) 661 } 662 if string(res) != "success2\n" { 663 t.Fatalf("Expected content of %s: %q, got: %q", fn, "success2\n", res) 664 } 665 } 666 logDone("run - mutable network files") 667 }