Browse Source

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

Conflicts:
	buildfile_test.go
Solomon Hykes 12 years ago
parent
commit
c1a5318d8e
2 changed files with 81 additions and 80 deletions
  1. 50 38
      archive.go
  2. 31 42
      buildfile_test.go

+ 50 - 38
archive.go

@@ -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 !srcSt.IsDir() {
+		return CopyFileWithTar(src, dst)
+	}
+	// 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 {
-		if !os.IsNotExist(err) {
-			return err
-		}
-	} else {
-		dstExists = true
+		return err
 	}
-	// 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])
-		}
+		return fmt.Errorf("Can't copy a directory")
 	}
-	// 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)
+	// Clean up the trailing /
+	if dst[len(dst)-1] == '/' {
+		dst = path.Join(dst, filepath.Base(src))
 	}
-	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
-		}
+	// Create the holding directory if necessary
+	if err := os.MkdirAll(filepath.Dir(dst), 0700); err != nil && !os.IsExist(err) {
+		return err
 	}
-	if !srcSt.IsDir() {
-		return TarUntar(path.Dir(src), []string{path.Base(src)}, dstDir)
+	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
 	}
-	return TarUntar(src, nil, dstDir)
+	tw.Close()
+	return Untar(buf, filepath.Dir(dst))
 }
 
 // CmdStream executes a command, and returns its stdout as a stream.

+ 31 - 42
buildfile_test.go

@@ -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)
-			}
-		}
 	}
 }