commands_test.go 30 KB

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