浏览代码

Merge pull request #6915 from vieux/bump_v1.1.1

Bump v1.1.1
Victor Vieux 11 年之前
父节点
当前提交
ecd20a96df

+ 5 - 0
CHANGELOG.md

@@ -1,5 +1,10 @@
 # Changelog
 
+## 1.1.1 (2014-07-09)
+
+#### Builder
+* Fix issue with ADD
+
 ## 1.1.0 (2014-07-03)
 
 #### Notable features since 1.0.1

+ 1 - 1
VERSION

@@ -1 +1 @@
-1.1.0
+1.1.1

+ 4 - 36
archive/archive.go

@@ -378,13 +378,10 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error)
 }
 
 // Untar reads a stream of bytes from `archive`, parses it as a tar archive,
-// and unpacks it into the directory at `dest`.
+// and unpacks it into the directory at `path`.
 // The archive may be compressed with one of the following algorithms:
 //  identity (uncompressed), gzip, bzip2, xz.
-// If `dest` does not exist, it is created unless there are multiple entries in `archive`.
-// In the latter case, an error is returned.
-// If `dest` is an existing file, it gets overwritten.
-// If `dest` is an existing directory, its files get merged (with overwrite for conflicting files).
+// FIXME: specify behavior when target path exists vs. doesn't exist.
 func Untar(archive io.Reader, dest string, options *TarOptions) error {
 	if archive == nil {
 		return fmt.Errorf("Empty archive")
@@ -398,22 +395,7 @@ func Untar(archive io.Reader, dest string, options *TarOptions) error {
 
 	tr := tar.NewReader(decompressedArchive)
 
-	var (
-		dirs            []*tar.Header
-		create          bool
-		multipleEntries bool
-	)
-
-	if fi, err := os.Lstat(dest); err != nil {
-		if !os.IsNotExist(err) {
-			return err
-		}
-		// destination does not exist, so it is assumed it has to be created.
-		create = true
-	} else if !fi.IsDir() {
-		// destination exists and is not a directory, so it will be overwritten.
-		create = true
-	}
+	var dirs []*tar.Header
 
 	// Iterate through the files in the archive.
 	for {
@@ -426,11 +408,6 @@ func Untar(archive io.Reader, dest string, options *TarOptions) error {
 			return err
 		}
 
-		// Return an error if destination needs to be created and there is more than 1 entry in the tar stream.
-		if create && multipleEntries {
-			return fmt.Errorf("Trying to untar an archive with multiple entries to an inexistant target `%s`: did you mean `%s` instead?", dest, filepath.Dir(dest))
-		}
-
 		// Normalize name, for safety and for a simple is-root check
 		hdr.Name = filepath.Clean(hdr.Name)
 
@@ -446,12 +423,7 @@ func Untar(archive io.Reader, dest string, options *TarOptions) error {
 			}
 		}
 
-		var path string
-		if create {
-			path = dest // we are renaming hdr.Name to dest
-		} else {
-			path = filepath.Join(dest, hdr.Name)
-		}
+		path := filepath.Join(dest, hdr.Name)
 
 		// If path exits we almost always just want to remove and replace it
 		// The only exception is when it is a directory *and* the file from
@@ -467,14 +439,10 @@ func Untar(archive io.Reader, dest string, options *TarOptions) error {
 				}
 			}
 		}
-
 		if err := createTarFile(path, dest, hdr, tr, options == nil || !options.NoLchown); err != nil {
 			return err
 		}
 
-		// Successfully added an entry. Predicting multiple entries for next iteration (not current one).
-		multipleEntries = true
-
 		// Directory mtimes must be handled at the end to avoid further
 		// file creation in them to modify the directory mtime
 		if hdr.Typeflag == tar.TypeDir {

+ 0 - 41
archive/archive_test.go

@@ -160,47 +160,6 @@ func TestTarWithOptions(t *testing.T) {
 	}
 }
 
-func TestTarUntarFile(t *testing.T) {
-	origin, err := ioutil.TempDir("", "docker-test-untar-origin-file")
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer os.RemoveAll(origin)
-
-	if err := os.MkdirAll(path.Join(origin, "before"), 0700); err != nil {
-		t.Fatal(err)
-	}
-	if err := os.MkdirAll(path.Join(origin, "after"), 0700); err != nil {
-		t.Fatal(err)
-	}
-	if err := ioutil.WriteFile(path.Join(origin, "before", "file"), []byte("hello world"), 0700); err != nil {
-		t.Fatal(err)
-	}
-	if err := ioutil.WriteFile(path.Join(origin, "after", "file2"), []byte("please overwrite me"), 0700); err != nil {
-		t.Fatal(err)
-	}
-
-	tar, err := TarWithOptions(path.Join(origin, "before"), &TarOptions{Compression: Uncompressed, Includes: []string{"file"}})
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	if err := Untar(tar, path.Join(origin, "after", "file2"), nil); err != nil {
-		t.Fatal(err)
-	}
-
-	catCmd := exec.Command("cat", path.Join(origin, "after", "file2"))
-	out, err := CmdStream(catCmd, nil)
-	if err != nil {
-		t.Fatalf("Failed to start command: %s", err)
-	}
-	if output, err := ioutil.ReadAll(out); err != nil {
-		t.Error(err)
-	} else if string(output) != "hello world" {
-		t.Fatalf("Expected 'hello world', got '%s'", output)
-	}
-}
-
 // Some tar archives such as http://haproxy.1wt.eu/download/1.5/src/devel/haproxy-1.5-dev21.tar.gz
 // use PAX Global Extended Headers.
 // Failing prevents the archives from being uncompressed during ADD

+ 3 - 0
integration-cli/build_tests/TestBuildAddTar/1/Dockerfile

@@ -0,0 +1,3 @@
+FROM busybox
+ADD test.tar /test.tar
+RUN cat /test.tar/test/foo

二进制
integration-cli/build_tests/TestBuildAddTar/1/test.tar


+ 3 - 0
integration-cli/build_tests/TestBuildAddTar/2/Dockerfile

@@ -0,0 +1,3 @@
+FROM busybox
+ADD test.tar /
+RUN cat /test/foo

二进制
integration-cli/build_tests/TestBuildAddTar/2/test.tar


+ 199 - 0
integration-cli/docker_cli_build_test.go

@@ -1564,3 +1564,202 @@ func TestDockerignoringDockerfile(t *testing.T) {
 	}
 	logDone("build - test .dockerignore of Dockerfile")
 }
