Merge pull request #9826 from icecrime/8318_whitespace_add_copy

Support whitespace in paths for ADD and COPY
This commit is contained in:
Tibor Vass 2015-01-19 18:37:53 -05:00
commit 50ff27caa1
6 changed files with 235 additions and 13 deletions

View File

@ -51,8 +51,8 @@ func init() {
"env": parseEnv, "env": parseEnv,
"maintainer": parseString, "maintainer": parseString,
"from": parseString, "from": parseString,
"add": parseStringsWhitespaceDelimited, "add": parseMaybeJSONToList,
"copy": parseStringsWhitespaceDelimited, "copy": parseMaybeJSONToList,
"run": parseMaybeJSON, "run": parseMaybeJSON,
"cmd": parseMaybeJSON, "cmd": parseMaybeJSON,
"entrypoint": parseMaybeJSON, "entrypoint": parseMaybeJSON,

View File

@ -0,0 +1,9 @@
FROM ubuntu:14.04
MAINTAINER Seongyeol Lim <seongyeol37@gmail.com>
COPY . /go/src/github.com/docker/docker
ADD . /
ADD [ "vimrc", "/tmp" ]
COPY [ "bashrc", "/tmp" ]
COPY [ "test file", "/tmp" ]
ADD [ "test file", "/tmp/test file" ]

View File

@ -0,0 +1,8 @@
(from "ubuntu:14.04")
(maintainer "Seongyeol Lim <seongyeol37@gmail.com>")
(copy "." "/go/src/github.com/docker/docker")
(add "." "/")
(add "vimrc" "/tmp")
(copy "bashrc" "/tmp")
(copy "test file" "/tmp")
(add "test file" "/tmp/test file")

View File

@ -132,7 +132,11 @@ or
interactively, as with the following command: **docker run -t -i image bash** interactively, as with the following command: **docker run -t -i image bash**
**ADD** **ADD**
--**ADD <src>... <dest>** The ADD instruction copies new files, directories --ADD has two forms:
**ADD <src>... <dest>**
**ADD ["<src>"... "<dest>"]** This form is required for paths containing
whitespace.
The ADD instruction copies new files, directories
or remote file URLs to the filesystem of the container at path <dest>. or remote file URLs to the filesystem of the container at path <dest>.
Mutliple <src> resources may be specified but if they are files or directories Mutliple <src> resources may be specified but if they are files or directories
then they must be relative to the source directory that is being built then they must be relative to the source directory that is being built
@ -142,7 +146,11 @@ or
and gid of 0. and gid of 0.
**COPY** **COPY**
--**COPY <src> <dest>** The COPY instruction copies new files from <src> and --COPY has two forms:
**COPY <src>... <dest>**
**COPY ["<src>"... "<dest>"]** This form is required for paths containing
whitespace.
The COPY instruction copies new files from <src> and
adds them to the filesystem of the container at path <dest>. The <src> must be adds them to the filesystem of the container at path <dest>. The <src> must be
the path to a file or directory relative to the source directory that is the path to a file or directory relative to the source directory that is
being built (the context of the build) or a remote file URL. The `<dest>` is an being built (the context of the build) or a remote file URL. The `<dest>` is an

View File

@ -388,7 +388,11 @@ change them using `docker run --env <key>=<value>`.
## ADD ## ADD
ADD <src>... <dest> ADD has two forms:
- `ADD <src>... <dest>`
- `ADD ["<src>"... "<dest>"]` (this form is required for paths containing
whitespace)
The `ADD` instruction copies new files, directories or remote file URLs from `<src>` The `ADD` instruction copies new files, directories or remote file URLs from `<src>`
and adds them to the filesystem of the container at the path `<dest>`. and adds them to the filesystem of the container at the path `<dest>`.
@ -488,7 +492,11 @@ The copy obeys the following rules:
## COPY ## COPY
COPY <src>... <dest> COPY has two forms:
- `COPY <src>... <dest>`
- `COPY ["<src>"... "<dest>"]` (this form is required for paths containing
whitespace)
The `COPY` instruction copies new files or directories from `<src>` The `COPY` instruction copies new files or directories from `<src>`
and adds them to the filesystem of the container at the path `<dest>`. and adds them to the filesystem of the container at the path `<dest>`.

View File

@ -802,7 +802,7 @@ RUN [ $(ls -l /exists/exists_file | awk '{print $3":"$4}') = 'dockerio:dockerio'
if _, err := buildImageFromContext(name, ctx, true); err != nil { if _, err := buildImageFromContext(name, ctx, true); err != nil {
t.Fatal(err) t.Fatal(err)
} }
logDone("build - mulitple file copy/add tests") logDone("build - multiple file copy/add tests")
} }
func TestBuildAddMultipleFilesToFile(t *testing.T) { func TestBuildAddMultipleFilesToFile(t *testing.T) {
@ -810,7 +810,7 @@ func TestBuildAddMultipleFilesToFile(t *testing.T) {
defer deleteImages(name) defer deleteImages(name)
ctx, err := fakeContext(`FROM scratch ctx, err := fakeContext(`FROM scratch
ADD file1.txt file2.txt test ADD file1.txt file2.txt test
`, `,
map[string]string{ map[string]string{
"file1.txt": "test1", "file1.txt": "test1",
"file2.txt": "test1", "file2.txt": "test1",
@ -822,18 +822,41 @@ func TestBuildAddMultipleFilesToFile(t *testing.T) {
expected := "When using ADD with more than one source file, the destination must be a directory and end with a /" expected := "When using ADD with more than one source file, the destination must be a directory and end with a /"
if _, err := buildImageFromContext(name, ctx, true); err == nil || !strings.Contains(err.Error(), expected) { if _, err := buildImageFromContext(name, ctx, true); err == nil || !strings.Contains(err.Error(), expected) {
t.Fatalf("Wrong error: (should contain \"%s\") got:\n%v", expected, err) t.Fatalf("Wrong error: (should contain %q) got:\n%v", expected, err)
} }
logDone("build - multiple add files to file") logDone("build - multiple add files to file")
} }
func TestBuildJSONAddMultipleFilesToFile(t *testing.T) {
name := "testjsonaddmultiplefilestofile"
defer deleteImages(name)
ctx, err := fakeContext(`FROM scratch
ADD ["file1.txt", "file2.txt", "test"]
`,
map[string]string{
"file1.txt": "test1",
"file2.txt": "test1",
})
defer ctx.Close()
if err != nil {
t.Fatal(err)
}
expected := "When using ADD with more than one source file, the destination must be a directory and end with a /"
if _, err := buildImageFromContext(name, ctx, true); err == nil || !strings.Contains(err.Error(), expected) {
t.Fatalf("Wrong error: (should contain %q) got:\n%v", expected, err)
}
logDone("build - multiple add files to file json syntax")
}
func TestBuildAddMultipleFilesToFileWild(t *testing.T) { func TestBuildAddMultipleFilesToFileWild(t *testing.T) {
name := "testaddmultiplefilestofilewild" name := "testaddmultiplefilestofilewild"
defer deleteImages(name) defer deleteImages(name)
ctx, err := fakeContext(`FROM scratch ctx, err := fakeContext(`FROM scratch
ADD file*.txt test ADD file*.txt test
`, `,
map[string]string{ map[string]string{
"file1.txt": "test1", "file1.txt": "test1",
"file2.txt": "test1", "file2.txt": "test1",
@ -845,18 +868,41 @@ func TestBuildAddMultipleFilesToFileWild(t *testing.T) {
expected := "When using ADD with more than one source file, the destination must be a directory and end with a /" expected := "When using ADD with more than one source file, the destination must be a directory and end with a /"
if _, err := buildImageFromContext(name, ctx, true); err == nil || !strings.Contains(err.Error(), expected) { if _, err := buildImageFromContext(name, ctx, true); err == nil || !strings.Contains(err.Error(), expected) {
t.Fatalf("Wrong error: (should contain \"%s\") got:\n%v", expected, err) t.Fatalf("Wrong error: (should contain %q) got:\n%v", expected, err)
} }
logDone("build - multiple add files to file wild") logDone("build - multiple add files to file wild")
} }
func TestBuildJSONAddMultipleFilesToFileWild(t *testing.T) {
name := "testjsonaddmultiplefilestofilewild"
defer deleteImages(name)
ctx, err := fakeContext(`FROM scratch
ADD ["file*.txt", "test"]
`,
map[string]string{
"file1.txt": "test1",
"file2.txt": "test1",
})
defer ctx.Close()
if err != nil {
t.Fatal(err)
}
expected := "When using ADD with more than one source file, the destination must be a directory and end with a /"
if _, err := buildImageFromContext(name, ctx, true); err == nil || !strings.Contains(err.Error(), expected) {
t.Fatalf("Wrong error: (should contain %q) got:\n%v", expected, err)
}
logDone("build - multiple add files to file wild json syntax")
}
func TestBuildCopyMultipleFilesToFile(t *testing.T) { func TestBuildCopyMultipleFilesToFile(t *testing.T) {
name := "testcopymultiplefilestofile" name := "testcopymultiplefilestofile"
defer deleteImages(name) defer deleteImages(name)
ctx, err := fakeContext(`FROM scratch ctx, err := fakeContext(`FROM scratch
COPY file1.txt file2.txt test COPY file1.txt file2.txt test
`, `,
map[string]string{ map[string]string{
"file1.txt": "test1", "file1.txt": "test1",
"file2.txt": "test1", "file2.txt": "test1",
@ -868,12 +914,155 @@ func TestBuildCopyMultipleFilesToFile(t *testing.T) {
expected := "When using COPY with more than one source file, the destination must be a directory and end with a /" expected := "When using COPY with more than one source file, the destination must be a directory and end with a /"
if _, err := buildImageFromContext(name, ctx, true); err == nil || !strings.Contains(err.Error(), expected) { if _, err := buildImageFromContext(name, ctx, true); err == nil || !strings.Contains(err.Error(), expected) {
t.Fatalf("Wrong error: (should contain \"%s\") got:\n%v", expected, err) t.Fatalf("Wrong error: (should contain %q) got:\n%v", expected, err)
} }
logDone("build - multiple copy files to file") logDone("build - multiple copy files to file")
} }
func TestBuildJSONCopyMultipleFilesToFile(t *testing.T) {
name := "testjsoncopymultiplefilestofile"
defer deleteImages(name)
ctx, err := fakeContext(`FROM scratch
COPY ["file1.txt", "file2.txt", "test"]
`,
map[string]string{
"file1.txt": "test1",
"file2.txt": "test1",
})
defer ctx.Close()
if err != nil {
t.Fatal(err)
}
expected := "When using COPY with more than one source file, the destination must be a directory and end with a /"
if _, err := buildImageFromContext(name, ctx, true); err == nil || !strings.Contains(err.Error(), expected) {
t.Fatalf("Wrong error: (should contain %q) got:\n%v", expected, err)
}
logDone("build - multiple copy files to file json syntax")
}
func TestBuildAddFileWithWhitespace(t *testing.T) {
name := "testaddfilewithwhitespace"
defer deleteImages(name)
ctx, err := fakeContext(`FROM busybox
RUN mkdir "/test dir"
RUN mkdir "/test_dir"
ADD [ "test file1", "/test_file1" ]
ADD [ "test_file2", "/test file2" ]
ADD [ "test file3", "/test file3" ]
ADD [ "test dir/test_file4", "/test_dir/test_file4" ]
ADD [ "test_dir/test_file5", "/test dir/test_file5" ]
ADD [ "test dir/test_file6", "/test dir/test_file6" ]
RUN [ $(cat "/test_file1") = 'test1' ]
RUN [ $(cat "/test file2") = 'test2' ]
RUN [ $(cat "/test file3") = 'test3' ]
RUN [ $(cat "/test_dir/test_file4") = 'test4' ]
RUN [ $(cat "/test dir/test_file5") = 'test5' ]
RUN [ $(cat "/test dir/test_file6") = 'test6' ]`,
map[string]string{
"test file1": "test1",
"test_file2": "test2",
"test file3": "test3",
"test dir/test_file4": "test4",
"test_dir/test_file5": "test5",
"test dir/test_file6": "test6",
})
defer ctx.Close()
if err != nil {
t.Fatal(err)
}
if _, err := buildImageFromContext(name, ctx, true); err != nil {
t.Fatal(err)
}
logDone("build - add file with whitespace")
}
func TestBuildCopyFileWithWhitespace(t *testing.T) {
name := "testcopyfilewithwhitespace"
defer deleteImages(name)
ctx, err := fakeContext(`FROM busybox
RUN mkdir "/test dir"
RUN mkdir "/test_dir"
COPY [ "test file1", "/test_file1" ]
COPY [ "test_file2", "/test file2" ]
COPY [ "test file3", "/test file3" ]
COPY [ "test dir/test_file4", "/test_dir/test_file4" ]
COPY [ "test_dir/test_file5", "/test dir/test_file5" ]
COPY [ "test dir/test_file6", "/test dir/test_file6" ]
RUN [ $(cat "/test_file1") = 'test1' ]
RUN [ $(cat "/test file2") = 'test2' ]
RUN [ $(cat "/test file3") = 'test3' ]
RUN [ $(cat "/test_dir/test_file4") = 'test4' ]
RUN [ $(cat "/test dir/test_file5") = 'test5' ]
RUN [ $(cat "/test dir/test_file6") = 'test6' ]`,
map[string]string{
"test file1": "test1",
"test_file2": "test2",
"test file3": "test3",
"test dir/test_file4": "test4",
"test_dir/test_file5": "test5",
"test dir/test_file6": "test6",
})
defer ctx.Close()
if err != nil {
t.Fatal(err)
}
if _, err := buildImageFromContext(name, ctx, true); err != nil {
t.Fatal(err)
}
logDone("build - copy file with whitespace")
}
func TestBuildAddMultipleFilesToFileWithWhitespace(t *testing.T) {
name := "testaddmultiplefilestofilewithwhitespace"
defer deleteImages(name)
ctx, err := fakeContext(`FROM busybox
ADD [ "test file1", "test file2", "test" ]
`,
map[string]string{
"test file1": "test1",
"test file2": "test2",
})
defer ctx.Close()
if err != nil {
t.Fatal(err)
}
expected := "When using ADD with more than one source file, the destination must be a directory and end with a /"
if _, err := buildImageFromContext(name, ctx, true); err == nil || !strings.Contains(err.Error(), expected) {
t.Fatalf("Wrong error: (should contain %q) got:\n%v", expected, err)
}
logDone("build - multiple add files to file with whitespace")
}
func TestBuildCopyMultipleFilesToFileWithWhitespace(t *testing.T) {
name := "testcopymultiplefilestofilewithwhitespace"
defer deleteImages(name)
ctx, err := fakeContext(`FROM busybox
COPY [ "test file1", "test file2", "test" ]
`,
map[string]string{
"test file1": "test1",
"test file2": "test2",
})
defer ctx.Close()
if err != nil {
t.Fatal(err)
}
expected := "When using COPY with more than one source file, the destination must be a directory and end with a /"
if _, err := buildImageFromContext(name, ctx, true); err == nil || !strings.Contains(err.Error(), expected) {
t.Fatalf("Wrong error: (should contain %q) got:\n%v", expected, err)
}
logDone("build - multiple copy files to file with whitespace")
}
func TestBuildCopyWildcard(t *testing.T) { func TestBuildCopyWildcard(t *testing.T) {
name := "testcopywildcard" name := "testcopywildcard"
defer deleteImages(name) defer deleteImages(name)