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. c.Skip("racey test")
  135. cmds := []struct {
  136. binary string
  137. args []string
  138. timeout time.Duration
  139. expectedOut string
  140. expectedExitCode int
  141. expectedError error
  142. }{
  143. {
  144. "doesnotexists",
  145. []string{},
  146. 5 * time.Millisecond,
  147. `Command doesnotexists not found.`,
  148. 1,
  149. fmt.Errorf(`"" failed with errors: exit status 1 : "Command doesnotexists not found."`),
  150. },
  151. {
  152. dockerBinary,
  153. []string{"an", "error"},
  154. 5 * time.Millisecond,
  155. `an error has occurred`,
  156. 1,
  157. fmt.Errorf(`"an error" failed with errors: exit status 1 : "an error has occurred"`),
  158. },
  159. {
  160. dockerBinary,
  161. []string{"a", "command", "that", "times", "out"},
  162. 5 * time.Millisecond,
  163. "",
  164. 0,
  165. fmt.Errorf(`"a command that times out" failed with errors: command timed out : ""`),
  166. },
  167. {
  168. dockerBinary,
  169. []string{"run", "-ti", "ubuntu", "echo", "hello"},
  170. 5 * time.Millisecond,
  171. "hello",
  172. 0,
  173. nil,
  174. },
  175. }
  176. for _, cmd := range cmds {
  177. out, exitCode, error := DockerCmdWithTimeout(cmd.binary, cmd.timeout, cmd.args...)
  178. c.Assert(out, check.Equals, cmd.expectedOut, check.Commentf("Expected output %q for arguments %v, got %q", cmd.expectedOut, cmd.args, out))
  179. c.Assert(exitCode, check.Equals, cmd.expectedExitCode, check.Commentf("Expected exitCode %q for arguments %v, got %q", cmd.expectedExitCode, cmd.args, exitCode))
  180. if cmd.expectedError != nil {
  181. c.Assert(error, check.NotNil, check.Commentf("Expected an error %q, got nothing", cmd.expectedError))
  182. 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()))
  183. } else {
  184. c.Assert(error, check.IsNil, check.Commentf("Expected no error, got %v", error))
  185. }
  186. }
  187. }
  188. // DockerCmdInDir tests
  189. func (s *DockerCmdSuite) TestDockerCmdInDir(c *check.C) {
  190. tempFolder, err := ioutil.TempDir("", "test-docker-cmd-in-dir")
  191. c.Assert(err, check.IsNil)
  192. cmds := []struct {
  193. binary string
  194. args []string
  195. expectedOut string
  196. expectedExitCode int
  197. expectedError error
  198. }{
  199. {
  200. "doesnotexists",
  201. []string{},
  202. `Command doesnotexists not found.`,
  203. 1,
  204. fmt.Errorf(`"dir:%s" failed with errors: exit status 1 : "Command doesnotexists not found."`, tempFolder),
  205. },
  206. {
  207. dockerBinary,
  208. []string{"an", "error"},
  209. `an error has occurred`,
  210. 1,
  211. fmt.Errorf(`"dir:%s an error" failed with errors: exit status 1 : "an error has occurred"`, tempFolder),
  212. },
  213. {
  214. dockerBinary,
  215. []string{"run", "-ti", "ubuntu", "echo", "hello"},
  216. "hello",
  217. 0,
  218. nil,
  219. },
  220. }
  221. for _, cmd := range cmds {
  222. // We prepend the arguments with dir:thefolder.. the fake command will check
  223. // that the current workdir is the same as the one we are passing.
  224. args := append([]string{"dir:" + tempFolder}, cmd.args...)
  225. out, exitCode, error := DockerCmdInDir(cmd.binary, tempFolder, args...)
  226. c.Assert(out, check.Equals, cmd.expectedOut, check.Commentf("Expected output %q for arguments %v, got %q", cmd.expectedOut, cmd.args, out))
  227. c.Assert(exitCode, check.Equals, cmd.expectedExitCode, check.Commentf("Expected exitCode %q for arguments %v, got %q", cmd.expectedExitCode, cmd.args, exitCode))
  228. if cmd.expectedError != nil {
  229. c.Assert(error, check.NotNil, check.Commentf("Expected an error %q, got nothing", cmd.expectedError))
  230. 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()))
  231. } else {
  232. c.Assert(error, check.IsNil, check.Commentf("Expected no error, got %v", error))
  233. }
  234. }
  235. }
  236. // DockerCmdInDirWithTimeout tests
  237. func (s *DockerCmdSuite) TestDockerCmdInDirWithTimeout(c *check.C) {
  238. tempFolder, err := ioutil.TempDir("", "test-docker-cmd-in-dir")
  239. c.Assert(err, check.IsNil)
  240. cmds := []struct {
  241. binary string
  242. args []string
  243. timeout time.Duration
  244. expectedOut string
  245. expectedExitCode int
  246. expectedError error
  247. }{
  248. {
  249. "doesnotexists",
  250. []string{},
  251. 5 * time.Millisecond,
  252. `Command doesnotexists not found.`,
  253. 1,
  254. fmt.Errorf(`"dir:%s" failed with errors: exit status 1 : "Command doesnotexists not found."`, tempFolder),
  255. },
  256. {
  257. dockerBinary,
  258. []string{"an", "error"},
  259. 5 * time.Millisecond,
  260. `an error has occurred`,
  261. 1,
  262. fmt.Errorf(`"dir:%s an error" failed with errors: exit status 1 : "an error has occurred"`, tempFolder),
  263. },
  264. {
  265. dockerBinary,
  266. []string{"a", "command", "that", "times", "out"},
  267. 5 * time.Millisecond,
  268. "",
  269. 0,
  270. fmt.Errorf(`"dir:%s a command that times out" failed with errors: command timed out : ""`, tempFolder),
  271. },
  272. {
  273. dockerBinary,
  274. []string{"run", "-ti", "ubuntu", "echo", "hello"},
  275. 5 * time.Millisecond,
  276. "hello",
  277. 0,
  278. nil,
  279. },
  280. }
  281. for _, cmd := range cmds {
  282. // We prepend the arguments with dir:thefolder.. the fake command will check
  283. // that the current workdir is the same as the one we are passing.
  284. args := append([]string{"dir:" + tempFolder}, cmd.args...)
  285. out, exitCode, error := DockerCmdInDirWithTimeout(cmd.binary, cmd.timeout, tempFolder, args...)
  286. c.Assert(out, check.Equals, cmd.expectedOut, check.Commentf("Expected output %q for arguments %v, got %q", cmd.expectedOut, cmd.args, out))
  287. c.Assert(exitCode, check.Equals, cmd.expectedExitCode, check.Commentf("Expected exitCode %q for arguments %v, got %q", cmd.expectedExitCode, cmd.args, exitCode))
  288. if cmd.expectedError != nil {
  289. c.Assert(error, check.NotNil, check.Commentf("Expected an error %q, got nothing", cmd.expectedError))
  290. 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()))
  291. } else {
  292. c.Assert(error, check.IsNil, check.Commentf("Expected no error, got %v", error))
  293. }
  294. }
  295. }
  296. // Helpers :)
  297. // Type implementing the io.Writer interface for analyzing output.
  298. type String struct {
  299. value string
  300. }
  301. // The only function required by the io.Writer interface. Will append
  302. // written data to the String.value string.
  303. func (s *String) Write(p []byte) (n int, err error) {
  304. s.value += string(p)
  305. return len(p), nil
  306. }
  307. // Helper function that mock the exec.Command call (and call the test binary)
  308. func fakeExecCommand(command string, args ...string) *exec.Cmd {
  309. cs := []string{"-test.run=TestHelperProcess", "--", command}
  310. cs = append(cs, args...)
  311. cmd := exec.Command(os.Args[0], cs...)
  312. cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
  313. return cmd
  314. }
  315. func TestHelperProcess(t *testing.T) {
  316. if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
  317. return
  318. }
  319. args := os.Args
  320. // Previous arguments are tests stuff, that looks like :
  321. // /tmp/go-build970079519/…/_test/integration.test -test.run=TestHelperProcess --
  322. cmd, args := args[3], args[4:]
  323. // Handle the case where args[0] is dir:...
  324. if len(args) > 0 && strings.HasPrefix(args[0], "dir:") {
  325. expectedCwd := args[0][4:]
  326. if len(args) > 1 {
  327. args = args[1:]
  328. }
  329. cwd, err := os.Getwd()
  330. if err != nil {
  331. fmt.Fprintf(os.Stderr, "Failed to get workingdir: %v", err)
  332. os.Exit(1)
  333. }
  334. // This checks that the given path is the same as the currend working dire
  335. if expectedCwd != cwd {
  336. fmt.Fprintf(os.Stderr, "Current workdir should be %q, but is %q", expectedCwd, cwd)
  337. }
  338. }
  339. switch cmd {
  340. case dockerBinary:
  341. argsStr := strings.Join(args, " ")
  342. switch argsStr {
  343. case "an exitCode 127":
  344. fmt.Fprintf(os.Stderr, "an error has occurred with exitCode 127")
  345. os.Exit(127)
  346. case "an error":
  347. fmt.Fprintf(os.Stderr, "an error has occurred")
  348. os.Exit(1)
  349. case "a command that times out":
  350. time.Sleep(10 * time.Millisecond)
  351. fmt.Fprintf(os.Stdout, "too long, should be killed")
  352. os.Exit(0)
  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. }