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