Browse code

Merge pull request #6915 from vieux/bump_v1.1.1

Bump v1.1.1

Victor Vieux authored on 2014/07/10 04:16:22
Showing 9 changed files
... ...
@@ -1,5 +1,10 @@
1 1
 # Changelog
2 2
 
3
+## 1.1.1 (2014-07-09)
4
+
5
+#### Builder
6
+* Fix issue with ADD
7
+
3 8
 ## 1.1.0 (2014-07-03)
4 9
 
5 10
 #### Notable features since 1.0.1
... ...
@@ -1 +1 @@
1
-1.1.0
1
+1.1.1
... ...
@@ -378,13 +378,10 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error)
378 378
 }
379 379
 
380 380
 // Untar reads a stream of bytes from `archive`, parses it as a tar archive,
381
-// and unpacks it into the directory at `dest`.
381
+// and unpacks it into the directory at `path`.
382 382
 // The archive may be compressed with one of the following algorithms:
383 383
 //  identity (uncompressed), gzip, bzip2, xz.
384
-// If `dest` does not exist, it is created unless there are multiple entries in `archive`.
385
-// In the latter case, an error is returned.
386
-// If `dest` is an existing file, it gets overwritten.
387
-// If `dest` is an existing directory, its files get merged (with overwrite for conflicting files).
384
+// FIXME: specify behavior when target path exists vs. doesn't exist.
388 385
 func Untar(archive io.Reader, dest string, options *TarOptions) error {
389 386
 	if archive == nil {
390 387
 		return fmt.Errorf("Empty archive")
... ...
@@ -398,22 +395,7 @@ func Untar(archive io.Reader, dest string, options *TarOptions) error {
398 398
 
399 399
 	tr := tar.NewReader(decompressedArchive)
400 400
 
401
-	var (
402
-		dirs            []*tar.Header
403
-		create          bool
404
-		multipleEntries bool
405
-	)
406
-
407
-	if fi, err := os.Lstat(dest); err != nil {
408
-		if !os.IsNotExist(err) {
409
-			return err
410
-		}
411
-		// destination does not exist, so it is assumed it has to be created.
412
-		create = true
413
-	} else if !fi.IsDir() {
414
-		// destination exists and is not a directory, so it will be overwritten.
415
-		create = true
416
-	}
401
+	var dirs []*tar.Header
417 402
 
418 403
 	// Iterate through the files in the archive.
419 404
 	for {
... ...
@@ -426,11 +408,6 @@ func Untar(archive io.Reader, dest string, options *TarOptions) error {
426 426
 			return err
427 427
 		}
428 428
 
429
-		// Return an error if destination needs to be created and there is more than 1 entry in the tar stream.
430
-		if create && multipleEntries {
431
-			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))
432
-		}
433
-
434 429
 		// Normalize name, for safety and for a simple is-root check
435 430
 		hdr.Name = filepath.Clean(hdr.Name)
436 431
 
... ...
@@ -446,12 +423,7 @@ func Untar(archive io.Reader, dest string, options *TarOptions) error {
446 446
 			}
447 447
 		}
448 448
 
449
-		var path string
450
-		if create {
451
-			path = dest // we are renaming hdr.Name to dest
452
-		} else {
453
-			path = filepath.Join(dest, hdr.Name)
454
-		}
449
+		path := filepath.Join(dest, hdr.Name)
455 450
 
456 451
 		// If path exits we almost always just want to remove and replace it
457 452
 		// 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 {
467 467
 				}
468 468
 			}
469 469
 		}
470
-
471 470
 		if err := createTarFile(path, dest, hdr, tr, options == nil || !options.NoLchown); err != nil {
472 471
 			return err
473 472
 		}
474 473
 
475
-		// Successfully added an entry. Predicting multiple entries for next iteration (not current one).
476
-		multipleEntries = true
477
-
478 474
 		// Directory mtimes must be handled at the end to avoid further
479 475
 		// file creation in them to modify the directory mtime
