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