github.com/fabiokung/docker@v0.11.2-0.20170222101415-4534dcd49497/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 "net/http" 9 "os" 10 "os/exec" 11 "reflect" 12 "runtime" 13 "sort" 14 "strings" 15 "sync" 16 "time" 17 18 "github.com/docker/docker/integration-cli/checker" 19 "github.com/docker/docker/integration-cli/request" 20 icmd "github.com/docker/docker/pkg/testutil/cmd" 21 "github.com/go-check/check" 22 ) 23 24 func (s *DockerSuite) TestExec(c *check.C) { 25 testRequires(c, DaemonIsLinux) 26 out, _ := dockerCmd(c, "run", "-d", "--name", "testing", "busybox", "sh", "-c", "echo test > /tmp/file && top") 27 c.Assert(waitRun(strings.TrimSpace(out)), check.IsNil) 28 29 out, _ = dockerCmd(c, "exec", "testing", "cat", "/tmp/file") 30 out = strings.Trim(out, "\r\n") 31 c.Assert(out, checker.Equals, "test") 32 33 } 34 35 func (s *DockerSuite) TestExecInteractive(c *check.C) { 36 testRequires(c, DaemonIsLinux) 37 dockerCmd(c, "run", "-d", "--name", "testing", "busybox", "sh", "-c", "echo test > /tmp/file && top") 38 39 execCmd := exec.Command(dockerBinary, "exec", "-i", "testing", "sh") 40 stdin, err := execCmd.StdinPipe() 41 c.Assert(err, checker.IsNil) 42 stdout, err := execCmd.StdoutPipe() 43 c.Assert(err, checker.IsNil) 44 45 err = execCmd.Start() 46 c.Assert(err, checker.IsNil) 47 _, err = stdin.Write([]byte("cat /tmp/file\n")) 48 c.Assert(err, checker.IsNil) 49 50 r := bufio.NewReader(stdout) 51 line, err := r.ReadString('\n') 52 c.Assert(err, checker.IsNil) 53 line = strings.TrimSpace(line) 54 c.Assert(line, checker.Equals, "test") 55 err = stdin.Close() 56 c.Assert(err, checker.IsNil) 57 errChan := make(chan error) 58 go func() { 59 errChan <- execCmd.Wait() 60 close(errChan) 61 }() 62 select { 63 case err := <-errChan: 64 c.Assert(err, checker.IsNil) 65 case <-time.After(1 * time.Second): 66 c.Fatal("docker exec failed to exit on stdin close") 67 } 68 69 } 70 71 func (s *DockerSuite) TestExecAfterContainerRestart(c *check.C) { 72 out, _ := runSleepingContainer(c) 73 cleanedContainerID := strings.TrimSpace(out) 74 c.Assert(waitRun(cleanedContainerID), check.IsNil) 75 dockerCmd(c, "restart", cleanedContainerID) 76 c.Assert(waitRun(cleanedContainerID), check.IsNil) 77 78 out, _ = dockerCmd(c, "exec", cleanedContainerID, "echo", "hello") 79 outStr := strings.TrimSpace(out) 80 c.Assert(outStr, checker.Equals, "hello") 81 } 82 83 func (s *DockerDaemonSuite) TestExecAfterDaemonRestart(c *check.C) { 84 // TODO Windows CI: Requires a little work to get this ported. 85 testRequires(c, DaemonIsLinux, SameHostDaemon) 86 s.d.StartWithBusybox(c) 87 88 out, err := s.d.Cmd("run", "-d", "--name", "top", "-p", "80", "busybox:latest", "top") 89 c.Assert(err, checker.IsNil, check.Commentf("Could not run top: %s", out)) 90 91 s.d.Restart(c) 92 93 out, err = s.d.Cmd("start", "top") 94 c.Assert(err, checker.IsNil, check.Commentf("Could not start top after daemon restart: %s", out)) 95 96 out, err = s.d.Cmd("exec", "top", "echo", "hello") 97 c.Assert(err, checker.IsNil, check.Commentf("Could not exec on container top: %s", out)) 98 99 outStr := strings.TrimSpace(string(out)) 100 c.Assert(outStr, checker.Equals, "hello") 101 } 102 103 // Regression test for #9155, #9044 104 func (s *DockerSuite) TestExecEnv(c *check.C) { 105 // TODO Windows CI: This one is interesting and may just end up being a feature 106 // difference between Windows and Linux. On Windows, the environment is passed 107 // into the process that is launched, not into the machine environment. Hence 108 // a subsequent exec will not have LALA set/ 109 testRequires(c, DaemonIsLinux) 110 runSleepingContainer(c, "-e", "LALA=value1", "-e", "LALA=value2", "-d", "--name", "testing") 111 c.Assert(waitRun("testing"), check.IsNil) 112 113 out, _ := dockerCmd(c, "exec", "testing", "env") 114 c.Assert(out, checker.Not(checker.Contains), "LALA=value1") 115 c.Assert(out, checker.Contains, "LALA=value2") 116 c.Assert(out, checker.Contains, "HOME=/root") 117 } 118 119 func (s *DockerSuite) TestExecSetEnv(c *check.C) { 120 testRequires(c, DaemonIsLinux) 121 runSleepingContainer(c, "-e", "HOME=/root", "-d", "--name", "testing") 122 c.Assert(waitRun("testing"), check.IsNil) 123 124 out, _ := dockerCmd(c, "exec", "-e", "HOME=/another", "-e", "ABC=xyz", "testing", "env") 125 c.Assert(out, checker.Not(checker.Contains), "HOME=/root") 126 c.Assert(out, checker.Contains, "HOME=/another") 127 c.Assert(out, checker.Contains, "ABC=xyz") 128 } 129 130 func (s *DockerSuite) TestExecExitStatus(c *check.C) { 131 runSleepingContainer(c, "-d", "--name", "top") 132 133 result := icmd.RunCommand(dockerBinary, "exec", "top", "sh", "-c", "exit 23") 134 c.Assert(result, icmd.Matches, icmd.Expected{ExitCode: 23, Error: "exit status 23"}) 135 } 136 137 func (s *DockerSuite) TestExecPausedContainer(c *check.C) { 138 testRequires(c, IsPausable) 139 defer unpauseAllContainers(c) 140 141 out, _ := runSleepingContainer(c, "-d", "--name", "testing") 142 ContainerID := strings.TrimSpace(out) 143 144 dockerCmd(c, "pause", "testing") 145 out, _, err := dockerCmdWithError("exec", "-i", "-t", ContainerID, "echo", "hello") 146 c.Assert(err, checker.NotNil, check.Commentf("container should fail to exec new command if it is paused")) 147 148 expected := ContainerID + " is paused, unpause the container before exec" 149 c.Assert(out, checker.Contains, expected, check.Commentf("container should not exec new command if it is paused")) 150 } 151 152 // regression test for #9476 153 func (s *DockerSuite) TestExecTTYCloseStdin(c *check.C) { 154 // TODO Windows CI: This requires some work to port to Windows. 155 testRequires(c, DaemonIsLinux) 156 dockerCmd(c, "run", "-d", "-it", "--name", "exec_tty_stdin", "busybox") 157 158 cmd := exec.Command(dockerBinary, "exec", "-i", "exec_tty_stdin", "cat") 159 stdinRw, err := cmd.StdinPipe() 160 c.Assert(err, checker.IsNil) 161 162 stdinRw.Write([]byte("test")) 163 stdinRw.Close() 164 165 out, _, err := runCommandWithOutput(cmd) 166 c.Assert(err, checker.IsNil, check.Commentf(out)) 167 168 out, _ = dockerCmd(c, "top", "exec_tty_stdin") 169 outArr := strings.Split(out, "\n") 170 c.Assert(len(outArr), checker.LessOrEqualThan, 3, check.Commentf("exec process left running")) 171 c.Assert(out, checker.Not(checker.Contains), "nsenter-exec") 172 } 173 174 func (s *DockerSuite) TestExecTTYWithoutStdin(c *check.C) { 175 out, _ := dockerCmd(c, "run", "-d", "-ti", "busybox") 176 id := strings.TrimSpace(out) 177 c.Assert(waitRun(id), checker.IsNil) 178 179 errChan := make(chan error) 180 go func() { 181 defer close(errChan) 182 183 cmd := exec.Command(dockerBinary, "exec", "-ti", id, "true") 184 if _, err := cmd.StdinPipe(); err != nil { 185 errChan <- err 186 return 187 } 188 189 expected := "the input device is not a TTY" 190 if runtime.GOOS == "windows" { 191 expected += ". If you are using mintty, try prefixing the command with 'winpty'" 192 } 193 if out, _, err := runCommandWithOutput(cmd); err == nil { 194 errChan <- fmt.Errorf("exec should have failed") 195 return 196 } else if !strings.Contains(out, expected) { 197 errChan <- fmt.Errorf("exec failed with error %q: expected %q", out, expected) 198 return 199 } 200 }() 201 202 select { 203 case err := <-errChan: 204 c.Assert(err, check.IsNil) 205 case <-time.After(3 * time.Second): 206 c.Fatal("exec is running but should have failed") 207 } 208 } 209 210 // FIXME(vdemeester) this should be a unit tests on cli/command/container package 211 func (s *DockerSuite) TestExecParseError(c *check.C) { 212 // TODO Windows CI: Requires some extra work. Consider copying the 213 // runSleepingContainer helper to have an exec version. 214 testRequires(c, DaemonIsLinux) 215 dockerCmd(c, "run", "-d", "--name", "top", "busybox", "top") 216 217 // Test normal (non-detached) case first 218 icmd.RunCommand(dockerBinary, "exec", "top").Assert(c, icmd.Expected{ 219 ExitCode: 1, 220 Error: "exit status 1", 221 Err: "See 'docker exec --help'", 222 }) 223 } 224 225 func (s *DockerSuite) TestExecStopNotHanging(c *check.C) { 226 // TODO Windows CI: Requires some extra work. Consider copying the 227 // runSleepingContainer helper to have an exec version. 228 testRequires(c, DaemonIsLinux) 229 dockerCmd(c, "run", "-d", "--name", "testing", "busybox", "top") 230 231 err := exec.Command(dockerBinary, "exec", "testing", "top").Start() 232 c.Assert(err, checker.IsNil) 233 234 type dstop struct { 235 out []byte 236 err error 237 } 238 239 ch := make(chan dstop) 240 go func() { 241 out, err := exec.Command(dockerBinary, "stop", "testing").CombinedOutput() 242 ch <- dstop{out, err} 243 close(ch) 244 }() 245 select { 246 case <-time.After(3 * time.Second): 247 c.Fatal("Container stop timed out") 248 case s := <-ch: 249 c.Assert(s.err, check.IsNil) 250 } 251 } 252 253 func (s *DockerSuite) TestExecCgroup(c *check.C) { 254 // Not applicable on Windows - using Linux specific functionality 255 testRequires(c, NotUserNamespace) 256 testRequires(c, DaemonIsLinux) 257 dockerCmd(c, "run", "-d", "--name", "testing", "busybox", "top") 258 259 out, _ := dockerCmd(c, "exec", "testing", "cat", "/proc/1/cgroup") 260 containerCgroups := sort.StringSlice(strings.Split(out, "\n")) 261 262 var wg sync.WaitGroup 263 var mu sync.Mutex 264 execCgroups := []sort.StringSlice{} 265 errChan := make(chan error) 266 // exec a few times concurrently to get consistent failure 267 for i := 0; i < 5; i++ { 268 wg.Add(1) 269 go func() { 270 out, _, err := dockerCmdWithError("exec", "testing", "cat", "/proc/self/cgroup") 271 if err != nil { 272 errChan <- err 273 return 274 } 275 cg := sort.StringSlice(strings.Split(out, "\n")) 276 277 mu.Lock() 278 execCgroups = append(execCgroups, cg) 279 mu.Unlock() 280 wg.Done() 281 }() 282 } 283 wg.Wait() 284 close(errChan) 285 286 for err := range errChan { 287 c.Assert(err, checker.IsNil) 288 } 289 290 for _, cg := range execCgroups { 291 if !reflect.DeepEqual(cg, containerCgroups) { 292 fmt.Println("exec cgroups:") 293 for _, name := range cg { 294 fmt.Printf(" %s\n", name) 295 } 296 297 fmt.Println("container cgroups:") 298 for _, name := range containerCgroups { 299 fmt.Printf(" %s\n", name) 300 } 301 c.Fatal("cgroups mismatched") 302 } 303 } 304 } 305 306 func (s *DockerSuite) TestExecInspectID(c *check.C) { 307 out, _ := runSleepingContainer(c, "-d") 308 id := strings.TrimSuffix(out, "\n") 309 310 out = inspectField(c, id, "ExecIDs") 311 c.Assert(out, checker.Equals, "[]", check.Commentf("ExecIDs should be empty, got: %s", out)) 312 313 // Start an exec, have it block waiting so we can do some checking 314 cmd := exec.Command(dockerBinary, "exec", id, "sh", "-c", 315 "while ! test -e /execid1; do sleep 1; done") 316 317 err := cmd.Start() 318 c.Assert(err, checker.IsNil, check.Commentf("failed to start the exec cmd")) 319 320 // Give the exec 10 chances/seconds to start then give up and stop the test 321 tries := 10 322 for i := 0; i < tries; i++ { 323 // Since its still running we should see exec as part of the container 324 out = strings.TrimSpace(inspectField(c, id, "ExecIDs")) 325 326 if out != "[]" && out != "<no value>" { 327 break 328 } 329 c.Assert(i+1, checker.Not(checker.Equals), tries, check.Commentf("ExecIDs still empty after 10 second")) 330 time.Sleep(1 * time.Second) 331 } 332 333 // Save execID for later 334 execID, err := inspectFilter(id, "index .ExecIDs 0") 335 c.Assert(err, checker.IsNil, check.Commentf("failed to get the exec id")) 336 337 // End the exec by creating the missing file 338 err = exec.Command(dockerBinary, "exec", id, 339 "sh", "-c", "touch /execid1").Run() 340 341 c.Assert(err, checker.IsNil, check.Commentf("failed to run the 2nd exec cmd")) 342 343 // Wait for 1st exec to complete 344 cmd.Wait() 345 346 // Give the exec 10 chances/seconds to stop then give up and stop the test 347 for i := 0; i < tries; i++ { 348 // Since its still running we should see exec as part of the container 349 out = strings.TrimSpace(inspectField(c, id, "ExecIDs")) 350 351 if out == "[]" { 352 break 353 } 354 c.Assert(i+1, checker.Not(checker.Equals), tries, check.Commentf("ExecIDs still not empty after 10 second")) 355 time.Sleep(1 * time.Second) 356 } 357 358 // But we should still be able to query the execID 359 sc, body, _ := request.SockRequest("GET", "/exec/"+execID+"/json", nil, daemonHost()) 360 361 c.Assert(sc, checker.Equals, http.StatusOK, check.Commentf("received status != 200 OK: %d\n%s", sc, body)) 362 363 // Now delete the container and then an 'inspect' on the exec should 364 // result in a 404 (not 'container not running') 365 out, ec := dockerCmd(c, "rm", "-f", id) 366 c.Assert(ec, checker.Equals, 0, check.Commentf("error removing container: %s", out)) 367 sc, body, _ = request.SockRequest("GET", "/exec/"+execID+"/json", nil, daemonHost()) 368 c.Assert(sc, checker.Equals, http.StatusNotFound, check.Commentf("received status != 404: %d\n%s", sc, body)) 369 } 370 371 func (s *DockerSuite) TestLinksPingLinkedContainersOnRename(c *check.C) { 372 // Problematic on Windows as Windows does not support links 373 testRequires(c, DaemonIsLinux) 374 var out string 375 out, _ = dockerCmd(c, "run", "-d", "--name", "container1", "busybox", "top") 376 idA := strings.TrimSpace(out) 377 c.Assert(idA, checker.Not(checker.Equals), "", check.Commentf("%s, id should not be nil", out)) 378 out, _ = dockerCmd(c, "run", "-d", "--link", "container1:alias1", "--name", "container2", "busybox", "top") 379 idB := strings.TrimSpace(out) 380 c.Assert(idB, checker.Not(checker.Equals), "", check.Commentf("%s, id should not be nil", out)) 381 382 dockerCmd(c, "exec", "container2", "ping", "-c", "1", "alias1", "-W", "1") 383 dockerCmd(c, "rename", "container1", "container_new") 384 dockerCmd(c, "exec", "container2", "ping", "-c", "1", "alias1", "-W", "1") 385 } 386 387 func (s *DockerSuite) TestRunMutableNetworkFiles(c *check.C) { 388 // Not applicable on Windows to Windows CI. 389 testRequires(c, SameHostDaemon, DaemonIsLinux) 390 for _, fn := range []string{"resolv.conf", "hosts"} { 391 deleteAllContainers(c) 392 393 content := runCommandAndReadContainerFile(c, fn, dockerBinary, "run", "-d", "--name", "c1", "busybox", "sh", "-c", fmt.Sprintf("echo success >/etc/%s && top", fn)) 394 395 c.Assert(strings.TrimSpace(string(content)), checker.Equals, "success", check.Commentf("Content was not what was modified in the container", string(content))) 396 397 out, _ := dockerCmd(c, "run", "-d", "--name", "c2", "busybox", "top") 398 contID := strings.TrimSpace(out) 399 netFilePath := containerStorageFile(contID, fn) 400 401 f, err := os.OpenFile(netFilePath, os.O_WRONLY|os.O_SYNC|os.O_APPEND, 0644) 402 c.Assert(err, checker.IsNil) 403 404 if _, err := f.Seek(0, 0); err != nil { 405 f.Close() 406 c.Fatal(err) 407 } 408 409 if err := f.Truncate(0); err != nil { 410 f.Close() 411 c.Fatal(err) 412 } 413 414 if _, err := f.Write([]byte("success2\n")); err != nil { 415 f.Close() 416 c.Fatal(err) 417 } 418 f.Close() 419 420 res, _ := dockerCmd(c, "exec", contID, "cat", "/etc/"+fn) 421 c.Assert(res, checker.Equals, "success2\n") 422 } 423 } 424 425 func (s *DockerSuite) TestExecWithUser(c *check.C) { 426 // TODO Windows CI: This may be fixable in the future once Windows 427 // supports users 428 testRequires(c, DaemonIsLinux) 429 dockerCmd(c, "run", "-d", "--name", "parent", "busybox", "top") 430 431 out, _ := dockerCmd(c, "exec", "-u", "1", "parent", "id") 432 c.Assert(out, checker.Contains, "uid=1(daemon) gid=1(daemon)") 433 434 out, _ = dockerCmd(c, "exec", "-u", "root", "parent", "id") 435 c.Assert(out, checker.Contains, "uid=0(root) gid=0(root)", check.Commentf("exec with user by id expected daemon user got %s", out)) 436 } 437 438 func (s *DockerSuite) TestExecWithPrivileged(c *check.C) { 439 // Not applicable on Windows 440 testRequires(c, DaemonIsLinux, NotUserNamespace) 441 // Start main loop which attempts mknod repeatedly 442 dockerCmd(c, "run", "-d", "--name", "parent", "--cap-drop=ALL", "busybox", "sh", "-c", `while (true); do if [ -e /exec_priv ]; then cat /exec_priv && mknod /tmp/sda b 8 0 && echo "Success"; else echo "Privileged exec has not run yet"; fi; usleep 10000; done`) 443 444 // Check exec mknod doesn't work 445 icmd.RunCommand(dockerBinary, "exec", "parent", "sh", "-c", "mknod /tmp/sdb b 8 16").Assert(c, icmd.Expected{ 446 ExitCode: 1, 447 Err: "Operation not permitted", 448 }) 449 450 // Check exec mknod does work with --privileged 451 result := icmd.RunCommand(dockerBinary, "exec", "--privileged", "parent", "sh", "-c", `echo "Running exec --privileged" > /exec_priv && mknod /tmp/sdb b 8 16 && usleep 50000 && echo "Finished exec --privileged" > /exec_priv && echo ok`) 452 result.Assert(c, icmd.Success) 453 454 actual := strings.TrimSpace(result.Combined()) 455 c.Assert(actual, checker.Equals, "ok", check.Commentf("exec mknod in --cap-drop=ALL container with --privileged failed, output: %q", result.Combined())) 456 457 // Check subsequent unprivileged exec cannot mknod 458 icmd.RunCommand(dockerBinary, "exec", "parent", "sh", "-c", "mknod /tmp/sdc b 8 32").Assert(c, icmd.Expected{ 459 ExitCode: 1, 460 Err: "Operation not permitted", 461 }) 462 // Confirm at no point was mknod allowed 463 result = icmd.RunCommand(dockerBinary, "logs", "parent") 464 result.Assert(c, icmd.Success) 465 c.Assert(result.Combined(), checker.Not(checker.Contains), "Success") 466 467 } 468 469 func (s *DockerSuite) TestExecWithImageUser(c *check.C) { 470 // Not applicable on Windows 471 testRequires(c, DaemonIsLinux) 472 name := "testbuilduser" 473 buildImageSuccessfully(c, name, withDockerfile(`FROM busybox 474 RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd 475 USER dockerio`)) 476 dockerCmd(c, "run", "-d", "--name", "dockerioexec", name, "top") 477 478 out, _ := dockerCmd(c, "exec", "dockerioexec", "whoami") 479 c.Assert(out, checker.Contains, "dockerio", check.Commentf("exec with user by id expected dockerio user got %s", out)) 480 } 481 482 func (s *DockerSuite) TestExecOnReadonlyContainer(c *check.C) { 483 // Windows does not support read-only 484 // --read-only + userns has remount issues 485 testRequires(c, DaemonIsLinux, NotUserNamespace) 486 dockerCmd(c, "run", "-d", "--read-only", "--name", "parent", "busybox", "top") 487 dockerCmd(c, "exec", "parent", "true") 488 } 489 490 func (s *DockerSuite) TestExecUlimits(c *check.C) { 491 testRequires(c, DaemonIsLinux) 492 name := "testexeculimits" 493 runSleepingContainer(c, "-d", "--ulimit", "nproc=21", "--name", name) 494 c.Assert(waitRun(name), checker.IsNil) 495 496 out, _, err := dockerCmdWithError("exec", name, "sh", "-c", "ulimit -p") 497 c.Assert(err, checker.IsNil) 498 c.Assert(strings.TrimSpace(out), checker.Equals, "21") 499 } 500 501 // #15750 502 func (s *DockerSuite) TestExecStartFails(c *check.C) { 503 // TODO Windows CI. This test should be portable. Figure out why it fails 504 // currently. 505 testRequires(c, DaemonIsLinux) 506 name := "exec-15750" 507 runSleepingContainer(c, "-d", "--name", name) 508 c.Assert(waitRun(name), checker.IsNil) 509 510 out, _, err := dockerCmdWithError("exec", name, "no-such-cmd") 511 c.Assert(err, checker.NotNil, check.Commentf(out)) 512 c.Assert(out, checker.Contains, "executable file not found") 513 } 514 515 // Fix regression in https://github.com/docker/docker/pull/26461#issuecomment-250287297 516 func (s *DockerSuite) TestExecWindowsPathNotWiped(c *check.C) { 517 testRequires(c, DaemonIsWindows) 518 out, _ := dockerCmd(c, "run", "-d", "--name", "testing", minimalBaseImage(), "powershell", "start-sleep", "60") 519 c.Assert(waitRun(strings.TrimSpace(out)), check.IsNil) 520 521 out, _ = dockerCmd(c, "exec", "testing", "powershell", "write-host", "$env:PATH") 522 out = strings.ToLower(strings.Trim(out, "\r\n")) 523 c.Assert(out, checker.Contains, `windowspowershell\v1.0`) 524 } 525 526 func (s *DockerSuite) TestExecEnvLinksHost(c *check.C) { 527 testRequires(c, DaemonIsLinux) 528 runSleepingContainer(c, "-d", "--name", "foo") 529 runSleepingContainer(c, "-d", "--link", "foo:db", "--hostname", "myhost", "--name", "bar") 530 out, _ := dockerCmd(c, "exec", "bar", "env") 531 c.Assert(out, checker.Contains, "HOSTNAME=myhost") 532 c.Assert(out, checker.Contains, "DB_NAME=/bar/db") 533 } 534 535 func (s *DockerSuite) TestExecWindowsOpenHandles(c *check.C) { 536 testRequires(c, DaemonIsWindows) 537 runSleepingContainer(c, "-d", "--name", "test") 538 exec := make(chan bool) 539 go func() { 540 dockerCmd(c, "exec", "test", "cmd", "/c", "start sleep 10") 541 exec <- true 542 }() 543 544 for { 545 top := make(chan string) 546 var out string 547 go func() { 548 out, _ := dockerCmd(c, "top", "test") 549 top <- out 550 }() 551 552 select { 553 case <-time.After(time.Second * 5): 554 c.Error("timed out waiting for top while exec is exiting") 555 case out = <-top: 556 break 557 } 558 559 if strings.Count(out, "busybox.exe") == 2 && !strings.Contains(out, "cmd.exe") { 560 // The initial exec process (cmd.exe) has exited, and both sleeps are currently running 561 break 562 } 563 time.Sleep(1 * time.Second) 564 } 565 566 inspect := make(chan bool) 567 go func() { 568 dockerCmd(c, "inspect", "test") 569 inspect <- true 570 }() 571 572 select { 573 case <-time.After(time.Second * 5): 574 c.Error("timed out waiting for inspect while exec is exiting") 575 case <-inspect: 576 break 577 } 578 579 // Ensure the background sleep is still running 580 out, _ := dockerCmd(c, "top", "test") 581 c.Assert(strings.Count(out, "busybox.exe"), checker.Equals, 2) 582 583 // The exec should exit when the background sleep exits 584 select { 585 case <-time.After(time.Second * 15): 586 c.Error("timed out waiting for async exec to exit") 587 case <-exec: 588 // Ensure the background sleep has actually exited 589 out, _ := dockerCmd(c, "top", "test") 590 c.Assert(strings.Count(out, "busybox.exe"), checker.Equals, 1) 591 break 592 } 593 }