480 476
 		if hdr.Typeflag == tar.TypeDir {
... ...
@@ -160,47 +160,6 @@ func TestTarWithOptions(t *testing.T) {
160 160
 	}
161 161
 }
162 162
 
163
-func TestTarUntarFile(t *testing.T) {
164
-	origin, err := ioutil.TempDir("", "docker-test-untar-origin-file")
165
-	if err != nil {
166
-		t.Fatal(err)
167
-	}
168
-	defer os.RemoveAll(origin)
169
-
170
-	if err := os.MkdirAll(path.Join(origin, "before"), 0700); err != nil {
171
-		t.Fatal(err)
172
-	}
173
-	if err := os.MkdirAll(path.Join(origin, "after"), 0700); err != nil {
174
-		t.Fatal(err)
175
-	}
176
-	if err := ioutil.WriteFile(path.Join(origin, "before", "file"), []byte("hello world"), 0700); err != nil {
177
-		t.Fatal(err)
178
-	}
179
-	if err := ioutil.WriteFile(path.Join(origin, "after", "file2"), []byte("please overwrite me"), 0700); err != nil {
180
-		t.Fatal(err)
181
-	}
182
-
183
-	tar, err := TarWithOptions(path.Join(origin, "before"), &TarOptions{Compression: Uncompressed, Includes: []string{"file"}})
184
-	if err != nil {
185
-		t.Fatal(err)
186
-	}
187
-
188
-	if err := Untar(tar, path.Join(origin, "after", "file2"), nil); err != nil {
189
-		t.Fatal(err)
190
-	}
191
-
192
-	catCmd := exec.Command("cat", path.Join(origin, "after", "file2"))
193
-	out, err := CmdStream(catCmd, nil)
194
-	if err != nil {
195
-		t.Fatalf("Failed to start command: %s", err)
196
-	}
197
-	if output, err := ioutil.ReadAll(out); err != nil {
198
-		t.Error(err)
199
-	} else if string(output) != "hello world" {
200
-		t.Fatalf("Expected 'hello world', got '%s'", output)
201
-	}
202
-}
203
-
204 163
 // Some tar archives such as http://haproxy.1wt.eu/download/1.5/src/devel/haproxy-1.5-dev21.tar.gz
205 164
 // use PAX Global Extended Headers.
206 165
 // Failing prevents the archives from being uncompressed during ADD
207 166
new file mode 100644
... ...
@@ -0,0 +1,3 @@
0
+FROM busybox
1
+ADD test.tar /test.tar
2
+RUN cat /test.tar/test/foo
0 3
new file mode 100644
1 4
Binary files /dev/null and b/integration-cli/build_tests/TestBuildAddTar/1/test.tar differ
2 5
new file mode 100644
... ...
@@ -0,0 +1,3 @@
0
+FROM busybox
1
+ADD test.tar /
2
+RUN cat /test/foo
0 3
new file mode 100644
1 4
Binary files /dev/null and b/integration-cli/build_tests/TestBuildAddTar/2/test.tar differ
... ...
@@ -1564,3 +1564,202 @@ func TestDockerignoringDockerfile(t *testing.T) {
1564 1564
 	}
1565 1565
 	logDone("build - test .dockerignore of Dockerfile")
1566 1566
 }
