commands_test.go 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971
  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 stay alive when the client disconnects
  283. // but the client detaches.
  284. func TestRunDisconnectTty(t *testing.T) {
  285. stdin, stdinPipe := io.Pipe()
  286. stdout, stdoutPipe := io.Pipe()
  287. cli := docker.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
  288. defer cleanup(globalEngine, t)
  289. c1 := make(chan struct{})
  290. go func() {
  291. defer close(c1)
  292. // We're simulating a disconnect so the return value doesn't matter. What matters is the
  293. // fact that CmdRun returns.
  294. if err := cli.CmdRun("-i", "-t", unitTestImageID, "/bin/cat"); err != nil {
  295. utils.Debugf("Error CmdRun: %s", err)
  296. }
  297. }()
  298. container := waitContainerStart(t, 10*time.Second)
  299. state := setRaw(t, container)
  300. defer unsetRaw(t, container, state)
  301. // Client disconnect after run -i should keep stdin out in TTY mode
  302. setTimeout(t, "Read/Write assertion timed out", 2*time.Second, func() {
  303. if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 150); err != nil {
  304. t.Fatal(err)
  305. }
  306. })
  307. // Close pipes (simulate disconnect)
  308. if err := closeWrap(stdin, stdinPipe, stdout, stdoutPipe); err != nil {
  309. t.Fatal(err)
  310. }
  311. // wait for CmdRun to return
  312. setTimeout(t, "Waiting for CmdRun timed out", 5*time.Second, func() {
  313. <-c1
  314. })
  315. // In tty mode, we expect the process to stay alive even after client's stdin closes.
  316. // Give some time to monitor to do his thing
  317. container.WaitTimeout(500 * time.Millisecond)
  318. if !container.State.IsRunning() {
  319. t.Fatalf("/bin/cat should still be running after closing stdin (tty mode)")
  320. }
  321. }
  322. // TestAttachStdin checks attaching to stdin without stdout and stderr.
  323. // 'docker run -i -a stdin' should sends the client's stdin to the command,
  324. // then detach from it and print the container id.
  325. func TestRunAttachStdin(t *testing.T) {
  326. stdin, stdinPipe := io.Pipe()
  327. stdout, stdoutPipe := io.Pipe()
  328. cli := docker.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
  329. defer cleanup(globalEngine, t)
  330. ch := make(chan struct{})
  331. go func() {
  332. defer close(ch)
  333. cli.CmdRun("-i", "-a", "stdin", unitTestImageID, "sh", "-c", "echo hello && cat && sleep 5")
  334. }()
  335. // Send input to the command, close stdin
  336. setTimeout(t, "Write timed out", 10*time.Second, func() {
  337. if _, err := stdinPipe.Write([]byte("hi there\n")); err != nil {
  338. t.Fatal(err)
  339. }
  340. if err := stdinPipe.Close(); err != nil {
  341. t.Fatal(err)
  342. }
  343. })
  344. container := globalRuntime.List()[0]
  345. // Check output
  346. setTimeout(t, "Reading command output time out", 10*time.Second, func() {
  347. cmdOutput, err := bufio.NewReader(stdout).ReadString('\n')
  348. if err != nil {
  349. t.Fatal(err)
  350. }
  351. if cmdOutput != container.ID+"\n" {
  352. t.Fatalf("Wrong output: should be '%s', not '%s'\n", container.ID+"\n", cmdOutput)
  353. }
  354. })
  355. // wait for CmdRun to return
  356. setTimeout(t, "Waiting for CmdRun timed out", 5*time.Second, func() {
  357. <-ch
  358. })
  359. setTimeout(t, "Waiting for command to exit timed out", 10*time.Second, func() {
  360. container.Wait()
  361. })
  362. // Check logs
  363. if cmdLogs, err := container.ReadLog("json"); err != nil {
  364. t.Fatal(err)
  365. } else {
  366. if output, err := ioutil.ReadAll(cmdLogs); err != nil {
  367. t.Fatal(err)
  368. } else {
  369. expectedLogs := []string{"{\"log\":\"hello\\n\",\"stream\":\"stdout\"", "{\"log\":\"hi there\\n\",\"stream\":\"stdout\""}
  370. for _, expectedLog := range expectedLogs {
  371. if !strings.Contains(string(output), expectedLog) {
  372. t.Fatalf("Unexpected logs: should contains '%s', it is not '%s'\n", expectedLog, output)
  373. }
  374. }
  375. }
  376. }
  377. }
  378. // TestRunDetach checks attaching and detaching with the escape sequence.
  379. func TestRunDetach(t *testing.T) {
  380. stdin, stdinPipe := io.Pipe()
  381. stdout, stdoutPipe := io.Pipe()
  382. cli := docker.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
  383. defer cleanup(globalEngine, t)
  384. ch := make(chan struct{})
  385. go func() {
  386. defer close(ch)
  387. cli.CmdRun("-i", "-t", unitTestImageID, "cat")
  388. }()
  389. container := waitContainerStart(t, 10*time.Second)
  390. state := setRaw(t, container)
  391. defer unsetRaw(t, container, state)
  392. setTimeout(t, "First read/write assertion timed out", 2*time.Second, func() {
  393. if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 150); err != nil {
  394. t.Fatal(err)
  395. }
  396. })
  397. setTimeout(t, "Escape sequence timeout", 5*time.Second, func() {
  398. stdinPipe.Write([]byte{16})
  399. time.Sleep(100 * time.Millisecond)
  400. stdinPipe.Write([]byte{17})
  401. })
  402. // wait for CmdRun to return
  403. setTimeout(t, "Waiting for CmdRun timed out", 15*time.Second, func() {
  404. <-ch
  405. })
  406. closeWrap(stdin, stdinPipe, stdout, stdoutPipe)
  407. time.Sleep(500 * time.Millisecond)
  408. if !container.State.IsRunning() {
  409. t.Fatal("The detached container should be still running")
  410. }
  411. setTimeout(t, "Waiting for container to die timed out", 20*time.Second, func() {
  412. container.Kill()
  413. })
  414. }
  415. // TestAttachDetach checks that attach in tty mode can be detached using the long container ID
  416. func TestAttachDetach(t *testing.T) {
  417. stdin, stdinPipe := io.Pipe()
  418. stdout, stdoutPipe := io.Pipe()
  419. cli := docker.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
  420. defer cleanup(globalEngine, t)
  421. ch := make(chan struct{})
  422. go func() {
  423. defer close(ch)
  424. if err := cli.CmdRun("-i", "-t", "-d", unitTestImageID, "cat"); err != nil {
  425. t.Fatal(err)
  426. }
  427. }()
  428. container := waitContainerStart(t, 10*time.Second)
  429. setTimeout(t, "Reading container's id timed out", 10*time.Second, func() {
  430. buf := make([]byte, 1024)
  431. n, err := stdout.Read(buf)
  432. if err != nil {
  433. t.Fatal(err)
  434. }
  435. if strings.Trim(string(buf[:n]), " \r\n") != container.ID {
  436. t.Fatalf("Wrong ID received. Expect %s, received %s", container.ID, buf[:n])
  437. }
  438. })
  439. setTimeout(t, "Starting container timed out", 10*time.Second, func() {
  440. <-ch
  441. })
  442. state := setRaw(t, container)
  443. defer unsetRaw(t, container, state)
  444. stdin, stdinPipe = io.Pipe()
  445. stdout, stdoutPipe = io.Pipe()
  446. cli = docker.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
  447. ch = make(chan struct{})
  448. go func() {
  449. defer close(ch)
  450. if err := cli.CmdAttach(container.ID); err != nil {
  451. if err != io.ErrClosedPipe {
  452. t.Fatal(err)
  453. }
  454. }
  455. }()
  456. setTimeout(t, "First read/write assertion timed out", 2*time.Second, func() {
  457. if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 150); err != nil {
  458. if err != io.ErrClosedPipe {
  459. t.Fatal(err)
  460. }
  461. }
  462. })
  463. setTimeout(t, "Escape sequence timeout", 5*time.Second, func() {
  464. stdinPipe.Write([]byte{16})
  465. time.Sleep(100 * time.Millisecond)
  466. stdinPipe.Write([]byte{17})
  467. })
  468. // wait for CmdRun to return
  469. setTimeout(t, "Waiting for CmdAttach timed out", 15*time.Second, func() {
  470. <-ch
  471. })
  472. closeWrap(stdin, stdinPipe, stdout, stdoutPipe)
  473. time.Sleep(500 * time.Millisecond)
  474. if !container.State.IsRunning() {
  475. t.Fatal("The detached container should be still running")
  476. }
  477. setTimeout(t, "Waiting for container to die timedout", 5*time.Second, func() {
  478. container.Kill()
  479. })
  480. }
  481. // TestAttachDetachTruncatedID checks that attach in tty mode can be detached
  482. func TestAttachDetachTruncatedID(t *testing.T) {
  483. stdin, stdinPipe := io.Pipe()
  484. stdout, stdoutPipe := io.Pipe()
  485. cli := docker.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
  486. defer cleanup(globalEngine, t)
  487. // Discard the CmdRun output
  488. go stdout.Read(make([]byte, 1024))
  489. setTimeout(t, "Starting container timed out", 2*time.Second, func() {
  490. if err := cli.CmdRun("-i", "-t", "-d", unitTestImageID, "cat"); err != nil {
  491. t.Fatal(err)
  492. }
  493. })
  494. container := waitContainerStart(t, 10*time.Second)
  495. state := setRaw(t, container)
  496. defer unsetRaw(t, container, state)
  497. stdin, stdinPipe = io.Pipe()
  498. stdout, stdoutPipe = io.Pipe()
  499. cli = docker.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
  500. ch := make(chan struct{})
  501. go func() {
  502. defer close(ch)
  503. if err := cli.CmdAttach(utils.TruncateID(container.ID)); err != nil {
  504. if err != io.ErrClosedPipe {
  505. t.Fatal(err)
  506. }
  507. }
  508. }()
  509. setTimeout(t, "First read/write assertion timed out", 2*time.Second, func() {
  510. if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 150); err != nil {
  511. if err != io.ErrClosedPipe {
  512. t.Fatal(err)
  513. }
  514. }
  515. })
  516. setTimeout(t, "Escape sequence timeout", 5*time.Second, func() {
  517. stdinPipe.Write([]byte{16})
  518. time.Sleep(100 * time.Millisecond)
  519. stdinPipe.Write([]byte{17})
  520. })
  521. // wait for CmdRun to return
  522. setTimeout(t, "Waiting for CmdAttach timed out", 15*time.Second, func() {
  523. <-ch
  524. })
  525. closeWrap(stdin, stdinPipe, stdout, stdoutPipe)
  526. time.Sleep(500 * time.Millisecond)
  527. if !container.State.IsRunning() {
  528. t.Fatal("The detached container should be still running")
  529. }
  530. setTimeout(t, "Waiting for container to die timedout", 5*time.Second, func() {
  531. container.Kill()
  532. })
  533. }
  534. // Expected behaviour, the process stays alive when the client disconnects
  535. func TestAttachDisconnect(t *testing.T) {
  536. stdin, stdinPipe := io.Pipe()
  537. stdout, stdoutPipe := io.Pipe()
  538. cli := docker.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
  539. defer cleanup(globalEngine, t)
  540. go func() {
  541. // Start a process in daemon mode
  542. if err := cli.CmdRun("-d", "-i", unitTestImageID, "/bin/cat"); err != nil {
  543. utils.Debugf("Error CmdRun: %s", err)
  544. }
  545. }()
  546. setTimeout(t, "Waiting for CmdRun timed out", 10*time.Second, func() {
  547. if _, err := bufio.NewReader(stdout).ReadString('\n'); err != nil {
  548. t.Fatal(err)
  549. }
  550. })
  551. setTimeout(t, "Waiting for the container to be started timed out", 10*time.Second, func() {
  552. for {
  553. l := globalRuntime.List()
  554. if len(l) == 1 && l[0].State.IsRunning() {
  555. break
  556. }
  557. time.Sleep(10 * time.Millisecond)
  558. }
  559. })
  560. container := globalRuntime.List()[0]
  561. // Attach to it
  562. c1 := make(chan struct{})
  563. go func() {
  564. // We're simulating a disconnect so the return value doesn't matter. What matters is the
  565. // fact that CmdAttach returns.
  566. cli.CmdAttach(container.ID)
  567. close(c1)
  568. }()
  569. setTimeout(t, "First read/write assertion timed out", 2*time.Second, func() {
  570. if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 150); err != nil {
  571. t.Fatal(err)
  572. }
  573. })
  574. // Close pipes (client disconnects)
  575. if err := closeWrap(stdin, stdinPipe, stdout, stdoutPipe); err != nil {
  576. t.Fatal(err)
  577. }
  578. // Wait for attach to finish, the client disconnected, therefore, Attach finished his job
  579. setTimeout(t, "Waiting for CmdAttach timed out", 2*time.Second, func() {
  580. <-c1
  581. })
  582. // We closed stdin, expect /bin/cat to still be running
  583. // Wait a little bit to make sure container.monitor() did his thing
  584. err := container.WaitTimeout(500 * time.Millisecond)
  585. if err == nil || !container.State.IsRunning() {
  586. t.Fatalf("/bin/cat is not running after closing stdin")
  587. }
  588. // Try to avoid the timeout in destroy. Best effort, don't check error
  589. cStdin, _ := container.StdinPipe()
  590. cStdin.Close()
  591. container.Wait()
  592. }
  593. // Expected behaviour: container gets deleted automatically after exit
  594. func TestRunAutoRemove(t *testing.T) {
  595. t.Skip("Fixme. Skipping test for now, race condition")
  596. stdout, stdoutPipe := io.Pipe()
  597. cli := docker.NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
  598. defer cleanup(globalEngine, t)
  599. c := make(chan struct{})
  600. go func() {
  601. defer close(c)
  602. if err := cli.CmdRun("-rm", unitTestImageID, "hostname"); err != nil {
  603. t.Fatal(err)
  604. }
  605. }()
  606. var temporaryContainerID string
  607. setTimeout(t, "Reading command output time out", 2*time.Second, func() {
  608. cmdOutput, err := bufio.NewReader(stdout).ReadString('\n')
  609. if err != nil {
  610. t.Fatal(err)
  611. }
  612. temporaryContainerID = cmdOutput
  613. if err := closeWrap(stdout, stdoutPipe); err != nil {
  614. t.Fatal(err)
  615. }
  616. })
  617. setTimeout(t, "CmdRun timed out", 10*time.Second, func() {
  618. <-c
  619. })
  620. time.Sleep(500 * time.Millisecond)
  621. if len(globalRuntime.List()) > 0 {
  622. t.Fatalf("failed to remove container automatically: container %s still exists", temporaryContainerID)
  623. }
  624. }
  625. func TestCmdLogs(t *testing.T) {
  626. t.Skip("Test not impemented")
  627. cli := docker.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto, testDaemonAddr)
  628. defer cleanup(globalEngine, t)
  629. if err := cli.CmdRun(unitTestImageID, "sh", "-c", "ls -l"); err != nil {
  630. t.Fatal(err)
  631. }
  632. if err := cli.CmdRun("-t", unitTestImageID, "sh", "-c", "ls -l"); err != nil {
  633. t.Fatal(err)
  634. }
  635. if err := cli.CmdLogs(globalRuntime.List()[0].ID); err != nil {
  636. t.Fatal(err)
  637. }
  638. }
  639. // Expected behaviour: error out when attempting to bind mount non-existing source paths
  640. func TestRunErrorBindNonExistingSource(t *testing.T) {
  641. cli := docker.NewDockerCli(nil, nil, ioutil.Discard, testDaemonProto, testDaemonAddr)
  642. defer cleanup(globalEngine, t)
  643. c := make(chan struct{})
  644. go func() {
  645. defer close(c)
  646. // This check is made at runtime, can't be "unit tested"
  647. if err := cli.CmdRun("-v", "/i/dont/exist:/tmp", unitTestImageID, "echo 'should fail'"); err == nil {
  648. t.Fatal("should have failed to run when using /i/dont/exist as a source for the bind mount")
  649. }
  650. }()
  651. setTimeout(t, "CmdRun timed out", 5*time.Second, func() {
  652. <-c
  653. })
  654. }
  655. func TestImagesViz(t *testing.T) {
  656. stdout, stdoutPipe := io.Pipe()
  657. cli := docker.NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
  658. defer cleanup(globalEngine, t)
  659. image := buildTestImages(t, globalEngine)
  660. c := make(chan struct{})
  661. go func() {
  662. defer close(c)
  663. if err := cli.CmdImages("-viz"); err != nil {
  664. t.Fatal(err)
  665. }
  666. stdoutPipe.Close()
  667. }()
  668. setTimeout(t, "Reading command output time out", 2*time.Second, func() {
  669. cmdOutputBytes, err := ioutil.ReadAll(bufio.NewReader(stdout))
  670. if err != nil {
  671. t.Fatal(err)
  672. }
  673. cmdOutput := string(cmdOutputBytes)
  674. regexpStrings := []string{
  675. "digraph docker {",
  676. fmt.Sprintf("base -> \"%s\" \\[style=invis]", unitTestImageIDShort),
  677. fmt.Sprintf("label=\"%s\\\\n%s:latest\"", unitTestImageIDShort, unitTestImageName),
  678. fmt.Sprintf("label=\"%s\\\\n%s:%s\"", utils.TruncateID(image.ID), "test", "latest"),
  679. "base \\[style=invisible]",
  680. }
  681. compiledRegexps := []*regexp.Regexp{}
  682. for _, regexpString := range regexpStrings {
  683. regexp, err := regexp.Compile(regexpString)
  684. if err != nil {
  685. fmt.Println("Error in regex string: ", err)
  686. return
  687. }
  688. compiledRegexps = append(compiledRegexps, regexp)
  689. }
  690. for _, regexp := range compiledRegexps {
  691. if !regexp.MatchString(cmdOutput) {
  692. t.Fatalf("images -viz content '%s' did not match regexp '%s'", cmdOutput, regexp)
  693. }
  694. }
  695. })
  696. }
  697. func TestImagesTree(t *testing.T) {
  698. stdout, stdoutPipe := io.Pipe()
  699. cli := docker.NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
  700. defer cleanup(globalEngine, t)
  701. image := buildTestImages(t, globalEngine)
  702. c := make(chan struct{})
  703. go func() {
  704. defer close(c)
  705. if err := cli.CmdImages("-tree"); err != nil {
  706. t.Fatal(err)
  707. }
  708. stdoutPipe.Close()
  709. }()
  710. setTimeout(t, "Reading command output time out", 2*time.Second, func() {
  711. cmdOutputBytes, err := ioutil.ReadAll(bufio.NewReader(stdout))
  712. if err != nil {
  713. t.Fatal(err)
  714. }
  715. cmdOutput := string(cmdOutputBytes)
  716. regexpStrings := []string{
  717. fmt.Sprintf("└─%s Virtual Size: \\d+.\\d+ MB Tags: %s:latest", unitTestImageIDShort, unitTestImageName),
  718. "(?m) └─[0-9a-f]+.*",
  719. "(?m) └─[0-9a-f]+.*",
  720. "(?m) └─[0-9a-f]+.*",
  721. fmt.Sprintf("(?m)^ └─%s Virtual Size: \\d+.\\d+ MB Tags: test:latest", utils.TruncateID(image.ID)),
  722. }
  723. compiledRegexps := []*regexp.Regexp{}
  724. for _, regexpString := range regexpStrings {
  725. regexp, err := regexp.Compile(regexpString)
  726. if err != nil {
  727. fmt.Println("Error in regex string: ", err)
  728. return
  729. }
  730. compiledRegexps = append(compiledRegexps, regexp)
  731. }
  732. for _, regexp := range compiledRegexps {
  733. if !regexp.MatchString(cmdOutput) {
  734. t.Fatalf("images -tree content '%s' did not match regexp '%s'", cmdOutput, regexp)
  735. }
  736. }
  737. })
  738. }
  739. func buildTestImages(t *testing.T, eng *engine.Engine) *docker.Image {
  740. var testBuilder = testContextTemplate{
  741. `
  742. from {IMAGE}
  743. run sh -c 'echo root:testpass > /tmp/passwd'
  744. run mkdir -p /var/run/sshd
  745. run [ "$(cat /tmp/passwd)" = "root:testpass" ]
  746. run [ "$(ls -d /var/run/sshd)" = "/var/run/sshd" ]
  747. `,
  748. nil,
  749. nil,
  750. }
  751. image, err := buildImage(testBuilder, t, eng, true)
  752. if err != nil {
  753. t.Fatal(err)
  754. }
  755. err = mkServerFromEngine(eng, t).ContainerTag(image.ID, "test", "latest", false)
  756. if err != nil {
  757. t.Fatal(err)
  758. }
  759. return image
  760. }
  761. // #2098 - Docker cidFiles only contain short version of the containerId
  762. //sudo docker run -cidfile /tmp/docker_test.cid ubuntu echo "test"
  763. // TestRunCidFile tests that run -cidfile returns the longid
  764. func TestRunCidFile(t *testing.T) {
  765. stdout, stdoutPipe := io.Pipe()
  766. tmpDir, err := ioutil.TempDir("", "TestRunCidFile")
  767. if err != nil {
  768. t.Fatal(err)
  769. }
  770. tmpCidFile := path.Join(tmpDir, "cid")
  771. cli := docker.NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
  772. defer cleanup(globalEngine, t)
  773. c := make(chan struct{})
  774. go func() {
  775. defer close(c)
  776. if err := cli.CmdRun("-cidfile", tmpCidFile, unitTestImageID, "ls"); err != nil {
  777. t.Fatal(err)
  778. }
  779. }()
  780. defer os.RemoveAll(tmpDir)
  781. setTimeout(t, "Reading command output time out", 2*time.Second, func() {
  782. cmdOutput, err := bufio.NewReader(stdout).ReadString('\n')
  783. if err != nil {
  784. t.Fatal(err)
  785. }
  786. if len(cmdOutput) < 1 {
  787. t.Fatalf("'ls' should return something , not '%s'", cmdOutput)
  788. }
  789. //read the tmpCidFile
  790. buffer, err := ioutil.ReadFile(tmpCidFile)
  791. if err != nil {
  792. t.Fatal(err)
  793. }
  794. id := string(buffer)
  795. if len(id) != len("2bf44ea18873287bd9ace8a4cb536a7cbe134bed67e805fdf2f58a57f69b320c") {
  796. t.Fatalf("-cidfile should be a long id, not '%s'", id)
  797. }
  798. //test that its a valid cid? (though the container is gone..)
  799. //remove the file and dir.
  800. })
  801. setTimeout(t, "CmdRun timed out", 5*time.Second, func() {
  802. <-c
  803. })
  804. }