commands_test.go 25 KB

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