commands_test.go 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189
  1. package docker
  2. import (
  3. "bufio"
  4. "fmt"
  5. "github.com/dotcloud/docker/api/client"
  6. "github.com/dotcloud/docker/daemon"
  7. "github.com/dotcloud/docker/engine"
  8. "github.com/dotcloud/docker/image"
  9. "github.com/dotcloud/docker/pkg/term"
  10. "github.com/dotcloud/docker/utils"
  11. "io"
  12. "io/ioutil"
  13. "os"
  14. "path"
  15. "regexp"
  16. "strconv"
  17. "strings"
  18. "syscall"
  19. "testing"
  20. "time"
  21. )
  22. func closeWrap(args ...io.Closer) error {
  23. e := false
  24. ret := fmt.Errorf("Error closing elements")
  25. for _, c := range args {
  26. if err := c.Close(); err != nil {
  27. e = true
  28. ret = fmt.Errorf("%s\n%s", ret, err)
  29. }
  30. }
  31. if e {
  32. return ret
  33. }
  34. return nil
  35. }
  36. func setRaw(t *testing.T, c *daemon.Container) *term.State {
  37. pty, err := c.GetPtyMaster()
  38. if err != nil {
  39. t.Fatal(err)
  40. }
  41. state, err := term.MakeRaw(pty.Fd())
  42. if err != nil {
  43. t.Fatal(err)
  44. }
  45. return state
  46. }
  47. func unsetRaw(t *testing.T, c *daemon.Container, state *term.State) {
  48. pty, err := c.GetPtyMaster()
  49. if err != nil {
  50. t.Fatal(err)
  51. }
  52. term.RestoreTerminal(pty.Fd(), state)
  53. }
  54. func waitContainerStart(t *testing.T, timeout time.Duration) *daemon.Container {
  55. var container *daemon.Container
  56. setTimeout(t, "Waiting for the container to be started timed out", timeout, func() {
  57. for {
  58. l := globalDaemon.List()
  59. if len(l) == 1 && l[0].State.IsRunning() {
  60. container = l[0]
  61. break
  62. }
  63. time.Sleep(10 * time.Millisecond)
  64. }
  65. })
  66. if container == nil {
  67. t.Fatal("An error occured while waiting for the container to start")
  68. }
  69. return container
  70. }
  71. func setTimeout(t *testing.T, msg string, d time.Duration, f func()) {
  72. c := make(chan bool)
  73. // Make sure we are not too long
  74. go func() {
  75. time.Sleep(d)
  76. c <- true
  77. }()
  78. go func() {
  79. f()
  80. c <- false
  81. }()
  82. if <-c && msg != "" {
  83. t.Fatal(msg)
  84. }
  85. }
  86. func expectPipe(expected string, r io.Reader) error {
  87. o, err := bufio.NewReader(r).ReadString('\n')
  88. if err != nil {
  89. return err
  90. }
  91. if strings.Trim(o, " \r\n") != expected {
  92. return fmt.Errorf("Unexpected output. Expected [%s], received [%s]", expected, o)
  93. }
  94. return nil
  95. }
  96. func assertPipe(input, output string, r io.Reader, w io.Writer, count int) error {
  97. for i := 0; i < count; i++ {
  98. if _, err := w.Write([]byte(input)); err != nil {
  99. return err
  100. }
  101. if err := expectPipe(output, r); err != nil {
  102. return err
  103. }
  104. }
  105. return nil
  106. }
  107. // TestRunHostname checks that 'docker run -h' correctly sets a custom hostname
  108. func TestRunHostname(t *testing.T) {
  109. stdout, stdoutPipe := io.Pipe()
  110. cli := client.NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
  111. defer cleanup(globalEngine, t)
  112. c := make(chan struct{})
  113. go func() {
  114. defer close(c)
  115. if err := cli.CmdRun("-h", "foobar", unitTestImageID, "hostname"); err != nil {
  116. t.Fatal(err)
  117. }
  118. }()
  119. setTimeout(t, "Reading command output time out", 2*time.Second, func() {
  120. cmdOutput, err := bufio.NewReader(stdout).ReadString('\n')
  121. if err != nil {
  122. t.Fatal(err)
  123. }
  124. if cmdOutput != "foobar\n" {
  125. t.Fatalf("'hostname' should display '%s', not '%s'", "foobar\n", cmdOutput)
  126. }
  127. })
  128. container := globalDaemon.List()[0]
  129. setTimeout(t, "CmdRun timed out", 10*time.Second, func() {
  130. <-c
  131. go func() {
  132. cli.CmdWait(container.ID)
  133. }()
  134. if _, err := bufio.NewReader(stdout).ReadString('\n'); err != nil {
  135. t.Fatal(err)
  136. }
  137. })
  138. // Cleanup pipes
  139. if err := closeWrap(stdout, stdoutPipe); err != nil {
  140. t.Fatal(err)
  141. }
  142. }
  143. // TestRunWorkdir checks that 'docker run -w' correctly sets a custom working directory
  144. func TestRunWorkdir(t *testing.T) {
  145. stdout, stdoutPipe := io.Pipe()
  146. cli := client.NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
  147. defer cleanup(globalEngine, t)
  148. c := make(chan struct{})
  149. go func() {
  150. defer close(c)
  151. if err := cli.CmdRun("-w", "/foo/bar", unitTestImageID, "pwd"); err != nil {
  152. t.Fatal(err)
  153. }
  154. }()
  155. setTimeout(t, "Reading command output time out", 2*time.Second, func() {
  156. cmdOutput, err := bufio.NewReader(stdout).ReadString('\n')
  157. if err != nil {
  158. t.Fatal(err)
  159. }
  160. if cmdOutput != "/foo/bar\n" {
  161. t.Fatalf("'pwd' should display '%s', not '%s'", "/foo/bar\n", cmdOutput)
  162. }
  163. })
  164. container := globalDaemon.List()[0]
  165. setTimeout(t, "CmdRun timed out", 10*time.Second, func() {
  166. <-c
  167. go func() {
  168. cli.CmdWait(container.ID)
  169. }()
  170. if _, err := bufio.NewReader(stdout).ReadString('\n'); err != nil {
  171. t.Fatal(err)
  172. }
  173. })
  174. // Cleanup pipes
  175. if err := closeWrap(stdout, stdoutPipe); err != nil {
  176. t.Fatal(err)
  177. }
  178. }
  179. // TestRunWorkdirExists checks that 'docker run -w' correctly sets a custom working directory, even if it exists
  180. func TestRunWorkdirExists(t *testing.T) {
  181. stdout, stdoutPipe := io.Pipe()
  182. cli := client.NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
  183. defer cleanup(globalEngine, t)
  184. c := make(chan struct{})
  185. go func() {
  186. defer close(c)
  187. if err := cli.CmdRun("-w", "/proc", unitTestImageID, "pwd"); err != nil {
  188. t.Fatal(err)
  189. }
  190. }()
  191. setTimeout(t, "Reading command output time out", 2*time.Second, func() {
  192. cmdOutput, err := bufio.NewReader(stdout).ReadString('\n')
  193. if err != nil {
  194. t.Fatal(err)
  195. }
  196. if cmdOutput != "/proc\n" {
  197. t.Fatalf("'pwd' should display '%s', not '%s'", "/proc\n", cmdOutput)
  198. }
  199. })
  200. container := globalDaemon.List()[0]
  201. setTimeout(t, "CmdRun timed out", 5*time.Second, func() {
  202. <-c
  203. go func() {
  204. cli.CmdWait(container.ID)
  205. }()
  206. if _, err := bufio.NewReader(stdout).ReadString('\n'); err != nil {
  207. t.Fatal(err)
  208. }
  209. })
  210. // Cleanup pipes
  211. if err := closeWrap(stdout, stdoutPipe); err != nil {
  212. t.Fatal(err)
  213. }
  214. }
  215. // TestRunWorkdirExistsAndIsFile checks that if 'docker run -w' with existing file can be detected
  216. func TestRunWorkdirExistsAndIsFile(t *testing.T) {
  217. cli := client.NewDockerCli(nil, nil, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
  218. defer cleanup(globalEngine, t)
  219. c := make(chan struct{})
  220. go func() {
  221. defer close(c)
  222. if err := cli.CmdRun("-w", "/bin/cat", unitTestImageID, "pwd"); err == nil {
  223. t.Fatal("should have failed to run when using /bin/cat as working dir.")
  224. }
  225. }()
  226. setTimeout(t, "CmdRun timed out", 5*time.Second, func() {
  227. <-c
  228. })
  229. }
  230. func TestRunExit(t *testing.T) {
  231. stdin, stdinPipe := io.Pipe()
  232. stdout, stdoutPipe := io.Pipe()
  233. cli := client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
  234. defer cleanup(globalEngine, t)
  235. c1 := make(chan struct{})
  236. go func() {
  237. cli.CmdRun("-i", unitTestImageID, "/bin/cat")
  238. close(c1)
  239. }()
  240. setTimeout(t, "Read/Write assertion timed out", 2*time.Second, func() {
  241. if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 150); err != nil {
  242. t.Fatal(err)
  243. }
  244. })
  245. container := globalDaemon.List()[0]
  246. // Closing /bin/cat stdin, expect it to exit
  247. if err := stdin.Close(); err != nil {
  248. t.Fatal(err)
  249. }
  250. // as the process exited, CmdRun must finish and unblock. Wait for it
  251. setTimeout(t, "Waiting for CmdRun timed out", 10*time.Second, func() {
  252. <-c1
  253. go func() {
  254. cli.CmdWait(container.ID)
  255. }()
  256. if _, err := bufio.NewReader(stdout).ReadString('\n'); err != nil {
  257. t.Fatal(err)
  258. }
  259. })
  260. // Make sure that the client has been disconnected
  261. setTimeout(t, "The client should have been disconnected once the remote process exited.", 2*time.Second, func() {
  262. // Expecting pipe i/o error, just check that read does not block
  263. stdin.Read([]byte{})
  264. })
  265. // Cleanup pipes
  266. if err := closeWrap(stdin, stdinPipe, stdout, stdoutPipe); err != nil {
  267. t.Fatal(err)
  268. }
  269. }
  270. // Expected behaviour: the process dies when the client disconnects
  271. func TestRunDisconnect(t *testing.T) {
  272. stdin, stdinPipe := io.Pipe()
  273. stdout, stdoutPipe := io.Pipe()
  274. cli := client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
  275. defer cleanup(globalEngine, t)
  276. c1 := make(chan struct{})
  277. go func() {
  278. // We're simulating a disconnect so the return value doesn't matter. What matters is the
  279. // fact that CmdRun returns.
  280. cli.CmdRun("-i", unitTestImageID, "/bin/cat")
  281. close(c1)
  282. }()
  283. setTimeout(t, "Read/Write assertion timed out", 2*time.Second, func() {
  284. if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 150); err != nil {
  285. t.Fatal(err)
  286. }
  287. })
  288. // Close pipes (simulate disconnect)
  289. if err := closeWrap(stdin, stdinPipe, stdout, stdoutPipe); err != nil {
  290. t.Fatal(err)
  291. }
  292. // as the pipes are close, we expect the process to die,
  293. // therefore CmdRun to unblock. Wait for CmdRun
  294. setTimeout(t, "Waiting for CmdRun timed out", 2*time.Second, func() {
  295. <-c1
  296. })
  297. // Client disconnect after run -i should cause stdin to be closed, which should
  298. // cause /bin/cat to exit.
  299. setTimeout(t, "Waiting for /bin/cat to exit timed out", 2*time.Second, func() {
  300. container := globalDaemon.List()[0]
  301. container.Wait()
  302. if container.State.IsRunning() {
  303. t.Fatalf("/bin/cat is still running after closing stdin")
  304. }
  305. })
  306. }
  307. // Expected behaviour: the process stay alive when the client disconnects
  308. // but the client detaches.
  309. func TestRunDisconnectTty(t *testing.T) {
  310. stdin, stdinPipe := io.Pipe()
  311. stdout, stdoutPipe := io.Pipe()
  312. cli := client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
  313. defer cleanup(globalEngine, t)
  314. c1 := make(chan struct{})
  315. go func() {
  316. defer close(c1)
  317. // We're simulating a disconnect so the return value doesn't matter. What matters is the
  318. // fact that CmdRun returns.
  319. if err := cli.CmdRun("-i", "-t", unitTestImageID, "/bin/cat"); err != nil {
  320. utils.Debugf("Error CmdRun: %s", err)
  321. }
  322. }()
  323. container := waitContainerStart(t, 10*time.Second)
  324. state := setRaw(t, container)
  325. defer unsetRaw(t, container, state)
  326. // Client disconnect after run -i should keep stdin out in TTY mode
  327. setTimeout(t, "Read/Write assertion timed out", 2*time.Second, func() {
  328. if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 150); err != nil {
  329. t.Fatal(err)
  330. }
  331. })
  332. // Close pipes (simulate disconnect)
  333. if err := closeWrap(stdin, stdinPipe, stdout, stdoutPipe); err != nil {
  334. t.Fatal(err)
  335. }
  336. // wait for CmdRun to return
  337. setTimeout(t, "Waiting for CmdRun timed out", 5*time.Second, func() {
  338. <-c1
  339. })
  340. // In tty mode, we expect the process to stay alive even after client's stdin closes.
  341. // Give some time to monitor to do his thing
  342. container.WaitTimeout(500 * time.Millisecond)
  343. if !container.State.IsRunning() {
  344. t.Fatalf("/bin/cat should still be running after closing stdin (tty mode)")
  345. }
  346. }
  347. // TestAttachStdin checks attaching to stdin without stdout and stderr.
  348. // 'docker run -i -a stdin' should sends the client's stdin to the command,
  349. // then detach from it and print the container id.
  350. func TestRunAttachStdin(t *testing.T) {
  351. stdin, stdinPipe := io.Pipe()
  352. stdout, stdoutPipe := io.Pipe()
  353. cli := client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
  354. defer cleanup(globalEngine, t)
  355. ch := make(chan struct{})
  356. go func() {
  357. defer close(ch)
  358. cli.CmdRun("-i", "-a", "stdin", unitTestImageID, "sh", "-c", "echo hello && cat && sleep 5")
  359. }()
  360. // Send input to the command, close stdin
  361. setTimeout(t, "Write timed out", 10*time.Second, func() {
  362. if _, err := stdinPipe.Write([]byte("hi there\n")); err != nil {
  363. t.Fatal(err)
  364. }
  365. if err := stdinPipe.Close(); err != nil {
  366. t.Fatal(err)
  367. }
  368. })
  369. container := globalDaemon.List()[0]
  370. // Check output
  371. setTimeout(t, "Reading command output time out", 10*time.Second, func() {
  372. cmdOutput, err := bufio.NewReader(stdout).ReadString('\n')
  373. if err != nil {
  374. t.Fatal(err)
  375. }
  376. if cmdOutput != container.ID+"\n" {
  377. t.Fatalf("Wrong output: should be '%s', not '%s'\n", container.ID+"\n", cmdOutput)
  378. }
  379. })
  380. // wait for CmdRun to return
  381. setTimeout(t, "Waiting for CmdRun timed out", 5*time.Second, func() {
  382. <-ch
  383. })
  384. setTimeout(t, "Waiting for command to exit timed out", 10*time.Second, func() {
  385. container.Wait()
  386. })
  387. // Check logs
  388. if cmdLogs, err := container.ReadLog("json"); err != nil {
  389. t.Fatal(err)
  390. } else {
  391. if output, err := ioutil.ReadAll(cmdLogs); err != nil {
  392. t.Fatal(err)
  393. } else {
  394. expectedLogs := []string{"{\"log\":\"hello\\n\",\"stream\":\"stdout\"", "{\"log\":\"hi there\\n\",\"stream\":\"stdout\""}
  395. for _, expectedLog := range expectedLogs {
  396. if !strings.Contains(string(output), expectedLog) {
  397. t.Fatalf("Unexpected logs: should contains '%s', it is not '%s'\n", expectedLog, output)
  398. }
  399. }
  400. }
  401. }
  402. }
  403. // TestRunDetach checks attaching and detaching with the escape sequence.
  404. func TestRunDetach(t *testing.T) {
  405. stdin, stdinPipe := io.Pipe()
  406. stdout, stdoutPipe := io.Pipe()
  407. cli := client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
  408. defer cleanup(globalEngine, t)
  409. ch := make(chan struct{})
  410. go func() {
  411. defer close(ch)
  412. cli.CmdRun("-i", "-t", unitTestImageID, "cat")
  413. }()
  414. container := waitContainerStart(t, 10*time.Second)
  415. state := setRaw(t, container)
  416. defer unsetRaw(t, container, state)
  417. setTimeout(t, "First read/write assertion timed out", 2*time.Second, func() {
  418. if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 150); err != nil {
  419. t.Fatal(err)
  420. }
  421. })
  422. setTimeout(t, "Escape sequence timeout", 5*time.Second, func() {
  423. stdinPipe.Write([]byte{16})
  424. time.Sleep(100 * time.Millisecond)
  425. stdinPipe.Write([]byte{17})
  426. })
  427. // wait for CmdRun to return
  428. setTimeout(t, "Waiting for CmdRun timed out", 15*time.Second, func() {
  429. <-ch
  430. })
  431. closeWrap(stdin, stdinPipe, stdout, stdoutPipe)
  432. time.Sleep(500 * time.Millisecond)
  433. if !container.State.IsRunning() {
  434. t.Fatal("The detached container should be still running")
  435. }
  436. setTimeout(t, "Waiting for container to die timed out", 20*time.Second, func() {
  437. container.Kill()
  438. })
  439. }
  440. // TestAttachDetach checks that attach in tty mode can be detached using the long container ID
  441. func TestAttachDetach(t *testing.T) {
  442. stdin, stdinPipe := io.Pipe()
  443. stdout, stdoutPipe := io.Pipe()
  444. cli := client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
  445. defer cleanup(globalEngine, t)
  446. ch := make(chan struct{})
  447. go func() {
  448. defer close(ch)
  449. if err := cli.CmdRun("-i", "-t", "-d", unitTestImageID, "cat"); err != nil {
  450. t.Fatal(err)
  451. }
  452. }()
  453. container := waitContainerStart(t, 10*time.Second)
  454. setTimeout(t, "Reading container's id timed out", 10*time.Second, func() {
  455. buf := make([]byte, 1024)
  456. n, err := stdout.Read(buf)
  457. if err != nil {
  458. t.Fatal(err)
  459. }
  460. if strings.Trim(string(buf[:n]), " \r\n") != container.ID {
  461. t.Fatalf("Wrong ID received. Expect %s, received %s", container.ID, buf[:n])
  462. }
  463. })
  464. setTimeout(t, "Starting container timed out", 10*time.Second, func() {
  465. <-ch
  466. })
  467. state := setRaw(t, container)
  468. defer unsetRaw(t, container, state)
  469. stdin, stdinPipe = io.Pipe()
  470. stdout, stdoutPipe = io.Pipe()
  471. cli = client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
  472. ch = make(chan struct{})
  473. go func() {
  474. defer close(ch)
  475. if err := cli.CmdAttach(container.ID); err != nil {
  476. if err != io.ErrClosedPipe {
  477. t.Fatal(err)
  478. }
  479. }
  480. }()
  481. setTimeout(t, "First read/write assertion timed out", 2*time.Second, func() {
  482. if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 150); err != nil {
  483. if err != io.ErrClosedPipe {
  484. t.Fatal(err)
  485. }
  486. }
  487. })
  488. setTimeout(t, "Escape sequence timeout", 5*time.Second, func() {
  489. stdinPipe.Write([]byte{16})
  490. time.Sleep(100 * time.Millisecond)
  491. stdinPipe.Write([]byte{17})
  492. })
  493. // wait for CmdRun to return
  494. setTimeout(t, "Waiting for CmdAttach timed out", 15*time.Second, func() {
  495. <-ch
  496. })
  497. closeWrap(stdin, stdinPipe, stdout, stdoutPipe)
  498. time.Sleep(500 * time.Millisecond)
  499. if !container.State.IsRunning() {
  500. t.Fatal("The detached container should be still running")
  501. }
  502. setTimeout(t, "Waiting for container to die timedout", 5*time.Second, func() {
  503. container.Kill()
  504. })
  505. }
  506. // TestAttachDetachTruncatedID checks that attach in tty mode can be detached
  507. func TestAttachDetachTruncatedID(t *testing.T) {
  508. stdin, stdinPipe := io.Pipe()
  509. stdout, stdoutPipe := io.Pipe()
  510. cli := client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
  511. defer cleanup(globalEngine, t)
  512. // Discard the CmdRun output
  513. go stdout.Read(make([]byte, 1024))
  514. setTimeout(t, "Starting container timed out", 2*time.Second, func() {
  515. if err := cli.CmdRun("-i", "-t", "-d", unitTestImageID, "cat"); err != nil {
  516. t.Fatal(err)
  517. }
  518. })
  519. container := waitContainerStart(t, 10*time.Second)
  520. state := setRaw(t, container)
  521. defer unsetRaw(t, container, state)
  522. stdin, stdinPipe = io.Pipe()
  523. stdout, stdoutPipe = io.Pipe()
  524. cli = client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
  525. ch := make(chan struct{})
  526. go func() {
  527. defer close(ch)
  528. if err := cli.CmdAttach(utils.TruncateID(container.ID)); err != nil {
  529. if err != io.ErrClosedPipe {
  530. t.Fatal(err)
  531. }
  532. }
  533. }()
  534. setTimeout(t, "First read/write assertion timed out", 2*time.Second, func() {
  535. if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 150); err != nil {
  536. if err != io.ErrClosedPipe {
  537. t.Fatal(err)
  538. }
  539. }
  540. })
  541. setTimeout(t, "Escape sequence timeout", 5*time.Second, func() {
  542. stdinPipe.Write([]byte{16})
  543. time.Sleep(100 * time.Millisecond)
  544. stdinPipe.Write([]byte{17})
  545. })
  546. // wait for CmdRun to return
  547. setTimeout(t, "Waiting for CmdAttach timed out", 15*time.Second, func() {
  548. <-ch
  549. })
  550. closeWrap(stdin, stdinPipe, stdout, stdoutPipe)
  551. time.Sleep(500 * time.Millisecond)
  552. if !container.State.IsRunning() {
  553. t.Fatal("The detached container should be still running")
  554. }
  555. setTimeout(t, "Waiting for container to die timedout", 5*time.Second, func() {
  556. container.Kill()
  557. })
  558. }
  559. // Expected behaviour, the process stays alive when the client disconnects
  560. func TestAttachDisconnect(t *testing.T) {
  561. stdin, stdinPipe := io.Pipe()
  562. stdout, stdoutPipe := io.Pipe()
  563. cli := client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
  564. defer cleanup(globalEngine, t)
  565. go func() {
  566. // Start a process in daemon mode
  567. if err := cli.CmdRun("-d", "-i", unitTestImageID, "/bin/cat"); err != nil {
  568. utils.Debugf("Error CmdRun: %s", err)
  569. }
  570. }()
  571. setTimeout(t, "Waiting for CmdRun timed out", 10*time.Second, func() {
  572. if _, err := bufio.NewReader(stdout).ReadString('\n'); err != nil {
  573. t.Fatal(err)
  574. }
  575. })
  576. setTimeout(t, "Waiting for the container to be started timed out", 10*time.Second, func() {
  577. for {
  578. l := globalDaemon.List()
  579. if len(l) == 1 && l[0].State.IsRunning() {
  580. break
  581. }
  582. time.Sleep(10 * time.Millisecond)
  583. }
  584. })
  585. container := globalDaemon.List()[0]
  586. // Attach to it
  587. c1 := make(chan struct{})
  588. go func() {
  589. // We're simulating a disconnect so the return value doesn't matter. What matters is the
  590. // fact that CmdAttach returns.
  591. cli.CmdAttach(container.ID)
  592. close(c1)
  593. }()
  594. setTimeout(t, "First read/write assertion timed out", 2*time.Second, func() {
  595. if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 150); err != nil {
  596. t.Fatal(err)
  597. }
  598. })
  599. // Close pipes (client disconnects)
  600. if err := closeWrap(stdin, stdinPipe, stdout, stdoutPipe); err != nil {
  601. t.Fatal(err)
  602. }
  603. // Wait for attach to finish, the client disconnected, therefore, Attach finished his job
  604. setTimeout(t, "Waiting for CmdAttach timed out", 2*time.Second, func() {
  605. <-c1
  606. })
  607. // We closed stdin, expect /bin/cat to still be running
  608. // Wait a little bit to make sure container.monitor() did his thing
  609. err := container.WaitTimeout(500 * time.Millisecond)
  610. if err == nil || !container.State.IsRunning() {
  611. t.Fatalf("/bin/cat is not running after closing stdin")
  612. }
  613. // Try to avoid the timeout in destroy. Best effort, don't check error
  614. cStdin, _ := container.StdinPipe()
  615. cStdin.Close()
  616. container.Wait()
  617. }
  618. // Expected behaviour: container gets deleted automatically after exit
  619. func TestRunAutoRemove(t *testing.T) {
  620. t.Skip("Fixme. Skipping test for now, race condition")
  621. stdout, stdoutPipe := io.Pipe()
  622. cli := client.NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
  623. defer cleanup(globalEngine, t)
  624. c := make(chan struct{})
  625. go func() {
  626. defer close(c)
  627. if err := cli.CmdRun("--rm", unitTestImageID, "hostname"); err != nil {
  628. t.Fatal(err)
  629. }
  630. }()
  631. var temporaryContainerID string
  632. setTimeout(t, "Reading command output time out", 2*time.Second, func() {
  633. cmdOutput, err := bufio.NewReader(stdout).ReadString('\n')
  634. if err != nil {
  635. t.Fatal(err)
  636. }
  637. temporaryContainerID = cmdOutput
  638. if err := closeWrap(stdout, stdoutPipe); err != nil {
  639. t.Fatal(err)
  640. }
  641. })
  642. setTimeout(t, "CmdRun timed out", 10*time.Second, func() {
  643. <-c
  644. })
  645. time.Sleep(500 * time.Millisecond)
  646. if len(globalDaemon.List()) > 0 {
  647. t.Fatalf("failed to remove container automatically: container %s still exists", temporaryContainerID)
  648. }
  649. }
  650. func TestCmdLogs(t *testing.T) {
  651. t.Skip("Test not impemented")
  652. cli := client.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
  653. defer cleanup(globalEngine, t)
  654. if err := cli.CmdRun(unitTestImageID, "sh", "-c", "ls -l"); err != nil {
  655. t.Fatal(err)
  656. }
  657. if err := cli.CmdRun("-t", unitTestImageID, "sh", "-c", "ls -l"); err != nil {
  658. t.Fatal(err)
  659. }
  660. if err := cli.CmdLogs(globalDaemon.List()[0].ID); err != nil {
  661. t.Fatal(err)
  662. }
  663. }
  664. // Expected behaviour: error out when attempting to bind mount non-existing source paths
  665. func TestRunErrorBindNonExistingSource(t *testing.T) {
  666. cli := client.NewDockerCli(nil, nil, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
  667. defer cleanup(globalEngine, t)
  668. c := make(chan struct{})
  669. go func() {
  670. defer close(c)
  671. // This check is made at runtime, can't be "unit tested"
  672. if err := cli.CmdRun("-v", "/i/dont/exist:/tmp", unitTestImageID, "echo 'should fail'"); err == nil {
  673. t.Fatal("should have failed to run when using /i/dont/exist as a source for the bind mount")
  674. }
  675. }()
  676. setTimeout(t, "CmdRun timed out", 5*time.Second, func() {
  677. <-c
  678. })
  679. }
  680. func TestImagesViz(t *testing.T) {
  681. stdout, stdoutPipe := io.Pipe()
  682. cli := client.NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
  683. defer cleanup(globalEngine, t)
  684. image := buildTestImages(t, globalEngine)
  685. c := make(chan struct{})
  686. go func() {
  687. defer close(c)
  688. if err := cli.CmdImages("--viz"); err != nil {
  689. t.Fatal(err)
  690. }
  691. stdoutPipe.Close()
  692. }()
  693. setTimeout(t, "Reading command output time out", 2*time.Second, func() {
  694. cmdOutputBytes, err := ioutil.ReadAll(bufio.NewReader(stdout))
  695. if err != nil {
  696. t.Fatal(err)
  697. }
  698. cmdOutput := string(cmdOutputBytes)
  699. regexpStrings := []string{
  700. "digraph docker {",
  701. fmt.Sprintf("base -> \"%s\" \\[style=invis]", unitTestImageIDShort),
  702. fmt.Sprintf("label=\"%s\\\\n%s:latest\"", unitTestImageIDShort, unitTestImageName),
  703. fmt.Sprintf("label=\"%s\\\\n%s:%s\"", utils.TruncateID(image.ID), "test", "latest"),
  704. "base \\[style=invisible]",
  705. }
  706. compiledRegexps := []*regexp.Regexp{}
  707. for _, regexpString := range regexpStrings {
  708. regexp, err := regexp.Compile(regexpString)
  709. if err != nil {
  710. fmt.Println("Error in regex string: ", err)
  711. return
  712. }
  713. compiledRegexps = append(compiledRegexps, regexp)
  714. }
  715. for _, regexp := range compiledRegexps {
  716. if !regexp.MatchString(cmdOutput) {
  717. t.Fatalf("images --viz content '%s' did not match regexp '%s'", cmdOutput, regexp)
  718. }
  719. }
  720. })
  721. }
  722. func TestImagesTree(t *testing.T) {
  723. stdout, stdoutPipe := io.Pipe()
  724. cli := client.NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
  725. defer cleanup(globalEngine, t)
  726. image := buildTestImages(t, globalEngine)
  727. c := make(chan struct{})
  728. go func() {
  729. defer close(c)
  730. if err := cli.CmdImages("--tree"); err != nil {
  731. t.Fatal(err)
  732. }
  733. stdoutPipe.Close()
  734. }()
  735. setTimeout(t, "Reading command output time out", 2*time.Second, func() {
  736. cmdOutputBytes, err := ioutil.ReadAll(bufio.NewReader(stdout))
  737. if err != nil {
  738. t.Fatal(err)
  739. }
  740. cmdOutput := string(cmdOutputBytes)
  741. regexpStrings := []string{
  742. fmt.Sprintf("└─%s Virtual Size: \\d+.\\d+ MB Tags: %s:latest", unitTestImageIDShort, unitTestImageName),
  743. "(?m) └─[0-9a-f]+.*",
  744. "(?m) └─[0-9a-f]+.*",
  745. "(?m) └─[0-9a-f]+.*",
  746. fmt.Sprintf("(?m)^ └─%s Virtual Size: \\d+.\\d+ MB Tags: test:latest", utils.TruncateID(image.ID)),
  747. }
  748. compiledRegexps := []*regexp.Regexp{}
  749. for _, regexpString := range regexpStrings {
  750. regexp, err := regexp.Compile(regexpString)
  751. if err != nil {
  752. fmt.Println("Error in regex string: ", err)
  753. return
  754. }
  755. compiledRegexps = append(compiledRegexps, regexp)
  756. }
  757. for _, regexp := range compiledRegexps {
  758. if !regexp.MatchString(cmdOutput) {
  759. t.Fatalf("images --tree content '%s' did not match regexp '%s'", cmdOutput, regexp)
  760. }
  761. }
  762. })
  763. }
  764. func buildTestImages(t *testing.T, eng *engine.Engine) *image.Image {
  765. var testBuilder = testContextTemplate{
  766. `
  767. from {IMAGE}
  768. run sh -c 'echo root:testpass > /tmp/passwd'
  769. run mkdir -p /var/run/sshd
  770. run [ "$(cat /tmp/passwd)" = "root:testpass" ]
  771. run [ "$(ls -d /var/run/sshd)" = "/var/run/sshd" ]
  772. `,
  773. nil,
  774. nil,
  775. }
  776. image, err := buildImage(testBuilder, t, eng, true)
  777. if err != nil {
  778. t.Fatal(err)
  779. }
  780. if err := eng.Job("tag", image.ID, "test").Run(); err != nil {
  781. t.Fatal(err)
  782. }
  783. return image
  784. }
  785. // #2098 - Docker cidFiles only contain short version of the containerId
  786. //sudo docker run --cidfile /tmp/docker_test.cid ubuntu echo "test"
  787. // TestRunCidFile tests that run --cidfile returns the longid
  788. func TestRunCidFileCheckIDLength(t *testing.T) {
  789. stdout, stdoutPipe := io.Pipe()
  790. tmpDir, err := ioutil.TempDir("", "TestRunCidFile")
  791. if err != nil {
  792. t.Fatal(err)
  793. }
  794. tmpCidFile := path.Join(tmpDir, "cid")
  795. cli := client.NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
  796. defer cleanup(globalEngine, t)
  797. c := make(chan struct{})
  798. go func() {
  799. defer close(c)
  800. if err := cli.CmdRun("--cidfile", tmpCidFile, unitTestImageID, "ls"); err != nil {
  801. t.Fatal(err)
  802. }
  803. }()
  804. defer os.RemoveAll(tmpDir)
  805. setTimeout(t, "Reading command output time out", 2*time.Second, func() {
  806. cmdOutput, err := bufio.NewReader(stdout).ReadString('\n')
  807. if err != nil {
  808. t.Fatal(err)
  809. }
  810. if len(cmdOutput) < 1 {
  811. t.Fatalf("'ls' should return something , not '%s'", cmdOutput)
  812. }
  813. //read the tmpCidFile
  814. buffer, err := ioutil.ReadFile(tmpCidFile)
  815. if err != nil {
  816. t.Fatal(err)
  817. }
  818. id := string(buffer)
  819. if len(id) != len("2bf44ea18873287bd9ace8a4cb536a7cbe134bed67e805fdf2f58a57f69b320c") {
  820. t.Fatalf("--cidfile should be a long id, not '%s'", id)
  821. }
  822. //test that its a valid cid? (though the container is gone..)
  823. //remove the file and dir.
  824. })
  825. setTimeout(t, "CmdRun timed out", 5*time.Second, func() {
  826. <-c
  827. })
  828. }
  829. // Ensure that CIDFile gets deleted if it's empty
  830. // Perform this test by making `docker run` fail
  831. func TestRunCidFileCleanupIfEmpty(t *testing.T) {
  832. tmpDir, err := ioutil.TempDir("", "TestRunCidFile")
  833. if err != nil {
  834. t.Fatal(err)
  835. }
  836. tmpCidFile := path.Join(tmpDir, "cid")
  837. cli := client.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
  838. defer cleanup(globalEngine, t)
  839. c := make(chan struct{})
  840. go func() {
  841. defer close(c)
  842. if err := cli.CmdRun("--cidfile", tmpCidFile, unitTestImageID); err == nil {
  843. t.Fatal("running without a command should haveve failed")
  844. }
  845. if _, err := os.Stat(tmpCidFile); err == nil {
  846. t.Fatalf("empty CIDFile '%s' should've been deleted", tmpCidFile)
  847. }
  848. }()
  849. defer os.RemoveAll(tmpDir)
  850. setTimeout(t, "CmdRun timed out", 5*time.Second, func() {
  851. <-c
  852. })
  853. }
  854. func TestContainerOrphaning(t *testing.T) {
  855. // setup a temporary directory
  856. tmpDir, err := ioutil.TempDir("", "project")
  857. if err != nil {
  858. t.Fatal(err)
  859. }
  860. defer os.RemoveAll(tmpDir)
  861. // setup a CLI and server
  862. cli := client.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
  863. defer cleanup(globalEngine, t)
  864. srv := mkServerFromEngine(globalEngine, t)
  865. // closure to build something
  866. buildSomething := func(template string, image string) string {
  867. dockerfile := path.Join(tmpDir, "Dockerfile")
  868. replacer := strings.NewReplacer("{IMAGE}", unitTestImageID)
  869. contents := replacer.Replace(template)
  870. ioutil.WriteFile(dockerfile, []byte(contents), 0x777)
  871. if err := cli.CmdBuild("-t", image, tmpDir); err != nil {
  872. t.Fatal(err)
  873. }
  874. img, err := srv.ImageInspect(image)
  875. if err != nil {
  876. t.Fatal(err)
  877. }
  878. return img.ID
  879. }
  880. // build an image
  881. imageName := "orphan-test"
  882. template1 := `
  883. from {IMAGE}
  884. cmd ["/bin/echo", "holla"]
  885. `
  886. img1 := buildSomething(template1, imageName)
  887. // create a container using the fist image
  888. if err := cli.CmdRun(imageName); err != nil {
  889. t.Fatal(err)
  890. }
  891. // build a new image that splits lineage
  892. template2 := `
  893. from {IMAGE}
  894. cmd ["/bin/echo", "holla"]
  895. expose 22
  896. `
  897. buildSomething(template2, imageName)
  898. // remove the second image by name
  899. resp := engine.NewTable("", 0)
  900. if err := srv.DeleteImage(imageName, resp, true, false, false); err == nil {
  901. t.Fatal("Expected error, got none")
  902. }
  903. // see if we deleted the first image (and orphaned the container)
  904. for _, i := range resp.Data {
  905. if img1 == i.Get("Deleted") {
  906. t.Fatal("Orphaned image with container")
  907. }
  908. }
  909. }
  910. func TestCmdKill(t *testing.T) {
  911. var (
  912. stdin, stdinPipe = io.Pipe()
  913. stdout, stdoutPipe = io.Pipe()
  914. cli = client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
  915. cli2 = client.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
  916. )
  917. defer cleanup(globalEngine, t)
  918. ch := make(chan struct{})
  919. go func() {
  920. defer close(ch)
  921. cli.CmdRun("-i", "-t", unitTestImageID, "sh", "-c", "trap 'echo SIGUSR1' USR1; trap 'echo SIGUSR2' USR2; echo Ready; while true; do read; done")
  922. }()
  923. container := waitContainerStart(t, 10*time.Second)
  924. setTimeout(t, "Read Ready timed out", 3*time.Second, func() {
  925. if err := expectPipe("Ready", stdout); err != nil {
  926. t.Fatal(err)
  927. }
  928. })
  929. setTimeout(t, "SIGUSR1 timed out", 2*time.Second, func() {
  930. for i := 0; i < 10; i++ {
  931. if err := cli2.CmdKill("-s", strconv.Itoa(int(syscall.SIGUSR1)), container.ID); err != nil {
  932. t.Fatal(err)
  933. }
  934. if err := expectPipe("SIGUSR1", stdout); err != nil {
  935. t.Fatal(err)
  936. }
  937. }
  938. })
  939. setTimeout(t, "SIGUSR2 timed out", 2*time.Second, func() {
  940. for i := 0; i < 20; i++ {
  941. sig := "USR2"
  942. if i%2 != 0 {
  943. // Swap to testing "SIGUSR2" for every odd iteration
  944. sig = "SIGUSR2"
  945. }
  946. if err := cli2.CmdKill("--signal="+sig, container.ID); err != nil {
  947. t.Fatal(err)
  948. }
  949. if err := expectPipe("SIGUSR2", stdout); err != nil {
  950. t.Fatal(err)
  951. }
  952. }
  953. })
  954. stdout.Close()
  955. time.Sleep(500 * time.Millisecond)
  956. if !container.State.IsRunning() {
  957. t.Fatal("The container should be still running")
  958. }
  959. setTimeout(t, "Waiting for container timedout", 5*time.Second, func() {
  960. if err := cli2.CmdKill(container.ID); err != nil {
  961. t.Fatal(err)
  962. }
  963. <-ch
  964. if err := cli2.CmdWait(container.ID); err != nil {
  965. t.Fatal(err)
  966. }
  967. })
  968. closeWrap(stdin, stdinPipe, stdout, stdoutPipe)
  969. }
  970. func TestRunTTYCommitRun(t *testing.T) {
  971. cli := api.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
  972. defer cleanup(globalEngine, t)
  973. ch := make(chan struct{})
  974. go func() {
  975. defer close(ch)
  976. cli.CmdRun("-t", unitTestImageID, "/bin/ls")
  977. }()
  978. container := waitContainerStart(t, 10*time.Second)
  979. time.Sleep(500 * time.Millisecond)
  980. setTimeout(t, "Waiting for container timedout", 5*time.Second, func() {
  981. <-ch
  982. })
  983. cli.CmdCommit(container.ID, "ttytest")
  984. if err := cli.CmdRun("ttytest", "/bin/ls"); err != nil {
  985. t.Fatal(err)
  986. }
  987. }