Merge branch 'build-add-file' into simpler-build-upload
Conflicts: buildfile_test.go
This commit is contained in:
commit
c1a5318d8e
2 changed files with 86 additions and 85 deletions
98
archive.go
98
archive.go
|
@ -1,7 +1,9 @@
|
||||||
package docker
|
package docker
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"archive/tar"
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/dotcloud/docker/utils"
|
"github.com/dotcloud/docker/utils"
|
||||||
|
@ -10,6 +12,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Archive io.Reader
|
type Archive io.Reader
|
||||||
|
@ -160,51 +163,60 @@ func CopyWithTar(src, dst string) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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() {
|
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.
|
// CmdStream executes a command, and returns its stdout as a stream.
|
||||||
|
|
|
@ -21,16 +21,6 @@ type testContextTemplate struct {
|
||||||
dockerfile string
|
dockerfile string
|
||||||
// Additional files in the context, eg [][2]string{"./passwd", "gordon"}
|
// Additional files in the context, eg [][2]string{"./passwd", "gordon"}
|
||||||
files [][2]string
|
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 table of all the contexts to build and test.
|
||||||
|
@ -44,21 +34,44 @@ var testContexts []testContextTemplate = []testContextTemplate{
|
||||||
from docker-ut
|
from docker-ut
|
||||||
run sh -c 'echo root:testpass > /tmp/passwd'
|
run sh -c 'echo root:testpass > /tmp/passwd'
|
||||||
run mkdir -p /var/run/sshd
|
run mkdir -p /var/run/sshd
|
||||||
|
run [ "$(cat /tmp/passwd)" = "root:testpass" ]
|
||||||
|
run [ "$(ls -d /var/run/sshd)" = "/var/run/sshd" ]
|
||||||
`,
|
`,
|
||||||
nil,
|
nil,
|
||||||
[]testCommand{
|
|
||||||
{[]string{"cat", "/tmp/passwd"}, "root:testpass\n"},
|
|
||||||
{[]string{"ls", "-d", "/var/run/sshd"}, "/var/run/sshd\n"},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
`
|
`
|
||||||
from docker-ut
|
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!"}},
|
[][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}
|
srv := &Server{runtime: runtime}
|
||||||
|
|
||||||
buildfile := NewBuildFile(srv, ioutil.Discard)
|
buildfile := NewBuildFile(srv, ioutil.Discard)
|
||||||
|
if _, err := buildfile.Build(mkTestContext(ctx.dockerfile, ctx.files, t)); err != nil {
|
||||||
imgID, err := buildfile.Build(mkTestContext(ctx.dockerfile, ctx.files, t))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue