commands_test.go 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108
  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. var (
  868. stdin, stdinPipe = io.Pipe()
  869. stdout, stdoutPipe = io.Pipe()
  870. cli = api.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
  871. cli2 = api.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto, testDaemonAddr)
  872. )
  873. defer cleanup(globalEngine, t)
  874. ch := make(chan struct{})
  875. go func() {
  876. defer close(ch)
  877. cli.CmdRun("-i", "-t", unitTestImageID, "sh", "-c", "trap 'echo SIGUSR1' USR1; trap 'echo SIGUSR2' USR2; echo Ready; while true; do read; done")
  878. }()
  879. container := waitContainerStart(t, 10*time.Second)
  880. setTimeout(t, "Read Ready timed out", 3*time.Second, func() {
  881. if err := expectPipe("Ready", stdout); err != nil {
  882. t.Fatal(err)
  883. }
  884. })
  885. setTimeout(t, "SIGUSR1 timed out", 2*time.Second, func() {
  886. for i := 0; i < 10; i++ {
  887. if err := cli2.CmdKill("-s", strconv.Itoa(int(syscall.SIGUSR1)), container.ID); err != nil {
  888. t.Fatal(err)
  889. }
  890. if err := expectPipe("SIGUSR1", stdout); err != nil {
  891. t.Fatal(err)
  892. }
  893. }
  894. })
  895. setTimeout(t, "SIGUSR2 timed out", 2*time.Second, func() {
  896. for i := 0; i < 10; i++ {
  897. if err := cli2.CmdKill("--signal=USR2", container.ID); err != nil {
  898. t.Fatal(err)
  899. }
  900. if err := expectPipe("SIGUSR2", stdout); err != nil {
  901. t.Fatal(err)
  902. }
  903. }
  904. })
  905. stdout.Close()
  906. time.Sleep(500 * time.Millisecond)
  907. if !container.State.IsRunning() {
  908. t.Fatal("The container should be still running")
  909. }
  910. setTimeout(t, "Waiting for container timedout", 5*time.Second, func() {
  911. if err := cli2.CmdKill(container.ID); err != nil {
  912. t.Fatal(err)
  913. }
  914. <-ch
  915. if err := cli2.CmdWait(container.ID); err != nil {
  916. t.Fatal(err)
  917. }
  918. })
  919. closeWrap(stdin, stdinPipe, stdout, stdoutPipe)
  920. }