commands_test.go 30 KB

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