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