dockerCmd_utils_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. package integration
  2. import (
  3. "fmt"
  4. "os"
  5. "os/exec"
  6. "testing"
  7. "io/ioutil"
  8. "strings"
  9. "time"
  10. "github.com/go-check/check"
  11. )
  12. const dockerBinary = "docker"
  13. // Setup go-check for this test
  14. func Test(t *testing.T) {
  15. check.TestingT(t)
  16. }
  17. func init() {
  18. check.Suite(&DockerCmdSuite{})
  19. }
  20. type DockerCmdSuite struct{}
  21. // Fake the exec.Command to use our mock.
  22. func (s *DockerCmdSuite) SetUpTest(c *check.C) {
  23. execCommand = fakeExecCommand
  24. }
  25. // And bring it back to normal after the test.
  26. func (s *DockerCmdSuite) TearDownTest(c *check.C) {
  27. execCommand = exec.Command
  28. }
  29. // DockerCmdWithError tests
  30. func (s *DockerCmdSuite) TestDockerCmdWithError(c *check.C) {
  31. cmds := []struct {
  32. binary string
  33. args []string
  34. expectedOut string
  35. expectedExitCode int
  36. expectedError error
  37. }{
  38. {
  39. "doesnotexists",
  40. []string{},
  41. "Command doesnotexists not found.",
  42. 1,
  43. fmt.Errorf("exit status 1"),
  44. },
  45. {
  46. dockerBinary,
  47. []string{"an", "error"},
  48. "an error has occurred",
  49. 1,
  50. fmt.Errorf("exit status 1"),
  51. },
  52. {
  53. dockerBinary,
  54. []string{"an", "exitCode", "127"},
  55. "an error has occurred with exitCode 127",
  56. 127,
  57. fmt.Errorf("exit status 127"),
  58. },
  59. {
  60. dockerBinary,
  61. []string{"run", "-ti", "ubuntu", "echo", "hello"},
  62. "hello",
  63. 0,
  64. nil,
  65. },
  66. }
  67. for _, cmd := range cmds {
  68. out, exitCode, error := DockerCmdWithError(cmd.binary, cmd.args...)
  69. c.Assert(out, check.Equals, cmd.expectedOut, check.Commentf("Expected output %q for arguments %v, got %q", cmd.expectedOut, cmd.args, out))
  70. c.Assert(exitCode, check.Equals, cmd.expectedExitCode, check.Commentf("Expected exitCode %q for arguments %v, got %q", cmd.expectedExitCode, cmd.args, exitCode))
  71. if cmd.expectedError != nil {
  72. c.Assert(error, check.NotNil, check.Commentf("Expected an error %q, got nothing", cmd.expectedError))
  73. c.Assert(error.Error(), check.Equals, cmd.expectedError.Error(), check.Commentf("Expected error %q for arguments %v, got %q", cmd.expectedError.Error(), cmd.args, error.Error()))
  74. } else {
  75. c.Assert(error, check.IsNil, check.Commentf("Expected no error, got %v", error))
  76. }
  77. }
  78. }
  79. // DockerCmdWithStdoutStderr tests
  80. type dockerCmdWithStdoutStderrErrorSuite struct{}
  81. func (s *dockerCmdWithStdoutStderrErrorSuite) Test(c *check.C) {
  82. // Should fail, the test too
  83. DockerCmdWithStdoutStderr(dockerBinary, c, "an", "error")
  84. }
  85. type dockerCmdWithStdoutStderrSuccessSuite struct{}
  86. func (s *dockerCmdWithStdoutStderrSuccessSuite) Test(c *check.C) {
  87. stdout, stderr, exitCode := DockerCmdWithStdoutStderr(dockerBinary, c, "run", "-ti", "ubuntu", "echo", "hello")
  88. c.Assert(stdout, check.Equals, "hello")
  89. c.Assert(stderr, check.Equals, "")
  90. c.Assert(exitCode, check.Equals, 0)
  91. }
  92. func (s *DockerCmdSuite) TestDockerCmdWithStdoutStderrError(c *check.C) {
  93. // Run error suite, should fail.
  94. output := String{}
  95. result := check.Run(&dockerCmdWithStdoutStderrErrorSuite{}, &check.RunConf{Output: &output})
  96. c.Check(result.Succeeded, check.Equals, 0)
  97. c.Check(result.Failed, check.Equals, 1)
  98. }
  99. func (s *DockerCmdSuite) TestDockerCmdWithStdoutStderrSuccess(c *check.C) {
  100. // Run error suite, should fail.
  101. output := String{}
  102. result := check.Run(&dockerCmdWithStdoutStderrSuccessSuite{}, &check.RunConf{Output: &output})
  103. c.Check(result.Succeeded, check.Equals, 1)
  104. c.Check(result.Failed, check.Equals, 0)
  105. }
  106. // DockerCmd tests
  107. type dockerCmdErrorSuite struct{}
  108. func (s *dockerCmdErrorSuite) Test(c *check.C) {
  109. // Should fail, the test too
  110. DockerCmd(dockerBinary, c, "an", "error")
  111. }
  112. type dockerCmdSuccessSuite struct{}
  113. func (s *dockerCmdSuccessSuite) Test(c *check.C) {
  114. stdout, exitCode := DockerCmd(dockerBinary, c, "run", "-ti", "ubuntu", "echo", "hello")
  115. c.Assert(stdout, check.Equals, "hello")
  116. c.Assert(exitCode, check.Equals, 0)
  117. }
  118. func (s *DockerCmdSuite) TestDockerCmdError(c *check.C) {
  119. // Run error suite, should fail.
  120. output := String{}
  121. result := check.Run(&dockerCmdErrorSuite{}, &check.RunConf{Output: &output})
  122. c.Check(result.Succeeded, check.Equals, 0)
  123. c.Check(result.Failed, check.Equals, 1)
  124. }
  125. func (s *DockerCmdSuite) TestDockerCmdSuccess(c *check.C) {
  126. // Run error suite, should fail.
  127. output := String{}
  128. result := check.Run(&dockerCmdSuccessSuite{}, &check.RunConf{Output: &output})
  129. c.Check(result.Succeeded, check.Equals, 1)
  130. c.Check(result.Failed, check.Equals, 0)
  131. }
  132. // DockerCmdWithTimeout tests
  133. func (s *DockerCmdSuite) TestDockerCmdWithTimeout(c *check.C) {
  134. cmds := []struct {
  135. binary string
  136. args []string
  137. timeout time.Duration
  138. expectedOut string
  139. expectedExitCode int
  140. expectedError error
  141. }{
  142. {
  143. "doesnotexists",
  144. []string{},
  145. 200 * time.Millisecond,
  146. `Command doesnotexists not found.`,
  147. 1,
  148. fmt.Errorf(`"" failed with errors: exit status 1 : "Command doesnotexists not found."`),
  149. },
  150. {
  151. dockerBinary,
  152. []string{"an", "error"},
  153. 200 * time.Millisecond,
  154. `an error has occurred`,
  155. 1,
  156. fmt.Errorf(`"an error" failed with errors: exit status 1 : "an error has occurred"`),
  157. },
  158. {
  159. dockerBinary,
  160. []string{"a", "command", "that", "times", "out"},
  161. 5 * time.Millisecond,
  162. "",
  163. 0,
  164. fmt.Errorf(`"a command that times out" failed with errors: command timed out : ""`),
  165. },
  166. {
  167. dockerBinary,
  168. []string{"run", "-ti", "ubuntu", "echo", "hello"},
  169. 200 * time.Millisecond,
  170. "hello",
  171. 0,
  172. nil,
  173. },
  174. }
  175. for _, cmd := range cmds {
  176. out, exitCode, error := DockerCmdWithTimeout(cmd.binary, cmd.timeout, cmd.args...)
  177. c.Assert(out, check.Equals, cmd.expectedOut, check.Commentf("Expected output %q for arguments %v, got %q", cmd.expectedOut, cmd.args, out))
  178. c.Assert(exitCode, check.Equals, cmd.expectedExitCode, check.Commentf("Expected exitCode %q for arguments %v, got %q", cmd.expectedExitCode, cmd.args, exitCode))
  179. if cmd.expectedError != nil {
  180. c.Assert(error, check.NotNil, check.Commentf("Expected an error %q, got nothing", cmd.expectedError))
  181. c.Assert(error.Error(), check.Equals, cmd.expectedError.Error(), check.Commentf("Expected error %q for arguments %v, got %q", cmd.expectedError.Error(), cmd.args, error.Error()))
  182. } else {
  183. c.Assert(error, check.IsNil, check.Commentf("Expected no error, got %v", error))
  184. }
  185. }
  186. }
  187. // DockerCmdInDir tests
  188. func (s *DockerCmdSuite) TestDockerCmdInDir(c *check.C) {
  189. tempFolder, err := ioutil.TempDir("", "test-docker-cmd-in-dir")
  190. c.Assert(err, check.IsNil)
  191. cmds := []struct {
  192. binary string
  193. args []string
  194. expectedOut string
  195. expectedExitCode int
  196. expectedError error
  197. }{
  198. {
  199. "doesnotexists",
  200. []string{},
  201. `Command doesnotexists not found.`,
  202. 1,
  203. fmt.Errorf(`"dir:%s" failed with errors: exit status 1 : "Command doesnotexists not found."`, tempFolder),
  204. },
  205. {
  206. dockerBinary,
  207. []string{"an", "error"},
  208. `an error has occurred`,
  209. 1,
  210. fmt.Errorf(`"dir:%s an error" failed with errors: exit status 1 : "an error has occurred"`, tempFolder),
  211. },
  212. {
  213. dockerBinary,
  214. []string{"run", "-ti", "ubuntu", "echo", "hello"},
  215. "hello",
  216. 0,
  217. nil,
  218. },
  219. }
  220. for _, cmd := range cmds {
  221. // We prepend the arguments with dir:thefolder.. the fake command will check
  222. // that the current workdir is the same as the one we are passing.
  223. args := append([]string{"dir:" + tempFolder}, cmd.args...)
  224. out, exitCode, error := DockerCmdInDir(cmd.binary, tempFolder, args...)
  225. c.Assert(out, check.Equals, cmd.expectedOut, check.Commentf("Expected output %q for arguments %v, got %q", cmd.expectedOut, cmd.args, out))
  226. c.Assert(exitCode, check.Equals, cmd.expectedExitCode, check.Commentf("Expected exitCode %q for arguments %v, got %q", cmd.expectedExitCode, cmd.args, exitCode))
  227. if cmd.expectedError != nil {
  228. c.Assert(error, check.NotNil, check.Commentf("Expected an error %q, got nothing", cmd.expectedError))
  229. c.Assert(error.Error(), check.Equals, cmd.expectedError.Error(), check.Commentf("Expected error %q for arguments %v, got %q", cmd.expectedError.Error(), cmd.args, error.Error()))
  230. } else {
  231. c.Assert(error, check.IsNil, check.Commentf("Expected no error, got %v", error))
  232. }
  233. }
  234. }
  235. // DockerCmdInDirWithTimeout tests
  236. func (s *DockerCmdSuite) TestDockerCmdInDirWithTimeout(c *check.C) {
  237. tempFolder, err := ioutil.TempDir("", "test-docker-cmd-in-dir")
  238. c.Assert(err, check.IsNil)
  239. cmds := []struct {
  240. binary string
  241. args []string
  242. timeout time.Duration
  243. expectedOut string
  244. expectedExitCode int
  245. expectedError error
  246. }{
  247. {
  248. "doesnotexists",
  249. []string{},
  250. 200 * time.Millisecond,
  251. `Command doesnotexists not found.`,
  252. 1,
  253. fmt.Errorf(`"dir:%s" failed with errors: exit status 1 : "Command doesnotexists not found."`, tempFolder),
  254. },
  255. {
  256. dockerBinary,
  257. []string{"an", "error"},
  258. 200 * time.Millisecond,
  259. `an error has occurred`,
  260. 1,
  261. fmt.Errorf(`"dir:%s an error" failed with errors: exit status 1 : "an error has occurred"`, tempFolder),
  262. },
  263. {
  264. dockerBinary,
  265. []string{"a", "command", "that", "times", "out"},
  266. 5 * time.Millisecond,
  267. "",
  268. 0,
  269. fmt.Errorf(`"dir:%s a command that times out" failed with errors: command timed out : ""`, tempFolder),
  270. },
  271. {
  272. dockerBinary,
  273. []string{"run", "-ti", "ubuntu", "echo", "hello"},
  274. 200 * time.Millisecond,
  275. "hello",
  276. 0,
  277. nil,
  278. },
  279. }
  280. for _, cmd := range cmds {
  281. // We prepend the arguments with dir:thefolder.. the fake command will check
  282. // that the current workdir is the same as the one we are passing.
  283. args := append([]string{"dir:" + tempFolder}, cmd.args...)
  284. out, exitCode, error := DockerCmdInDirWithTimeout(cmd.binary, cmd.timeout, tempFolder, args...)
  285. c.Assert(out, check.Equals, cmd.expectedOut, check.Commentf("Expected output %q for arguments %v, got %q", cmd.expectedOut, cmd.args, out))
  286. c.Assert(exitCode, check.Equals, cmd.expectedExitCode, check.Commentf("Expected exitCode %q for arguments %v, got %q", cmd.expectedExitCode, cmd.args, exitCode))
  287. if cmd.expectedError != nil {
  288. c.Assert(error, check.NotNil, check.Commentf("Expected an error %q, got nothing", cmd.expectedError))
  289. c.Assert(error.Error(), check.Equals, cmd.expectedError.Error(), check.Commentf("Expected error %q for arguments %v, got %q", cmd.expectedError.Error(), cmd.args, error.Error()))
  290. } else {
  291. c.Assert(error, check.IsNil, check.Commentf("Expected no error, got %v", error))
  292. }
  293. }
  294. }
  295. // Helpers :)
  296. // Type implementing the io.Writer interface for analyzing output.
  297. type String struct {
  298. value string
  299. }
  300. // The only function required by the io.Writer interface. Will append
  301. // written data to the String.value string.
  302. func (s *String) Write(p []byte) (n int, err error) {
  303. s.value += string(p)
  304. return len(p), nil
  305. }
  306. // Helper function that mock the exec.Command call (and call the test binary)
  307. func fakeExecCommand(command string, args ...string) *exec.Cmd {
  308. cs := []string{"-test.run=TestHelperProcess", "--", command}
  309. cs = append(cs, args...)
  310. cmd := exec.Command(os.Args[0], cs...)
  311. cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
  312. return cmd
  313. }
  314. func TestHelperProcess(t *testing.T) {
  315. if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
  316. return
  317. }
  318. args := os.Args
  319. // Previous arguments are tests stuff, that looks like :
  320. // /tmp/go-build970079519/…/_test/integration.test -test.run=TestHelperProcess --
  321. cmd, args := args[3], args[4:]
  322. // Handle the case where args[0] is dir:...
  323. if len(args) > 0 && strings.HasPrefix(args[0], "dir:") {
  324. expectedCwd := args[0][4:]
  325. if len(args) > 1 {
  326. args = args[1:]
  327. }
  328. cwd, err := os.Getwd()
  329. if err != nil {
  330. fmt.Fprintf(os.Stderr, "Failed to get workingdir: %v", err)
  331. os.Exit(1)
  332. }
  333. // This checks that the given path is the same as the currend working dire
  334. if expectedCwd != cwd {
  335. fmt.Fprintf(os.Stderr, "Current workdir should be %q, but is %q", expectedCwd, cwd)
  336. }
  337. }
  338. switch cmd {
  339. case dockerBinary:
  340. argsStr := strings.Join(args, " ")
  341. switch argsStr {
  342. case "an exitCode 127":
  343. fmt.Fprintf(os.Stderr, "an error has occurred with exitCode 127")
  344. os.Exit(127)
  345. case "an error":
  346. fmt.Fprintf(os.Stderr, "an error has occurred")
  347. os.Exit(1)
  348. case "a command that times out":
  349. time.Sleep(10 * time.Millisecond)
  350. fmt.Fprintf(os.Stdout, "too long, should be killed")
  351. // A random exit code (that should never happened in tests)
  352. os.Exit(7)
  353. case "run -ti ubuntu echo hello":
  354. fmt.Fprintf(os.Stdout, "hello")
  355. default:
  356. fmt.Fprintf(os.Stdout, "no arguments")
  357. }
  358. default:
  359. fmt.Fprintf(os.Stderr, "Command %s not found.", cmd)
  360. os.Exit(1)
  361. }
  362. // some code here to check arguments perhaps?
  363. os.Exit(0)
  364. }