Browse code

Merge pull request #9635 from duglin/Issue3936

Allow for relative paths on ADD/COPY

Alexander Morozov authored on 2014/12/18 23:58:50
Showing 6 changed files
... ...
@@ -172,15 +172,12 @@ func workdir(b *Builder, args []string, attributes map[string]bool, original str
172 172
 
173 173
 	workdir := args[0]
174 174
 
175
-	if workdir[0] == '/' {
176
-		b.Config.WorkingDir = workdir
177
-	} else {
178
-		if b.Config.WorkingDir == "" {
179
-			b.Config.WorkingDir = "/"
180
-		}
181
-		b.Config.WorkingDir = filepath.Join(b.Config.WorkingDir, workdir)
175
+	if !filepath.IsAbs(workdir) {
176
+		workdir = filepath.Join("/", b.Config.WorkingDir, workdir)
182 177
 	}
183 178
 
179
+	b.Config.WorkingDir = workdir
180
+
184 181
 	return b.commit("", b.Config.Cmd, fmt.Sprintf("WORKDIR %v", workdir))
185 182
 }
186 183
 
... ...
@@ -217,6 +217,18 @@ func calcCopyInfo(b *Builder, cmdName string, cInfos *[]*copyInfo, origPath stri
217 217
 	}
218 218
 	origPath = strings.TrimPrefix(origPath, "./")
219 219
 
220
+	// Twiddle the destPath when its a relative path - meaning, make it
221
+	// relative to the WORKINGDIR
222
+	if !filepath.IsAbs(destPath) {
223
+		hasSlash := strings.HasSuffix(destPath, "/")
224
+		destPath = filepath.Join("/", b.Config.WorkingDir, destPath)
225
+
226
+		// Make sure we preserve any trailing slash
227
+		if hasSlash {
228
+			destPath += "/"
229
+		}
230
+	}
231
+
220 232
 	// In the remote/URL case, download it and gen its hashcode
221 233
 	if urlutil.IsURL(origPath) {
222 234
 		if !allowRemote {
... ...
@@ -132,12 +132,22 @@ or
132 132
 
133 133
 **ADD**
134 134
  --**ADD <src>... <dest>** The ADD instruction copies new files, directories
135
- or remote file URLs to the filesystem of the container at path <dest>.  
135
+ or remote file URLs to the filesystem of the container at path <dest>.
136 136
  Mutliple <src> resources may be specified but if they are files or directories
137
- then they must be relative to the source directory that is being built 
138
- (the context of the build).  <dest> is the absolute path to
139
- which the source is copied inside the target container.  All new files and
140
- directories are created with mode 0755, with uid and gid 0.
137
+ then they must be relative to the source directory that is being built
138
+ (the context of the build). The <dest> is the absolute path, or path relative
139
+ to `WORKDIR`, into which the source is copied inside the target container.
140
+ All new files and directories are created with mode 0755 and with the uid 
141
+ and gid of 0.
142
+
143
+**COPY**
144
+ --**COPY <src> <dest>** The COPY instruction copies new files from <src> and
145
+ adds them to the filesystem of the container at path <dest>. The <src> must be
146
+ the path to a file or directory relative to the source directory that is
147
+ being built (the context of the build) or a remote file URL. The `<dest>` is an
148
+ absolute path, or a path relative to `WORKDIR`, into which the source will
149
+ be copied inside the target container. All new files and directories are
150
+ created with mode 0755 and with the uid and gid of 0.
141 151
 
142 152
 **ENTRYPOINT**
143 153
  --**ENTRYPOINT** has two forms: ENTRYPOINT ["executable", "param1", "param2"]
... ...
@@ -65,10 +65,12 @@ directory called httpd may be used to store Dockerfiles for Apache web
65 65
 server images.
66 66
 
67 67
 It is also a good practice to add the files required for the image to the
68
-sub-directory. These files will then be specified with the `ADD` instruction
69
-in the Dockerfile. Note: If you include a tar file (a good practice!), then
70
-Docker will automatically extract the contents of the tar file
71
-specified within the `ADD` instruction into the specified target.
68
+sub-directory. These files will then be specified with the `COPY` or `ADD`
69
+instructions in the `Dockerfile`.
70
+
71
+Note: If you include a tar file (a good practice), then Docker will
72
+automatically extract the contents of the tar file specified within the `ADD`
73
+instruction into the specified target.
72 74
 
73 75
 ## Building an image and naming that image
74 76
 
... ...
@@ -397,8 +397,10 @@ For most command line uses this should act as expected, for example:
397 397
     ADD hom* /mydir/        # adds all files starting with "hom"
398 398
     ADD hom?.txt /mydir/    # ? is replaced with any single character
399 399
 
400
-The `<dest>` is the absolute path to which the source will be copied inside the
401
-destination container.
400
+The `<dest>` is an absolute path, or a path relative to `WORKDIR`, into which
401
+the source will be copied inside the destination container.
402
+
403
+    ADD test aDir/          # adds "test" to `WORKDIR`/aDir/
402 404
 
403 405
 All new files and directories are created with a UID and GID of 0.
404 406
 
... ...
@@ -494,8 +496,10 @@ For most command line uses this should act as expected, for example:
494 494
     COPY hom* /mydir/        # adds all files starting with "hom"
495 495
     COPY hom?.txt /mydir/    # ? is replaced with any single character
496 496
 
497
-The `<dest>` is the absolute path to which the source will be copied inside the
498
-destination container.
497
+The `<dest>` is an absolute path, or a path relative to `WORKDIR`, into which
498
+the source will be copied inside the destination container.
499
+
500
+    COPY test aDir/          # adds "test" to `WORKDIR`/aDir/
499 501
 
500 502
 All new files and directories are created with a UID and GID of 0.
501 503
 
... ...
@@ -1829,6 +1829,46 @@ func TestBuildWorkdirWithEnvVariables(t *testing.T) {
1829 1829
 	logDone("build - workdir with env variables")
1830 1830
 }
1831 1831
 
1832
+func TestBuildRelativeCopy(t *testing.T) {
1833
+	name := "testbuildrelativecopy"
1834
+	defer deleteImages(name)
1835
+	dockerfile := `
1836
+		FROM busybox
1837
+			WORKDIR /test1
1838
+			WORKDIR test2
1839
+			RUN [ "$PWD" = '/test1/test2' ]
1840
+			COPY foo ./
1841
+			RUN [ "$(cat /test1/test2/foo)" = 'hello' ]
1842
+			ADD foo ./bar/baz
1843
+			RUN [ "$(cat /test1/test2/bar/baz)" = 'hello' ]
1844
+			COPY foo ./bar/baz2
1845
+			RUN [ "$(cat /test1/test2/bar/baz2)" = 'hello' ]
1846
+			WORKDIR ..
1847
+			COPY foo ./
1848
+			RUN [ "$(cat /test1/foo)" = 'hello' ]
1849
+			COPY foo /test3/
1850
+			RUN [ "$(cat /test3/foo)" = 'hello' ]
1851
+			WORKDIR /test4
1852
+			COPY . .
1853
+			RUN [ "$(cat /test4/foo)" = 'hello' ]
1854
+			WORKDIR /test5/test6
1855
+			COPY foo ../
1856
+			RUN [ "$(cat /test5/foo)" = 'hello' ]
1857
+			`
1858
+	ctx, err := fakeContext(dockerfile, map[string]string{
1859
+		"foo": "hello",
1860
+	})
1861
+	defer ctx.Close()
1862
+	if err != nil {
1863
+		t.Fatal(err)
1864
+	}
1865
+	_, err = buildImageFromContext(name, ctx, false)
1866
+	if err != nil {
1867
+		t.Fatal(err)
1868
+	}
1869
+	logDone("build - relative copy/add")
1870
+}
1871
+
1832 1872
 func TestBuildEnv(t *testing.T) {
1833 1873
 	name := "testbuildenv"
1834 1874
 	expected := "[PATH=/test:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin PORT=2375]"