commands_test.go 25 KB

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