713c7d49a1
This removes various skips that accounted for running the integration tests
against older versions of the daemon before 20.10 (API version v1.41). Those
versions are EOL, and we don't run tests against them.
This reverts most of e440831802
, and similar
PRs.
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"
|
|
"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)
|
|
|
|
defer body.Close()
|
|
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]
|
|
|
|
client := testEnv.APIClient()
|
|
ctx := testutil.GetContext(c)
|
|
|
|
// check parentID is correct
|
|
// Parent is graphdriver-only
|
|
if !testEnv.UsingSnapshotter() {
|
|
image, _, err := client.ImageInspectWithRaw(ctx, childID)
|
|
assert.NilError(c, err)
|
|
|
|
assert.Check(c, is.Equal(parentID, image.Parent))
|
|
}
|
|
}
|
|
|
|
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, types.ImagePushOptions{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(c *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(c, json.Unmarshal(line, &entry))
|
|
if entry.Aux.ID != "" {
|
|
ids = append(ids, entry.Aux.ID)
|
|
}
|
|
}
|
|
return ids
|
|
}
|