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