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