docker_api_build_test.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660
  1. package main
  2. import (
  3. "archive/tar"
  4. "bytes"
  5. "encoding/json"
  6. "fmt"
  7. "io"
  8. "io/ioutil"
  9. "net/http"
  10. "regexp"
  11. "strings"
  12. "github.com/docker/docker/api/types"
  13. "github.com/docker/docker/integration-cli/checker"
  14. "github.com/docker/docker/integration-cli/cli/build/fakecontext"
  15. "github.com/docker/docker/integration-cli/cli/build/fakegit"
  16. "github.com/docker/docker/integration-cli/cli/build/fakestorage"
  17. "github.com/docker/docker/integration-cli/request"
  18. "github.com/go-check/check"
  19. "github.com/moby/buildkit/session"
  20. "github.com/moby/buildkit/session/filesync"
  21. "github.com/stretchr/testify/assert"
  22. "github.com/stretchr/testify/require"
  23. "golang.org/x/net/context"
  24. "golang.org/x/sync/errgroup"
  25. )
  26. func (s *DockerSuite) TestBuildAPIDockerFileRemote(c *check.C) {
  27. testRequires(c, NotUserNamespace)
  28. var testD string
  29. if testEnv.OSType == "windows" {
  30. testD = `FROM busybox
  31. RUN find / -name ba*
  32. RUN find /tmp/`
  33. } else {
  34. // -xdev is required because sysfs can cause EPERM
  35. testD = `FROM busybox
  36. RUN find / -xdev -name ba*
  37. RUN find /tmp/`
  38. }
  39. server := fakestorage.New(c, "", fakecontext.WithFiles(map[string]string{"testD": testD}))
  40. defer server.Close()
  41. res, body, err := request.Post("/build?dockerfile=baz&remote="+server.URL()+"/testD", request.JSON)
  42. c.Assert(err, checker.IsNil)
  43. c.Assert(res.StatusCode, checker.Equals, http.StatusOK)
  44. buf, err := request.ReadBody(body)
  45. c.Assert(err, checker.IsNil)
  46. // Make sure Dockerfile exists.
  47. // Make sure 'baz' doesn't exist ANYWHERE despite being mentioned in the URL
  48. out := string(buf)
  49. c.Assert(out, checker.Contains, "RUN find /tmp")
  50. c.Assert(out, checker.Not(checker.Contains), "baz")
  51. }
  52. func (s *DockerSuite) TestBuildAPIRemoteTarballContext(c *check.C) {
  53. buffer := new(bytes.Buffer)
  54. tw := tar.NewWriter(buffer)
  55. defer tw.Close()
  56. dockerfile := []byte("FROM busybox")
  57. err := tw.WriteHeader(&tar.Header{
  58. Name: "Dockerfile",
  59. Size: int64(len(dockerfile)),
  60. })
  61. // failed to write tar file header
  62. c.Assert(err, checker.IsNil)
  63. _, err = tw.Write(dockerfile)
  64. // failed to write tar file content
  65. c.Assert(err, checker.IsNil)
  66. // failed to close tar archive
  67. c.Assert(tw.Close(), checker.IsNil)
  68. server := fakestorage.New(c, "", fakecontext.WithBinaryFiles(map[string]*bytes.Buffer{
  69. "testT.tar": buffer,
  70. }))
  71. defer server.Close()
  72. res, b, err := request.Post("/build?remote="+server.URL()+"/testT.tar", request.ContentType("application/tar"))
  73. c.Assert(err, checker.IsNil)
  74. c.Assert(res.StatusCode, checker.Equals, http.StatusOK)
  75. b.Close()
  76. }
  77. func (s *DockerSuite) TestBuildAPIRemoteTarballContextWithCustomDockerfile(c *check.C) {
  78. buffer := new(bytes.Buffer)
  79. tw := tar.NewWriter(buffer)
  80. defer tw.Close()
  81. dockerfile := []byte(`FROM busybox
  82. RUN echo 'wrong'`)
  83. err := tw.WriteHeader(&tar.Header{
  84. Name: "Dockerfile",
  85. Size: int64(len(dockerfile)),
  86. })
  87. // failed to write tar file header
  88. c.Assert(err, checker.IsNil)
  89. _, err = tw.Write(dockerfile)
  90. // failed to write tar file content
  91. c.Assert(err, checker.IsNil)
  92. custom := []byte(`FROM busybox
  93. RUN echo 'right'
  94. `)
  95. err = tw.WriteHeader(&tar.Header{
  96. Name: "custom",
  97. Size: int64(len(custom)),
  98. })
  99. // failed to write tar file header
  100. c.Assert(err, checker.IsNil)
  101. _, err = tw.Write(custom)
  102. // failed to write tar file content
  103. c.Assert(err, checker.IsNil)
  104. // failed to close tar archive
  105. c.Assert(tw.Close(), checker.IsNil)
  106. server := fakestorage.New(c, "", fakecontext.WithBinaryFiles(map[string]*bytes.Buffer{
  107. "testT.tar": buffer,
  108. }))
  109. defer server.Close()
  110. url := "/build?dockerfile=custom&remote=" + server.URL() + "/testT.tar"
  111. res, body, err := request.Post(url, request.ContentType("application/tar"))
  112. c.Assert(err, checker.IsNil)
  113. c.Assert(res.StatusCode, checker.Equals, http.StatusOK)
  114. defer body.Close()
  115. content, err := request.ReadBody(body)
  116. c.Assert(err, checker.IsNil)
  117. // Build used the wrong dockerfile.
  118. c.Assert(string(content), checker.Not(checker.Contains), "wrong")
  119. }
  120. func (s *DockerSuite) TestBuildAPILowerDockerfile(c *check.C) {
  121. git := fakegit.New(c, "repo", map[string]string{
  122. "dockerfile": `FROM busybox
  123. RUN echo from dockerfile`,
  124. }, false)
  125. defer git.Close()
  126. res, body, err := request.Post("/build?remote="+git.RepoURL, request.JSON)
  127. c.Assert(err, checker.IsNil)
  128. c.Assert(res.StatusCode, checker.Equals, http.StatusOK)
  129. buf, err := request.ReadBody(body)
  130. c.Assert(err, checker.IsNil)
  131. out := string(buf)
  132. c.Assert(out, checker.Contains, "from dockerfile")
  133. }
  134. func (s *DockerSuite) TestBuildAPIBuildGitWithF(c *check.C) {
  135. git := fakegit.New(c, "repo", map[string]string{
  136. "baz": `FROM busybox
  137. RUN echo from baz`,
  138. "Dockerfile": `FROM busybox
  139. RUN echo from Dockerfile`,
  140. }, false)
  141. defer git.Close()
  142. // Make sure it tries to 'dockerfile' query param value
  143. res, body, err := request.Post("/build?dockerfile=baz&remote="+git.RepoURL, request.JSON)
  144. c.Assert(err, checker.IsNil)
  145. c.Assert(res.StatusCode, checker.Equals, http.StatusOK)
  146. buf, err := request.ReadBody(body)
  147. c.Assert(err, checker.IsNil)
  148. out := string(buf)
  149. c.Assert(out, checker.Contains, "from baz")
  150. }
  151. func (s *DockerSuite) TestBuildAPIDoubleDockerfile(c *check.C) {
  152. testRequires(c, UnixCli) // dockerfile overwrites Dockerfile on Windows
  153. git := fakegit.New(c, "repo", map[string]string{
  154. "Dockerfile": `FROM busybox
  155. RUN echo from Dockerfile`,
  156. "dockerfile": `FROM busybox
  157. RUN echo from dockerfile`,
  158. }, false)
  159. defer git.Close()
  160. // Make sure it tries to 'dockerfile' query param value
  161. res, body, err := request.Post("/build?remote="+git.RepoURL, request.JSON)
  162. c.Assert(err, checker.IsNil)
  163. c.Assert(res.StatusCode, checker.Equals, http.StatusOK)
  164. buf, err := request.ReadBody(body)
  165. c.Assert(err, checker.IsNil)
  166. out := string(buf)
  167. c.Assert(out, checker.Contains, "from Dockerfile")
  168. }
  169. func (s *DockerSuite) TestBuildAPIUnnormalizedTarPaths(c *check.C) {
  170. // Make sure that build context tars with entries of the form
  171. // x/./y don't cause caching false positives.
  172. buildFromTarContext := func(fileContents []byte) string {
  173. buffer := new(bytes.Buffer)
  174. tw := tar.NewWriter(buffer)
  175. defer tw.Close()
  176. dockerfile := []byte(`FROM busybox
  177. COPY dir /dir/`)
  178. err := tw.WriteHeader(&tar.Header{
  179. Name: "Dockerfile",
  180. Size: int64(len(dockerfile)),
  181. })
  182. //failed to write tar file header
  183. c.Assert(err, checker.IsNil)
  184. _, err = tw.Write(dockerfile)
  185. // failed to write Dockerfile in tar file content
  186. c.Assert(err, checker.IsNil)
  187. err = tw.WriteHeader(&tar.Header{
  188. Name: "dir/./file",
  189. Size: int64(len(fileContents)),
  190. })
  191. //failed to write tar file header
  192. c.Assert(err, checker.IsNil)
  193. _, err = tw.Write(fileContents)
  194. // failed to write file contents in tar file content
  195. c.Assert(err, checker.IsNil)
  196. // failed to close tar archive
  197. c.Assert(tw.Close(), checker.IsNil)
  198. res, body, err := request.Post("/build", request.RawContent(ioutil.NopCloser(buffer)), request.ContentType("application/x-tar"))
  199. c.Assert(err, checker.IsNil)
  200. c.Assert(res.StatusCode, checker.Equals, http.StatusOK)
  201. out, err := request.ReadBody(body)
  202. c.Assert(err, checker.IsNil)
  203. lines := strings.Split(string(out), "\n")
  204. c.Assert(len(lines), checker.GreaterThan, 1)
  205. c.Assert(lines[len(lines)-2], checker.Matches, ".*Successfully built [0-9a-f]{12}.*")
  206. re := regexp.MustCompile("Successfully built ([0-9a-f]{12})")
  207. matches := re.FindStringSubmatch(lines[len(lines)-2])
  208. return matches[1]
  209. }
  210. imageA := buildFromTarContext([]byte("abc"))
  211. imageB := buildFromTarContext([]byte("def"))
  212. c.Assert(imageA, checker.Not(checker.Equals), imageB)
  213. }
  214. func (s *DockerSuite) TestBuildOnBuildWithCopy(c *check.C) {
  215. dockerfile := `
  216. FROM ` + minimalBaseImage() + ` as onbuildbase
  217. ONBUILD COPY file /file
  218. FROM onbuildbase
  219. `
  220. ctx := fakecontext.New(c, "",
  221. fakecontext.WithDockerfile(dockerfile),
  222. fakecontext.WithFile("file", "some content"),
  223. )
  224. defer ctx.Close()
  225. res, body, err := request.Post(
  226. "/build",
  227. request.RawContent(ctx.AsTarReader(c)),
  228. request.ContentType("application/x-tar"))
  229. c.Assert(err, checker.IsNil)
  230. c.Assert(res.StatusCode, checker.Equals, http.StatusOK)
  231. out, err := request.ReadBody(body)
  232. c.Assert(err, checker.IsNil)
  233. c.Assert(string(out), checker.Contains, "Successfully built")
  234. }
  235. func (s *DockerSuite) TestBuildOnBuildCache(c *check.C) {
  236. build := func(dockerfile string) []byte {
  237. ctx := fakecontext.New(c, "",
  238. fakecontext.WithDockerfile(dockerfile),
  239. )
  240. defer ctx.Close()
  241. res, body, err := request.Post(
  242. "/build",
  243. request.RawContent(ctx.AsTarReader(c)),
  244. request.ContentType("application/x-tar"))
  245. require.NoError(c, err)
  246. assert.Equal(c, http.StatusOK, res.StatusCode)
  247. out, err := request.ReadBody(body)
  248. require.NoError(c, err)
  249. assert.Contains(c, string(out), "Successfully built")
  250. return out
  251. }
  252. dockerfile := `
  253. FROM ` + minimalBaseImage() + ` as onbuildbase
  254. ENV something=bar
  255. ONBUILD ENV foo=bar
  256. `
  257. build(dockerfile)
  258. dockerfile += "FROM onbuildbase"
  259. out := build(dockerfile)
  260. imageIDs := getImageIDsFromBuild(c, out)
  261. assert.Len(c, imageIDs, 2)
  262. parentID, childID := imageIDs[0], imageIDs[1]
  263. client := testEnv.APIClient()
  264. // check parentID is correct
  265. image, _, err := client.ImageInspectWithRaw(context.Background(), childID)
  266. require.NoError(c, err)
  267. assert.Equal(c, parentID, image.Parent)
  268. }
  269. func (s *DockerRegistrySuite) TestBuildCopyFromForcePull(c *check.C) {
  270. client := testEnv.APIClient()
  271. repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
  272. // tag the image to upload it to the private registry
  273. err := client.ImageTag(context.TODO(), "busybox", repoName)
  274. assert.Nil(c, err)
  275. // push the image to the registry
  276. rc, err := client.ImagePush(context.TODO(), repoName, types.ImagePushOptions{RegistryAuth: "{}"})
  277. assert.Nil(c, err)
  278. _, err = io.Copy(ioutil.Discard, rc)
  279. assert.Nil(c, err)
  280. dockerfile := fmt.Sprintf(`
  281. FROM %s AS foo
  282. RUN touch abc
  283. FROM %s
  284. COPY --from=foo /abc /
  285. `, repoName, repoName)
  286. ctx := fakecontext.New(c, "",
  287. fakecontext.WithDockerfile(dockerfile),
  288. )
  289. defer ctx.Close()
  290. res, body, err := request.Post(
  291. "/build?pull=1",
  292. request.RawContent(ctx.AsTarReader(c)),
  293. request.ContentType("application/x-tar"))
  294. require.NoError(c, err)
  295. assert.Equal(c, http.StatusOK, res.StatusCode)
  296. out, err := request.ReadBody(body)
  297. require.NoError(c, err)
  298. assert.Contains(c, string(out), "Successfully built")
  299. }
  300. func (s *DockerSuite) TestBuildAddRemoteNoDecompress(c *check.C) {
  301. buffer := new(bytes.Buffer)
  302. tw := tar.NewWriter(buffer)
  303. dt := []byte("contents")
  304. err := tw.WriteHeader(&tar.Header{
  305. Name: "foo",
  306. Size: int64(len(dt)),
  307. Mode: 0600,
  308. Typeflag: tar.TypeReg,
  309. })
  310. require.NoError(c, err)
  311. _, err = tw.Write(dt)
  312. require.NoError(c, err)
  313. err = tw.Close()
  314. require.NoError(c, err)
  315. server := fakestorage.New(c, "", fakecontext.WithBinaryFiles(map[string]*bytes.Buffer{
  316. "test.tar": buffer,
  317. }))
  318. defer server.Close()
  319. dockerfile := fmt.Sprintf(`
  320. FROM busybox
  321. ADD %s/test.tar /
  322. RUN [ -f test.tar ]
  323. `, server.URL())
  324. ctx := fakecontext.New(c, "",
  325. fakecontext.WithDockerfile(dockerfile),
  326. )
  327. defer ctx.Close()
  328. res, body, err := request.Post(
  329. "/build",
  330. request.RawContent(ctx.AsTarReader(c)),
  331. request.ContentType("application/x-tar"))
  332. require.NoError(c, err)
  333. assert.Equal(c, http.StatusOK, res.StatusCode)
  334. out, err := request.ReadBody(body)
  335. require.NoError(c, err)
  336. assert.Contains(c, string(out), "Successfully built")
  337. }
  338. func (s *DockerSuite) TestBuildChownOnCopy(c *check.C) {
  339. testRequires(c, DaemonIsLinux)
  340. dockerfile := `FROM busybox
  341. RUN echo 'test1:x:1001:1001::/bin:/bin/false' >> /etc/passwd
  342. RUN echo 'test1:x:1001:' >> /etc/group
  343. RUN echo 'test2:x:1002:' >> /etc/group
  344. COPY --chown=test1:1002 . /new_dir
  345. RUN ls -l /
  346. RUN [ $(ls -l / | grep new_dir | awk '{print $3":"$4}') = 'test1:test2' ]
  347. RUN [ $(ls -nl / | grep new_dir | awk '{print $3":"$4}') = '1001:1002' ]
  348. `
  349. ctx := fakecontext.New(c, "",
  350. fakecontext.WithDockerfile(dockerfile),
  351. fakecontext.WithFile("test_file1", "some test content"),
  352. )
  353. defer ctx.Close()
  354. res, body, err := request.Post(
  355. "/build",
  356. request.RawContent(ctx.AsTarReader(c)),
  357. request.ContentType("application/x-tar"))
  358. c.Assert(err, checker.IsNil)
  359. c.Assert(res.StatusCode, checker.Equals, http.StatusOK)
  360. out, err := request.ReadBody(body)
  361. require.NoError(c, err)
  362. assert.Contains(c, string(out), "Successfully built")
  363. }
  364. func (s *DockerSuite) TestBuildCopyCacheOnFileChange(c *check.C) {
  365. dockerfile := `FROM busybox
  366. COPY file /file`
  367. ctx1 := fakecontext.New(c, "",
  368. fakecontext.WithDockerfile(dockerfile),
  369. fakecontext.WithFile("file", "foo"))
  370. ctx2 := fakecontext.New(c, "",
  371. fakecontext.WithDockerfile(dockerfile),
  372. fakecontext.WithFile("file", "bar"))
  373. var build = func(ctx *fakecontext.Fake) string {
  374. res, body, err := request.Post("/build",
  375. request.RawContent(ctx.AsTarReader(c)),
  376. request.ContentType("application/x-tar"))
  377. require.NoError(c, err)
  378. assert.Equal(c, http.StatusOK, res.StatusCode)
  379. out, err := request.ReadBody(body)
  380. require.NoError(c, err)
  381. ids := getImageIDsFromBuild(c, out)
  382. return ids[len(ids)-1]
  383. }
  384. id1 := build(ctx1)
  385. id2 := build(ctx1)
  386. id3 := build(ctx2)
  387. if id1 != id2 {
  388. c.Fatal("didn't use the cache")
  389. }
  390. if id1 == id3 {
  391. c.Fatal("COPY With different source file should not share same cache")
  392. }
  393. }
  394. func (s *DockerSuite) TestBuildAddCacheOnFileChange(c *check.C) {
  395. dockerfile := `FROM busybox
  396. ADD file /file`
  397. ctx1 := fakecontext.New(c, "",
  398. fakecontext.WithDockerfile(dockerfile),
  399. fakecontext.WithFile("file", "foo"))
  400. ctx2 := fakecontext.New(c, "",
  401. fakecontext.WithDockerfile(dockerfile),
  402. fakecontext.WithFile("file", "bar"))
  403. var build = func(ctx *fakecontext.Fake) string {
  404. res, body, err := request.Post("/build",
  405. request.RawContent(ctx.AsTarReader(c)),
  406. request.ContentType("application/x-tar"))
  407. require.NoError(c, err)
  408. assert.Equal(c, http.StatusOK, res.StatusCode)
  409. out, err := request.ReadBody(body)
  410. require.NoError(c, err)
  411. ids := getImageIDsFromBuild(c, out)
  412. return ids[len(ids)-1]
  413. }
  414. id1 := build(ctx1)
  415. id2 := build(ctx1)
  416. id3 := build(ctx2)
  417. if id1 != id2 {
  418. c.Fatal("didn't use the cache")
  419. }
  420. if id1 == id3 {
  421. c.Fatal("COPY With different source file should not share same cache")
  422. }
  423. }
  424. func (s *DockerSuite) TestBuildWithSession(c *check.C) {
  425. testRequires(c, ExperimentalDaemon)
  426. dockerfile := `
  427. FROM busybox
  428. COPY file /
  429. RUN cat /file
  430. `
  431. fctx := fakecontext.New(c, "",
  432. fakecontext.WithFile("file", "some content"),
  433. )
  434. defer fctx.Close()
  435. out := testBuildWithSession(c, fctx.Dir, dockerfile)
  436. assert.Contains(c, out, "some content")
  437. fctx.Add("second", "contentcontent")
  438. dockerfile += `
  439. COPY second /
  440. RUN cat /second
  441. `
  442. out = testBuildWithSession(c, fctx.Dir, dockerfile)
  443. assert.Equal(c, strings.Count(out, "Using cache"), 2)
  444. assert.Contains(c, out, "contentcontent")
  445. client := testEnv.APIClient()
  446. du, err := client.DiskUsage(context.TODO())
  447. assert.Nil(c, err)
  448. assert.True(c, du.BuilderSize > 10)
  449. out = testBuildWithSession(c, fctx.Dir, dockerfile)
  450. assert.Equal(c, strings.Count(out, "Using cache"), 4)
  451. du2, err := client.DiskUsage(context.TODO())
  452. assert.Nil(c, err)
  453. assert.Equal(c, du.BuilderSize, du2.BuilderSize)
  454. // rebuild with regular tar, confirm cache still applies
  455. fctx.Add("Dockerfile", dockerfile)
  456. res, body, err := request.Post(
  457. "/build",
  458. request.RawContent(fctx.AsTarReader(c)),
  459. request.ContentType("application/x-tar"))
  460. require.NoError(c, err)
  461. assert.Equal(c, http.StatusOK, res.StatusCode)
  462. outBytes, err := request.ReadBody(body)
  463. require.NoError(c, err)
  464. assert.Contains(c, string(outBytes), "Successfully built")
  465. assert.Equal(c, strings.Count(string(outBytes), "Using cache"), 4)
  466. _, err = client.BuildCachePrune(context.TODO())
  467. assert.Nil(c, err)
  468. du, err = client.DiskUsage(context.TODO())
  469. assert.Nil(c, err)
  470. assert.Equal(c, du.BuilderSize, int64(0))
  471. }
  472. func testBuildWithSession(c *check.C, dir, dockerfile string) (outStr string) {
  473. client := testEnv.APIClient()
  474. sess, err := session.NewSession("foo1", "foo")
  475. assert.Nil(c, err)
  476. fsProvider := filesync.NewFSSyncProvider([]filesync.SyncedDir{
  477. {Dir: dir},
  478. })
  479. sess.Allow(fsProvider)
  480. g, ctx := errgroup.WithContext(context.Background())
  481. g.Go(func() error {
  482. return sess.Run(ctx, client.DialSession)
  483. })
  484. g.Go(func() error {
  485. res, body, err := request.Post("/build?remote=client-session&session="+sess.ID(), func(req *http.Request) error {
  486. req.Body = ioutil.NopCloser(strings.NewReader(dockerfile))
  487. return nil
  488. })
  489. if err != nil {
  490. return err
  491. }
  492. assert.Equal(c, res.StatusCode, http.StatusOK)
  493. out, err := request.ReadBody(body)
  494. require.NoError(c, err)
  495. assert.Contains(c, string(out), "Successfully built")
  496. sess.Close()
  497. outStr = string(out)
  498. return nil
  499. })
  500. err = g.Wait()
  501. assert.Nil(c, err)
  502. return
  503. }
  504. func (s *DockerSuite) TestBuildScratchCopy(c *check.C) {
  505. testRequires(c, DaemonIsLinux)
  506. dockerfile := `FROM scratch
  507. ADD Dockerfile /
  508. ENV foo bar`
  509. ctx := fakecontext.New(c, "",
  510. fakecontext.WithDockerfile(dockerfile),
  511. )
  512. defer ctx.Close()
  513. res, body, err := request.Post(
  514. "/build",
  515. request.RawContent(ctx.AsTarReader(c)),
  516. request.ContentType("application/x-tar"))
  517. c.Assert(err, checker.IsNil)
  518. c.Assert(res.StatusCode, checker.Equals, http.StatusOK)
  519. out, err := request.ReadBody(body)
  520. require.NoError(c, err)
  521. assert.Contains(c, string(out), "Successfully built")
  522. }
  523. type buildLine struct {
  524. Stream string
  525. Aux struct {
  526. ID string
  527. }
  528. }
  529. func getImageIDsFromBuild(c *check.C, output []byte) []string {
  530. ids := []string{}
  531. for _, line := range bytes.Split(output, []byte("\n")) {
  532. if len(line) == 0 {
  533. continue
  534. }
  535. entry := buildLine{}
  536. require.NoError(c, json.Unmarshal(line, &entry))
  537. if entry.Aux.ID != "" {
  538. ids = append(ids, entry.Aux.ID)
  539. }
  540. }
  541. return ids
  542. }