docker_cli_exec_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. // +build !test_no_exec
  2. package main
  3. import (
  4. "bufio"
  5. "fmt"
  6. "os"
  7. "os/exec"
  8. "reflect"
  9. "sort"
  10. "strings"
  11. "sync"
  12. "testing"
  13. "time"
  14. )
  15. func TestExec(t *testing.T) {
  16. runCmd := exec.Command(dockerBinary, "run", "-d", "--name", "testing", "busybox", "sh", "-c", "echo test > /tmp/file && sleep 100")
  17. if out, _, _, err := runCommandWithStdoutStderr(runCmd); err != nil {
  18. t.Fatal(out, err)
  19. }
  20. execCmd := exec.Command(dockerBinary, "exec", "testing", "cat", "/tmp/file")
  21. out, _, err := runCommandWithOutput(execCmd)
  22. if err != nil {
  23. t.Fatal(out, err)
  24. }
  25. out = strings.Trim(out, "\r\n")
  26. if expected := "test"; out != expected {
  27. t.Errorf("container exec should've printed %q but printed %q", expected, out)
  28. }
  29. deleteAllContainers()
  30. logDone("exec - basic test")
  31. }
  32. func TestExecInteractiveStdinClose(t *testing.T) {
  33. defer deleteAllContainers()
  34. out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "-itd", "busybox", "/bin/cat"))
  35. if err != nil {
  36. t.Fatal(err)
  37. }
  38. contId := strings.TrimSpace(out)
  39. returnchan := make(chan struct{})
  40. go func() {
  41. var err error
  42. cmd := exec.Command(dockerBinary, "exec", "-i", contId, "/bin/ls", "/")
  43. cmd.Stdin = os.Stdin
  44. if err != nil {
  45. t.Fatal(err)
  46. }
  47. out, err := cmd.CombinedOutput()
  48. if err != nil {
  49. t.Fatal(err, out)
  50. }
  51. if string(out) == "" {
  52. t.Fatalf("Output was empty, likely blocked by standard input")
  53. }
  54. returnchan <- struct{}{}
  55. }()
  56. select {
  57. case <-returnchan:
  58. case <-time.After(10 * time.Second):
  59. t.Fatal("timed out running docker exec")
  60. }
  61. logDone("exec - interactive mode closes stdin after execution")
  62. }
  63. func TestExecInteractive(t *testing.T) {
  64. runCmd := exec.Command(dockerBinary, "run", "-d", "--name", "testing", "busybox", "sh", "-c", "echo test > /tmp/file && sleep 100")
  65. if out, _, _, err := runCommandWithStdoutStderr(runCmd); err != nil {
  66. t.Fatal(out, err)
  67. }
  68. execCmd := exec.Command(dockerBinary, "exec", "-i", "testing", "sh")
  69. stdin, err := execCmd.StdinPipe()
  70. if err != nil {
  71. t.Fatal(err)
  72. }
  73. stdout, err := execCmd.StdoutPipe()
  74. if err != nil {
  75. t.Fatal(err)
  76. }
  77. if err := execCmd.Start(); err != nil {
  78. t.Fatal(err)
  79. }
  80. if _, err := stdin.Write([]byte("cat /tmp/file\n")); err != nil {
  81. t.Fatal(err)
  82. }
  83. r := bufio.NewReader(stdout)
  84. line, err := r.ReadString('\n')
  85. if err != nil {
  86. t.Fatal(err)
  87. }
  88. line = strings.TrimSpace(line)
  89. if line != "test" {
  90. t.Fatalf("Output should be 'test', got '%q'", line)
  91. }
  92. if err := stdin.Close(); err != nil {
  93. t.Fatal(err)
  94. }
  95. finish := make(chan struct{})
  96. go func() {
  97. if err := execCmd.Wait(); err != nil {
  98. t.Fatal(err)
  99. }
  100. close(finish)
  101. }()
  102. select {
  103. case <-finish:
  104. case <-time.After(1 * time.Second):
  105. t.Fatal("docker exec failed to exit on stdin close")
  106. }
  107. deleteAllContainers()
  108. logDone("exec - Interactive test")
  109. }
  110. func TestExecAfterContainerRestart(t *testing.T) {
  111. runCmd := exec.Command(dockerBinary, "run", "-d", "busybox", "top")
  112. out, _, err := runCommandWithOutput(runCmd)
  113. if err != nil {
  114. t.Fatal(out, err)
  115. }
  116. cleanedContainerID := stripTrailingCharacters(out)
  117. runCmd = exec.Command(dockerBinary, "restart", cleanedContainerID)
  118. if out, _, err = runCommandWithOutput(runCmd); err != nil {
  119. t.Fatal(out, err)
  120. }
  121. runCmd = exec.Command(dockerBinary, "exec", cleanedContainerID, "echo", "hello")
  122. out, _, err = runCommandWithOutput(runCmd)
  123. if err != nil {
  124. t.Fatal(out, err)
  125. }
  126. outStr := strings.TrimSpace(out)
  127. if outStr != "hello" {
  128. t.Errorf("container should've printed hello, instead printed %q", outStr)
  129. }
  130. deleteAllContainers()
  131. logDone("exec - exec running container after container restart")
  132. }
  133. func TestExecAfterDaemonRestart(t *testing.T) {
  134. d := NewDaemon(t)
  135. if err := d.StartWithBusybox(); err != nil {
  136. t.Fatalf("Could not start daemon with busybox: %v", err)
  137. }
  138. defer d.Stop()
  139. if out, err := d.Cmd("run", "-d", "--name", "top", "-p", "80", "busybox:latest", "top"); err != nil {
  140. t.Fatalf("Could not run top: err=%v\n%s", err, out)
  141. }
  142. if err := d.Restart(); err != nil {
  143. t.Fatalf("Could not restart daemon: %v", err)
  144. }
  145. if out, err := d.Cmd("start", "top"); err != nil {
  146. t.Fatalf("Could not start top after daemon restart: err=%v\n%s", err, out)
  147. }
  148. out, err := d.Cmd("exec", "top", "echo", "hello")
  149. if err != nil {
  150. t.Fatalf("Could not exec on container top: err=%v\n%s", err, out)
  151. }
  152. outStr := strings.TrimSpace(string(out))
  153. if outStr != "hello" {
  154. t.Errorf("container should've printed hello, instead printed %q", outStr)
  155. }
  156. logDone("exec - exec running container after daemon restart")
  157. }
  158. // Regresssion test for #9155, #9044
  159. func TestExecEnv(t *testing.T) {
  160. defer deleteAllContainers()
  161. runCmd := exec.Command(dockerBinary, "run",
  162. "-e", "LALA=value1",
  163. "-e", "LALA=value2",
  164. "-d", "--name", "testing", "busybox", "top")
  165. if out, _, _, err := runCommandWithStdoutStderr(runCmd); err != nil {
  166. t.Fatal(out, err)
  167. }
  168. execCmd := exec.Command(dockerBinary, "exec", "testing", "env")
  169. out, _, err := runCommandWithOutput(execCmd)
  170. if err != nil {
  171. t.Fatal(out, err)
  172. }
  173. if strings.Contains(out, "LALA=value1") ||
  174. !strings.Contains(out, "LALA=value2") ||
  175. !strings.Contains(out, "HOME=/root") {
  176. t.Errorf("exec env(%q), expect %q, %q", out, "LALA=value2", "HOME=/root")
  177. }
  178. logDone("exec - exec inherits correct env")
  179. }
  180. func TestExecExitStatus(t *testing.T) {
  181. runCmd := exec.Command(dockerBinary, "run", "-d", "--name", "top", "busybox", "top")
  182. if out, _, _, err := runCommandWithStdoutStderr(runCmd); err != nil {
  183. t.Fatal(out, err)
  184. }
  185. // Test normal (non-detached) case first
  186. cmd := exec.Command(dockerBinary, "exec", "top", "sh", "-c", "exit 23")
  187. ec, _ := runCommand(cmd)
  188. if ec != 23 {
  189. t.Fatalf("Should have had an ExitCode of 23, not: %d", ec)
  190. }
  191. logDone("exec - exec non-zero ExitStatus")
  192. }
  193. func TestExecPausedContainer(t *testing.T) {
  194. defer deleteAllContainers()
  195. defer unpauseAllContainers()
  196. runCmd := exec.Command(dockerBinary, "run", "-d", "--name", "testing", "busybox", "top")
  197. out, _, err := runCommandWithOutput(runCmd)
  198. if err != nil {
  199. t.Fatal(out, err)
  200. }
  201. ContainerID := stripTrailingCharacters(out)
  202. pausedCmd := exec.Command(dockerBinary, "pause", "testing")
  203. out, _, _, err = runCommandWithStdoutStderr(pausedCmd)
  204. if err != nil {
  205. t.Fatal(out, err)
  206. }
  207. execCmd := exec.Command(dockerBinary, "exec", "-i", "-t", ContainerID, "echo", "hello")
  208. out, _, err = runCommandWithOutput(execCmd)
  209. if err == nil {
  210. t.Fatal("container should fail to exec new command if it is paused")
  211. }
  212. expected := ContainerID + " is paused, unpause the container before exec"
  213. if !strings.Contains(out, expected) {
  214. t.Fatal("container should not exec new command if it is paused")
  215. }
  216. logDone("exec - exec should not exec a pause container")
  217. }
  218. // regression test for #9476
  219. func TestExecTtyCloseStdin(t *testing.T) {
  220. defer deleteAllContainers()
  221. cmd := exec.Command(dockerBinary, "run", "-d", "-it", "--name", "exec_tty_stdin", "busybox")
  222. if out, _, err := runCommandWithOutput(cmd); err != nil {
  223. t.Fatal(out, err)
  224. }
  225. cmd = exec.Command(dockerBinary, "exec", "-i", "exec_tty_stdin", "cat")
  226. stdinRw, err := cmd.StdinPipe()
  227. if err != nil {
  228. t.Fatal(err)
  229. }
  230. stdinRw.Write([]byte("test"))
  231. stdinRw.Close()
  232. if out, _, err := runCommandWithOutput(cmd); err != nil {
  233. t.Fatal(out, err)
  234. }
  235. cmd = exec.Command(dockerBinary, "top", "exec_tty_stdin")
  236. out, _, err := runCommandWithOutput(cmd)
  237. if err != nil {
  238. t.Fatal(out, err)
  239. }
  240. outArr := strings.Split(out, "\n")
  241. if len(outArr) > 3 || strings.Contains(out, "nsenter-exec") {
  242. // This is the really bad part
  243. if out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "rm", "-f", "exec_tty_stdin")); err != nil {
  244. t.Fatal(out, err)
  245. }
  246. t.Fatalf("exec process left running\n\t %s", out)
  247. }
  248. logDone("exec - stdin is closed properly with tty enabled")
  249. }
  250. func TestExecTtyWithoutStdin(t *testing.T) {
  251. defer deleteAllContainers()
  252. cmd := exec.Command(dockerBinary, "run", "-d", "-ti", "busybox")
  253. out, _, err := runCommandWithOutput(cmd)
  254. if err != nil {
  255. t.Fatalf("failed to start container: %v (%v)", out, err)
  256. }
  257. id := strings.TrimSpace(out)
  258. if err := waitRun(id); err != nil {
  259. t.Fatal(err)
  260. }
  261. defer func() {
  262. cmd := exec.Command(dockerBinary, "kill", id)
  263. if out, _, err := runCommandWithOutput(cmd); err != nil {
  264. t.Fatalf("failed to kill container: %v (%v)", out, err)
  265. }
  266. }()
  267. done := make(chan struct{})
  268. go func() {
  269. defer close(done)
  270. cmd := exec.Command(dockerBinary, "exec", "-ti", id, "true")
  271. if _, err := cmd.StdinPipe(); err != nil {
  272. t.Fatal(err)
  273. }
  274. expected := "cannot enable tty mode"
  275. if out, _, err := runCommandWithOutput(cmd); err == nil {
  276. t.Fatal("exec should have failed")
  277. } else if !strings.Contains(out, expected) {
  278. t.Fatalf("exec failed with error %q: expected %q", out, expected)
  279. }
  280. }()
  281. select {
  282. case <-done:
  283. case <-time.After(3 * time.Second):
  284. t.Fatal("exec is running but should have failed")
  285. }
  286. logDone("exec - forbid piped stdin to tty enabled container")
  287. }
  288. func TestExecParseError(t *testing.T) {
  289. defer deleteAllContainers()
  290. runCmd := exec.Command(dockerBinary, "run", "-d", "--name", "top", "busybox", "top")
  291. if out, _, err := runCommandWithOutput(runCmd); err != nil {
  292. t.Fatal(out, err)
  293. }
  294. // Test normal (non-detached) case first
  295. cmd := exec.Command(dockerBinary, "exec", "top")
  296. if _, stderr, code, err := runCommandWithStdoutStderr(cmd); err == nil || !strings.Contains(stderr, "See '"+dockerBinary+" exec --help'") || code == 0 {
  297. t.Fatalf("Should have thrown error & point to help: %s", stderr)
  298. }
  299. logDone("exec - error on parseExec should point to help")
  300. }
  301. func TestExecStopNotHanging(t *testing.T) {
  302. defer deleteAllContainers()
  303. if out, err := exec.Command(dockerBinary, "run", "-d", "--name", "testing", "busybox", "top").CombinedOutput(); err != nil {
  304. t.Fatal(out, err)
  305. }
  306. if err := exec.Command(dockerBinary, "exec", "testing", "top").Start(); err != nil {
  307. t.Fatal(err)
  308. }
  309. wait := make(chan struct{})
  310. go func() {
  311. if out, err := exec.Command(dockerBinary, "stop", "testing").CombinedOutput(); err != nil {
  312. t.Fatal(out, err)
  313. }
  314. close(wait)
  315. }()
  316. select {
  317. case <-time.After(3 * time.Second):
  318. t.Fatal("Container stop timed out")
  319. case <-wait:
  320. }
  321. logDone("exec - container with exec not hanging on stop")
  322. }
  323. func TestExecCgroup(t *testing.T) {
  324. defer deleteAllContainers()
  325. var cmd *exec.Cmd
  326. cmd = exec.Command(dockerBinary, "run", "-d", "--name", "testing", "busybox", "top")
  327. _, err := runCommand(cmd)
  328. if err != nil {
  329. t.Fatal(err)
  330. }
  331. cmd = exec.Command(dockerBinary, "exec", "testing", "cat", "/proc/1/cgroup")
  332. out, _, err := runCommandWithOutput(cmd)
  333. if err != nil {
  334. t.Fatal(out, err)
  335. }
  336. containerCgroups := sort.StringSlice(strings.Split(string(out), "\n"))
  337. var wg sync.WaitGroup
  338. var s sync.Mutex
  339. execCgroups := []sort.StringSlice{}
  340. // exec a few times concurrently to get consistent failure
  341. for i := 0; i < 5; i++ {
  342. wg.Add(1)
  343. go func() {
  344. cmd = exec.Command(dockerBinary, "exec", "testing", "cat", "/proc/self/cgroup")
  345. out, _, err := runCommandWithOutput(cmd)
  346. if err != nil {
  347. t.Fatal(out, err)
  348. }
  349. cg := sort.StringSlice(strings.Split(string(out), "\n"))
  350. s.Lock()
  351. execCgroups = append(execCgroups, cg)
  352. s.Unlock()
  353. wg.Done()
  354. }()
  355. }
  356. wg.Wait()
  357. for _, cg := range execCgroups {
  358. if !reflect.DeepEqual(cg, containerCgroups) {
  359. fmt.Println("exec cgroups:")
  360. for _, name := range cg {
  361. fmt.Printf(" %s\n", name)
  362. }
  363. fmt.Println("container cgroups:")
  364. for _, name := range containerCgroups {
  365. fmt.Printf(" %s\n", name)
  366. }
  367. t.Fatal("cgroups mismatched")
  368. }
  369. }
  370. logDone("exec - exec has the container cgroups")
  371. }
  372. func TestInspectExecID(t *testing.T) {
  373. defer deleteAllContainers()
  374. out, exitCode, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "-d", "busybox", "top"))
  375. if exitCode != 0 || err != nil {
  376. t.Fatalf("failed to run container: %s, %v", out, err)
  377. }
  378. id := strings.TrimSuffix(out, "\n")
  379. out, err = inspectField(id, "ExecIDs")
  380. if err != nil {
  381. t.Fatalf("failed to inspect container: %s, %v", out, err)
  382. }
  383. if out != "<no value>" {
  384. t.Fatalf("ExecIDs should be empty, got: %s", out)
  385. }
  386. exitCode, err = runCommand(exec.Command(dockerBinary, "exec", "-d", id, "ls", "/"))
  387. if exitCode != 0 || err != nil {
  388. t.Fatalf("failed to exec in container: %s, %v", out, err)
  389. }
  390. out, err = inspectField(id, "ExecIDs")
  391. if err != nil {
  392. t.Fatalf("failed to inspect container: %s, %v", out, err)
  393. }
  394. out = strings.TrimSuffix(out, "\n")
  395. if out == "[]" || out == "<no value>" {
  396. t.Fatalf("ExecIDs should not be empty, got: %s", out)
  397. }
  398. logDone("inspect - inspect a container with ExecIDs")
  399. }