Merge branch 'build-add-file' into simpler-build-upload

Conflicts:
	buildfile_test.go
This commit is contained in:
Solomon Hykes 2013-06-20 20:42:19 -07:00
commit c1a5318d8e
2 changed files with 86 additions and 85 deletions

View file

@ -1,7 +1,9 @@
package docker
import (
"archive/tar"
"bufio"
"bytes"
"errors"
"fmt"
"github.com/dotcloud/docker/utils"
@ -10,6 +12,7 @@ import (
"os"
"os/exec"
"path"
"path/filepath"
)
type Archive io.Reader
@ -160,51 +163,60 @@ func CopyWithTar(src, dst string) error {
if err != nil {
return err
}
var dstExists bool
dstSt, err := os.Stat(dst)
if err != nil {
if !os.IsNotExist(err) {
return err
}
} else {
dstExists = true
}
// Things that can go wrong if the source is a directory
if srcSt.IsDir() {
// The destination exists and is a regular file
if dstExists && !dstSt.IsDir() {
return fmt.Errorf("Can't copy a directory over a regular file")
}
// Things that can go wrong if the source is a regular file
} else {
utils.Debugf("The destination exists, it's a directory, and doesn't end in /")
// The destination exists, it's a directory, and doesn't end in /
if dstExists && dstSt.IsDir() && dst[len(dst)-1] != '/' {
return fmt.Errorf("Can't copy a regular file over a directory %s |%s|", dst, dst[len(dst)-1])
}
}
// Create the destination
var dstDir string
if srcSt.IsDir() || dst[len(dst)-1] == '/' {
// The destination ends in /, or the source is a directory
// --> dst is the holding directory and needs to be created for -C
dstDir = dst
} else {
// The destination doesn't end in /
// --> dst is the file
dstDir = path.Dir(dst)
}
if !dstExists {
// Create the holding directory if necessary
utils.Debugf("Creating the holding directory %s", dstDir)
if err := os.MkdirAll(dstDir, 0700); err != nil && !os.IsExist(err) {
return err
}
}
if !srcSt.IsDir() {
return TarUntar(path.Dir(src), []string{path.Base(src)}, dstDir)
return CopyFileWithTar(src, dst)
}
return TarUntar(src, nil, dstDir)
// Create dst, copy src's content into it
utils.Debugf("Creating dest directory: %s", dst)
if err := os.MkdirAll(dst, 0700); err != nil && !os.IsExist(err) {
return err
}
utils.Debugf("Calling TarUntar(%s, %s)", src, dst)
return TarUntar(src, nil, dst)
}
// CopyFileWithTar emulates the behavior of the 'cp' command-line
// for a single file. It copies a regular file from path `src` to
// path `dst`, and preserves all its metadata.
//
// If `dst` ends with a trailing slash '/', the final destination path
// will be `dst/base(src)`.
func CopyFileWithTar(src, dst string) error {
utils.Debugf("CopyFileWithTar(%s, %s)", src, dst)
srcSt, err := os.Stat(src)
if err != nil {
return err
}
if srcSt.IsDir() {
return fmt.Errorf("Can't copy a directory")
}
// Clean up the trailing /
if dst[len(dst)-1] == '/' {
dst = path.Join(dst, filepath.Base(src))
}
// Create the holding directory if necessary
if err := os.MkdirAll(filepath.Dir(dst), 0700); err != nil && !os.IsExist(err) {
return err
}
buf := new(bytes.Buffer)
tw := tar.NewWriter(buf)
hdr, err := tar.FileInfoHeader(srcSt, "")
if err != nil {
return err
}
hdr.Name = filepath.Base(dst)
if err := tw.WriteHeader(hdr); err != nil {
return err
}
srcF, err := os.Open(src)
if err != nil {
return err
}
if _, err := io.Copy(tw, srcF); err != nil {
return err
}
tw.Close()
return Untar(buf, filepath.Dir(dst))
}
// CmdStream executes a command, and returns its stdout as a stream.

View file

@ -21,16 +21,6 @@ type testContextTemplate struct {
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.
@ -44,21 +34,44 @@ var testContexts []testContextTemplate = []testContextTemplate{
from docker-ut
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,
[]testCommand{
{[]string{"cat", "/tmp/passwd"}, "root:testpass\n"},
{[]string{"ls", "-d", "/var/run/sshd"}, "/var/run/sshd\n"},
},
},
{
`
from docker-ut
add foo /usr/lib/bla/bar`,
add foo /usr/lib/bla/bar
run [ "$(cat /usr/lib/bla/bar)" = 'hello world!' ]
`,
[][2]string{{"foo", "hello world!"}},
[]testCommand{
{[]string{"cat", "/usr/lib/bla/bar"}, "hello world!"},
},
{
`
from docker-ut
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"},
},
},
}
@ -80,32 +93,8 @@ func TestBuild(t *testing.T) {
srv := &Server{runtime: runtime}
buildfile := NewBuildFile(srv, ioutil.Discard)
imgID, err := buildfile.Build(mkTestContext(ctx.dockerfile, ctx.files, t))
if err != nil {
if _, err := buildfile.Build(mkTestContext(ctx.dockerfile, ctx.files, t)); err != nil {
t.Fatal(err)
}
builder := NewBuilder(runtime)
for _, testCmd := range ctx.tests {
container, err := builder.Create(
&Config{
Image: imgID,
Cmd: testCmd.cmd,
},
)
if err != nil {
t.Fatal(err)
}
defer runtime.Destroy(container)
output, err := container.Output()
if err != nil {
t.Fatal(err)
}
if string(output) != testCmd.output {
t.Fatalf("Unexpected output. Read '%s', expected '%s'", output, testCmd.output)
}
}
}
}