github.com/noxiouz/docker@v0.7.3-0.20160629055221-3d231c78e8c5/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 "github.com/go-check/check" 20 ) 21 22 func (s *DockerSuite) TestExec(c *check.C) { 23 testRequires(c, DaemonIsLinux) 24 out, _ := dockerCmd(c, "run", "-d", "--name", "testing", "busybox", "sh", "-c", "echo test > /tmp/file && top") 25 c.Assert(waitRun(strings.TrimSpace(out)), check.IsNil) 26 27 out, _ = dockerCmd(c, "exec", "testing", "cat", "/tmp/file") 28 out = strings.Trim(out, "\r\n") 29 c.Assert(out, checker.Equals, "test") 30 31 } 32 33 func (s *DockerSuite) TestExecInteractive(c *check.C) { 34 testRequires(c, DaemonIsLinux) 35 dockerCmd(c, "run", "-d", "--name", "testing", "busybox", "sh", "-c", "echo test > /tmp/file && top") 36 37 execCmd := exec.Command(dockerBinary, "exec", "-i", "testing", "sh") 38 stdin, err := execCmd.StdinPipe() 39 c.Assert(err, checker.IsNil) 40 stdout, err := execCmd.StdoutPipe() 41 c.Assert(err, checker.IsNil) 42 43 err = execCmd.Start() 44 c.Assert(err, checker.IsNil) 45 _, err = stdin.Write([]byte("cat /tmp/file\n")) 46 c.Assert(err, checker.IsNil) 47 48 r := bufio.NewReader(stdout) 49 line, err := r.ReadString('\n') 50 c.Assert(err, checker.IsNil) 51 line = strings.TrimSpace(line) 52 c.Assert(line, checker.Equals, "test") 53 err = stdin.Close() 54 c.Assert(err, checker.IsNil) 55 errChan := make(chan error) 56 go func() { 57 errChan <- execCmd.Wait() 58 close(errChan) 59 }() 60 select { 61 case err := <-errChan: 62 c.Assert(err, checker.IsNil) 63 case <-time.After(1 * time.Second): 64 c.Fatal("docker exec failed to exit on stdin close") 65 } 66 67 } 68 69 func (s *DockerSuite) TestExecAfterContainerRestart(c *check.C) { 70 testRequires(c, DaemonIsLinux) 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) TestExecExitStatus(c *check.C) { 123 runSleepingContainer(c, "-d", "--name", "top") 124 125 // Test normal (non-detached) case first 126 cmd := exec.Command(dockerBinary, "exec", "top", "sh", "-c", "exit 23") 127 ec, _ := runCommand(cmd) 128 c.Assert(ec, checker.Equals, 23) 129 } 130 131 func (s *DockerSuite) TestExecPausedContainer(c *check.C) { 132 // Windows does not support pause 133 testRequires(c, DaemonIsLinux) 134 defer unpauseAllContainers() 135 136 out, _ := dockerCmd(c, "run", "-d", "--name", "testing", "busybox", "top") 137 ContainerID := strings.TrimSpace(out) 138 139 dockerCmd(c, "pause", "testing") 140 out, _, err := dockerCmdWithError("exec", "-i", "-t", ContainerID, "echo", "hello") 141 c.Assert(err, checker.NotNil, check.Commentf("container should fail to exec new conmmand if it is paused")) 142 143 expected := ContainerID + " is paused, unpause the container before exec" 144 c.Assert(out, checker.Contains, expected, check.Commentf("container should not exec new command if it is paused")) 145 } 146 147 // regression test for #9476 148 func (s *DockerSuite) TestExecTTYCloseStdin(c *check.C) { 149 // TODO Windows CI: This requires some work to port to Windows. 150 testRequires(c, DaemonIsLinux) 151 dockerCmd(c, "run", "-d", "-it", "--name", "exec_tty_stdin", "busybox") 152 153 cmd := exec.Command(dockerBinary, "exec", "-i", "exec_tty_stdin", "cat") 154 stdinRw, err := cmd.StdinPipe() 155 c.Assert(err, checker.IsNil) 156 157 stdinRw.Write([]byte("test")) 158 stdinRw.Close() 159 160 out, _, err := runCommandWithOutput(cmd) 161 c.Assert(err, checker.IsNil, check.Commentf(out)) 162 163 out, _ = dockerCmd(c, "top", "exec_tty_stdin") 164 outArr := strings.Split(out, "\n") 165 c.Assert(len(outArr), checker.LessOrEqualThan, 3, check.Commentf("exec process left running")) 166 c.Assert(out, checker.Not(checker.Contains), "nsenter-exec") 167 } 168 169 func (s *DockerSuite) TestExecTTYWithoutStdin(c *check.C) { 170 // TODO Windows CI: This requires some work to port to Windows. 171 testRequires(c, DaemonIsLinux) 172 out, _ := dockerCmd(c, "run", "-d", "-ti", "busybox") 173 id := strings.TrimSpace(out) 174 c.Assert(waitRun(id), checker.IsNil) 175 176 errChan := make(chan error) 177 go func() { 178 defer close(errChan) 179 180 cmd := exec.Command(dockerBinary, "exec", "-ti", id, "true") 181 if _, err := cmd.StdinPipe(); err != nil { 182 errChan <- err 183 return 184 } 185 186 expected := "the input device is not a TTY" 187 if runtime.GOOS == "windows" { 188 expected += ". If you are using mintty, try prefixing the command with 'winpty'" 189 } 190 if out, _, err := runCommandWithOutput(cmd); err == nil { 191 errChan <- fmt.Errorf("exec should have failed") 192 return 193 } else if !strings.Contains(out, expected) { 194 errChan <- fmt.Errorf("exec failed with error %q: expected %q", out, expected) 195 return 196 } 197 }() 198 199 select { 200 case err := <-errChan: 201 c.Assert(err, check.IsNil) 202 case <-time.After(3 * time.Second): 203 c.Fatal("exec is running but should have failed") 204 } 205 } 206 207 func (s *DockerSuite) TestExecParseError(c *check.C) { 208 // TODO Windows CI: Requires some extra work. Consider copying the 209 // runSleepingContainer helper to have an exec version. 210 testRequires(c, DaemonIsLinux) 211 dockerCmd(c, "run", "-d", "--name", "top", "busybox", "top") 212 213 // Test normal (non-detached) case first 214 cmd := exec.Command(dockerBinary, "exec", "top") 215 _, stderr, _, err := runCommandWithStdoutStderr(cmd) 216 c.Assert(err, checker.NotNil) 217 c.Assert(stderr, checker.Contains, "See '"+dockerBinary+" exec --help'") 218 } 219 220 func (s *DockerSuite) TestExecStopNotHanging(c *check.C) { 221 // TODO Windows CI: Requires some extra work. Consider copying the 222 // runSleepingContainer helper to have an exec version. 223 testRequires(c, DaemonIsLinux) 224 dockerCmd(c, "run", "-d", "--name", "testing", "busybox", "top") 225 226 err := exec.Command(dockerBinary, "exec", "testing", "top").Start() 227 c.Assert(err, checker.IsNil) 228 229 type dstop struct { 230 out []byte 231 err error 232 } 233 234 ch := make(chan dstop) 235 go func() { 236 out, err := exec.Command(dockerBinary, "stop", "testing").CombinedOutput() 237 ch <- dstop{out, err} 238 close(ch) 239 }() 240 select { 241 case <-time.After(3 * time.Second): 242 c.Fatal("Container stop timed out") 243 case s := <-ch: 244 c.Assert(s.err, check.IsNil) 245 } 246 } 247 248 func (s *DockerSuite) TestExecCgroup(c *check.C) { 249 // Not applicable on Windows - using Linux specific functionality 250 testRequires(c, NotUserNamespace) 251 testRequires(c, DaemonIsLinux) 252 dockerCmd(c, "run", "-d", "--name", "testing", "busybox", "top") 253 254 out, _ := dockerCmd(c, "exec", "testing", "cat", "/proc/1/cgroup") 255 containerCgroups := sort.StringSlice(strings.Split(out, "\n")) 256 257 var wg sync.WaitGroup 258 var mu sync.Mutex 259 execCgroups := []sort.StringSlice{} 260 errChan := make(chan error) 261 // exec a few times concurrently to get consistent failure 262 for i := 0; i < 5; i++ { 263 wg.Add(1) 264 go func() { 265 out, _, err := dockerCmdWithError("exec", "testing", "cat", "/proc/self/cgroup") 266 if err != nil { 267 errChan <- err 268 return 269 } 270 cg := sort.StringSlice(strings.Split(out, "\n")) 271 272 mu.Lock() 273 execCgroups = append(execCgroups, cg) 274 mu.Unlock() 275 wg.Done() 276 }() 277 } 278 wg.Wait() 279 close(errChan) 280 281 for err := range errChan { 282 c.Assert(err, checker.IsNil) 283 } 284 285 for _, cg := range execCgroups { 286 if !reflect.DeepEqual(cg, containerCgroups) { 287 fmt.Println("exec cgroups:") 288 for _, name := range cg { 289 fmt.Printf(" %s\n", name) 290 } 291 292 fmt.Println("container cgroups:") 293 for _, name := range containerCgroups { 294 fmt.Printf(" %s\n", name) 295 } 296 c.Fatal("cgroups mismatched") 297 } 298 } 299 } 300 301 func (s *DockerSuite) TestExecInspectID(c *check.C) { 302 out, _ := runSleepingContainer(c, "-d") 303 id := strings.TrimSuffix(out, "\n") 304 305 out = inspectField(c, id, "ExecIDs") 306 c.Assert(out, checker.Equals, "[]", check.Commentf("ExecIDs should be empty, got: %s", out)) 307 308 // Start an exec, have it block waiting so we can do some checking 309 cmd := exec.Command(dockerBinary, "exec", id, "sh", "-c", 310 "while ! test -e /execid1; do sleep 1; done") 311 312 err := cmd.Start() 313 c.Assert(err, checker.IsNil, check.Commentf("failed to start the exec cmd")) 314 315 // Give the exec 10 chances/seconds to start then give up and stop the test 316 tries := 10 317 for i := 0; i < tries; i++ { 318 // Since its still running we should see exec as part of the container 319 out = strings.TrimSpace(inspectField(c, id, "ExecIDs")) 320 321 if out != "[]" && out != "<no value>" { 322 break 323 } 324 c.Assert(i+1, checker.Not(checker.Equals), tries, check.Commentf("ExecIDs still empty after 10 second")) 325 time.Sleep(1 * time.Second) 326 } 327 328 // Save execID for later 329 execID, err := inspectFilter(id, "index .ExecIDs 0") 330 c.Assert(err, checker.IsNil, check.Commentf("failed to get the exec id")) 331 332 // End the exec by creating the missing file 333 err = exec.Command(dockerBinary, "exec", id, 334 "sh", "-c", "touch /execid1").Run() 335 336 c.Assert(err, checker.IsNil, check.Commentf("failed to run the 2nd exec cmd")) 337 338 // Wait for 1st exec to complete 339 cmd.Wait() 340 341 // Give the exec 10 chances/seconds to stop then give up and stop the test 342 for i := 0; i < tries; i++ { 343 // Since its still running we should see exec as part of the container 344 out = strings.TrimSpace(inspectField(c, id, "ExecIDs")) 345 346 if out == "[]" { 347 break 348 } 349 c.Assert(i+1, checker.Not(checker.Equals), tries, check.Commentf("ExecIDs still not empty after 10 second")) 350 time.Sleep(1 * time.Second) 351 } 352 353 // But we should still be able to query the execID 354 sc, body, err := sockRequest("GET", "/exec/"+execID+"/json", nil) 355 c.Assert(sc, checker.Equals, http.StatusOK, check.Commentf("received status != 200 OK: %d\n%s", sc, body)) 356 357 // Now delete the container and then an 'inspect' on the exec should 358 // result in a 404 (not 'container not running') 359 out, ec := dockerCmd(c, "rm", "-f", id) 360 c.Assert(ec, checker.Equals, 0, check.Commentf("error removing container: %s", out)) 361 sc, body, err = sockRequest("GET", "/exec/"+execID+"/json", nil) 362 c.Assert(sc, checker.Equals, http.StatusNotFound, check.Commentf("received status != 404: %d\n%s", sc, body)) 363 } 364 365 func (s *DockerSuite) TestLinksPingLinkedContainersOnRename(c *check.C) { 366 // Problematic on Windows as Windows does not support links 367 testRequires(c, DaemonIsLinux) 368 var out string 369 out, _ = dockerCmd(c, "run", "-d", "--name", "container1", "busybox", "top") 370 idA := strings.TrimSpace(out) 371 c.Assert(idA, checker.Not(checker.Equals), "", check.Commentf("%s, id should not be nil", out)) 372 out, _ = dockerCmd(c, "run", "-d", "--link", "container1:alias1", "--name", "container2", "busybox", "top") 373 idB := strings.TrimSpace(out) 374 c.Assert(idB, checker.Not(checker.Equals), "", check.Commentf("%s, id should not be nil", out)) 375 376 dockerCmd(c, "exec", "container2", "ping", "-c", "1", "alias1", "-W", "1") 377 dockerCmd(c, "rename", "container1", "container_new") 378 dockerCmd(c, "exec", "container2", "ping", "-c", "1", "alias1", "-W", "1") 379 } 380 381 func (s *DockerSuite) TestRunMutableNetworkFiles(c *check.C) { 382 // Not applicable on Windows to Windows CI. 383 testRequires(c, SameHostDaemon, DaemonIsLinux) 384 for _, fn := range []string{"resolv.conf", "hosts"} { 385 deleteAllContainers() 386 387 content, err := runCommandAndReadContainerFile(fn, exec.Command(dockerBinary, "run", "-d", "--name", "c1", "busybox", "sh", "-c", fmt.Sprintf("echo success >/etc/%s && top", fn))) 388 c.Assert(err, checker.IsNil) 389 390 c.Assert(strings.TrimSpace(string(content)), checker.Equals, "success", check.Commentf("Content was not what was modified in the container", string(content))) 391 392 out, _ := dockerCmd(c, "run", "-d", "--name", "c2", "busybox", "top") 393 contID := strings.TrimSpace(out) 394 netFilePath := containerStorageFile(contID, fn) 395 396 f, err := os.OpenFile(netFilePath, os.O_WRONLY|os.O_SYNC|os.O_APPEND, 0644) 397 c.Assert(err, checker.IsNil) 398 399 if _, err := f.Seek(0, 0); err != nil { 400 f.Close() 401 c.Fatal(err) 402 } 403 404 if err := f.Truncate(0); err != nil { 405 f.Close() 406 c.Fatal(err) 407 } 408 409 if _, err := f.Write([]byte("success2\n")); err != nil { 410 f.Close() 411 c.Fatal(err) 412 } 413 f.Close() 414 415 res, _ := dockerCmd(c, "exec", contID, "cat", "/etc/"+fn) 416 c.Assert(res, checker.Equals, "success2\n") 417 } 418 } 419 420 func (s *DockerSuite) TestExecWithUser(c *check.C) { 421 // TODO Windows CI: This may be fixable in the future once Windows 422 // supports users 423 testRequires(c, DaemonIsLinux) 424 dockerCmd(c, "run", "-d", "--name", "parent", "busybox", "top") 425 426 out, _ := dockerCmd(c, "exec", "-u", "1", "parent", "id") 427 c.Assert(out, checker.Contains, "uid=1(daemon) gid=1(daemon)") 428 429 out, _ = dockerCmd(c, "exec", "-u", "root", "parent", "id") 430 c.Assert(out, checker.Contains, "uid=0(root) gid=0(root)", check.Commentf("exec with user by id expected daemon user got %s", out)) 431 } 432 433 func (s *DockerSuite) TestExecWithPrivileged(c *check.C) { 434 // Not applicable on Windows 435 testRequires(c, DaemonIsLinux, NotUserNamespace) 436 // Start main loop which attempts mknod repeatedly 437 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`) 438 439 // Check exec mknod doesn't work 440 cmd := exec.Command(dockerBinary, "exec", "parent", "sh", "-c", "mknod /tmp/sdb b 8 16") 441 out, _, err := runCommandWithOutput(cmd) 442 c.Assert(err, checker.NotNil, check.Commentf("exec mknod in --cap-drop=ALL container without --privileged should fail")) 443 c.Assert(out, checker.Contains, "Operation not permitted", check.Commentf("exec mknod in --cap-drop=ALL container without --privileged should fail")) 444 445 // Check exec mknod does work with --privileged 446 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`) 447 out, _, err = runCommandWithOutput(cmd) 448 c.Assert(err, checker.IsNil) 449 450 actual := strings.TrimSpace(out) 451 c.Assert(actual, checker.Equals, "ok", check.Commentf("exec mknod in --cap-drop=ALL container with --privileged failed, output: %q", out)) 452 453 // Check subsequent unprivileged exec cannot mknod 454 cmd = exec.Command(dockerBinary, "exec", "parent", "sh", "-c", "mknod /tmp/sdc b 8 32") 455 out, _, err = runCommandWithOutput(cmd) 456 c.Assert(err, checker.NotNil, check.Commentf("repeating exec mknod in --cap-drop=ALL container after --privileged without --privileged should fail")) 457 c.Assert(out, checker.Contains, "Operation not permitted", check.Commentf("repeating exec mknod in --cap-drop=ALL container after --privileged without --privileged should fail")) 458 459 // Confirm at no point was mknod allowed 460 logCmd := exec.Command(dockerBinary, "logs", "parent") 461 out, _, err = runCommandWithOutput(logCmd) 462 c.Assert(err, checker.IsNil) 463 c.Assert(out, checker.Not(checker.Contains), "Success") 464 465 } 466 467 func (s *DockerSuite) TestExecWithImageUser(c *check.C) { 468 // Not applicable on Windows 469 testRequires(c, DaemonIsLinux) 470 name := "testbuilduser" 471 _, err := buildImage(name, 472 `FROM busybox 473 RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd 474 USER dockerio`, 475 true) 476 c.Assert(err, checker.IsNil) 477 478 dockerCmd(c, "run", "-d", "--name", "dockerioexec", name, "top") 479 480 out, _ := dockerCmd(c, "exec", "dockerioexec", "whoami") 481 c.Assert(out, checker.Contains, "dockerio", check.Commentf("exec with user by id expected dockerio user got %s", out)) 482 } 483 484 func (s *DockerSuite) TestExecOnReadonlyContainer(c *check.C) { 485 // Windows does not support read-only 486 // --read-only + userns has remount issues 487 testRequires(c, DaemonIsLinux, NotUserNamespace) 488 dockerCmd(c, "run", "-d", "--read-only", "--name", "parent", "busybox", "top") 489 dockerCmd(c, "exec", "parent", "true") 490 } 491 492 func (s *DockerSuite) TestExecUlimits(c *check.C) { 493 testRequires(c, DaemonIsLinux) 494 name := "testexeculimits" 495 runSleepingContainer(c, "-d", "--ulimit", "nproc=21", "--name", name) 496 c.Assert(waitRun(name), checker.IsNil) 497 498 out, _, err := dockerCmdWithError("exec", name, "sh", "-c", "ulimit -p") 499 c.Assert(err, checker.IsNil) 500 c.Assert(strings.TrimSpace(out), checker.Equals, "21") 501 } 502 503 // #15750 504 func (s *DockerSuite) TestExecStartFails(c *check.C) { 505 // TODO Windows CI. This test should be portable. Figure out why it fails 506 // currently. 507 testRequires(c, DaemonIsLinux) 508 name := "exec-15750" 509 runSleepingContainer(c, "-d", "--name", name) 510 c.Assert(waitRun(name), checker.IsNil) 511 512 out, _, err := dockerCmdWithError("exec", name, "no-such-cmd") 513 c.Assert(err, checker.NotNil, check.Commentf(out)) 514 c.Assert(out, checker.Contains, "executable file not found") 515 }