Docker-DCO-1.1-Signed-off-by: Johan Euphrosine <proppy@google.com> (github: proppy)
Johan Euphrosine authored on 2014/05/10 07:26:41... | ... |
@@ -36,6 +36,10 @@ import ( |
36 | 36 |
"github.com/dotcloud/docker/utils/filters" |
37 | 37 |
) |
38 | 38 |
|
39 |
+const ( |
|
40 |
+ tarHeaderSize = 512 |
|
41 |
+) |
|
42 |
+ |
|
39 | 43 |
func (cli *DockerCli) CmdHelp(args ...string) error { |
40 | 44 |
if len(args) > 0 { |
41 | 45 |
method, exists := cli.getMethod(args[0]) |
... | ... |
@@ -113,13 +117,22 @@ func (cli *DockerCli) CmdBuild(args ...string) error { |
113 | 113 |
_, err = exec.LookPath("git") |
114 | 114 |
hasGit := err == nil |
115 | 115 |
if cmd.Arg(0) == "-" { |
116 |
- // As a special case, 'docker build -' will build from an empty context with the |
|
117 |
- // contents of stdin as a Dockerfile |
|
118 |
- dockerfile, err := ioutil.ReadAll(cli.in) |
|
119 |
- if err != nil { |
|
120 |
- return err |
|
116 |
+ // As a special case, 'docker build -' will build from either an empty context with the |
|
117 |
+ // contents of stdin as a Dockerfile, or a tar-ed context from stdin. |
|
118 |
+ buf := bufio.NewReader(cli.in) |
|
119 |
+ magic, err := buf.Peek(tarHeaderSize) |
|
120 |
+ if err != nil && err != io.EOF { |
|
121 |
+ return fmt.Errorf("failed to peek context header from stdin: %v", err) |
|
122 |
+ } |
|
123 |
+ if !archive.IsArchive(magic) { |
|
124 |
+ dockerfile, err := ioutil.ReadAll(buf) |
|
125 |
+ if err != nil { |
|
126 |
+ return fmt.Errorf("failed to read Dockerfile from stdin: %v", err) |
|
127 |
+ } |
|
128 |
+ context, err = archive.Generate("Dockerfile", string(dockerfile)) |
|
129 |
+ } else { |
|
130 |
+ context = ioutil.NopCloser(buf) |
|
121 | 131 |
} |
122 |
- context, err = archive.Generate("Dockerfile", string(dockerfile)) |
|
123 | 132 |
} else if utils.IsURL(cmd.Arg(0)) && (!utils.IsGIT(cmd.Arg(0)) || !hasGit) { |
124 | 133 |
isRemote = true |
125 | 134 |
} else { |
... | ... |
@@ -43,6 +43,16 @@ const ( |
43 | 43 |
Xz |
44 | 44 |
) |
45 | 45 |
|
46 |
+func IsArchive(header []byte) bool { |
|
47 |
+ compression := DetectCompression(header) |
|
48 |
+ if compression != Uncompressed { |
|
49 |
+ return true |
|
50 |
+ } |
|
51 |
+ r := tar.NewReader(bytes.NewBuffer(header)) |
|
52 |
+ _, err := r.Next() |
|
53 |
+ return err == nil |
|
54 |
+} |
|
55 |
+ |
|
46 | 56 |
func DetectCompression(source []byte) Compression { |
47 | 57 |
for compression, m := range map[Compression][]byte{ |
48 | 58 |
Bzip2: {0x42, 0x5A, 0x68}, |
... | ... |
@@ -238,10 +238,15 @@ All new files and directories are created with a uid and gid of 0. |
238 | 238 |
In the case where `<src>` is a remote file URL, the destination will have permissions 600. |
239 | 239 |
|
240 | 240 |
> **Note**: |
241 |
-> If you build using STDIN (`docker build - < somefile`), there is no |
|
242 |
-> build context, so the Dockerfile can only contain a URL based ADD |
|
243 |
-> statement. |
|
244 |
- |
|
241 |
+> If you build by passing a Dockerfile through STDIN (`docker build - < somefile`), |
|
242 |
+> there is no build context, so the Dockerfile can only contain a URL |
|
243 |
+> based ADD statement. |
|
244 |
+ |
|
245 |
+> You can also pass a compressed archive through STDIN: |
|
246 |
+> (`docker build - < archive.tar.gz`), the `Dockerfile` at the root of |
|
247 |
+> the archive and the rest of the archive will get used at the context |
|
248 |
+> of the build. |
|
249 |
+> |
|
245 | 250 |
> **Note**: |
246 | 251 |
> If your URL files are protected using authentication, you will need to |
247 | 252 |
> use `RUN wget` , `RUN curl` |
... | ... |
@@ -361,7 +366,7 @@ execute in `/bin/sh -c`: |
361 | 361 |
FROM ubuntu |
362 | 362 |
ENTRYPOINT wc -l - |
363 | 363 |
|
364 |
-For example, that Dockerfile's image will *always* take stdin as input |
|
364 |
+For example, that Dockerfile's image will *always* take STDIN as input |
|
365 | 365 |
("-") and print the number of lines ("-l"). If you wanted to make this |
366 | 366 |
optional but default, you could use a CMD: |
367 | 367 |
|
... | ... |
@@ -274,12 +274,17 @@ and the tag will be `2.0` |
274 | 274 |
|
275 | 275 |
$ sudo docker build - < Dockerfile |
276 | 276 |
|
277 |
-This will read a Dockerfile from *stdin* without |
|
277 |
+This will read a Dockerfile from STDIN without |
|
278 | 278 |
context. Due to the lack of a context, no contents of any local |
279 | 279 |
directory will be sent to the `docker` daemon. Since |
280 | 280 |
there is no context, a Dockerfile `ADD` |
281 | 281 |
only works if it refers to a remote URL. |
282 | 282 |
|
283 |
+ $ sudo docker build - < context.tar.gz |
|
284 |
+ |
|
285 |
+This will build an image for a compressed context read from STDIN. |
|
286 |
+Supported formats are: bzip2, gzip and xz. |
|
287 |
+ |
|
283 | 288 |
$ sudo docker build github.com/creack/docker-firefox |
284 | 289 |
|
285 | 290 |
This will clone the GitHub repository and use the cloned repository as |
... | ... |
@@ -531,7 +536,7 @@ URLs must start with `http` and point to a single |
531 | 531 |
file archive (.tar, .tar.gz, .tgz, .bzip, .tar.xz, or .txz) containing a |
532 | 532 |
root filesystem. If you would like to import from a local directory or |
533 | 533 |
archive, you can use the `-` parameter to take the |
534 |
-data from *stdin*. |
|
534 |
+data from STDIN. |
|
535 | 535 |
|
536 | 536 |
### Examples |
537 | 537 |
|
... | ... |
@@ -543,7 +548,7 @@ This will create a new untagged image. |
543 | 543 |
|
544 | 544 |
**Import from a local file:** |
545 | 545 |
|
546 |
-Import to docker via pipe and *stdin*. |
|
546 |
+Import to docker via pipe and STDIN. |
|
547 | 547 |
|
548 | 548 |
$ cat exampleimage.tgz | sudo docker import - exampleimagelocal:new |
549 | 549 |
|
... | ... |
@@ -9,6 +9,8 @@ import ( |
9 | 9 |
"strings" |
10 | 10 |
"testing" |
11 | 11 |
"time" |
12 |
+ |
|
13 |
+ "github.com/dotcloud/docker/archive" |
|
12 | 14 |
) |
13 | 15 |
|
14 | 16 |
func TestBuildCacheADD(t *testing.T) { |
... | ... |
@@ -1130,6 +1132,50 @@ func TestBuildADDLocalAndRemoteFilesWithCache(t *testing.T) { |
1130 | 1130 |
logDone("build - add local and remote file with cache") |
1131 | 1131 |
} |
1132 | 1132 |
|
1133 |
+func testContextTar(t *testing.T, compression archive.Compression) { |
|
1134 |
+ contextDirectory := filepath.Join(workingDirectory, "build_tests", "TestContextTar") |
|
1135 |
+ context, err := archive.Tar(contextDirectory, compression) |
|
1136 |
+ |
|
1137 |
+ if err != nil { |
|
1138 |
+ t.Fatalf("failed to build context tar: %v", err) |
|
1139 |
+ } |
|
1140 |
+ buildCmd := exec.Command(dockerBinary, "build", "-t", "contexttar", "-") |
|
1141 |
+ buildCmd.Stdin = context |
|
1142 |
+ |
|
1143 |
+ out, exitCode, err := runCommandWithOutput(buildCmd) |
|
1144 |
+ if err != nil || exitCode != 0 { |
|
1145 |
+ t.Fatalf("build failed to complete: %v %v", out, err) |
|
1146 |
+ } |
|
1147 |
+ deleteImages("contexttar") |
|
1148 |
+ logDone(fmt.Sprintf("build - build an image with a context tar, compression: %v", compression)) |
|
1149 |
+} |
|
1150 |
+ |
|
1151 |
+func TestContextTarGzip(t *testing.T) { |
|
1152 |
+ testContextTar(t, archive.Gzip) |
|
1153 |
+} |
|
1154 |
+ |
|
1155 |
+func TestContextTarNoCompression(t *testing.T) { |
|
1156 |
+ testContextTar(t, archive.Uncompressed) |
|
1157 |
+} |
|
1158 |
+ |
|
1159 |
+func TestNoContext(t *testing.T) { |
|
1160 |
+ buildCmd := exec.Command(dockerBinary, "build", "-t", "nocontext", "-") |
|
1161 |
+ buildCmd.Stdin = strings.NewReader("FROM busybox\nCMD echo ok\n") |
|
1162 |
+ |
|
1163 |
+ out, exitCode, err := runCommandWithOutput(buildCmd) |
|
1164 |
+ if err != nil || exitCode != 0 { |
|
1165 |
+ t.Fatalf("build failed to complete: %v %v", out, err) |
|
1166 |
+ } |
|
1167 |
+ |
|
1168 |
+ out, exitCode, err = cmd(t, "run", "nocontext") |
|
1169 |
+ if out != "ok\n" { |
|
1170 |
+ t.Fatalf("run produced invalid output: %q, expected %q", out, "ok") |
|
1171 |
+ } |
|
1172 |
+ |
|
1173 |
+ deleteImages("nocontext") |
|
1174 |
+ logDone("build - build an image with no context") |
|
1175 |
+} |
|
1176 |
+ |
|
1133 | 1177 |
// TODO: TestCaching |
1134 | 1178 |
func TestBuildADDLocalAndRemoteFilesWithoutCache(t *testing.T) { |
1135 | 1179 |
name := "testbuildaddlocalandremotefilewithoutcache" |