commands_test.go 25 KB

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