commands_test.go 21 KB

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