+
+func TestBuildLineBreak(t *testing.T) {
+	name := "testbuildlinebreak"
+	defer deleteImages(name)
+	_, err := buildImage(name,
+		`FROM  busybox
+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" ]`,
+		true)
+	if err != nil {
+		t.Fatal(err)
+	}
+	logDone("build - line break with \\")
+}
+
+func TestBuildEOLInLine(t *testing.T) {
+	name := "testbuildeolinline"
+	defer deleteImages(name)
+	_, err := buildImage(name,
+		`FROM   busybox
+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" ]`,
+		true)
+	if err != nil {
+		t.Fatal(err)
+	}
+	logDone("build - end of line in dockerfile instruction")
+}
+
+func TestBuildCommentsShebangs(t *testing.T) {
+	name := "testbuildcomments"
+	defer deleteImages(name)
+	_, err := buildImage(name,
+		`FROM busybox
+# This is an ordinary comment.
+RUN { echo '#!/bin/sh'; echo 'echo hello world'; } > /hello.sh
+RUN [ ! -x /hello.sh ]
+# comment with line break \
+RUN chmod +x /hello.sh
+RUN [ -x /hello.sh ]
+RUN [ "$(cat /hello.sh)" = $'#!/bin/sh\necho hello world' ]
+RUN [ "$(/hello.sh)" = "hello world" ]`,
+		true)
+	if err != nil {
+		t.Fatal(err)
+	}
+	logDone("build - comments and shebangs")
+}
+
+func TestBuildUsersAndGroups(t *testing.T) {
+	name := "testbuildusers"
+	defer deleteImages(name)
+	_, err := buildImage(name,
+		`FROM busybox
+
+# Make sure our defaults work
+RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)" = '0:0/root:root' ]
+
+# TODO decide if "args.user = strconv.Itoa(syscall.Getuid())" is acceptable behavior for changeUser in sysvinit instead of "return nil" when "USER" isn't specified (so that we get the proper group list even if that is the empty list, even in the default case of not supplying an explicit USER to run as, which implies USER 0)
+USER root
+RUN [ "$(id -G):$(id -Gn)" = '0 10:root wheel' ]
+
+# Setup dockerio user and group
+RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd
+RUN echo 'dockerio:x:1001:' >> /etc/group
+
+# Make sure we can switch to our user and all the information is exactly as we expect it to be
+USER dockerio
+RUN id -G
+RUN id -Gn
+RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1001/dockerio:dockerio/1001:dockerio' ]
+
+# Switch back to root and double check that worked exactly as we might expect it to
+USER root
+RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '0:0/root:root/0 10:root wheel' ]
+
+# Add a "supplementary" group for our dockerio user
+RUN echo 'supplementary:x:1002:dockerio' >> /etc/group
+
+# ... and then go verify that we get it like we expect
+USER dockerio
+RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1001/dockerio:dockerio/1001 1002:dockerio supplementary' ]
+USER 1001
+RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1001/dockerio:dockerio/1001 1002:dockerio supplementary' ]
+
+# super test the new "user:group" syntax
+USER dockerio:dockerio
+RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1001/dockerio:dockerio/1001:dockerio' ]
+USER 1001:dockerio
+RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1001/dockerio:dockerio/1001:dockerio' ]
+USER dockerio:1001
+RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1001/dockerio:dockerio/1001:dockerio' ]
+USER 1001:1001
+RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1001/dockerio:dockerio/1001:dockerio' ]
+USER dockerio:supplementary
+RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1002/dockerio:supplementary/1002:supplementary' ]
+USER dockerio:1002
+RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1002/dockerio:supplementary/1002:supplementary' ]
+USER 1001:supplementary
+RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1002/dockerio:supplementary/1002:supplementary' ]
+USER 1001:1002
+RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1002/dockerio:supplementary/1002:supplementary' ]
+
+# make sure unknown uid/gid still works properly
+USER 1042:1043
+RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1042:1043/1042:1043/1043:1043' ]`,
+		true)
+	if err != nil {
+		t.Fatal(err)
+	}
+	logDone("build - users and groups")
+}
+
+func TestBuildEnvUsage(t *testing.T) {
+	name := "testbuildenvusage"
+	defer deleteImages(name)
+	dockerfile := `FROM busybox
+ENV    FOO /foo/baz
+ENV    BAR /bar
+ENV    BAZ $BAR
+ENV    FOOPATH $PATH:$FOO
+RUN    [ "$BAR" = "$BAZ" ]
+RUN    [ "$FOOPATH" = "$PATH:/foo/baz" ]
+ENV	   FROM hello/docker/world
+ENV    TO /docker/world/hello
+ADD    $FROM $TO
+RUN    [ "$(cat $TO)" = "hello" ]`
+	ctx, err := fakeContext(dockerfile, map[string]string{
+		"hello/docker/world": "hello",
+	})
+	if err != nil {
+		t.Fatal(err)
+	}
+	_, err = buildImageFromContext(name, ctx, true)
+	if err != nil {
+		t.Fatal(err)
+	}
+	logDone("build - environment variables usage")
+}
+
+func TestBuildAddScript(t *testing.T) {
+	name := "testbuildaddscript"
+	defer deleteImages(name)
+	dockerfile := `
+FROM busybox
+ADD test /test
+RUN ["chmod","+x","/test"]
+RUN ["/test"]
+RUN [ "$(cat /testfile)" = 'test!' ]`
+	ctx, err := fakeContext(dockerfile, map[string]string{
+		"test": "#!/bin/sh\necho 'test!' > /testfile",
+	})
+	if err != nil {
+		t.Fatal(err)
+	}
+	_, err = buildImageFromContext(name, ctx, true)
+	if err != nil {
+		t.Fatal(err)
+	}
+	logDone("build - add and run script")
+}
+
+func TestBuildAddTar(t *testing.T) {
+
+	checkOutput := func(out string) {
+		n := -1
+		x := ""
+		for i, line := range strings.Split(out, "\n") {
+			if strings.HasPrefix(line, "Step 2") {
+				n = i + 2
+				x = line[strings.Index(line, "cat ")+4:]
+			}
+			if i == n {
+				if line != "Hi" {
+					t.Fatalf("Could not find contents of %s (expected 'Hi' got '%s'", x, line)
+				}
+				n = -2
+			}
+		}
+		if n > -2 {
+			t.Fatalf("Could not find contents of %s in build output", x)
+		}
+	}
+
+	for _, n := range []string{"1", "2"} {
+		buildDirectory := filepath.Join(workingDirectory, "build_tests", "TestBuildAddTar", n)
+		buildCmd := exec.Command(dockerBinary, "build", "-t", "testbuildaddtar", ".")
+		buildCmd.Dir = buildDirectory
+		out, _, err := runCommandWithOutput(buildCmd)
+		errorOut(err, t, fmt.Sprintf("build failed to complete for TestBuildAddTar/%s: %v", n, err))
+		checkOutput(out)
+	}
+}