| package docker |
| |
| import ( |
| "fmt" |
| "github.com/dotcloud/docker" |
| "github.com/dotcloud/docker/archive" |
| "github.com/dotcloud/docker/engine" |
| "github.com/dotcloud/docker/utils" |
| "io/ioutil" |
| "net" |
| "net/http" |
| "net/http/httptest" |
| "strings" |
| "testing" |
| ) |
| |
| // mkTestContext generates a build context from the contents of the provided dockerfile. |
| // This context is suitable for use as an argument to BuildFile.Build() |
| func mkTestContext(dockerfile string, files [][2]string, t *testing.T) archive.Archive { |
| context, err := docker.MkBuildContext(dockerfile, files) |
| if err != nil { |
| t.Fatal(err) |
| } |
| return context |
| } |
| |
| // 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 |
| // Additional remote files to host on a local HTTP server. |
| remoteFiles [][2]string |
| } |
| |
| // A table of all the contexts to build and test. |
| // A new docker runtime will be created and torn down for each context. |
| var testContexts = []testContextTemplate{ |
| { |
| ` |
| 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, |
| }, |
| |
| // 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, |
| }, |
| |
| // 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, |
| }, |
| { |
| ` |
| from {IMAGE} |
| add foo /usr/lib/bla/bar |
| run [ "$(cat /usr/lib/bla/bar)" = 'hello' ] |
| add http://{SERVERADDR}/baz /usr/lib/baz/quux |
| run [ "$(cat /usr/lib/baz/quux)" = 'world!' ] |
| `, |
| [][2]string{{"foo", "hello"}}, |
| [][2]string{{"/baz", "world!"}}, |
| }, |
| |
| { |
| ` |
| from {IMAGE} |
| 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"}, |
| }, |
| nil, |
| }, |
| |
| { |
| ` |
| 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"}}, |
| }, |
| |
| // 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 |
| { |
| ` |
| from {IMAGE} |
| env FOO BAR |
| run [ "$FOO" = "BAR" ] |
| env FOO BAZ |
| run [ "$FOO" = "BAZ" ] |
| `, |
| nil, |
| nil, |
| }, |
| |
| { |
| ` |
| from {IMAGE} |
| ENTRYPOINT /bin/echo |
| CMD Hello world |
| `, |
| nil, |
| nil, |
| }, |
| |
| { |
| ` |
| from {IMAGE} |
| VOLUME /test |
| CMD Hello world |
| `, |
| nil, |
| nil, |
| }, |
| |
| { |
| ` |
| 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, |
| }, |
| |
| // 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, |
| }, |
| } |
| |
| // FIXME: test building with 2 successive overlapping ADD commands |
| |
| 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 |
| } |
| |
| func TestBuild(t *testing.T) { |
| for _, ctx := range testContexts { |
| _, err := buildImage(ctx, t, nil, true) |
| if err != nil { |
| t.Fatal(err) |
| } |
| } |
| } |
| |
| func buildImage(context testContextTemplate, t *testing.T, eng *engine.Engine, useCache bool) (*docker.Image, error) { |
| if eng == nil { |
| eng = NewTestEngine(t) |
| runtime := mkRuntimeFromEngine(eng, t) |
| // FIXME: we might not need runtime, why not simply nuke |
| // the engine? |
| defer nuke(runtime) |
| } |
| srv := mkServerFromEngine(eng, t) |
| |
| 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:] |
| |
| 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") |
| } |
| dockerfile := constructDockerfile(context.dockerfile, ip, port) |
| |
| buildfile := docker.NewBuildFile(srv, ioutil.Discard, ioutil.Discard, false, useCache, false, ioutil.Discard, utils.NewStreamFormatter(false), nil, nil) |
| id, err := buildfile.Build(mkTestContext(dockerfile, context.files, t)) |
| if err != nil { |
| return nil, err |
| } |
| |
| return srv.ImageInspect(id) |
| } |
| |
| func TestVolume(t *testing.T) { |
| img, err := buildImage(testContextTemplate{` |
| from {IMAGE} |
| volume /test |
| cmd Hello world |
| `, nil, nil}, t, nil, true) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| if len(img.Config.Volumes) == 0 { |
| t.Fail() |
| } |
| for key := range img.Config.Volumes { |
| if key != "/test" { |
| t.Fail() |
| } |
| } |
| } |
| |
| func TestBuildMaintainer(t *testing.T) { |
| img, err := buildImage(testContextTemplate{` |
| from {IMAGE} |
| maintainer dockerio |
| `, nil, nil}, t, nil, true) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| if img.Author != "dockerio" { |
| t.Fail() |
| } |
| } |
| |
| func TestBuildUser(t *testing.T) { |
| img, err := buildImage(testContextTemplate{` |
| from {IMAGE} |
| user dockerio |
| `, nil, nil}, t, nil, true) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| if img.Config.User != "dockerio" { |
| t.Fail() |
| } |
| } |
| |
| func TestBuildEnv(t *testing.T) { |
| img, err := buildImage(testContextTemplate{` |
| from {IMAGE} |
| env port 4243 |
| `, |
| nil, nil}, t, nil, true) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| hasEnv := false |
| for _, envVar := range img.Config.Env { |
| if envVar == "port=4243" { |
| hasEnv = true |
| break |
| } |
| } |
| if !hasEnv { |
| t.Fail() |
| } |
| } |
| |
| func TestBuildCmd(t *testing.T) { |
| img, err := buildImage(testContextTemplate{` |
| from {IMAGE} |
| cmd ["/bin/echo", "Hello World"] |
| `, |
| nil, nil}, t, nil, true) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| 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) { |
| img, err := buildImage(testContextTemplate{` |
| from {IMAGE} |
| expose 4243 |
| `, |
| nil, nil}, t, nil, true) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| if img.Config.PortSpecs[0] != "4243" { |
| t.Fail() |
| } |
| } |
| |
| func TestBuildEntrypoint(t *testing.T) { |
| img, err := buildImage(testContextTemplate{` |
| from {IMAGE} |
| entrypoint ["/bin/echo"] |
| `, |
| nil, nil}, t, nil, true) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| if img.Config.Entrypoint[0] != "/bin/echo" { |
| t.Log(img.Config.Entrypoint[0]) |
| t.Fail() |
| } |
| } |
| |
| // testing #1405 - config.Cmd does not get cleaned up if |
| // utilizing cache |
| func TestBuildEntrypointRunCleanup(t *testing.T) { |
| eng := NewTestEngine(t) |
| defer nuke(mkRuntimeFromEngine(eng, t)) |
| |
| img, err := buildImage(testContextTemplate{` |
| from {IMAGE} |
| run echo "hello" |
| `, |
| nil, nil}, t, eng, true) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| img, err = buildImage(testContextTemplate{` |
| from {IMAGE} |
| run echo "hello" |
| add foo /foo |
| entrypoint ["/bin/echo"] |
| `, |
| [][2]string{{"foo", "HEYO"}}, nil}, t, eng, true) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| if len(img.Config.Cmd) != 0 { |
| t.Fail() |
| } |
| } |
| |
| func checkCacheBehavior(t *testing.T, template testContextTemplate, expectHit bool) (imageId string) { |
| eng := NewTestEngine(t) |
| defer nuke(mkRuntimeFromEngine(eng, t)) |
| |
| 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 |
| } |
| |
| 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 |
| } |
| |
| func TestBuildImageWithCache(t *testing.T) { |
| template := testContextTemplate{` |
| from {IMAGE} |
| maintainer dockerio |
| `, |
| nil, nil} |
| checkCacheBehavior(t, template, true) |
| } |
| |
| func TestBuildImageWithoutCache(t *testing.T) { |
| template := testContextTemplate{` |
| from {IMAGE} |
| maintainer dockerio |
| `, |
| nil, nil} |
| checkCacheBehavior(t, template, false) |
| } |
| |
| func TestBuildADDLocalFileWithCache(t *testing.T) { |
| template := testContextTemplate{` |
| from {IMAGE} |
| maintainer dockerio |
| run echo "first" |
| add foo /usr/lib/bla/bar |
| run [ "$(cat /usr/lib/bla/bar)" = "hello" ] |
| run echo "second" |
| add . /src/ |
| run [ "$(cat /src/foo)" = "hello" ] |
| `, |
| [][2]string{ |
| {"foo", "hello"}, |
| }, |
| nil} |
| 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.") |
| } |
| |
| 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.") |
| } |
| } |
| |
| 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) |
| } |
| |
| 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) |
| } |
| |
| 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) |
| } |
| |
| func TestForbiddenContextPath(t *testing.T) { |
| eng := NewTestEngine(t) |
| defer nuke(mkRuntimeFromEngine(eng, t)) |
| srv := mkServerFromEngine(eng, t) |
| |
| 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:] |
| |
| 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") |
| } |
| dockerfile := constructDockerfile(context.dockerfile, ip, port) |
| |
| buildfile := docker.NewBuildFile(srv, ioutil.Discard, ioutil.Discard, false, true, false, ioutil.Discard, utils.NewStreamFormatter(false), nil, nil) |
| _, err = buildfile.Build(mkTestContext(dockerfile, context.files, t)) |
| |
| if err == nil { |
| t.Log("Error should not be nil") |
| t.Fail() |
| } |
| |
| if err.Error() != "Forbidden path outside the build context: ../../ (/)" { |
| t.Logf("Error message is not expected: %s", err.Error()) |
| t.Fail() |
| } |
| } |
| |
| func TestBuildADDFileNotFound(t *testing.T) { |
| eng := NewTestEngine(t) |
| defer nuke(mkRuntimeFromEngine(eng, t)) |
| |
| 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:] |
| |
| 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") |
| } |
| dockerfile := constructDockerfile(context.dockerfile, ip, port) |
| |
| buildfile := docker.NewBuildFile(mkServerFromEngine(eng, t), ioutil.Discard, ioutil.Discard, false, true, false, ioutil.Discard, utils.NewStreamFormatter(false), nil, nil) |
| _, 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() |
| } |
| } |
| |
| func TestBuildInheritance(t *testing.T) { |
| eng := NewTestEngine(t) |
| defer nuke(mkRuntimeFromEngine(eng, t)) |
| |
| img, err := buildImage(testContextTemplate{` |
| from {IMAGE} |
| expose 4243 |
| `, |
| nil, nil}, t, eng, true) |
| |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| img2, _ := buildImage(testContextTemplate{fmt.Sprintf(` |
| from %s |
| entrypoint ["/bin/echo"] |
| `, img.ID), |
| nil, nil}, t, eng, true) |
| |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| // from child |
| if img2.Config.Entrypoint[0] != "/bin/echo" { |
| t.Fail() |
| } |
| |
| // from parent |
| if img.Config.PortSpecs[0] != "4243" { |
| t.Fail() |
| } |
| } |
| |
| 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) |
| } |
| } |
| |
| 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) |
| } |
| } |
| |
| 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. |
| } |