mirror of
https://github.com/moby/moby.git
synced 2026-01-11 18:51:37 +00:00
The image inspect response has various fields that were deprecated as part of the legacy builder, or Dockerfile syntax; - The `Parent` field is only used for the legacy builder, and only set for images that are built locally (i.e., not persisted when pulling an image). - The `DockerVersion` field is only set when building images with the legacy builder, and empty in most cases. This patch deprecates the fields in the `InspectResponse` go struct, as these fields will no longer be set in future once the legacy builder is removed (`Parent`, `DockerVersion`). The legacy builder's deprecation in [cli@4d8e457] / [cli@fd22746] (docker 23.0, API v1.42), however the related API fields were kept so that information of legacy images would not be discarded. The API continues to return these fields if set, allowing the client to print the fields for informational purposes when printing the raw response, but these fields should be considered "transitional", and not be depended on; deprecating the fields helps raise awareness. [cli@4d8e457]:4d8e45782b[cli@fd22746]:fd2274692fSigned-off-by: Sebastiaan van Stijn <github@gone.nl> (cherry picked from commitbd8a99b400) Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
572 lines
15 KiB
Go
572 lines
15 KiB
Go
package main
|
|
|
|
import (
|
|
"archive/tar"
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"regexp"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/docker/docker/api/types/image"
|
|
"github.com/docker/docker/testutil"
|
|
"github.com/docker/docker/testutil/fakecontext"
|
|
"github.com/docker/docker/testutil/fakegit"
|
|
"github.com/docker/docker/testutil/fakestorage"
|
|
"github.com/docker/docker/testutil/request"
|
|
"gotest.tools/v3/assert"
|
|
is "gotest.tools/v3/assert/cmp"
|
|
)
|
|
|
|
func (s *DockerAPISuite) TestBuildAPIDockerFileRemote(c *testing.T) {
|
|
testRequires(c, NotUserNamespace)
|
|
ctx := testutil.GetContext(c)
|
|
|
|
// -xdev is required because sysfs can cause EPERM
|
|
testD := `FROM busybox
|
|
RUN find / -xdev -name ba*
|
|
RUN find /tmp/`
|
|
server := fakestorage.New(c, "", fakecontext.WithFiles(map[string]string{"testD": testD}))
|
|
defer server.Close()
|
|
|
|
res, body, err := request.Post(ctx, "/build?dockerfile=baz&remote="+server.URL()+"/testD", request.JSON)
|
|
assert.NilError(c, err)
|
|
assert.Equal(c, res.StatusCode, http.StatusOK)
|
|
|
|
buf, err := request.ReadBody(body)
|
|
assert.NilError(c, err)
|
|
|
|
// Make sure Dockerfile exists.
|
|
// Make sure 'baz' doesn't exist ANYWHERE despite being mentioned in the URL
|
|
out := string(buf)
|
|
assert.Assert(c, is.Contains(out, "RUN find /tmp"))
|
|
assert.Assert(c, !strings.Contains(out, "baz"))
|
|
}
|
|
|
|
func (s *DockerAPISuite) TestBuildAPIRemoteTarballContext(c *testing.T) {
|
|
ctx := testutil.GetContext(c)
|
|
|
|
buffer := new(bytes.Buffer)
|
|
tw := tar.NewWriter(buffer)
|
|
defer tw.Close()
|
|
|
|
dockerfile := []byte("FROM busybox")
|
|
err := tw.WriteHeader(&tar.Header{
|
|
Name: "Dockerfile",
|
|
Size: int64(len(dockerfile)),
|
|
})
|
|
assert.NilError(c, err, "failed to write tar file header")
|
|
|
|
_, err = tw.Write(dockerfile)
|
|
assert.NilError(c, err, "failed to write tar file content")
|
|
assert.NilError(c, tw.Close(), "failed to close tar archive")
|
|
|
|
server := fakestorage.New(c, "", fakecontext.WithBinaryFiles(map[string]*bytes.Buffer{
|
|
"testT.tar": buffer,
|
|
}))
|
|
defer server.Close()
|
|
|
|
res, b, err := request.Post(ctx, "/build?remote="+server.URL()+"/testT.tar", request.ContentType("application/tar"))
|
|
assert.NilError(c, err)
|
|
assert.Equal(c, res.StatusCode, http.StatusOK)
|
|
b.Close()
|
|
}
|
|
|
|
func (s *DockerAPISuite) TestBuildAPIRemoteTarballContextWithCustomDockerfile(c *testing.T) {
|
|
buffer := new(bytes.Buffer)
|
|
tw := tar.NewWriter(buffer)
|
|
defer tw.Close()
|
|
|
|
dockerfile := []byte(`FROM busybox
|
|
RUN echo 'wrong'`)
|
|
err := tw.WriteHeader(&tar.Header{
|
|
Name: "Dockerfile",
|
|
Size: int64(len(dockerfile)),
|
|
})
|
|
// failed to write tar file header
|
|
assert.NilError(c, err)
|
|
|
|
_, err = tw.Write(dockerfile)
|
|
// failed to write tar file content
|
|
assert.NilError(c, err)
|
|
|
|
custom := []byte(`FROM busybox
|
|
RUN echo 'right'
|
|
`)
|
|
err = tw.WriteHeader(&tar.Header{
|
|
Name: "custom",
|
|
Size: int64(len(custom)),
|
|
})
|
|
|
|
// failed to write tar file header
|
|
assert.NilError(c, err)
|
|
|
|
_, err = tw.Write(custom)
|
|
// failed to write tar file content
|
|
assert.NilError(c, err)
|
|
|
|
// failed to close tar archive
|
|
assert.NilError(c, tw.Close())
|
|
|
|
server := fakestorage.New(c, "", fakecontext.WithBinaryFiles(map[string]*bytes.Buffer{
|
|
"testT.tar": buffer,
|
|
}))
|
|
defer server.Close()
|
|
|
|
ctx := testutil.GetContext(c)
|
|
url := "/build?dockerfile=custom&remote=" + server.URL() + "/testT.tar"
|
|
res, body, err := request.Post(ctx, url, request.ContentType("application/tar"))
|
|
assert.NilError(c, err)
|
|
assert.Equal(c, res.StatusCode, http.StatusOK)
|
|
|
|
content, err := request.ReadBody(body)
|
|
assert.NilError(c, err)
|
|
|
|
// Build used the wrong dockerfile.
|
|
assert.Assert(c, !strings.Contains(string(content), "wrong"))
|
|
}
|
|
|
|
func (s *DockerAPISuite) TestBuildAPILowerDockerfile(c *testing.T) {
|
|
git := fakegit.New(c, "repo", map[string]string{
|
|
"dockerfile": `FROM busybox
|
|
RUN echo from dockerfile`,
|
|
}, false)
|
|
defer git.Close()
|
|
|
|
ctx := testutil.GetContext(c)
|
|
res, body, err := request.Post(ctx, "/build?remote="+git.RepoURL, request.JSON)
|
|
assert.NilError(c, err)
|
|
assert.Equal(c, res.StatusCode, http.StatusOK)
|
|
|
|
buf, err := request.ReadBody(body)
|
|
assert.NilError(c, err)
|
|
|
|
out := string(buf)
|
|
assert.Assert(c, is.Contains(out, "from dockerfile"))
|
|
}
|
|
|
|
func (s *DockerAPISuite) TestBuildAPIBuildGitWithF(c *testing.T) {
|
|
git := fakegit.New(c, "repo", map[string]string{
|
|
"baz": `FROM busybox
|
|
RUN echo from baz`,
|
|
"Dockerfile": `FROM busybox
|
|
RUN echo from Dockerfile`,
|
|
}, false)
|
|
defer git.Close()
|
|
|
|
ctx := testutil.GetContext(c)
|
|
// Make sure it tries to 'dockerfile' query param value
|
|
res, body, err := request.Post(ctx, "/build?dockerfile=baz&remote="+git.RepoURL, request.JSON)
|
|
assert.NilError(c, err)
|
|
assert.Equal(c, res.StatusCode, http.StatusOK)
|
|
|
|
buf, err := request.ReadBody(body)
|
|
assert.NilError(c, err)
|
|
|
|
out := string(buf)
|
|
assert.Assert(c, is.Contains(out, "from baz"))
|
|
}
|
|
|
|
func (s *DockerAPISuite) TestBuildAPIDoubleDockerfile(c *testing.T) {
|
|
testRequires(c, UnixCli) // dockerfile overwrites Dockerfile on Windows
|
|
git := fakegit.New(c, "repo", map[string]string{
|
|
"Dockerfile": `FROM busybox
|
|
RUN echo from Dockerfile`,
|
|
"dockerfile": `FROM busybox
|
|
RUN echo from dockerfile`,
|
|
}, false)
|
|
defer git.Close()
|
|
|
|
ctx := testutil.GetContext(c)
|
|
|
|
// Make sure it tries to 'dockerfile' query param value
|
|
res, body, err := request.Post(ctx, "/build?remote="+git.RepoURL, request.JSON)
|
|
assert.NilError(c, err)
|
|
assert.Equal(c, res.StatusCode, http.StatusOK)
|
|
|
|
buf, err := request.ReadBody(body)
|
|
assert.NilError(c, err)
|
|
|
|
out := string(buf)
|
|
assert.Assert(c, is.Contains(out, "from Dockerfile"))
|
|
}
|
|
|
|
func (s *DockerAPISuite) TestBuildAPIUnnormalizedTarPaths(c *testing.T) {
|
|
// Make sure that build context tars with entries of the form
|
|
// x/./y don't cause caching false positives.
|
|
|
|
buildFromTarContext := func(fileContents []byte) string {
|
|
buffer := new(bytes.Buffer)
|
|
tw := tar.NewWriter(buffer)
|
|
defer tw.Close()
|
|
|
|
dockerfile := []byte(`FROM busybox
|
|
COPY dir /dir/`)
|
|
err := tw.WriteHeader(&tar.Header{
|
|
Name: "Dockerfile",
|
|
Size: int64(len(dockerfile)),
|
|
})
|
|
assert.NilError(c, err, "failed to write tar file header")
|
|
|
|
_, err = tw.Write(dockerfile)
|
|
assert.NilError(c, err, "failed to write Dockerfile in tar file content")
|
|
|
|
err = tw.WriteHeader(&tar.Header{
|
|
Name: "dir/./file",
|
|
Size: int64(len(fileContents)),
|
|
})
|
|
assert.NilError(c, err, "failed to write tar file header")
|
|
|
|
_, err = tw.Write(fileContents)
|
|
assert.NilError(c, err, "failed to write file contents in tar file content")
|
|
|
|
assert.NilError(c, tw.Close(), "failed to close tar archive")
|
|
|
|
ctx := testutil.GetContext(c)
|
|
|
|
res, body, err := request.Post(ctx, "/build", request.RawContent(io.NopCloser(buffer)), request.ContentType("application/x-tar"))
|
|
assert.NilError(c, err)
|
|
assert.Equal(c, res.StatusCode, http.StatusOK)
|
|
|
|
out, err := request.ReadBody(body)
|
|
assert.NilError(c, err)
|
|
lines := strings.Split(string(out), "\n")
|
|
assert.Assert(c, len(lines) > 1)
|
|
matched, err := regexp.MatchString(".*Successfully built [0-9a-f]{12}.*", lines[len(lines)-2])
|
|
assert.NilError(c, err)
|
|
assert.Assert(c, matched)
|
|
|
|
re := regexp.MustCompile("Successfully built ([0-9a-f]{12})")
|
|
matches := re.FindStringSubmatch(lines[len(lines)-2])
|
|
return matches[1]
|
|
}
|
|
|
|
imageA := buildFromTarContext([]byte("abc"))
|
|
imageB := buildFromTarContext([]byte("def"))
|
|
|
|
assert.Assert(c, imageA != imageB)
|
|
}
|
|
|
|
func (s *DockerAPISuite) TestBuildOnBuildWithCopy(c *testing.T) {
|
|
dockerfile := `
|
|
FROM ` + minimalBaseImage() + ` as onbuildbase
|
|
ONBUILD COPY file /file
|
|
|
|
FROM onbuildbase
|
|
`
|
|
bCtx := fakecontext.New(c, "",
|
|
fakecontext.WithDockerfile(dockerfile),
|
|
fakecontext.WithFile("file", "some content"),
|
|
)
|
|
defer bCtx.Close()
|
|
|
|
ctx := testutil.GetContext(c)
|
|
res, body, err := request.Post(
|
|
ctx,
|
|
"/build",
|
|
request.RawContent(bCtx.AsTarReader(c)),
|
|
request.ContentType("application/x-tar"))
|
|
assert.NilError(c, err)
|
|
assert.Equal(c, res.StatusCode, http.StatusOK)
|
|
|
|
out, err := request.ReadBody(body)
|
|
assert.NilError(c, err)
|
|
assert.Assert(c, is.Contains(string(out), "Successfully built"))
|
|
}
|
|
|
|
func (s *DockerAPISuite) TestBuildOnBuildCache(c *testing.T) {
|
|
build := func(dockerfile string) []byte {
|
|
bCtx := fakecontext.New(c, "",
|
|
fakecontext.WithDockerfile(dockerfile),
|
|
)
|
|
defer bCtx.Close()
|
|
|
|
ctx := testutil.GetContext(c)
|
|
res, body, err := request.Post(
|
|
ctx,
|
|
"/build",
|
|
request.RawContent(bCtx.AsTarReader(c)),
|
|
request.ContentType("application/x-tar"))
|
|
assert.NilError(c, err)
|
|
assert.Check(c, is.DeepEqual(http.StatusOK, res.StatusCode))
|
|
|
|
out, err := request.ReadBody(body)
|
|
assert.NilError(c, err)
|
|
assert.Assert(c, is.Contains(string(out), "Successfully built"))
|
|
return out
|
|
}
|
|
|
|
dockerfile := `
|
|
FROM ` + minimalBaseImage() + ` as onbuildbase
|
|
ENV something=bar
|
|
ONBUILD ENV foo=bar
|
|
`
|
|
build(dockerfile)
|
|
|
|
dockerfile += "FROM onbuildbase"
|
|
out := build(dockerfile)
|
|
|
|
imageIDs := getImageIDsFromBuild(c, out)
|
|
assert.Assert(c, is.Len(imageIDs, 2))
|
|
parentID, childID := imageIDs[0], imageIDs[1]
|
|
|
|
apiClient := testEnv.APIClient()
|
|
ctx := testutil.GetContext(c)
|
|
|
|
// check parentID is correct
|
|
// Parent is graphdriver-only
|
|
if !testEnv.UsingSnapshotter() {
|
|
image, err := apiClient.ImageInspect(ctx, childID)
|
|
assert.NilError(c, err)
|
|
|
|
assert.Check(c, is.Equal(parentID, image.Parent)) //nolint:staticcheck // ignore SA1019: field is deprecated, but still included in response when present.
|
|
}
|
|
}
|
|
|
|
func (s *DockerRegistrySuite) TestBuildCopyFromForcePull(c *testing.T) {
|
|
client := testEnv.APIClient()
|
|
|
|
repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
|
|
// tag the image to upload it to the private registry
|
|
ctx := testutil.GetContext(c)
|
|
err := client.ImageTag(ctx, "busybox", repoName)
|
|
assert.Check(c, err)
|
|
// push the image to the registry
|
|
rc, err := client.ImagePush(ctx, repoName, image.PushOptions{RegistryAuth: "{}"})
|
|
assert.Check(c, err)
|
|
_, err = io.Copy(io.Discard, rc)
|
|
assert.Check(c, err)
|
|
|
|
dockerfile := fmt.Sprintf(`
|
|
FROM %s AS foo
|
|
RUN touch abc
|
|
FROM %s
|
|
COPY --from=foo /abc /
|
|
`, repoName, repoName)
|
|
|
|
bCtx := fakecontext.New(c, "",
|
|
fakecontext.WithDockerfile(dockerfile),
|
|
)
|
|
defer bCtx.Close()
|
|
|
|
res, body, err := request.Post(
|
|
ctx,
|
|
"/build?pull=1",
|
|
request.RawContent(bCtx.AsTarReader(c)),
|
|
request.ContentType("application/x-tar"))
|
|
assert.NilError(c, err)
|
|
assert.Check(c, is.DeepEqual(http.StatusOK, res.StatusCode))
|
|
|
|
out, err := request.ReadBody(body)
|
|
assert.NilError(c, err)
|
|
assert.Check(c, is.Contains(string(out), "Successfully built"))
|
|
}
|
|
|
|
func (s *DockerAPISuite) TestBuildAddRemoteNoDecompress(c *testing.T) {
|
|
buffer := new(bytes.Buffer)
|
|
tw := tar.NewWriter(buffer)
|
|
dt := []byte("contents")
|
|
err := tw.WriteHeader(&tar.Header{
|
|
Name: "foo",
|
|
Size: int64(len(dt)),
|
|
Mode: 0o600,
|
|
Typeflag: tar.TypeReg,
|
|
})
|
|
assert.NilError(c, err)
|
|
_, err = tw.Write(dt)
|
|
assert.NilError(c, err)
|
|
err = tw.Close()
|
|
assert.NilError(c, err)
|
|
|
|
server := fakestorage.New(c, "", fakecontext.WithBinaryFiles(map[string]*bytes.Buffer{
|
|
"test.tar": buffer,
|
|
}))
|
|
defer server.Close()
|
|
|
|
dockerfile := fmt.Sprintf(`
|
|
FROM busybox
|
|
ADD %s/test.tar /
|
|
RUN [ -f test.tar ]
|
|
`, server.URL())
|
|
|
|
bCtx := fakecontext.New(c, "",
|
|
fakecontext.WithDockerfile(dockerfile),
|
|
)
|
|
defer bCtx.Close()
|
|
|
|
ctx := testutil.GetContext(c)
|
|
res, body, err := request.Post(
|
|
ctx,
|
|
"/build",
|
|
request.RawContent(bCtx.AsTarReader(c)),
|
|
request.ContentType("application/x-tar"))
|
|
assert.NilError(c, err)
|
|
assert.Check(c, is.DeepEqual(http.StatusOK, res.StatusCode))
|
|
|
|
out, err := request.ReadBody(body)
|
|
assert.NilError(c, err)
|
|
assert.Check(c, is.Contains(string(out), "Successfully built"))
|
|
}
|
|
|
|
func (s *DockerAPISuite) TestBuildChownOnCopy(c *testing.T) {
|
|
testRequires(c, DaemonIsLinux)
|
|
dockerfile := `FROM busybox
|
|
RUN echo 'test1:x:1001:1001::/bin:/bin/false' >> /etc/passwd
|
|
RUN echo 'test1:x:1001:' >> /etc/group
|
|
RUN echo 'test2:x:1002:' >> /etc/group
|
|
COPY --chown=test1:1002 . /new_dir
|
|
RUN ls -l /
|
|
RUN [ $(ls -l / | grep new_dir | awk '{print $3":"$4}') = 'test1:test2' ]
|
|
RUN [ $(ls -nl / | grep new_dir | awk '{print $3":"$4}') = '1001:1002' ]
|
|
`
|
|
bCtx := fakecontext.New(c, "",
|
|
fakecontext.WithDockerfile(dockerfile),
|
|
fakecontext.WithFile("test_file1", "some test content"),
|
|
)
|
|
defer bCtx.Close()
|
|
|
|
ctx := testutil.GetContext(c)
|
|
res, body, err := request.Post(
|
|
ctx,
|
|
"/build",
|
|
request.RawContent(bCtx.AsTarReader(c)),
|
|
request.ContentType("application/x-tar"))
|
|
assert.NilError(c, err)
|
|
assert.Equal(c, res.StatusCode, http.StatusOK)
|
|
|
|
out, err := request.ReadBody(body)
|
|
assert.NilError(c, err)
|
|
assert.Check(c, is.Contains(string(out), "Successfully built"))
|
|
}
|
|
|
|
func (s *DockerAPISuite) TestBuildCopyCacheOnFileChange(c *testing.T) {
|
|
dockerfile := `FROM busybox
|
|
COPY file /file`
|
|
|
|
ctx1 := fakecontext.New(c, "",
|
|
fakecontext.WithDockerfile(dockerfile),
|
|
fakecontext.WithFile("file", "foo"))
|
|
ctx2 := fakecontext.New(c, "",
|
|
fakecontext.WithDockerfile(dockerfile),
|
|
fakecontext.WithFile("file", "bar"))
|
|
|
|
ctx := testutil.GetContext(c)
|
|
build := func(bCtx *fakecontext.Fake) string {
|
|
res, body, err := request.Post(ctx, "/build",
|
|
request.RawContent(bCtx.AsTarReader(c)),
|
|
request.ContentType("application/x-tar"))
|
|
|
|
assert.NilError(c, err)
|
|
assert.Check(c, is.DeepEqual(http.StatusOK, res.StatusCode))
|
|
|
|
out, err := request.ReadBody(body)
|
|
assert.NilError(c, err)
|
|
assert.Assert(c, is.Contains(string(out), "Successfully built"))
|
|
|
|
ids := getImageIDsFromBuild(c, out)
|
|
assert.Assert(c, is.Len(ids, 1))
|
|
return ids[len(ids)-1]
|
|
}
|
|
|
|
id1 := build(ctx1)
|
|
id2 := build(ctx1)
|
|
id3 := build(ctx2)
|
|
|
|
if id1 != id2 {
|
|
c.Fatal("didn't use the cache")
|
|
}
|
|
if id1 == id3 {
|
|
c.Fatal("COPY With different source file should not share same cache")
|
|
}
|
|
}
|
|
|
|
func (s *DockerAPISuite) TestBuildAddCacheOnFileChange(c *testing.T) {
|
|
dockerfile := `FROM busybox
|
|
ADD file /file`
|
|
|
|
ctx1 := fakecontext.New(c, "",
|
|
fakecontext.WithDockerfile(dockerfile),
|
|
fakecontext.WithFile("file", "foo"))
|
|
ctx2 := fakecontext.New(c, "",
|
|
fakecontext.WithDockerfile(dockerfile),
|
|
fakecontext.WithFile("file", "bar"))
|
|
|
|
ctx := testutil.GetContext(c)
|
|
build := func(bCtx *fakecontext.Fake) string {
|
|
res, body, err := request.Post(ctx, "/build",
|
|
request.RawContent(bCtx.AsTarReader(c)),
|
|
request.ContentType("application/x-tar"))
|
|
|
|
assert.NilError(c, err)
|
|
assert.Check(c, is.DeepEqual(http.StatusOK, res.StatusCode))
|
|
|
|
out, err := request.ReadBody(body)
|
|
assert.NilError(c, err)
|
|
assert.Assert(c, is.Contains(string(out), "Successfully built"))
|
|
|
|
ids := getImageIDsFromBuild(c, out)
|
|
assert.Assert(c, is.Len(ids, 1))
|
|
return ids[len(ids)-1]
|
|
}
|
|
|
|
id1 := build(ctx1)
|
|
id2 := build(ctx1)
|
|
id3 := build(ctx2)
|
|
|
|
if id1 != id2 {
|
|
c.Fatal("didn't use the cache")
|
|
}
|
|
if id1 == id3 {
|
|
c.Fatal("COPY With different source file should not share same cache")
|
|
}
|
|
}
|
|
|
|
func (s *DockerAPISuite) TestBuildScratchCopy(c *testing.T) {
|
|
testRequires(c, DaemonIsLinux)
|
|
dockerfile := `FROM scratch
|
|
ADD Dockerfile /
|
|
ENV foo bar`
|
|
bCtx := fakecontext.New(c, "",
|
|
fakecontext.WithDockerfile(dockerfile),
|
|
)
|
|
defer bCtx.Close()
|
|
|
|
ctx := testutil.GetContext(c)
|
|
res, body, err := request.Post(
|
|
ctx,
|
|
"/build",
|
|
request.RawContent(bCtx.AsTarReader(c)),
|
|
request.ContentType("application/x-tar"))
|
|
assert.NilError(c, err)
|
|
assert.Equal(c, res.StatusCode, http.StatusOK)
|
|
|
|
out, err := request.ReadBody(body)
|
|
assert.NilError(c, err)
|
|
assert.Check(c, is.Contains(string(out), "Successfully built"))
|
|
}
|
|
|
|
type buildLine struct {
|
|
Stream string
|
|
Aux struct {
|
|
ID string
|
|
}
|
|
}
|
|
|
|
func getImageIDsFromBuild(t *testing.T, output []byte) []string {
|
|
var ids []string
|
|
for _, line := range bytes.Split(output, []byte("\n")) {
|
|
if len(line) == 0 {
|
|
continue
|
|
}
|
|
entry := buildLine{}
|
|
assert.NilError(t, json.Unmarshal(line, &entry))
|
|
if entry.Aux.ID != "" {
|
|
ids = append(ids, entry.Aux.ID)
|
|
}
|
|
}
|
|
return ids
|
|
}
|