mirror of https://github.com/docker/docs.git
* Builder: reorganized unit tests for better code reuse, and to test non-empty contexts
This commit is contained in:
parent
38554fc2a7
commit
061f8d12e0
|
@ -1,40 +1,75 @@
|
||||||
package docker
|
package docker
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/dotcloud/docker/utils"
|
"io/ioutil"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
const Dockerfile = `
|
|
||||||
# VERSION 0.1
|
|
||||||
# DOCKER-VERSION 0.2
|
|
||||||
|
|
||||||
from ` + unitTestImageName + `
|
|
||||||
run sh -c 'echo root:testpass > /tmp/passwd'
|
|
||||||
run mkdir -p /var/run/sshd
|
|
||||||
`
|
|
||||||
|
|
||||||
const DockerfileNoNewLine = `
|
|
||||||
# VERSION 0.1
|
|
||||||
# DOCKER-VERSION 0.2
|
|
||||||
|
|
||||||
from ` + unitTestImageName + `
|
|
||||||
run sh -c 'echo root:testpass > /tmp/passwd'
|
|
||||||
run mkdir -p /var/run/sshd`
|
|
||||||
|
|
||||||
// mkTestContext generates a build context from the contents of the provided dockerfile.
|
// mkTestContext generates a build context from the contents of the provided dockerfile.
|
||||||
// This context is suitable for use as an argument to BuildFile.Build()
|
// This context is suitable for use as an argument to BuildFile.Build()
|
||||||
func mkTestContext(dockerfile string, t *testing.T) Archive {
|
func mkTestContext(dockerfile string, files [][2]string, t *testing.T) Archive {
|
||||||
context, err := mkBuildContext(dockerfile)
|
context, err := mkBuildContext(dockerfile, files)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
return context
|
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
|
||||||
|
// Test commands to run in the resulting image
|
||||||
|
tests []testCommand
|
||||||
|
}
|
||||||
|
|
||||||
|
// A testCommand describes a command to run in a container, and the exact output required to pass the test
|
||||||
|
type testCommand struct {
|
||||||
|
// The command to run, eg. []string{"echo", "hello", "world"}
|
||||||
|
cmd []string
|
||||||
|
// The exact output expected, eg. "hello world\n"
|
||||||
|
output 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 = []testContextTemplate{
|
||||||
|
{
|
||||||
|
`
|
||||||
|
# VERSION 0.1
|
||||||
|
# DOCKER-VERSION 0.2
|
||||||
|
|
||||||
|
from docker-ut
|
||||||
|
run sh -c 'echo root:testpass > /tmp/passwd'
|
||||||
|
run mkdir -p /var/run/sshd
|
||||||
|
`,
|
||||||
|
nil,
|
||||||
|
[]testCommand{
|
||||||
|
{[]string{"cat", "/tmp/passwd"}, "root:testpass\n"},
|
||||||
|
{[]string{"ls", "-d", "/var/run/sshd"}, "/var/run/sshd\n"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
`
|
||||||
|
# VERSION 0.1
|
||||||
|
# DOCKER-VERSION 0.2
|
||||||
|
|
||||||
|
from docker-ut
|
||||||
|
run sh -c 'echo root:testpass > /tmp/passwd'
|
||||||
|
run mkdir -p /var/run/sshd`,
|
||||||
|
nil,
|
||||||
|
[]testCommand{
|
||||||
|
{[]string{"cat", "/tmp/passwd"}, "root:testpass\n"},
|
||||||
|
{[]string{"ls", "-d", "/var/run/sshd"}, "/var/run/sshd\n"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
func TestBuild(t *testing.T) {
|
func TestBuild(t *testing.T) {
|
||||||
dockerfiles := []string{Dockerfile, DockerfileNoNewLine}
|
for _, ctx := range testContexts {
|
||||||
for _, Dockerfile := range dockerfiles {
|
|
||||||
runtime, err := newTestRuntime()
|
runtime, err := newTestRuntime()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -43,50 +78,33 @@ func TestBuild(t *testing.T) {
|
||||||
|
|
||||||
srv := &Server{runtime: runtime}
|
srv := &Server{runtime: runtime}
|
||||||
|
|
||||||
buildfile := NewBuildFile(srv, &utils.NopWriter{})
|
buildfile := NewBuildFile(srv, ioutil.Discard)
|
||||||
|
|
||||||
imgID, err := buildfile.Build(mkTestContext(Dockerfile, t))
|
imgID, err := buildfile.Build(mkTestContext(ctx.dockerfile, ctx.files, t))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
builder := NewBuilder(runtime)
|
builder := NewBuilder(runtime)
|
||||||
container, err := builder.Create(
|
for _, testCmd := range ctx.tests {
|
||||||
&Config{
|
container, err := builder.Create(
|
||||||
Image: imgID,
|
&Config{
|
||||||
Cmd: []string{"cat", "/tmp/passwd"},
|
Image: imgID,
|
||||||
},
|
Cmd: testCmd.cmd,
|
||||||
)
|
},
|
||||||
if err != nil {
|
)
|
||||||
t.Fatal(err)
|
if err != nil {
|
||||||
}
|
t.Fatal(err)
|
||||||
defer runtime.Destroy(container)
|
}
|
||||||
|
defer runtime.Destroy(container)
|
||||||
|
|
||||||
output, err := container.Output()
|
output, err := container.Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if string(output) != "root:testpass\n" {
|
if string(output) != testCmd.output {
|
||||||
t.Fatalf("Unexpected output. Read '%s', expected '%s'", output, "root:testpass\n")
|
t.Fatalf("Unexpected output. Read '%s', expected '%s'", output, testCmd.output)
|
||||||
}
|
}
|
||||||
|
|
||||||
container2, err := builder.Create(
|
|
||||||
&Config{
|
|
||||||
Image: imgID,
|
|
||||||
Cmd: []string{"ls", "-d", "/var/run/sshd"},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer runtime.Destroy(container2)
|
|
||||||
|
|
||||||
output, err = container2.Output()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if string(output) != "/var/run/sshd\n" {
|
|
||||||
t.Fatal("/var/run/sshd has not been created")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
26
commands.go
26
commands.go
|
@ -132,18 +132,22 @@ func (cli *DockerCli) CmdInsert(args ...string) error {
|
||||||
|
|
||||||
// mkBuildContext returns an archive of an empty context with the contents
|
// mkBuildContext returns an archive of an empty context with the contents
|
||||||
// of `dockerfile` at the path ./Dockerfile
|
// of `dockerfile` at the path ./Dockerfile
|
||||||
func mkBuildContext(content string) (Archive, error) {
|
func mkBuildContext(dockerfile string, files [][2]string) (Archive, error) {
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
tw := tar.NewWriter(buf)
|
tw := tar.NewWriter(buf)
|
||||||
hdr := &tar.Header{
|
files = append(files, [2]string{"Dockerfile", dockerfile})
|
||||||
Name: "Dockerfile",
|
for _, file := range files {
|
||||||
Size: int64(len(content)),
|
name, content := file[0], file[1]
|
||||||
}
|
hdr := &tar.Header{
|
||||||
if err := tw.WriteHeader(hdr); err != nil {
|
Name: name,
|
||||||
return nil, err
|
Size: int64(len(content)),
|
||||||
}
|
}
|
||||||
if _, err := tw.Write([]byte(content)); err != nil {
|
if err := tw.WriteHeader(hdr); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
}
|
||||||
|
if _, err := tw.Write([]byte(content)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if err := tw.Close(); err != nil {
|
if err := tw.Close(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -174,7 +178,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
context, err = mkBuildContext(string(dockerfile))
|
context, err = mkBuildContext(string(dockerfile), nil)
|
||||||
} else {
|
} else {
|
||||||
context, err = Tar(cmd.Arg(0), Uncompressed)
|
context, err = Tar(cmd.Arg(0), Uncompressed)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue