docker_api_build_test.go 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. package main
  2. import (
  3. "archive/tar"
  4. "bytes"
  5. "net/http"
  6. "regexp"
  7. "strings"
  8. "github.com/docker/docker/pkg/integration/checker"
  9. "github.com/go-check/check"
  10. )
  11. func (s *DockerSuite) TestBuildApiDockerfilePath(c *check.C) {
  12. // Test to make sure we stop people from trying to leave the
  13. // build context when specifying the path to the dockerfile
  14. buffer := new(bytes.Buffer)
  15. tw := tar.NewWriter(buffer)
  16. defer tw.Close()
  17. dockerfile := []byte("FROM busybox")
  18. err := tw.WriteHeader(&tar.Header{
  19. Name: "Dockerfile",
  20. Size: int64(len(dockerfile)),
  21. })
  22. //failed to write tar file header
  23. c.Assert(err, checker.IsNil)
  24. _, err = tw.Write(dockerfile)
  25. // failed to write tar file content
  26. c.Assert(err, checker.IsNil)
  27. // failed to close tar archive
  28. c.Assert(tw.Close(), checker.IsNil)
  29. res, body, err := sockRequestRaw("POST", "/build?dockerfile=../Dockerfile", buffer, "application/x-tar")
  30. c.Assert(err, checker.IsNil)
  31. c.Assert(res.StatusCode, checker.Equals, http.StatusInternalServerError)
  32. out, err := readBody(body)
  33. c.Assert(err, checker.IsNil)
  34. // Didn't complain about leaving build context
  35. c.Assert(string(out), checker.Contains, "Forbidden path outside the build context")
  36. }
  37. func (s *DockerSuite) TestBuildApiDockerFileRemote(c *check.C) {
  38. testRequires(c, NotUserNamespace)
  39. testRequires(c, DaemonIsLinux)
  40. server, err := fakeStorage(map[string]string{
  41. "testD": `FROM busybox
  42. COPY * /tmp/
  43. RUN find / -name ba*
  44. RUN find /tmp/`,
  45. })
  46. c.Assert(err, checker.IsNil)
  47. defer server.Close()
  48. res, body, err := sockRequestRaw("POST", "/build?dockerfile=baz&remote="+server.URL()+"/testD", nil, "application/json")
  49. c.Assert(err, checker.IsNil)
  50. c.Assert(res.StatusCode, checker.Equals, http.StatusOK)
  51. buf, err := readBody(body)
  52. c.Assert(err, checker.IsNil)
  53. // Make sure Dockerfile exists.
  54. // Make sure 'baz' doesn't exist ANYWHERE despite being mentioned in the URL
  55. out := string(buf)
  56. c.Assert(out, checker.Contains, "/tmp/Dockerfile")
  57. c.Assert(out, checker.Not(checker.Contains), "baz")
  58. }
  59. func (s *DockerSuite) TestBuildApiRemoteTarballContext(c *check.C) {
  60. testRequires(c, DaemonIsLinux)
  61. buffer := new(bytes.Buffer)
  62. tw := tar.NewWriter(buffer)
  63. defer tw.Close()
  64. dockerfile := []byte("FROM busybox")
  65. err := tw.WriteHeader(&tar.Header{
  66. Name: "Dockerfile",
  67. Size: int64(len(dockerfile)),
  68. })
  69. // failed to write tar file header
  70. c.Assert(err, checker.IsNil)
  71. _, err = tw.Write(dockerfile)
  72. // failed to write tar file content
  73. c.Assert(err, checker.IsNil)
  74. // failed to close tar archive
  75. c.Assert(tw.Close(), checker.IsNil)
  76. server, err := fakeBinaryStorage(map[string]*bytes.Buffer{
  77. "testT.tar": buffer,
  78. })
  79. c.Assert(err, checker.IsNil)
  80. defer server.Close()
  81. res, b, err := sockRequestRaw("POST", "/build?remote="+server.URL()+"/testT.tar", nil, "application/tar")
  82. c.Assert(err, checker.IsNil)
  83. c.Assert(res.StatusCode, checker.Equals, http.StatusOK)
  84. b.Close()
  85. }
  86. func (s *DockerSuite) TestBuildApiRemoteTarballContextWithCustomDockerfile(c *check.C) {
  87. testRequires(c, DaemonIsLinux)
  88. buffer := new(bytes.Buffer)
  89. tw := tar.NewWriter(buffer)
  90. defer tw.Close()
  91. dockerfile := []byte(`FROM busybox
  92. RUN echo 'wrong'`)
  93. err := tw.WriteHeader(&tar.Header{
  94. Name: "Dockerfile",
  95. Size: int64(len(dockerfile)),
  96. })
  97. // failed to write tar file header
  98. c.Assert(err, checker.IsNil)
  99. _, err = tw.Write(dockerfile)
  100. // failed to write tar file content
  101. c.Assert(err, checker.IsNil)
  102. custom := []byte(`FROM busybox
  103. RUN echo 'right'
  104. `)
  105. err = tw.WriteHeader(&tar.Header{
  106. Name: "custom",
  107. Size: int64(len(custom)),
  108. })
  109. // failed to write tar file header
  110. c.Assert(err, checker.IsNil)
  111. _, err = tw.Write(custom)
  112. // failed to write tar file content
  113. c.Assert(err, checker.IsNil)
  114. // failed to close tar archive
  115. c.Assert(tw.Close(), checker.IsNil)
  116. server, err := fakeBinaryStorage(map[string]*bytes.Buffer{
  117. "testT.tar": buffer,
  118. })
  119. c.Assert(err, checker.IsNil)
  120. defer server.Close()
  121. url := "/build?dockerfile=custom&remote=" + server.URL() + "/testT.tar"
  122. res, body, err := sockRequestRaw("POST", url, nil, "application/tar")
  123. c.Assert(err, checker.IsNil)
  124. c.Assert(res.StatusCode, checker.Equals, http.StatusOK)
  125. defer body.Close()
  126. content, err := readBody(body)
  127. c.Assert(err, checker.IsNil)
  128. // Build used the wrong dockerfile.
  129. c.Assert(string(content), checker.Not(checker.Contains), "wrong")
  130. }
  131. func (s *DockerSuite) TestBuildApiLowerDockerfile(c *check.C) {
  132. testRequires(c, DaemonIsLinux)
  133. git, err := newFakeGit("repo", map[string]string{
  134. "dockerfile": `FROM busybox
  135. RUN echo from dockerfile`,
  136. }, false)
  137. c.Assert(err, checker.IsNil)
  138. defer git.Close()
  139. res, body, err := sockRequestRaw("POST", "/build?remote="+git.RepoURL, nil, "application/json")
  140. c.Assert(err, checker.IsNil)
  141. c.Assert(res.StatusCode, checker.Equals, http.StatusOK)
  142. buf, err := readBody(body)
  143. c.Assert(err, checker.IsNil)
  144. out := string(buf)
  145. c.Assert(out, checker.Contains, "from dockerfile")
  146. }
  147. func (s *DockerSuite) TestBuildApiBuildGitWithF(c *check.C) {
  148. testRequires(c, DaemonIsLinux)
  149. git, err := newFakeGit("repo", map[string]string{
  150. "baz": `FROM busybox
  151. RUN echo from baz`,
  152. "Dockerfile": `FROM busybox
  153. RUN echo from Dockerfile`,
  154. }, false)
  155. c.Assert(err, checker.IsNil)
  156. defer git.Close()
  157. // Make sure it tries to 'dockerfile' query param value
  158. res, body, err := sockRequestRaw("POST", "/build?dockerfile=baz&remote="+git.RepoURL, nil, "application/json")
  159. c.Assert(err, checker.IsNil)
  160. c.Assert(res.StatusCode, checker.Equals, http.StatusOK)
  161. buf, err := readBody(body)
  162. c.Assert(err, checker.IsNil)
  163. out := string(buf)
  164. c.Assert(out, checker.Contains, "from baz")
  165. }
  166. func (s *DockerSuite) TestBuildApiDoubleDockerfile(c *check.C) {
  167. testRequires(c, UnixCli) // dockerfile overwrites Dockerfile on Windows
  168. git, err := newFakeGit("repo", map[string]string{
  169. "Dockerfile": `FROM busybox
  170. RUN echo from Dockerfile`,
  171. "dockerfile": `FROM busybox
  172. RUN echo from dockerfile`,
  173. }, false)
  174. c.Assert(err, checker.IsNil)
  175. defer git.Close()
  176. // Make sure it tries to 'dockerfile' query param value
  177. res, body, err := sockRequestRaw("POST", "/build?remote="+git.RepoURL, nil, "application/json")
  178. c.Assert(err, checker.IsNil)
  179. c.Assert(res.StatusCode, checker.Equals, http.StatusOK)
  180. buf, err := readBody(body)
  181. c.Assert(err, checker.IsNil)
  182. out := string(buf)
  183. c.Assert(out, checker.Contains, "from Dockerfile")
  184. }
  185. func (s *DockerSuite) TestBuildApiDockerfileSymlink(c *check.C) {
  186. // Test to make sure we stop people from trying to leave the
  187. // build context when specifying a symlink as the path to the dockerfile
  188. buffer := new(bytes.Buffer)
  189. tw := tar.NewWriter(buffer)
  190. defer tw.Close()
  191. err := tw.WriteHeader(&tar.Header{
  192. Name: "Dockerfile",
  193. Typeflag: tar.TypeSymlink,
  194. Linkname: "/etc/passwd",
  195. })
  196. // failed to write tar file header
  197. c.Assert(err, checker.IsNil)
  198. // failed to close tar archive
  199. c.Assert(tw.Close(), checker.IsNil)
  200. res, body, err := sockRequestRaw("POST", "/build", buffer, "application/x-tar")
  201. c.Assert(err, checker.IsNil)
  202. c.Assert(res.StatusCode, checker.Equals, http.StatusInternalServerError)
  203. out, err := readBody(body)
  204. c.Assert(err, checker.IsNil)
  205. // The reason the error is "Cannot locate specified Dockerfile" is because
  206. // in the builder, the symlink is resolved within the context, therefore
  207. // Dockerfile -> /etc/passwd becomes etc/passwd from the context which is
  208. // a nonexistent file.
  209. c.Assert(string(out), checker.Contains, "Cannot locate specified Dockerfile: Dockerfile", check.Commentf("Didn't complain about leaving build context"))
  210. }
  211. func (s *DockerSuite) TestBuildApiUnnormalizedTarPaths(c *check.C) {
  212. // Make sure that build context tars with entries of the form
  213. // x/./y don't cause caching false positives.
  214. buildFromTarContext := func(fileContents []byte) string {
  215. buffer := new(bytes.Buffer)
  216. tw := tar.NewWriter(buffer)
  217. defer tw.Close()
  218. dockerfile := []byte(`FROM busybox
  219. COPY dir /dir/`)
  220. err := tw.WriteHeader(&tar.Header{
  221. Name: "Dockerfile",
  222. Size: int64(len(dockerfile)),
  223. })
  224. //failed to write tar file header
  225. c.Assert(err, checker.IsNil)
  226. _, err = tw.Write(dockerfile)
  227. // failed to write Dockerfile in tar file content
  228. c.Assert(err, checker.IsNil)
  229. err = tw.WriteHeader(&tar.Header{
  230. Name: "dir/./file",
  231. Size: int64(len(fileContents)),
  232. })
  233. //failed to write tar file header
  234. c.Assert(err, checker.IsNil)
  235. _, err = tw.Write(fileContents)
  236. // failed to write file contents in tar file content
  237. c.Assert(err, checker.IsNil)
  238. // failed to close tar archive
  239. c.Assert(tw.Close(), checker.IsNil)
  240. res, body, err := sockRequestRaw("POST", "/build", buffer, "application/x-tar")
  241. c.Assert(err, checker.IsNil)
  242. c.Assert(res.StatusCode, checker.Equals, http.StatusOK)
  243. out, err := readBody(body)
  244. c.Assert(err, checker.IsNil)
  245. lines := strings.Split(string(out), "\n")
  246. c.Assert(len(lines), checker.GreaterThan, 1)
  247. c.Assert(lines[len(lines)-2], checker.Matches, ".*Successfully built [0-9a-f]{12}.*")
  248. re := regexp.MustCompile("Successfully built ([0-9a-f]{12})")
  249. matches := re.FindStringSubmatch(lines[len(lines)-2])
  250. return matches[1]
  251. }
  252. imageA := buildFromTarContext([]byte("abc"))
  253. imageB := buildFromTarContext([]byte("def"))
  254. c.Assert(imageA, checker.Not(checker.Equals), imageB)
  255. }