2013-05-29 02:31:06 +04:00
|
|
|
package docker
|
|
|
|
|
|
|
|
import (
|
2013-07-02 14:47:37 +04:00
|
|
|
"fmt"
|
2013-11-14 10:10:20 +04:00
|
|
|
"github.com/dotcloud/docker"
|
2013-11-01 03:57:45 +04:00
|
|
|
"github.com/dotcloud/docker/archive"
|
2013-11-14 10:10:20 +04:00
|
|
|
"github.com/dotcloud/docker/engine"
|
2013-10-12 16:14:52 +04:00
|
|
|
"github.com/dotcloud/docker/utils"
|
2013-06-15 22:07:49 +04:00
|
|
|
"io/ioutil"
|
2013-07-08 10:24:52 +04:00
|
|
|
"net"
|
|
|
|
"net/http"
|
|
|
|
"net/http/httptest"
|
|
|
|
"strings"
|
2013-05-29 02:31:06 +04:00
|
|
|
"testing"
|
|
|
|
)
|
|
|
|
|
2013-06-15 22:07:49 +04:00
|
|
|
// mkTestContext generates a build context from the contents of the provided dockerfile.
|
|
|
|
// This context is suitable for use as an argument to BuildFile.Build()
|
2013-11-01 03:57:45 +04:00
|
|
|
func mkTestContext(dockerfile string, files [][2]string, t *testing.T) archive.Archive {
|
2013-11-14 10:10:20 +04:00
|
|
|
context, err := docker.MkBuildContext(dockerfile, files)
|
2013-06-15 22:07:49 +04:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
return context
|
|
|
|
}
|
2013-05-29 02:31:06 +04:00
|
|
|
|
2013-06-15 22:07:49 +04:00
|
|
|
// A testContextTemplate describes a build context and how to test it
|
|
|
|
type testContextTemplate struct {
|
|
|
|
// Contents of the Dockerfile
|
|
|
|
dockerfile string
|
|
|
|
// Additional files in the context, eg [][2]string{"./passwd", "gordon"}
|
|
|
|
files [][2]string
|
2013-07-08 10:24:52 +04:00
|
|
|
// Additional remote files to host on a local HTTP server.
|
|
|
|
remoteFiles [][2]string
|
2013-06-15 22:07:49 +04:00
|
|
|
}
|
2013-06-11 18:39:06 +04:00
|
|
|
|
2013-06-15 22:07:49 +04:00
|
|
|
// A table of all the contexts to build and test.
|
|
|
|
// A new docker runtime will be created and torn down for each context.
|
2013-07-03 02:27:22 +04:00
|
|
|
var testContexts = []testContextTemplate{
|
2013-06-15 22:07:49 +04:00
|
|
|
{
|
|
|
|
`
|
2013-07-08 10:24:52 +04:00
|
|
|
from {IMAGE}
|
2013-06-11 18:39:06 +04:00
|
|
|
run sh -c 'echo root:testpass > /tmp/passwd'
|
2013-06-14 05:52:41 +04:00
|
|
|
run mkdir -p /var/run/sshd
|
2013-06-21 07:16:39 +04:00
|
|
|
run [ "$(cat /tmp/passwd)" = "root:testpass" ]
|
|
|
|
run [ "$(ls -d /var/run/sshd)" = "/var/run/sshd" ]
|
2013-06-15 22:07:49 +04:00
|
|
|
`,
|
|
|
|
nil,
|
2013-07-08 10:24:52 +04:00
|
|
|
nil,
|
2013-06-15 22:07:49 +04:00
|
|
|
},
|
2013-06-15 22:35:56 +04:00
|
|
|
|
2013-09-10 02:21:04 +04:00
|
|
|
// Exactly the same as above, except uses a line split with a \ to test
|
|
|
|
// multiline support.
|
|
|
|
{
|
|
|
|
`
|
|
|
|
from {IMAGE}
|
|
|
|
run sh -c 'echo root:testpass \
|
|
|
|
> /tmp/passwd'
|
|
|
|
run mkdir -p /var/run/sshd
|
|
|
|
run [ "$(cat /tmp/passwd)" = "root:testpass" ]
|
|
|
|
run [ "$(ls -d /var/run/sshd)" = "/var/run/sshd" ]
|
|
|
|
`,
|
|
|
|
nil,
|
|
|
|
nil,
|
|
|
|
},
|
|
|
|
|
2013-09-10 05:22:58 +04:00
|
|
|
// Line containing literal "\n"
|
|
|
|
{
|
|
|
|
`
|
|
|
|
from {IMAGE}
|
|
|
|
run sh -c 'echo root:testpass > /tmp/passwd'
|
|
|
|
run echo "foo \n bar"; echo "baz"
|
|
|
|
run mkdir -p /var/run/sshd
|
|
|
|
run [ "$(cat /tmp/passwd)" = "root:testpass" ]
|
|
|
|
run [ "$(ls -d /var/run/sshd)" = "/var/run/sshd" ]
|
|
|
|
`,
|
|
|
|
nil,
|
|
|
|
nil,
|
|
|
|
},
|
2013-06-15 22:35:56 +04:00
|
|
|
{
|
|
|
|
`
|
2013-07-08 10:24:52 +04:00
|
|
|
from {IMAGE}
|
2013-06-21 07:42:19 +04:00
|
|
|
add foo /usr/lib/bla/bar
|
2013-07-08 10:24:52 +04:00
|
|
|
run [ "$(cat /usr/lib/bla/bar)" = 'hello' ]
|
|
|
|
add http://{SERVERADDR}/baz /usr/lib/baz/quux
|
|
|
|
run [ "$(cat /usr/lib/baz/quux)" = 'world!' ]
|
2013-06-21 07:42:19 +04:00
|
|
|
`,
|
2013-07-08 10:24:52 +04:00
|
|
|
[][2]string{{"foo", "hello"}},
|
|
|
|
[][2]string{{"/baz", "world!"}},
|
2013-06-21 07:42:19 +04:00
|
|
|
},
|
2013-06-11 18:39:06 +04:00
|
|
|
|
2013-06-21 07:42:19 +04:00
|
|
|
{
|
|
|
|
`
|
2013-07-08 10:24:52 +04:00
|
|
|
from {IMAGE}
|
2013-06-21 07:42:19 +04:00
|
|
|
add f /
|
|
|
|
run [ "$(cat /f)" = "hello" ]
|
|
|
|
add f /abc
|
|
|
|
run [ "$(cat /abc)" = "hello" ]
|
|
|
|
add f /x/y/z
|
|
|
|
run [ "$(cat /x/y/z)" = "hello" ]
|
|
|
|
add f /x/y/d/
|
|
|
|
run [ "$(cat /x/y/d/f)" = "hello" ]
|
|
|
|
add d /
|
|
|
|
run [ "$(cat /ga)" = "bu" ]
|
|
|
|
add d /somewhere
|
|
|
|
run [ "$(cat /somewhere/ga)" = "bu" ]
|
|
|
|
add d /anotherplace/
|
|
|
|
run [ "$(cat /anotherplace/ga)" = "bu" ]
|
|
|
|
add d /somewheeeere/over/the/rainbooow
|
|
|
|
run [ "$(cat /somewheeeere/over/the/rainbooow/ga)" = "bu" ]
|
|
|
|
`,
|
|
|
|
[][2]string{
|
|
|
|
{"f", "hello"},
|
|
|
|
{"d/ga", "bu"},
|
2013-06-15 22:35:56 +04:00
|
|
|
},
|
2013-07-08 10:24:52 +04:00
|
|
|
nil,
|
2013-06-15 22:35:56 +04:00
|
|
|
},
|
2013-06-15 03:43:39 +04:00
|
|
|
|
2013-07-08 11:43:22 +04:00
|
|
|
{
|
|
|
|
`
|
|
|
|
from {IMAGE}
|
|
|
|
add http://{SERVERADDR}/x /a/b/c
|
|
|
|
run [ "$(cat /a/b/c)" = "hello" ]
|
|
|
|
add http://{SERVERADDR}/x?foo=bar /
|
|
|
|
run [ "$(cat /x)" = "hello" ]
|
|
|
|
add http://{SERVERADDR}/x /d/
|
|
|
|
run [ "$(cat /d/x)" = "hello" ]
|
|
|
|
add http://{SERVERADDR} /e
|
|
|
|
run [ "$(cat /e)" = "blah" ]
|
|
|
|
`,
|
|
|
|
nil,
|
|
|
|
[][2]string{{"/x", "hello"}, {"/", "blah"}},
|
|
|
|
},
|
|
|
|
|
2013-12-30 11:10:48 +04:00
|
|
|
// Comments, shebangs, and executability, oh my!
|
|
|
|
{
|
|
|
|
`
|
|
|
|
FROM {IMAGE}
|
|
|
|
# This is an ordinary comment.
|
|
|
|
RUN { echo '#!/bin/sh'; echo 'echo hello world'; } > /hello.sh
|
|
|
|
RUN [ ! -x /hello.sh ]
|
|
|
|
RUN chmod +x /hello.sh
|
|
|
|
RUN [ -x /hello.sh ]
|
|
|
|
RUN [ "$(cat /hello.sh)" = $'#!/bin/sh\necho hello world' ]
|
|
|
|
RUN [ "$(/hello.sh)" = "hello world" ]
|
|
|
|
`,
|
|
|
|
nil,
|
|
|
|
nil,
|
|
|
|
},
|
|
|
|
|
|
|
|
// Environment variable
|
|
|
|
{
|
|
|
|
`
|
|
|
|
from {IMAGE}
|
|
|
|
env FOO BAR
|
|
|
|
run [ "$FOO" = "BAR" ]
|
|
|
|
`,
|
|
|
|
nil,
|
|
|
|
nil,
|
|
|
|
},
|
|
|
|
|
|
|
|
// Environment overwriting
|
2013-06-21 09:02:36 +04:00
|
|
|
{
|
|
|
|
`
|
2013-07-08 10:24:52 +04:00
|
|
|
from {IMAGE}
|
2013-06-21 09:02:36 +04:00
|
|
|
env FOO BAR
|
|
|
|
run [ "$FOO" = "BAR" ]
|
2013-12-30 11:10:48 +04:00
|
|
|
env FOO BAZ
|
|
|
|
run [ "$FOO" = "BAZ" ]
|
2013-06-25 06:20:05 +04:00
|
|
|
`,
|
|
|
|
nil,
|
2013-07-08 10:24:52 +04:00
|
|
|
nil,
|
2013-06-25 06:20:05 +04:00
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
`
|
2013-07-08 10:24:52 +04:00
|
|
|
from {IMAGE}
|
2013-06-25 06:20:05 +04:00
|
|
|
ENTRYPOINT /bin/echo
|
|
|
|
CMD Hello world
|
2013-07-04 06:33:30 +04:00
|
|
|
`,
|
|
|
|
nil,
|
2013-07-08 10:24:52 +04:00
|
|
|
nil,
|
2013-07-04 06:33:30 +04:00
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
`
|
2013-07-08 10:24:52 +04:00
|
|
|
from {IMAGE}
|
2013-07-04 06:33:30 +04:00
|
|
|
VOLUME /test
|
|
|
|
CMD Hello world
|
2013-06-21 09:02:36 +04:00
|
|
|
`,
|
|
|
|
nil,
|
2013-07-08 10:24:52 +04:00
|
|
|
nil,
|
2013-06-21 09:02:36 +04:00
|
|
|
},
|
2013-07-09 11:01:45 +04:00
|
|
|
|
|
|
|
{
|
|
|
|
`
|
|
|
|
from {IMAGE}
|
|
|
|
env FOO /foo/baz
|
|
|
|
env BAR /bar
|
|
|
|
env BAZ $BAR
|
|
|
|
env FOOPATH $PATH:$FOO
|
|
|
|
run [ "$BAR" = "$BAZ" ]
|
|
|
|
run [ "$FOOPATH" = "$PATH:/foo/baz" ]
|
|
|
|
`,
|
|
|
|
nil,
|
|
|
|
nil,
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
`
|
|
|
|
from {IMAGE}
|
|
|
|
env FOO /bar
|
|
|
|
env TEST testdir
|
|
|
|
env BAZ /foobar
|
|
|
|
add testfile $BAZ/
|
|
|
|
add $TEST $FOO
|
|
|
|
run [ "$(cat /foobar/testfile)" = "test1" ]
|
|
|
|
run [ "$(cat /bar/withfile)" = "test2" ]
|
|
|
|
`,
|
|
|
|
[][2]string{
|
|
|
|
{"testfile", "test1"},
|
|
|
|
{"testdir/withfile", "test2"},
|
|
|
|
},
|
|
|
|
nil,
|
|
|
|
},
|
2014-01-11 01:37:07 +04:00
|
|
|
|
|
|
|
// JSON!
|
|
|
|
{
|
|
|
|
`
|
|
|
|
FROM {IMAGE}
|
|
|
|
RUN ["/bin/echo","hello","world"]
|
|
|
|
CMD ["/bin/true"]
|
|
|
|
ENTRYPOINT ["/bin/echo","your command -->"]
|
|
|
|
`,
|
|
|
|
nil,
|
|
|
|
nil,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
`
|
|
|
|
FROM {IMAGE}
|
|
|
|
ADD test /test
|
|
|
|
RUN ["chmod","+x","/test"]
|
|
|
|
RUN ["/test"]
|
|
|
|
RUN [ "$(cat /testfile)" = 'test!' ]
|
|
|
|
`,
|
|
|
|
[][2]string{
|
|
|
|
{"test", "#!/bin/sh\necho 'test!' > /testfile"},
|
|
|
|
},
|
|
|
|
nil,
|
|
|
|
},
|
2013-06-21 09:02:36 +04:00
|
|
|
}
|
2013-06-15 03:43:39 +04:00
|
|
|
|
|
|
|
// FIXME: test building with 2 successive overlapping ADD commands
|
|
|
|
|
2013-07-08 10:24:52 +04:00
|
|
|
func constructDockerfile(template string, ip net.IP, port string) string {
|
|
|
|
serverAddr := fmt.Sprintf("%s:%s", ip, port)
|
|
|
|
replacer := strings.NewReplacer("{IMAGE}", unitTestImageID, "{SERVERADDR}", serverAddr)
|
|
|
|
return replacer.Replace(template)
|
|
|
|
}
|
|
|
|
|
|
|
|
func mkTestingFileServer(files [][2]string) (*httptest.Server, error) {
|
|
|
|
mux := http.NewServeMux()
|
|
|
|
for _, file := range files {
|
|
|
|
name, contents := file[0], file[1]
|
|
|
|
mux.HandleFunc(name, func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
w.Write([]byte(contents))
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is how httptest.NewServer sets up a net.Listener, except that our listener must accept remote
|
|
|
|
// connections (from the container).
|
|
|
|
listener, err := net.Listen("tcp", ":0")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
s := httptest.NewUnstartedServer(mux)
|
|
|
|
s.Listener = listener
|
|
|
|
s.Start()
|
|
|
|
return s, nil
|
|
|
|
}
|
|
|
|
|
2013-05-29 02:31:06 +04:00
|
|
|
func TestBuild(t *testing.T) {
|
2013-06-15 22:07:49 +04:00
|
|
|
for _, ctx := range testContexts {
|
2013-10-12 16:14:52 +04:00
|
|
|
_, err := buildImage(ctx, t, nil, true)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2013-05-29 02:31:06 +04:00
|
|
|
}
|
|
|
|
}
|
2013-07-08 17:02:06 +04:00
|
|
|
|
2013-10-12 16:14:52 +04:00
|
|
|
func buildImage(context testContextTemplate, t *testing.T, eng *engine.Engine, useCache bool) (*docker.Image, error) {
|
2013-11-14 10:10:20 +04:00
|
|
|
if eng == nil {
|
|
|
|
eng = NewTestEngine(t)
|
|
|
|
runtime := mkRuntimeFromEngine(eng, t)
|
|
|
|
// FIXME: we might not need runtime, why not simply nuke
|
|
|
|
// the engine?
|
2013-08-02 23:12:38 +04:00
|
|
|
defer nuke(runtime)
|
2013-07-08 17:02:06 +04:00
|
|
|
}
|
2013-11-14 10:10:20 +04:00
|
|
|
srv := mkServerFromEngine(eng, t)
|
2013-07-09 06:06:06 +04:00
|
|
|
|
2013-07-08 10:24:52 +04:00
|
|
|
httpServer, err := mkTestingFileServer(context.remoteFiles)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
defer httpServer.Close()
|
|
|
|
|
|
|
|
idx := strings.LastIndex(httpServer.URL, ":")
|
|
|
|
if idx < 0 {
|
|
|
|
t.Fatalf("could not get port from test http server address %s", httpServer.URL)
|
|
|
|
}
|
|
|
|
port := httpServer.URL[idx+1:]
|
|
|
|
|
2013-11-14 10:10:20 +04:00
|
|
|
iIP := eng.Hack_GetGlobalVar("httpapi.bridgeIP")
|
|
|
|
if iIP == nil {
|
|
|
|
t.Fatal("Legacy bridgeIP field not set in engine")
|
|
|
|
}
|
|
|
|
ip, ok := iIP.(net.IP)
|
|
|
|
if !ok {
|
|
|
|
panic("Legacy bridgeIP field in engine does not cast to net.IP")
|
|
|
|
}
|
2013-07-08 10:24:52 +04:00
|
|
|
dockerfile := constructDockerfile(context.dockerfile, ip, port)
|
|
|
|
|
2014-01-04 00:13:32 +04:00
|
|
|
buildfile := docker.NewBuildFile(srv, ioutil.Discard, ioutil.Discard, false, useCache, false, ioutil.Discard, utils.NewStreamFormatter(false), nil, nil)
|
2013-07-08 10:24:52 +04:00
|
|
|
id, err := buildfile.Build(mkTestContext(dockerfile, context.files, t))
|
2013-07-08 17:02:06 +04:00
|
|
|
if err != nil {
|
2013-10-12 16:14:52 +04:00
|
|
|
return nil, err
|
2013-07-08 17:02:06 +04:00
|
|
|
}
|
2013-07-09 06:06:06 +04:00
|
|
|
|
2013-10-12 16:14:52 +04:00
|
|
|
return srv.ImageInspect(id)
|
2013-07-09 06:06:06 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestVolume(t *testing.T) {
|
2013-10-12 16:14:52 +04:00
|
|
|
img, err := buildImage(testContextTemplate{`
|
2013-07-08 10:24:52 +04:00
|
|
|
from {IMAGE}
|
2013-07-09 06:06:06 +04:00
|
|
|
volume /test
|
|
|
|
cmd Hello world
|
2013-08-02 23:12:38 +04:00
|
|
|
`, nil, nil}, t, nil, true)
|
2013-10-12 16:14:52 +04:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2013-07-09 06:06:06 +04:00
|
|
|
|
2013-07-08 17:02:06 +04:00
|
|
|
if len(img.Config.Volumes) == 0 {
|
|
|
|
t.Fail()
|
|
|
|
}
|
2013-07-12 04:37:26 +04:00
|
|
|
for key := range img.Config.Volumes {
|
2013-07-08 17:02:06 +04:00
|
|
|
if key != "/test" {
|
|
|
|
t.Fail()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-07-09 06:06:06 +04:00
|
|
|
|
|
|
|
func TestBuildMaintainer(t *testing.T) {
|
2013-10-12 16:14:52 +04:00
|
|
|
img, err := buildImage(testContextTemplate{`
|
2013-07-08 10:24:52 +04:00
|
|
|
from {IMAGE}
|
2013-07-09 06:06:06 +04:00
|
|
|
maintainer dockerio
|
2013-08-02 23:12:38 +04:00
|
|
|
`, nil, nil}, t, nil, true)
|
2013-10-12 16:14:52 +04:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2013-07-09 06:06:06 +04:00
|
|
|
|
|
|
|
if img.Author != "dockerio" {
|
|
|
|
t.Fail()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-13 16:02:17 +04:00
|
|
|
func TestBuildUser(t *testing.T) {
|
2013-10-12 16:14:52 +04:00
|
|
|
img, err := buildImage(testContextTemplate{`
|
2013-08-13 16:02:17 +04:00
|
|
|
from {IMAGE}
|
|
|
|
user dockerio
|
|
|
|
`, nil, nil}, t, nil, true)
|
2013-10-12 16:14:52 +04:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2013-08-13 16:02:17 +04:00
|
|
|
|
|
|
|
if img.Config.User != "dockerio" {
|
|
|
|
t.Fail()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-09 06:06:06 +04:00
|
|
|
func TestBuildEnv(t *testing.T) {
|
2013-10-12 16:14:52 +04:00
|
|
|
img, err := buildImage(testContextTemplate{`
|
2013-07-08 10:24:52 +04:00
|
|
|
from {IMAGE}
|
2013-07-09 06:06:06 +04:00
|
|
|
env port 4243
|
|
|
|
`,
|
2013-08-02 23:12:38 +04:00
|
|
|
nil, nil}, t, nil, true)
|
2013-10-12 16:14:52 +04:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2013-07-09 11:01:45 +04:00
|
|
|
hasEnv := false
|
|
|
|
for _, envVar := range img.Config.Env {
|
|
|
|
if envVar == "port=4243" {
|
|
|
|
hasEnv = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !hasEnv {
|
2013-07-09 06:06:06 +04:00
|
|
|
t.Fail()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestBuildCmd(t *testing.T) {
|
2013-10-12 16:14:52 +04:00
|
|
|
img, err := buildImage(testContextTemplate{`
|
2013-07-08 10:24:52 +04:00
|
|
|
from {IMAGE}
|
2013-07-09 06:06:06 +04:00
|
|
|
cmd ["/bin/echo", "Hello World"]
|
|
|
|
`,
|
2013-08-02 23:12:38 +04:00
|
|
|
nil, nil}, t, nil, true)
|
2013-10-12 16:14:52 +04:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2013-07-09 06:06:06 +04:00
|
|
|
|
|
|
|
if img.Config.Cmd[0] != "/bin/echo" {
|
|
|
|
t.Log(img.Config.Cmd[0])
|
|
|
|
t.Fail()
|
|
|
|
}
|
|
|
|
if img.Config.Cmd[1] != "Hello World" {
|
|
|
|
t.Log(img.Config.Cmd[1])
|
|
|
|
t.Fail()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestBuildExpose(t *testing.T) {
|
2013-10-12 16:14:52 +04:00
|
|
|
img, err := buildImage(testContextTemplate{`
|
2013-07-08 10:24:52 +04:00
|
|
|
from {IMAGE}
|
2013-07-09 06:06:06 +04:00
|
|
|
expose 4243
|
|
|
|
`,
|
2013-08-02 23:12:38 +04:00
|
|
|
nil, nil}, t, nil, true)
|
2013-10-12 16:14:52 +04:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2013-07-09 06:06:06 +04:00
|
|
|
|
|
|
|
if img.Config.PortSpecs[0] != "4243" {
|
|
|
|
t.Fail()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestBuildEntrypoint(t *testing.T) {
|
2013-10-12 16:14:52 +04:00
|
|
|
img, err := buildImage(testContextTemplate{`
|
2013-07-08 10:24:52 +04:00
|
|
|
from {IMAGE}
|
2013-07-09 06:06:06 +04:00
|
|
|
entrypoint ["/bin/echo"]
|
|
|
|
`,
|
2013-08-02 23:12:38 +04:00
|
|
|
nil, nil}, t, nil, true)
|
2013-10-12 16:14:52 +04:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2013-07-09 06:06:06 +04:00
|
|
|
|
|
|
|
if img.Config.Entrypoint[0] != "/bin/echo" {
|
2013-12-28 06:06:26 +04:00
|
|
|
t.Log(img.Config.Entrypoint[0])
|
|
|
|
t.Fail()
|
2013-07-09 06:06:06 +04:00
|
|
|
}
|
|
|
|
}
|
2013-08-02 23:12:38 +04:00
|
|
|
|
2013-08-05 18:03:42 +04:00
|
|
|
// testing #1405 - config.Cmd does not get cleaned up if
|
|
|
|
// utilizing cache
|
|
|
|
func TestBuildEntrypointRunCleanup(t *testing.T) {
|
2013-11-14 10:10:20 +04:00
|
|
|
eng := NewTestEngine(t)
|
|
|
|
defer nuke(mkRuntimeFromEngine(eng, t))
|
2013-08-05 18:03:42 +04:00
|
|
|
|
2013-10-12 16:14:52 +04:00
|
|
|
img, err := buildImage(testContextTemplate{`
|
2013-08-05 18:03:42 +04:00
|
|
|
from {IMAGE}
|
|
|
|
run echo "hello"
|
|
|
|
`,
|
2013-11-14 10:10:20 +04:00
|
|
|
nil, nil}, t, eng, true)
|
2013-10-12 16:14:52 +04:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2013-08-05 18:03:42 +04:00
|
|
|
|
2013-10-12 16:14:52 +04:00
|
|
|
img, err = buildImage(testContextTemplate{`
|
2013-08-05 18:03:42 +04:00
|
|
|
from {IMAGE}
|
|
|
|
run echo "hello"
|
|
|
|
add foo /foo
|
|
|
|
entrypoint ["/bin/echo"]
|
|
|
|
`,
|
2013-11-14 10:10:20 +04:00
|
|
|
[][2]string{{"foo", "HEYO"}}, nil}, t, eng, true)
|
2013-10-12 16:14:52 +04:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2013-08-05 18:03:42 +04:00
|
|
|
|
|
|
|
if len(img.Config.Cmd) != 0 {
|
|
|
|
t.Fail()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-07 04:25:48 +04:00
|
|
|
func checkCacheBehavior(t *testing.T, template testContextTemplate, expectHit bool) (imageId string) {
|
2013-11-14 10:10:20 +04:00
|
|
|
eng := NewTestEngine(t)
|
|
|
|
defer nuke(mkRuntimeFromEngine(eng, t))
|
2013-08-02 23:12:38 +04:00
|
|
|
|
2013-10-12 16:14:52 +04:00
|
|
|
img, err := buildImage(template, t, eng, true)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2014-01-07 04:25:48 +04:00
|
|
|
imageId = img.ID
|
2013-08-02 23:12:38 +04:00
|
|
|
|
2013-12-12 09:33:15 +04:00
|
|
|
img, err = buildImage(template, t, eng, expectHit)
|
2013-10-12 16:14:52 +04:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2013-08-02 23:12:38 +04:00
|
|
|
|
2014-01-07 04:25:48 +04:00
|
|
|
if hit := imageId == img.ID; hit != expectHit {
|
|
|
|
t.Fatalf("Cache misbehavior, got hit=%t, expected hit=%t: (first: %s, second %s)", hit, expectHit, imageId, img.ID)
|
2013-08-02 23:12:38 +04:00
|
|
|
}
|
2014-01-07 04:25:48 +04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func checkCacheBehaviorFromEngime(t *testing.T, template testContextTemplate, expectHit bool, eng *engine.Engine) (imageId string) {
|
|
|
|
img, err := buildImage(template, t, eng, true)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
imageId = img.ID
|
|
|
|
|
|
|
|
img, err = buildImage(template, t, eng, expectHit)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if hit := imageId == img.ID; hit != expectHit {
|
|
|
|
t.Fatalf("Cache misbehavior, got hit=%t, expected hit=%t: (first: %s, second %s)", hit, expectHit, imageId, img.ID)
|
|
|
|
}
|
|
|
|
return
|
2013-08-02 23:12:38 +04:00
|
|
|
}
|
|
|
|
|
2013-12-12 09:33:15 +04:00
|
|
|
func TestBuildImageWithCache(t *testing.T) {
|
|
|
|
template := testContextTemplate{`
|
|
|
|
from {IMAGE}
|
|
|
|
maintainer dockerio
|
|
|
|
`,
|
|
|
|
nil, nil}
|
|
|
|
checkCacheBehavior(t, template, true)
|
|
|
|
}
|
2013-08-02 23:12:38 +04:00
|
|
|
|
2013-12-12 09:33:15 +04:00
|
|
|
func TestBuildImageWithoutCache(t *testing.T) {
|
2013-08-02 23:12:38 +04:00
|
|
|
template := testContextTemplate{`
|
|
|
|
from {IMAGE}
|
|
|
|
maintainer dockerio
|
|
|
|
`,
|
|
|
|
nil, nil}
|
2013-12-12 09:33:15 +04:00
|
|
|
checkCacheBehavior(t, template, false)
|
|
|
|
}
|
2013-08-02 23:12:38 +04:00
|
|
|
|
2013-12-12 09:33:15 +04:00
|
|
|
func TestBuildADDLocalFileWithCache(t *testing.T) {
|
|
|
|
template := testContextTemplate{`
|
|
|
|
from {IMAGE}
|
|
|
|
maintainer dockerio
|
|
|
|
run echo "first"
|
|
|
|
add foo /usr/lib/bla/bar
|
2014-01-07 04:25:48 +04:00
|
|
|
run [ "$(cat /usr/lib/bla/bar)" = "hello" ]
|
2013-12-12 09:33:15 +04:00
|
|
|
run echo "second"
|
2014-01-07 04:25:48 +04:00
|
|
|
add . /src/
|
|
|
|
run [ "$(cat /src/foo)" = "hello" ]
|
2013-12-12 09:33:15 +04:00
|
|
|
`,
|
2014-01-07 04:25:48 +04:00
|
|
|
[][2]string{
|
|
|
|
{"foo", "hello"},
|
|
|
|
},
|
2013-12-12 09:33:15 +04:00
|
|
|
nil}
|
2014-01-07 04:25:48 +04:00
|
|
|
eng := NewTestEngine(t)
|
|
|
|
defer nuke(mkRuntimeFromEngine(eng, t))
|
|
|
|
|
|
|
|
id1 := checkCacheBehaviorFromEngime(t, template, true, eng)
|
|
|
|
template.files = append(template.files, [2]string{"bar", "hello2"})
|
|
|
|
id2 := checkCacheBehaviorFromEngime(t, template, true, eng)
|
|
|
|
if id1 == id2 {
|
|
|
|
t.Fatal("The cache should have been invalided but hasn't.")
|
|
|
|
}
|
|
|
|
id3 := checkCacheBehaviorFromEngime(t, template, true, eng)
|
|
|
|
if id2 != id3 {
|
|
|
|
t.Fatal("The cache should have been used but hasn't.")
|
|
|
|
}
|
|
|
|
template.files[1][1] = "hello3"
|
|
|
|
id4 := checkCacheBehaviorFromEngime(t, template, true, eng)
|
|
|
|
if id3 == id4 {
|
|
|
|
t.Fatal("The cache should have been invalided but hasn't.")
|
|
|
|
}
|
|
|
|
template.dockerfile += `
|
|
|
|
add ./bar /src2/
|
|
|
|
run ls /src2/bar
|
|
|
|
`
|
|
|
|
id5 := checkCacheBehaviorFromEngime(t, template, true, eng)
|
|
|
|
if id4 == id5 {
|
|
|
|
t.Fatal("The cache should have been invalided but hasn't.")
|
|
|
|
}
|
|
|
|
template.files[1][1] = "hello4"
|
|
|
|
id6 := checkCacheBehaviorFromEngime(t, template, true, eng)
|
|
|
|
if id5 == id6 {
|
|
|
|
t.Fatal("The cache should have been invalided but hasn't.")
|
|
|
|
}
|
2014-01-08 04:53:55 +04:00
|
|
|
|
|
|
|
template.dockerfile += `
|
|
|
|
add bar /src2/bar2
|
|
|
|
add /bar /src2/bar3
|
|
|
|
run ls /src2/bar2 /src2/bar3
|
|
|
|
`
|
|
|
|
id7 := checkCacheBehaviorFromEngime(t, template, true, eng)
|
|
|
|
if id6 == id7 {
|
|
|
|
t.Fatal("The cache should have been invalided but hasn't.")
|
|
|
|
}
|
|
|
|
template.files[1][1] = "hello5"
|
|
|
|
id8 := checkCacheBehaviorFromEngime(t, template, true, eng)
|
|
|
|
if id7 == id8 {
|
|
|
|
t.Fatal("The cache should have been invalided but hasn't.")
|
|
|
|
}
|
2013-12-12 09:33:15 +04:00
|
|
|
}
|
2013-08-02 23:12:38 +04:00
|
|
|
|
2013-12-12 09:33:15 +04:00
|
|
|
func TestBuildADDLocalFileWithoutCache(t *testing.T) {
|
|
|
|
template := testContextTemplate{`
|
|
|
|
from {IMAGE}
|
|
|
|
maintainer dockerio
|
|
|
|
run echo "first"
|
|
|
|
add foo /usr/lib/bla/bar
|
|
|
|
run echo "second"
|
|
|
|
`,
|
|
|
|
[][2]string{{"foo", "hello"}},
|
|
|
|
nil}
|
|
|
|
checkCacheBehavior(t, template, false)
|
|
|
|
}
|
2013-08-02 23:12:38 +04:00
|
|
|
|
2014-01-07 07:42:57 +04:00
|
|
|
func TestBuildADDCurrentDirectoryWithCache(t *testing.T) {
|
|
|
|
template := testContextTemplate{`
|
|
|
|
from {IMAGE}
|
|
|
|
maintainer dockerio
|
|
|
|
add . /usr/lib/bla
|
|
|
|
`,
|
|
|
|
nil, nil}
|
|
|
|
checkCacheBehavior(t, template, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestBuildADDCurrentDirectoryWithoutCache(t *testing.T) {
|
|
|
|
template := testContextTemplate{`
|
|
|
|
from {IMAGE}
|
|
|
|
maintainer dockerio
|
|
|
|
add . /usr/lib/bla
|
|
|
|
`,
|
|
|
|
nil, nil}
|
|
|
|
checkCacheBehavior(t, template, false)
|
|
|
|
}
|
|
|
|
|
2013-12-12 09:33:15 +04:00
|
|
|
func TestBuildADDRemoteFileWithCache(t *testing.T) {
|
|
|
|
template := testContextTemplate{`
|
|
|
|
from {IMAGE}
|
|
|
|
maintainer dockerio
|
|
|
|
run echo "first"
|
|
|
|
add http://{SERVERADDR}/baz /usr/lib/baz/quux
|
|
|
|
run echo "second"
|
|
|
|
`,
|
|
|
|
nil,
|
|
|
|
[][2]string{{"/baz", "world!"}}}
|
|
|
|
checkCacheBehavior(t, template, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestBuildADDRemoteFileWithoutCache(t *testing.T) {
|
|
|
|
template := testContextTemplate{`
|
|
|
|
from {IMAGE}
|
|
|
|
maintainer dockerio
|
|
|
|
run echo "first"
|
|
|
|
add http://{SERVERADDR}/baz /usr/lib/baz/quux
|
|
|
|
run echo "second"
|
|
|
|
`,
|
|
|
|
nil,
|
|
|
|
[][2]string{{"/baz", "world!"}}}
|
|
|
|
checkCacheBehavior(t, template, false)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestBuildADDLocalAndRemoteFilesWithCache(t *testing.T) {
|
|
|
|
template := testContextTemplate{`
|
|
|
|
from {IMAGE}
|
|
|
|
maintainer dockerio
|
|
|
|
run echo "first"
|
|
|
|
add foo /usr/lib/bla/bar
|
|
|
|
add http://{SERVERADDR}/baz /usr/lib/baz/quux
|
|
|
|
run echo "second"
|
|
|
|
`,
|
|
|
|
[][2]string{{"foo", "hello"}},
|
|
|
|
[][2]string{{"/baz", "world!"}}}
|
|
|
|
checkCacheBehavior(t, template, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestBuildADDLocalAndRemoteFilesWithoutCache(t *testing.T) {
|
|
|
|
template := testContextTemplate{`
|
|
|
|
from {IMAGE}
|
|
|
|
maintainer dockerio
|
|
|
|
run echo "first"
|
|
|
|
add foo /usr/lib/bla/bar
|
|
|
|
add http://{SERVERADDR}/baz /usr/lib/baz/quux
|
|
|
|
run echo "second"
|
|
|
|
`,
|
|
|
|
[][2]string{{"foo", "hello"}},
|
|
|
|
[][2]string{{"/baz", "world!"}}}
|
|
|
|
checkCacheBehavior(t, template, false)
|
2013-08-02 23:12:38 +04:00
|
|
|
}
|
2013-08-06 20:58:52 +04:00
|
|
|
|
|
|
|
func TestForbiddenContextPath(t *testing.T) {
|
2013-11-14 10:10:20 +04:00
|
|
|
eng := NewTestEngine(t)
|
|
|
|
defer nuke(mkRuntimeFromEngine(eng, t))
|
|
|
|
srv := mkServerFromEngine(eng, t)
|
2013-08-06 20:58:52 +04:00
|
|
|
|
|
|
|
context := testContextTemplate{`
|
|
|
|
from {IMAGE}
|
|
|
|
maintainer dockerio
|
|
|
|
add ../../ test/
|
|
|
|
`,
|
|
|
|
[][2]string{{"test.txt", "test1"}, {"other.txt", "other"}}, nil}
|
|
|
|
|
|
|
|
httpServer, err := mkTestingFileServer(context.remoteFiles)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
defer httpServer.Close()
|
|
|
|
|
|
|
|
idx := strings.LastIndex(httpServer.URL, ":")
|
|
|
|
if idx < 0 {
|
|
|
|
t.Fatalf("could not get port from test http server address %s", httpServer.URL)
|
|
|
|
}
|
|
|
|
port := httpServer.URL[idx+1:]
|
|
|
|
|
2013-11-14 10:10:20 +04:00
|
|
|
iIP := eng.Hack_GetGlobalVar("httpapi.bridgeIP")
|
|
|
|
if iIP == nil {
|
|
|
|
t.Fatal("Legacy bridgeIP field not set in engine")
|
|
|
|
}
|
|
|
|
ip, ok := iIP.(net.IP)
|
|
|
|
if !ok {
|
|
|
|
panic("Legacy bridgeIP field in engine does not cast to net.IP")
|
|
|
|
}
|
2013-08-06 20:58:52 +04:00
|
|
|
dockerfile := constructDockerfile(context.dockerfile, ip, port)
|
|
|
|
|
2014-01-04 00:13:32 +04:00
|
|
|
buildfile := docker.NewBuildFile(srv, ioutil.Discard, ioutil.Discard, false, true, false, ioutil.Discard, utils.NewStreamFormatter(false), nil, nil)
|
2013-08-06 20:58:52 +04:00
|
|
|
_, err = buildfile.Build(mkTestContext(dockerfile, context.files, t))
|
|
|
|
|
|
|
|
if err == nil {
|
|
|
|
t.Log("Error should not be nil")
|
|
|
|
t.Fail()
|
|
|
|
}
|
|
|
|
|
2013-11-14 09:19:31 +04:00
|
|
|
if err.Error() != "Forbidden path outside the build context: ../../ (/)" {
|
2013-08-06 20:58:52 +04:00
|
|
|
t.Logf("Error message is not expected: %s", err.Error())
|
|
|
|
t.Fail()
|
|
|
|
}
|
|
|
|
}
|
2013-09-05 16:28:02 +04:00
|
|
|
|
|
|
|
func TestBuildADDFileNotFound(t *testing.T) {
|
2013-11-14 10:10:20 +04:00
|
|
|
eng := NewTestEngine(t)
|
|
|
|
defer nuke(mkRuntimeFromEngine(eng, t))
|
2013-09-05 16:28:02 +04:00
|
|
|
|
|
|
|
context := testContextTemplate{`
|
|
|
|
from {IMAGE}
|
|
|
|
add foo /usr/local/bar
|
|
|
|
`,
|
|
|
|
nil, nil}
|
|
|
|
|
|
|
|
httpServer, err := mkTestingFileServer(context.remoteFiles)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
defer httpServer.Close()
|
|
|
|
|
|
|
|
idx := strings.LastIndex(httpServer.URL, ":")
|
|
|
|
if idx < 0 {
|
|
|
|
t.Fatalf("could not get port from test http server address %s", httpServer.URL)
|
|
|
|
}
|
|
|
|
port := httpServer.URL[idx+1:]
|
|
|
|
|
2013-11-14 10:10:20 +04:00
|
|
|
iIP := eng.Hack_GetGlobalVar("httpapi.bridgeIP")
|
|
|
|
if iIP == nil {
|
|
|
|
t.Fatal("Legacy bridgeIP field not set in engine")
|
|
|
|
}
|
|
|
|
ip, ok := iIP.(net.IP)
|
|
|
|
if !ok {
|
|
|
|
panic("Legacy bridgeIP field in engine does not cast to net.IP")
|
|
|
|
}
|
2013-09-05 16:28:02 +04:00
|
|
|
dockerfile := constructDockerfile(context.dockerfile, ip, port)
|
|
|
|
|
2014-01-04 00:13:32 +04:00
|
|
|
buildfile := docker.NewBuildFile(mkServerFromEngine(eng, t), ioutil.Discard, ioutil.Discard, false, true, false, ioutil.Discard, utils.NewStreamFormatter(false), nil, nil)
|
2013-09-05 16:28:02 +04:00
|
|
|
_, err = buildfile.Build(mkTestContext(dockerfile, context.files, t))
|
|
|
|
|
|
|
|
if err == nil {
|
|
|
|
t.Log("Error should not be nil")
|
|
|
|
t.Fail()
|
|
|
|
}
|
|
|
|
|
|
|
|
if err.Error() != "foo: no such file or directory" {
|
|
|
|
t.Logf("Error message is not expected: %s", err.Error())
|
|
|
|
t.Fail()
|
|
|
|
}
|
|
|
|
}
|
2013-11-05 01:20:14 +04:00
|
|
|
|
|
|
|
func TestBuildInheritance(t *testing.T) {
|
2013-11-14 10:10:20 +04:00
|
|
|
eng := NewTestEngine(t)
|
|
|
|
defer nuke(mkRuntimeFromEngine(eng, t))
|
2013-11-05 01:20:14 +04:00
|
|
|
|
2013-10-12 16:14:52 +04:00
|
|
|
img, err := buildImage(testContextTemplate{`
|
2013-11-05 01:20:14 +04:00
|
|
|
from {IMAGE}
|
|
|
|
expose 4243
|
|
|
|
`,
|
2013-11-14 10:10:20 +04:00
|
|
|
nil, nil}, t, eng, true)
|
2013-11-05 01:20:14 +04:00
|
|
|
|
2013-10-12 16:14:52 +04:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
img2, _ := buildImage(testContextTemplate{fmt.Sprintf(`
|
2013-11-05 01:20:14 +04:00
|
|
|
from %s
|
|
|
|
entrypoint ["/bin/echo"]
|
|
|
|
`, img.ID),
|
2013-11-14 10:10:20 +04:00
|
|
|
nil, nil}, t, eng, true)
|
2013-11-05 01:20:14 +04:00
|
|
|
|
2013-10-12 16:14:52 +04:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2013-11-05 01:20:14 +04:00
|
|
|
// from child
|
|
|
|
if img2.Config.Entrypoint[0] != "/bin/echo" {
|
|
|
|
t.Fail()
|
|
|
|
}
|
|
|
|
|
|
|
|
// from parent
|
|
|
|
if img.Config.PortSpecs[0] != "4243" {
|
|
|
|
t.Fail()
|
|
|
|
}
|
|
|
|
}
|
2013-10-12 16:14:52 +04:00
|
|
|
|
|
|
|
func TestBuildFails(t *testing.T) {
|
|
|
|
_, err := buildImage(testContextTemplate{`
|
|
|
|
from {IMAGE}
|
|
|
|
run sh -c "exit 23"
|
|
|
|
`,
|
|
|
|
nil, nil}, t, nil, true)
|
|
|
|
|
|
|
|
if err == nil {
|
|
|
|
t.Fatal("Error should not be nil")
|
|
|
|
}
|
|
|
|
|
|
|
|
sterr, ok := err.(*utils.JSONError)
|
|
|
|
if !ok {
|
|
|
|
t.Fatalf("Error should be utils.JSONError")
|
|
|
|
}
|
|
|
|
if sterr.Code != 23 {
|
|
|
|
t.Fatalf("StatusCode %d unexpected, should be 23", sterr.Code)
|
|
|
|
}
|
|
|
|
}
|
2013-12-20 16:26:11 +04:00
|
|
|
|
|
|
|
func TestBuildFailsDockerfileEmpty(t *testing.T) {
|
|
|
|
_, err := buildImage(testContextTemplate{``, nil, nil}, t, nil, true)
|
|
|
|
|
|
|
|
if err != docker.ErrDockerfileEmpty {
|
|
|
|
t.Fatal("Expected: %v, got: %v", docker.ErrDockerfileEmpty, err)
|
|
|
|
}
|
|
|
|
}
|
2013-12-04 14:36:21 +04:00
|
|
|
|
|
|
|
func TestBuildOnBuildTrigger(t *testing.T) {
|
|
|
|
_, err := buildImage(testContextTemplate{`
|
|
|
|
from {IMAGE}
|
|
|
|
onbuild run echo here is the trigger
|
|
|
|
onbuild run touch foobar
|
|
|
|
`,
|
|
|
|
nil, nil,
|
|
|
|
},
|
|
|
|
t, nil, true,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
// FIXME: test that the 'foobar' file was created in the final build.
|
|
|
|
}
|