commands_test.go 29 KB

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