1567
+
1568
+func TestBuildLineBreak(t *testing.T) {
1569
+	name := "testbuildlinebreak"
1570
+	defer deleteImages(name)
1571
+	_, err := buildImage(name,
1572
+		`FROM  busybox
1573
+RUN    sh -c 'echo root:testpass \
1574
+	> /tmp/passwd'
1575
+RUN    mkdir -p /var/run/sshd
1576
+RUN    [ "$(cat /tmp/passwd)" = "root:testpass" ]
1577
+RUN    [ "$(ls -d /var/run/sshd)" = "/var/run/sshd" ]`,
1578
+		true)
1579
+	if err != nil {
1580
+		t.Fatal(err)
1581
+	}
1582
+	logDone("build - line break with \\")
1583
+}
1584
+
1585
+func TestBuildEOLInLine(t *testing.T) {
1586
+	name := "testbuildeolinline"
1587
+	defer deleteImages(name)
1588
+	_, err := buildImage(name,
1589
+		`FROM   busybox
1590
+RUN    sh -c 'echo root:testpass > /tmp/passwd'
1591
+RUN    echo "foo \n bar"; echo "baz"
1592
+RUN    mkdir -p /var/run/sshd
1593
+RUN    [ "$(cat /tmp/passwd)" = "root:testpass" ]
1594
+RUN    [ "$(ls -d /var/run/sshd)" = "/var/run/sshd" ]`,
1595
+		true)
1596
+	if err != nil {
1597
+		t.Fatal(err)
1598
+	}
1599
+	logDone("build - end of line in dockerfile instruction")
1600
+}
1601
+
1602
+func TestBuildCommentsShebangs(t *testing.T) {
1603
+	name := "testbuildcomments"
1604
+	defer deleteImages(name)
1605
+	_, err := buildImage(name,
1606
+		`FROM busybox
1607
+# This is an ordinary comment.
1608
+RUN { echo '#!/bin/sh'; echo 'echo hello world'; } > /hello.sh
1609
+RUN [ ! -x /hello.sh ]
1610
+# comment with line break \
1611
+RUN chmod +x /hello.sh
1612
+RUN [ -x /hello.sh ]
1613
+RUN [ "$(cat /hello.sh)" = $'#!/bin/sh\necho hello world' ]
1614
+RUN [ "$(/hello.sh)" = "hello world" ]`,
1615
+		true)
1616
+	if err != nil {
1617
+		t.Fatal(err)
1618
+	}
1619
+	logDone("build - comments and shebangs")
1620
+}
1621
+
1622
+func TestBuildUsersAndGroups(t *testing.T) {
1623
+	name := "testbuildusers"
1624
+	defer deleteImages(name)
1625
+	_, err := buildImage(name,
1626
+		`FROM busybox
1627
+
1628
+# Make sure our defaults work
1629
+RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)" = '0:0/root:root' ]
1630
+
1631
+# 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)
1632
+USER root
1633
+RUN [ "$(id -G):$(id -Gn)" = '0 10:root wheel' ]
1634
+
1635
+# Setup dockerio user and group
1636
+RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd
1637
+RUN echo 'dockerio:x:1001:' >> /etc/group
1638
+
1639
+# Make sure we can switch to our user and all the information is exactly as we expect it to be
1640
+USER dockerio
1641
+RUN id -G
1642
+RUN id -Gn
1643
+RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1001/dockerio:dockerio/1001:dockerio' ]
1644
+
1645
+# Switch back to root and double check that worked exactly as we might expect it to
1646
+USER root
1647
+RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '0:0/root:root/0 10:root wheel' ]
1648
+
1649
+# Add a "supplementary" group for our dockerio user
1650
+RUN echo 'supplementary:x:1002:dockerio' >> /etc/group
1651
+
1652
+# ... and then go verify that we get it like we expect
1653
+USER dockerio
1654
+RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1001/dockerio:dockerio/1001 1002:dockerio supplementary' ]
1655
+USER 1001
1656
+RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1001/dockerio:dockerio/1001 1002:dockerio supplementary' ]
1657
+
1658
+# super test the new "user:group" syntax
1659
+USER dockerio:dockerio
1660
+RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1001/dockerio:dockerio/1001:dockerio' ]
1661
+USER 1001:dockerio
1662
+RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1001/dockerio:dockerio/1001:dockerio' ]
1663
+USER dockerio:1001
1664
+RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1001/dockerio:dockerio/1001:dockerio' ]
1665
+USER 1001:1001
1666
+RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1001/dockerio:dockerio/1001:dockerio' ]
1667
+USER dockerio:supplementary
1668
+RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1002/dockerio:supplementary/1002:supplementary' ]
1669
+USER dockerio:1002
1670
+RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1002/dockerio:supplementary/1002:supplementary' ]
1671
+USER 1001:supplementary
1672
+RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1002/dockerio:supplementary/1002:supplementary' ]
1673
+USER 1001:1002
1674
+RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1001:1002/dockerio:supplementary/1002:supplementary' ]
1675
+
1676
+# make sure unknown uid/gid still works properly
1677
+USER 1042:1043
1678
+RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1042:1043/1042:1043/1043:1043' ]`,
1679
+		true)
1680
+	if err != nil {
1681
+		t.Fatal(err)
1682
+	}
1683
+	logDone("build - users and groups")
1684
+}
1685
+
1686
+func TestBuildEnvUsage(t *testing.T) {
1687
+	name := "testbuildenvusage"
1688
+	defer deleteImages(name)
1689
+	dockerfile := `FROM busybox
1690
+ENV    FOO /foo/baz
1691
+ENV    BAR /bar
1692
+ENV    BAZ $BAR
1693
+ENV    FOOPATH $PATH:$FOO
1694
+RUN    [ "$BAR" = "$BAZ" ]
1695
+RUN    [ "$FOOPATH" = "$PATH:/foo/baz" ]
1696
+ENV	   FROM hello/docker/world
1697
+ENV    TO /docker/world/hello
1698
+ADD    $FROM $TO
1699
+RUN    [ "$(cat $TO)" = "hello" ]`
1700
+	ctx, err := fakeContext(dockerfile, map[string]string{
1701
+		"hello/docker/world": "hello",
1702
+	})
1703
+	if err != nil {
1704
+		t.Fatal(err)
1705
+	}
1706
+	_, err = buildImageFromContext(name, ctx, true)
1707
+	if err != nil {
1708
+		t.Fatal(err)
1709
+	}
1710
+	logDone("build - environment variables usage")
1711
+}
1712
+
1713
+func TestBuildAddScript(t *testing.T) {
1714
+	name := "testbuildaddscript"
1715
+	defer deleteImages(name)
1716
+	dockerfile := `
1717
+FROM busybox
1718
+ADD test /test
1719
+RUN ["chmod","+x","/test"]
1720
+RUN ["/test"]
1721
+RUN [ "$(cat /testfile)" = 'test!' ]`
1722
+	ctx, err := fakeContext(dockerfile, map[string]string{
1723
+		"test": "#!/bin/sh\necho 'test!' > /testfile",
1724
+	})
1725
+	if err != nil {
1726
+		t.Fatal(err)
1727
+	}
1728
+	_, err = buildImageFromContext(name, ctx, true)
1729
+	if err != nil {
1730
+		t.Fatal(err)
1731
+	}
1732
+	logDone("build - add and run script")
1733
+}
1734
+
1735
+func TestBuildAddTar(t *testing.T) {
1736
+
1737
+	checkOutput := func(out string) {
1738
+		n := -1
1739
+		x := ""
1740
+		for i, line := range strings.Split(out, "\n") {
1741
+			if strings.HasPrefix(line, "Step 2") {
1742
+				n = i + 2
1743
+				x = line[strings.Index(line, "cat ")+4:]
1744
+			}
1745
+			if i == n {
1746
+				if line != "Hi" {
1747
+					t.Fatalf("Could not find contents of %s (expected 'Hi' got '%s'", x, line)
1748
+				}
1749
+				n = -2
1750
+			}
1751
+		}
1752
+		if n > -2 {
1753
+			t.Fatalf("Could not find contents of %s in build output", x)
1754
+		}
1755
+	}
1756
+
1757
+	for _, n := range []string{"1", "2"} {
1758
+		buildDirectory := filepath.Join(workingDirectory, "build_tests", "TestBuildAddTar", n)
1759
+		buildCmd := exec.Command(dockerBinary, "build", "-t", "testbuildaddtar", ".")
1760
+		buildCmd.Dir = buildDirectory
1761
+		out, _, err := runCommandWithOutput(buildCmd)
1762
+		errorOut(err, t, fmt.Sprintf("build failed to complete for TestBuildAddTar/%s: %v", n, err))
1763
+		checkOutput(out)
1764
+	}
1765
+}