github.com/jwhonce/docker@v0.6.7-0.20190327063223-da823cf3a5a3/integration-cli/docker_cli_exec_test.go (about) 1 // +build !test_no_exec 2 3 package main 4 5 import ( 6 "bufio" 7 "context" 8 "fmt" 9 "os" 10 "os/exec" 11 "reflect" 12 "runtime" 13 "sort" 14 "strconv" 15 "strings" 16 "sync" 17 "time" 18 19 "github.com/docker/docker/client" 20 "github.com/docker/docker/integration-cli/checker" 21 "github.com/docker/docker/integration-cli/cli" 22 "github.com/docker/docker/integration-cli/cli/build" 23 "github.com/docker/docker/pkg/parsers/kernel" 24 "github.com/go-check/check" 25 "gotest.tools/icmd" 26 ) 27 28 func (s *DockerSuite) TestExec(c *check.C) { 29 testRequires(c, DaemonIsLinux) 30 out, _ := dockerCmd(c, "run", "-d", "--name", "testing", "busybox", "sh", "-c", "echo test > /tmp/file && top") 31 c.Assert(waitRun(strings.TrimSpace(out)), check.IsNil) 32 33 out, _ = dockerCmd(c, "exec", "testing", "cat", "/tmp/file") 34 out = strings.Trim(out, "\r\n") 35 c.Assert(out, checker.Equals, "test") 36 37 } 38 39 func (s *DockerSuite) TestExecInteractive(c *check.C) { 40 testRequires(c, DaemonIsLinux) 41 dockerCmd(c, "run", "-d", "--name", "testing", "busybox", "sh", "-c", "echo test > /tmp/file && top") 42 43 execCmd := exec.Command(dockerBinary, "exec", "-i", "testing", "sh") 44 stdin, err := execCmd.StdinPipe() 45 c.Assert(err, checker.IsNil) 46 stdout, err := execCmd.StdoutPipe() 47 c.Assert(err, checker.IsNil) 48 49 err = execCmd.Start() 50 c.Assert(err, checker.IsNil) 51 _, err = stdin.Write([]byte("cat /tmp/file\n")) 52 c.Assert(err, checker.IsNil) 53 54 r := bufio.NewReader(stdout) 55 line, err := r.ReadString('\n') 56 c.Assert(err, checker.IsNil) 57 line = strings.TrimSpace(line) 58 c.Assert(line, checker.Equals, "test") 59 err = stdin.Close() 60 c.Assert(err, checker.IsNil) 61 errChan := make(chan error) 62 go func() { 63 errChan <- execCmd.Wait() 64 close(errChan) 65 }() 66 select { 67 case err := <-errChan: 68 c.Assert(err, checker.IsNil) 69 case <-time.After(1 * time.Second): 70 c.Fatal("docker exec failed to exit on stdin close") 71 } 72 73 } 74 75 func (s *DockerSuite) TestExecAfterContainerRestart(c *check.C) { 76 out := runSleepingContainer(c) 77 cleanedContainerID := strings.TrimSpace(out) 78 c.Assert(waitRun(cleanedContainerID), check.IsNil) 79 dockerCmd(c, "restart", cleanedContainerID) 80 c.Assert(waitRun(cleanedContainerID), check.IsNil) 81 82 out, _ = dockerCmd(c, "exec", cleanedContainerID, "echo", "hello") 83 outStr := strings.TrimSpace(out) 84 c.Assert(outStr, checker.Equals, "hello") 85 } 86 87 func (s *DockerDaemonSuite) TestExecAfterDaemonRestart(c *check.C) { 88 // TODO Windows CI: Requires a little work to get this ported. 89 testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon) 90 s.d.StartWithBusybox(c) 91 92 out, err := s.d.Cmd("run", "-d", "--name", "top", "-p", "80", "busybox:latest", "top") 93 c.Assert(err, checker.IsNil, check.Commentf("Could not run top: %s", out)) 94 95 s.d.Restart(c) 96 97 out, err = s.d.Cmd("start", "top") 98 c.Assert(err, checker.IsNil, check.Commentf("Could not start top after daemon restart: %s", out)) 99 100 out, err = s.d.Cmd("exec", "top", "echo", "hello") 101 c.Assert(err, checker.IsNil, check.Commentf("Could not exec on container top: %s", out)) 102 103 outStr := strings.TrimSpace(string(out)) 104 c.Assert(outStr, checker.Equals, "hello") 105 } 106 107 // Regression test for #9155, #9044 108 func (s *DockerSuite) TestExecEnv(c *check.C) { 109 // TODO Windows CI: This one is interesting and may just end up being a feature 110 // difference between Windows and Linux. On Windows, the environment is passed 111 // into the process that is launched, not into the machine environment. Hence 112 // a subsequent exec will not have LALA set/ 113 testRequires(c, DaemonIsLinux) 114 runSleepingContainer(c, "-e", "LALA=value1", "-e", "LALA=value2", "-d", "--name", "testing") 115 c.Assert(waitRun("testing"), check.IsNil) 116 117 out, _ := dockerCmd(c, "exec", "testing", "env") 118 c.Assert(out, checker.Not(checker.Contains), "LALA=value1") 119 c.Assert(out, checker.Contains, "LALA=value2") 120 c.Assert(out, checker.Contains, "HOME=/root") 121 } 122 123 func (s *DockerSuite) TestExecSetEnv(c *check.C) { 124 testRequires(c, DaemonIsLinux) 125 runSleepingContainer(c, "-e", "HOME=/root", "-d", "--name", "testing") 126 c.Assert(waitRun("testing"), check.IsNil) 127 128 out, _ := dockerCmd(c, "exec", "-e", "HOME=/another", "-e", "ABC=xyz", "testing", "env") 129 c.Assert(out, checker.Not(checker.Contains), "HOME=/root") 130 c.Assert(out, checker.Contains, "HOME=/another") 131 c.Assert(out, checker.Contains, "ABC=xyz") 132 } 133 134 func (s *DockerSuite) TestExecExitStatus(c *check.C) { 135 runSleepingContainer(c, "-d", "--name", "top") 136 137 result := icmd.RunCommand(dockerBinary, "exec", "top", "sh", "-c", "exit 23") 138 result.Assert(c, icmd.Expected{ExitCode: 23, Error: "exit status 23"}) 139 } 140 141 func (s *DockerSuite) TestExecPausedContainer(c *check.C) { 142 testRequires(c, IsPausable) 143 144 out := runSleepingContainer(c, "-d", "--name", "testing") 145 ContainerID := strings.TrimSpace(out) 146 147 dockerCmd(c, "pause", "testing") 148 out, _, err := dockerCmdWithError("exec", ContainerID, "echo", "hello") 149 c.Assert(err, checker.NotNil, check.Commentf("container should fail to exec new command 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("%s", 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 // FIXME(vdemeester) this should be a unit tests on cli/command/container package 214 func (s *DockerSuite) TestExecParseError(c *check.C) { 215 // TODO Windows CI: Requires some extra work. Consider copying the 216 // runSleepingContainer helper to have an exec version. 217 testRequires(c, DaemonIsLinux) 218 dockerCmd(c, "run", "-d", "--name", "top", "busybox", "top") 219 220 // Test normal (non-detached) case first 221 icmd.RunCommand(dockerBinary, "exec", "top").Assert(c, icmd.Expected{ 222 ExitCode: 1, 223 Error: "exit status 1", 224 Err: "See 'docker exec --help'", 225 }) 226 } 227 228 func (s *DockerSuite) TestExecStopNotHanging(c *check.C) { 229 // TODO Windows CI: Requires some extra work. Consider copying the 230 // runSleepingContainer helper to have an exec version. 231 testRequires(c, DaemonIsLinux) 232 dockerCmd(c, "run", "-d", "--name", "testing", "busybox", "top") 233 234 result := icmd.StartCmd(icmd.Command(dockerBinary, "exec", "testing", "top")) 235 result.Assert(c, icmd.Success) 236 go icmd.WaitOnCmd(0, result) 237 238 type dstop struct { 239 out string 240 err error 241 } 242 ch := make(chan dstop) 243 go func() { 244 result := icmd.RunCommand(dockerBinary, "stop", "testing") 245 ch <- dstop{result.Combined(), result.Error} 246 close(ch) 247 }() 248 select { 249 case <-time.After(3 * time.Second): 250 c.Fatal("Container stop timed out") 251 case s := <-ch: 252 c.Assert(s.err, check.IsNil) 253 } 254 } 255 256 func (s *DockerSuite) TestExecCgroup(c *check.C) { 257 // Not applicable on Windows - using Linux specific functionality 258 testRequires(c, NotUserNamespace) 259 testRequires(c, DaemonIsLinux) 260 dockerCmd(c, "run", "-d", "--name", "testing", "busybox", "top") 261 262 out, _ := dockerCmd(c, "exec", "testing", "cat", "/proc/1/cgroup") 263 containerCgroups := sort.StringSlice(strings.Split(out, "\n")) 264 265 var wg sync.WaitGroup 266 var mu sync.Mutex 267 var execCgroups []sort.StringSlice 268 errChan := make(chan error) 269 // exec a few times concurrently to get consistent failure 270 for i := 0; i < 5; i++ { 271 wg.Add(1) 272 go func() { 273 out, _, err := dockerCmdWithError("exec", "testing", "cat", "/proc/self/cgroup") 274 if err != nil { 275 errChan <- err 276 return 277 } 278 cg := sort.StringSlice(strings.Split(out, "\n")) 279 280 mu.Lock() 281 execCgroups = append(execCgroups, cg) 282 mu.Unlock() 283 wg.Done() 284 }() 285 } 286 wg.Wait() 287 close(errChan) 288 289 for err := range errChan { 290 c.Assert(err, checker.IsNil) 291 } 292 293 for _, cg := range execCgroups { 294 if !reflect.DeepEqual(cg, containerCgroups) { 295 fmt.Println("exec cgroups:") 296 for _, name := range cg { 297 fmt.Printf(" %s\n", name) 298 } 299 300 fmt.Println("container cgroups:") 301 for _, name := range containerCgroups { 302 fmt.Printf(" %s\n", name) 303 } 304 c.Fatal("cgroups mismatched") 305 } 306 } 307 } 308 309 func (s *DockerSuite) TestExecInspectID(c *check.C) { 310 out := runSleepingContainer(c, "-d") 311 id := strings.TrimSuffix(out, "\n") 312 313 out = inspectField(c, id, "ExecIDs") 314 c.Assert(out, checker.Equals, "[]", check.Commentf("ExecIDs should be empty, got: %s", out)) 315 316 // Start an exec, have it block waiting so we can do some checking 317 cmd := exec.Command(dockerBinary, "exec", id, "sh", "-c", 318 "while ! test -e /execid1; do sleep 1; done") 319 320 err := cmd.Start() 321 c.Assert(err, checker.IsNil, check.Commentf("failed to start the exec cmd")) 322 323 // Give the exec 10 chances/seconds to start then give up and stop the test 324 tries := 10 325 for i := 0; i < tries; i++ { 326 // Since its still running we should see exec as part of the container 327 out = strings.TrimSpace(inspectField(c, id, "ExecIDs")) 328 329 if out != "[]" && out != "<no value>" { 330 break 331 } 332 c.Assert(i+1, checker.Not(checker.Equals), tries, check.Commentf("ExecIDs still empty after 10 second")) 333 time.Sleep(1 * time.Second) 334 } 335 336 // Save execID for later 337 execID, err := inspectFilter(id, "index .ExecIDs 0") 338 c.Assert(err, checker.IsNil, check.Commentf("failed to get the exec id")) 339 340 // End the exec by creating the missing file 341 err = exec.Command(dockerBinary, "exec", id, 342 "sh", "-c", "touch /execid1").Run() 343 344 c.Assert(err, checker.IsNil, check.Commentf("failed to run the 2nd exec cmd")) 345 346 // Wait for 1st exec to complete 347 cmd.Wait() 348 349 // Give the exec 10 chances/seconds to stop then give up and stop the test 350 for i := 0; i < tries; i++ { 351 // Since its still running we should see exec as part of the container 352 out = strings.TrimSpace(inspectField(c, id, "ExecIDs")) 353 354 if out == "[]" { 355 break 356 } 357 c.Assert(i+1, checker.Not(checker.Equals), tries, check.Commentf("ExecIDs still not empty after 10 second")) 358 time.Sleep(1 * time.Second) 359 } 360 361 // But we should still be able to query the execID 362 cli, err := client.NewClientWithOpts(client.FromEnv) 363 c.Assert(err, checker.IsNil) 364 defer cli.Close() 365 366 _, err = cli.ContainerExecInspect(context.Background(), execID) 367 c.Assert(err, checker.IsNil) 368 369 // Now delete the container and then an 'inspect' on the exec should 370 // result in a 404 (not 'container not running') 371 out, ec := dockerCmd(c, "rm", "-f", id) 372 c.Assert(ec, checker.Equals, 0, check.Commentf("error removing container: %s", out)) 373 374 _, err = cli.ContainerExecInspect(context.Background(), execID) 375 expected := "No such exec instance" 376 c.Assert(err.Error(), checker.Contains, expected) 377 } 378 379 func (s *DockerSuite) TestLinksPingLinkedContainersOnRename(c *check.C) { 380 // Problematic on Windows as Windows does not support links 381 testRequires(c, DaemonIsLinux) 382 var out string 383 out, _ = dockerCmd(c, "run", "-d", "--name", "container1", "busybox", "top") 384 idA := strings.TrimSpace(out) 385 c.Assert(idA, checker.Not(checker.Equals), "", check.Commentf("%s, id should not be nil", out)) 386 out, _ = dockerCmd(c, "run", "-d", "--link", "container1:alias1", "--name", "container2", "busybox", "top") 387 idB := strings.TrimSpace(out) 388 c.Assert(idB, checker.Not(checker.Equals), "", check.Commentf("%s, id should not be nil", out)) 389 390 dockerCmd(c, "exec", "container2", "ping", "-c", "1", "alias1", "-W", "1") 391 dockerCmd(c, "rename", "container1", "container_new") 392 dockerCmd(c, "exec", "container2", "ping", "-c", "1", "alias1", "-W", "1") 393 } 394 395 func (s *DockerSuite) TestRunMutableNetworkFiles(c *check.C) { 396 // Not applicable on Windows to Windows CI. 397 testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux) 398 for _, fn := range []string{"resolv.conf", "hosts"} { 399 containers := cli.DockerCmd(c, "ps", "-q", "-a").Combined() 400 if containers != "" { 401 cli.DockerCmd(c, append([]string{"rm", "-fv"}, strings.Split(strings.TrimSpace(containers), "\n")...)...) 402 } 403 404 content := runCommandAndReadContainerFile(c, fn, dockerBinary, "run", "-d", "--name", "c1", "busybox", "sh", "-c", fmt.Sprintf("echo success >/etc/%s && top", fn)) 405 406 c.Assert(strings.TrimSpace(string(content)), checker.Equals, "success", check.Commentf("Content was not what was modified in the container", string(content))) 407 408 out, _ := dockerCmd(c, "run", "-d", "--name", "c2", "busybox", "top") 409 contID := strings.TrimSpace(out) 410 netFilePath := containerStorageFile(contID, fn) 411 412 f, err := os.OpenFile(netFilePath, os.O_WRONLY|os.O_SYNC|os.O_APPEND, 0644) 413 c.Assert(err, checker.IsNil) 414 415 if _, err := f.Seek(0, 0); err != nil { 416 f.Close() 417 c.Fatal(err) 418 } 419 420 if err := f.Truncate(0); err != nil { 421 f.Close() 422 c.Fatal(err) 423 } 424 425 if _, err := f.Write([]byte("success2\n")); err != nil { 426 f.Close() 427 c.Fatal(err) 428 } 429 f.Close() 430 431 res, _ := dockerCmd(c, "exec", contID, "cat", "/etc/"+fn) 432 c.Assert(res, checker.Equals, "success2\n") 433 } 434 } 435 436 func (s *DockerSuite) TestExecWithUser(c *check.C) { 437 // TODO Windows CI: This may be fixable in the future once Windows 438 // supports users 439 testRequires(c, DaemonIsLinux) 440 dockerCmd(c, "run", "-d", "--name", "parent", "busybox", "top") 441 442 out, _ := dockerCmd(c, "exec", "-u", "1", "parent", "id") 443 c.Assert(out, checker.Contains, "uid=1(daemon) gid=1(daemon)") 444 445 out, _ = dockerCmd(c, "exec", "-u", "root", "parent", "id") 446 c.Assert(out, checker.Contains, "uid=0(root) gid=0(root)", check.Commentf("exec with user by id expected daemon user got %s", out)) 447 } 448 449 func (s *DockerSuite) TestExecWithPrivileged(c *check.C) { 450 // Not applicable on Windows 451 testRequires(c, DaemonIsLinux, NotUserNamespace) 452 // Start main loop which attempts mknod repeatedly 453 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`) 454 455 // Check exec mknod doesn't work 456 icmd.RunCommand(dockerBinary, "exec", "parent", "sh", "-c", "mknod /tmp/sdb b 8 16").Assert(c, icmd.Expected{ 457 ExitCode: 1, 458 Err: "Operation not permitted", 459 }) 460 461 // Check exec mknod does work with --privileged 462 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`) 463 result.Assert(c, icmd.Success) 464 465 actual := strings.TrimSpace(result.Combined()) 466 c.Assert(actual, checker.Equals, "ok", check.Commentf("exec mknod in --cap-drop=ALL container with --privileged failed, output: %q", result.Combined())) 467 468 // Check subsequent unprivileged exec cannot mknod 469 icmd.RunCommand(dockerBinary, "exec", "parent", "sh", "-c", "mknod /tmp/sdc b 8 32").Assert(c, icmd.Expected{ 470 ExitCode: 1, 471 Err: "Operation not permitted", 472 }) 473 // Confirm at no point was mknod allowed 474 result = icmd.RunCommand(dockerBinary, "logs", "parent") 475 result.Assert(c, icmd.Success) 476 c.Assert(result.Combined(), checker.Not(checker.Contains), "Success") 477 478 } 479 480 func (s *DockerSuite) TestExecWithImageUser(c *check.C) { 481 // Not applicable on Windows 482 testRequires(c, DaemonIsLinux) 483 name := "testbuilduser" 484 buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox 485 RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd 486 USER dockerio`)) 487 dockerCmd(c, "run", "-d", "--name", "dockerioexec", name, "top") 488 489 out, _ := dockerCmd(c, "exec", "dockerioexec", "whoami") 490 c.Assert(out, checker.Contains, "dockerio", check.Commentf("exec with user by id expected dockerio user got %s", out)) 491 } 492 493 func (s *DockerSuite) TestExecOnReadonlyContainer(c *check.C) { 494 // Windows does not support read-only 495 // --read-only + userns has remount issues 496 testRequires(c, DaemonIsLinux, NotUserNamespace) 497 dockerCmd(c, "run", "-d", "--read-only", "--name", "parent", "busybox", "top") 498 dockerCmd(c, "exec", "parent", "true") 499 } 500 501 func (s *DockerSuite) TestExecUlimits(c *check.C) { 502 testRequires(c, DaemonIsLinux) 503 name := "testexeculimits" 504 runSleepingContainer(c, "-d", "--ulimit", "nofile=511:511", "--name", name) 505 c.Assert(waitRun(name), checker.IsNil) 506 507 out, _, err := dockerCmdWithError("exec", name, "sh", "-c", "ulimit -n") 508 c.Assert(err, checker.IsNil) 509 c.Assert(strings.TrimSpace(out), checker.Equals, "511") 510 } 511 512 // #15750 513 func (s *DockerSuite) TestExecStartFails(c *check.C) { 514 // TODO Windows CI. This test should be portable. Figure out why it fails 515 // currently. 516 testRequires(c, DaemonIsLinux) 517 name := "exec-15750" 518 runSleepingContainer(c, "-d", "--name", name) 519 c.Assert(waitRun(name), checker.IsNil) 520 521 out, _, err := dockerCmdWithError("exec", name, "no-such-cmd") 522 c.Assert(err, checker.NotNil, check.Commentf("%s", out)) 523 c.Assert(out, checker.Contains, "executable file not found") 524 } 525 526 // Fix regression in https://github.com/docker/docker/pull/26461#issuecomment-250287297 527 func (s *DockerSuite) TestExecWindowsPathNotWiped(c *check.C) { 528 testRequires(c, DaemonIsWindows) 529 out, _ := dockerCmd(c, "run", "-d", "--name", "testing", minimalBaseImage(), "powershell", "start-sleep", "60") 530 c.Assert(waitRun(strings.TrimSpace(out)), check.IsNil) 531 532 out, _ = dockerCmd(c, "exec", "testing", "powershell", "write-host", "$env:PATH") 533 out = strings.ToLower(strings.Trim(out, "\r\n")) 534 c.Assert(out, checker.Contains, `windowspowershell\v1.0`) 535 } 536 537 func (s *DockerSuite) TestExecEnvLinksHost(c *check.C) { 538 testRequires(c, DaemonIsLinux) 539 runSleepingContainer(c, "-d", "--name", "foo") 540 runSleepingContainer(c, "-d", "--link", "foo:db", "--hostname", "myhost", "--name", "bar") 541 out, _ := dockerCmd(c, "exec", "bar", "env") 542 c.Assert(out, checker.Contains, "HOSTNAME=myhost") 543 c.Assert(out, checker.Contains, "DB_NAME=/bar/db") 544 } 545 546 func (s *DockerSuite) TestExecWindowsOpenHandles(c *check.C) { 547 testRequires(c, DaemonIsWindows) 548 549 if runtime.GOOS == "windows" { 550 v, err := kernel.GetKernelVersion() 551 c.Assert(err, checker.IsNil) 552 build, _ := strconv.Atoi(strings.Split(strings.SplitN(v.String(), " ", 3)[2][1:], ".")[0]) 553 if build >= 17743 { 554 c.Skip("Temporarily disabled on RS5 17743+ builds due to platform bug") 555 556 // This is being tracked internally. @jhowardmsft. Summary of failure 557 // from an email in early July 2018 below: 558 // 559 // Platform regression. In cmd.exe by the look of it. I can repro 560 // it outside of CI. It fails the same on 17681, 17676 and even as 561 // far back as 17663, over a month old. From investigating, I can see 562 // what's happening in the container, but not the reason. The test 563 // starts a long-running container based on the Windows busybox image. 564 // It then adds another process (docker exec) to that container to 565 // sleep. It loops waiting for two instances of busybox.exe running, 566 // and cmd.exe to quit. What's actually happening is that the second 567 // exec hangs indefinitely, and from docker top, I can see 568 // "OpenWith.exe" running. 569 570 //Manual repro would be 571 //# Start the first long-running container 572 //docker run --rm -d --name test busybox sleep 300 573 574 //# In another window, docker top test. There should be a single instance of busybox.exe running 575 //# In a third window, docker exec test cmd /c start sleep 10 NOTE THIS HANGS UNTIL 5 MIN TIMEOUT 576 //# In the second window, run docker top test. Note that OpenWith.exe is running, one cmd.exe and only one busybox. I would expect no "OpenWith" and two busybox.exe's. 577 } 578 } 579 580 runSleepingContainer(c, "-d", "--name", "test") 581 exec := make(chan bool) 582 go func() { 583 dockerCmd(c, "exec", "test", "cmd", "/c", "start sleep 10") 584 exec <- true 585 }() 586 587 count := 0 588 for { 589 top := make(chan string) 590 var out string 591 go func() { 592 out, _ := dockerCmd(c, "top", "test") 593 top <- out 594 }() 595 596 select { 597 case <-time.After(time.Second * 5): 598 c.Fatal("timed out waiting for top while exec is exiting") 599 case out = <-top: 600 break 601 } 602 603 if strings.Count(out, "busybox.exe") == 2 && !strings.Contains(out, "cmd.exe") { 604 // The initial exec process (cmd.exe) has exited, and both sleeps are currently running 605 break 606 } 607 count++ 608 if count >= 30 { 609 c.Fatal("too many retries") 610 } 611 time.Sleep(1 * time.Second) 612 } 613 614 inspect := make(chan bool) 615 go func() { 616 dockerCmd(c, "inspect", "test") 617 inspect <- true 618 }() 619 620 select { 621 case <-time.After(time.Second * 5): 622 c.Fatal("timed out waiting for inspect while exec is exiting") 623 case <-inspect: 624 break 625 } 626 627 // Ensure the background sleep is still running 628 out, _ := dockerCmd(c, "top", "test") 629 c.Assert(strings.Count(out, "busybox.exe"), checker.Equals, 2) 630 631 // The exec should exit when the background sleep exits 632 select { 633 case <-time.After(time.Second * 15): 634 c.Fatal("timed out waiting for async exec to exit") 635 case <-exec: 636 // Ensure the background sleep has actually exited 637 out, _ := dockerCmd(c, "top", "test") 638 c.Assert(strings.Count(out, "busybox.exe"), checker.Equals, 1) 639 break 640 } 641 }