commands_test.go 29 KB

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