commands_test.go 23 KB

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