commands_test.go 31 KB

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