docker_api_build_test.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640
  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.DaemonPlatform() == "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, err := request.NewClient()
  264. require.NoError(c, err)
  265. // check parentID is correct
  266. image, _, err := client.ImageInspectWithRaw(context.Background(), childID)
  267. require.NoError(c, err)
  268. assert.Equal(c, parentID, image.Parent)
  269. }
  270. func (s *DockerRegistrySuite) TestBuildCopyFromForcePull(c *check.C) {
  271. client, err := request.NewClient()
  272. require.NoError(c, err)
  273. repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
  274. // tag the image to upload it to the private registry
  275. err = client.ImageTag(context.TODO(), "busybox", repoName)
  276. assert.Nil(c, err)
  277. // push the image to the registry
  278. rc, err := client.ImagePush(context.TODO(), repoName, types.ImagePushOptions{RegistryAuth: "{}"})
  279. assert.Nil(c, err)
  280. _, err = io.Copy(ioutil.Discard, rc)
  281. assert.Nil(c, err)
  282. dockerfile := fmt.Sprintf(`
  283. FROM %s AS foo
  284. RUN touch abc
  285. FROM %s
  286. COPY --from=foo /abc /
  287. `, repoName, repoName)
  288. ctx := fakecontext.New(c, "",
  289. fakecontext.WithDockerfile(dockerfile),
  290. )
  291. defer ctx.Close()
  292. res, body, err := request.Post(
  293. "/build?pull=1",
  294. request.RawContent(ctx.AsTarReader(c)),
  295. request.ContentType("application/x-tar"))
  296. require.NoError(c, err)
  297. assert.Equal(c, http.StatusOK, res.StatusCode)
  298. out, err := request.ReadBody(body)
  299. require.NoError(c, err)
  300. assert.Contains(c, string(out), "Successfully built")
  301. }
  302. func (s *DockerSuite) TestBuildAddRemoteNoDecompress(c *check.C) {
  303. buffer := new(bytes.Buffer)
  304. tw := tar.NewWriter(buffer)
  305. dt := []byte("contents")
  306. err := tw.WriteHeader(&tar.Header{
  307. Name: "foo",
  308. Size: int64(len(dt)),
  309. Mode: 0600,
  310. Typeflag: tar.TypeReg,
  311. })
  312. require.NoError(c, err)
  313. _, err = tw.Write(dt)
  314. require.NoError(c, err)
  315. err = tw.Close()
  316. require.NoError(c, err)
  317. server := fakestorage.New(c, "", fakecontext.WithBinaryFiles(map[string]*bytes.Buffer{
  318. "test.tar": buffer,
  319. }))
  320. defer server.Close()
  321. dockerfile := fmt.Sprintf(`
  322. FROM busybox
  323. ADD %s/test.tar /
  324. RUN [ -f test.tar ]
  325. `, server.URL())
  326. ctx := fakecontext.New(c, "",
  327. fakecontext.WithDockerfile(dockerfile),
  328. )
  329. defer ctx.Close()
  330. res, body, err := request.Post(
  331. "/build",
  332. request.RawContent(ctx.AsTarReader(c)),
  333. request.ContentType("application/x-tar"))
  334. require.NoError(c, err)
  335. assert.Equal(c, http.StatusOK, res.StatusCode)
  336. out, err := request.ReadBody(body)
  337. require.NoError(c, err)
  338. assert.Contains(c, string(out), "Successfully built")
  339. }
  340. func (s *DockerSuite) TestBuildChownOnCopy(c *check.C) {
  341. testRequires(c, DaemonIsLinux)
  342. dockerfile := `FROM busybox
  343. RUN echo 'test1:x:1001:1001::/bin:/bin/false' >> /etc/passwd
  344. RUN echo 'test1:x:1001:' >> /etc/group
  345. RUN echo 'test2:x:1002:' >> /etc/group
  346. COPY --chown=test1:1002 . /new_dir
  347. RUN ls -l /
  348. RUN [ $(ls -l / | grep new_dir | awk '{print $3":"$4}') = 'test1:test2' ]
  349. RUN [ $(ls -nl / | grep new_dir | awk '{print $3":"$4}') = '1001:1002' ]
  350. `
  351. ctx := fakecontext.New(c, "",
  352. fakecontext.WithDockerfile(dockerfile),
  353. fakecontext.WithFile("test_file1", "some test content"),
  354. )
  355. defer ctx.Close()
  356. res, body, err := request.Post(
  357. "/build",
  358. request.RawContent(ctx.AsTarReader(c)),
  359. request.ContentType("application/x-tar"))
  360. c.Assert(err, checker.IsNil)
  361. c.Assert(res.StatusCode, checker.Equals, http.StatusOK)
  362. out, err := request.ReadBody(body)
  363. require.NoError(c, err)
  364. assert.Contains(c, string(out), "Successfully built")
  365. }
  366. func (s *DockerSuite) TestBuildCopyCacheOnFileChange(c *check.C) {
  367. dockerfile := `FROM busybox
  368. COPY file /file`
  369. ctx1 := fakecontext.New(c, "",
  370. fakecontext.WithDockerfile(dockerfile),
  371. fakecontext.WithFile("file", "foo"))
  372. ctx2 := fakecontext.New(c, "",
  373. fakecontext.WithDockerfile(dockerfile),
  374. fakecontext.WithFile("file", "bar"))
  375. var build = func(ctx *fakecontext.Fake) string {
  376. res, body, err := request.Post("/build",
  377. request.RawContent(ctx.AsTarReader(c)),
  378. request.ContentType("application/x-tar"))
  379. require.NoError(c, err)
  380. assert.Equal(c, http.StatusOK, res.StatusCode)
  381. out, err := request.ReadBody(body)
  382. ids := getImageIDsFromBuild(c, out)
  383. return ids[len(ids)-1]
  384. }
  385. id1 := build(ctx1)
  386. id2 := build(ctx1)
  387. id3 := build(ctx2)
  388. if id1 != id2 {
  389. c.Fatal("didn't use the cache")
  390. }
  391. if id1 == id3 {
  392. c.Fatal("COPY With different source file should not share same cache")
  393. }
  394. }
  395. func (s *DockerSuite) TestBuildAddCacheOnFileChange(c *check.C) {
  396. dockerfile := `FROM busybox
  397. ADD file /file`
  398. ctx1 := fakecontext.New(c, "",
  399. fakecontext.WithDockerfile(dockerfile),
  400. fakecontext.WithFile("file", "foo"))
  401. ctx2 := fakecontext.New(c, "",
  402. fakecontext.WithDockerfile(dockerfile),
  403. fakecontext.WithFile("file", "bar"))
  404. var build = func(ctx *fakecontext.Fake) string {
  405. res, body, err := request.Post("/build",
  406. request.RawContent(ctx.AsTarReader(c)),
  407. request.ContentType("application/x-tar"))
  408. require.NoError(c, err)
  409. assert.Equal(c, http.StatusOK, res.StatusCode)
  410. out, err := request.ReadBody(body)
  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, err := request.NewClient()
  446. require.NoError(c, err)
  447. du, err := client.DiskUsage(context.TODO())
  448. assert.Nil(c, err)
  449. assert.True(c, du.BuilderSize > 10)
  450. out = testBuildWithSession(c, fctx.Dir, dockerfile)
  451. assert.Equal(c, strings.Count(out, "Using cache"), 4)
  452. du2, err := client.DiskUsage(context.TODO())
  453. assert.Nil(c, err)
  454. assert.Equal(c, du.BuilderSize, du2.BuilderSize)
  455. // rebuild with regular tar, confirm cache still applies
  456. fctx.Add("Dockerfile", dockerfile)
  457. res, body, err := request.Post(
  458. "/build",
  459. request.RawContent(fctx.AsTarReader(c)),
  460. request.ContentType("application/x-tar"))
  461. require.NoError(c, err)
  462. assert.Equal(c, http.StatusOK, res.StatusCode)
  463. outBytes, err := request.ReadBody(body)
  464. require.NoError(c, err)
  465. assert.Contains(c, string(outBytes), "Successfully built")
  466. assert.Equal(c, strings.Count(string(outBytes), "Using cache"), 4)
  467. _, err = client.BuildCachePrune(context.TODO())
  468. assert.Nil(c, err)
  469. du, err = client.DiskUsage(context.TODO())
  470. assert.Nil(c, err)
  471. assert.Equal(c, du.BuilderSize, int64(0))
  472. }
  473. func testBuildWithSession(c *check.C, dir, dockerfile string) (outStr string) {
  474. client, err := request.NewClient()
  475. require.NoError(c, err)
  476. sess, err := session.NewSession("foo1", "foo")
  477. assert.Nil(c, err)
  478. fsProvider := filesync.NewFSSyncProvider(dir, nil)
  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.UUID(), 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. type buildLine struct {
  505. Stream string
  506. Aux struct {
  507. ID string
  508. }
  509. }
  510. func getImageIDsFromBuild(c *check.C, output []byte) []string {
  511. ids := []string{}
  512. for _, line := range bytes.Split(output, []byte("\n")) {
  513. if len(line) == 0 {
  514. continue
  515. }
  516. entry := buildLine{}
  517. require.NoError(c, json.Unmarshal(line, &entry))
  518. if entry.Aux.ID != "" {
  519. ids = append(ids, entry.Aux.ID)
  520. }
  521. }
  522. return ids
  523. }