Browse code

Merge pull request #11907 from gcuisinier/11094-allow-import-from-file2

fix #11094 - Allow 'docker import' to load from local files

Sebastiaan van Stijn authored on 2015/06/21 04:17:58
Showing 4 changed files
... ...
@@ -4,20 +4,22 @@ import (
4 4
 	"fmt"
5 5
 	"io"
6 6
 	"net/url"
7
+	"os"
7 8
 
8 9
 	"github.com/docker/docker/opts"
9 10
 	flag "github.com/docker/docker/pkg/mflag"
10 11
 	"github.com/docker/docker/pkg/parsers"
12
+	"github.com/docker/docker/pkg/urlutil"
11 13
 	"github.com/docker/docker/registry"
12 14
 )
13 15
 
14 16
 // CmdImport creates an empty filesystem image, imports the contents of the tarball into the image, and optionally tags the image.
15 17
 //
16
-// The URL argument is the address of a tarball (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz) file. If the URL is '-', then the tar file is read from STDIN.
18
+// The URL argument is the address of a tarball (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz) file or a path to local file relative to docker client. If the URL is '-', then the tar file is read from STDIN.
17 19
 //
18
-// Usage: docker import [OPTIONS] URL [REPOSITORY[:TAG]]
20
+// Usage: docker import [OPTIONS] file|URL|- [REPOSITORY[:TAG]]
19 21
 func (cli *DockerCli) CmdImport(args ...string) error {
20
-	cmd := cli.Subcmd("import", []string{"URL|- [REPOSITORY[:TAG]]"}, "Create an empty filesystem image and import the contents of the\ntarball (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz) into it, then\noptionally tag it.", true)
22
+	cmd := cli.Subcmd("import", []string{"file|URL|- [REPOSITORY[:TAG]]"}, "Create an empty filesystem image and import the contents of the\ntarball (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz) into it, then\noptionally tag it.", true)
21 23
 	flChanges := opts.NewListOpts(nil)
22 24
 	cmd.Var(&flChanges, []string{"c", "-change"}, "Apply Dockerfile instruction to the created image")
23 25
 	cmd.Require(flag.Min, 1)
... ...
@@ -36,7 +38,7 @@ func (cli *DockerCli) CmdImport(args ...string) error {
36 36
 		v.Add("changes", change)
37 37
 	}
38 38
 	if cmd.NArg() == 3 {
39
-		fmt.Fprintf(cli.err, "[DEPRECATED] The format 'URL|- [REPOSITORY [TAG]]' has been deprecated. Please use URL|- [REPOSITORY[:TAG]]\n")
39
+		fmt.Fprintf(cli.err, "[DEPRECATED] The format 'file|URL|- [REPOSITORY [TAG]]' has been deprecated. Please use file|URL|- [REPOSITORY[:TAG]]\n")
40 40
 		v.Set("tag", cmd.Arg(2))
41 41
 	}
42 42
 
... ...
@@ -52,6 +54,15 @@ func (cli *DockerCli) CmdImport(args ...string) error {
52 52
 
53 53
 	if src == "-" {
54 54
 		in = cli.in
55
+	} else if !urlutil.IsURL(src) {
56
+		v.Set("fromSrc", "-")
57
+		file, err := os.Open(src)
58
+		if err != nil {
59
+			return err
60
+		}
61
+		defer file.Close()
62
+		in = file
63
+
55 64
 	}
56 65
 
57 66
 	sopts := &streamOpts{
... ...
@@ -1457,7 +1457,7 @@ NOTE: Docker will warn you if any containers exist that are using these untagged
1457 1457
 
1458 1458
 ## import
1459 1459
 
1460
-    Usage: docker import URL|- [REPOSITORY[:TAG]]
1460
+    Usage: docker import file|URL|- [REPOSITORY[:TAG]]
1461 1461
 
1462 1462
     Create an empty filesystem image and import the contents of the
1463 1463
 	tarball (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz) into it, then
... ...
@@ -1465,10 +1465,14 @@ NOTE: Docker will warn you if any containers exist that are using these untagged
1465 1465
 
1466 1466
       -c, --change=[]     Apply specified Dockerfile instructions while importing the image
1467 1467
 
1468
-URLs must start with `http` and point to a single file archive (.tar,
1469
-.tar.gz, .tgz, .bzip, .tar.xz, or .txz) containing a root filesystem. If
1470
-you would like to import from a local directory or archive, you can use
1471
-the `-` parameter to take the data from `STDIN`.
1468
+You can specify a `URL` or `-` (dash) to take data directly from `STDIN`. The
1469
+`URL` can point to an archive (.tar, .tar.gz, .tgz, .bzip, .tar.xz, or .txz)
1470
+containing a fileystem or to an individual file on the Docker host.  If you
1471
+specify an archive, Docker untars it in the container relative to the `/`
1472
+(root). If you specify an individual file, you must specify the full path within
1473
+the host. To import from a remote location, specify a `URI` that begins with the
1474
+`http://` or `https://` protocol.
1475
+
1472 1476
 
1473 1477
 The `--change` option will apply `Dockerfile` instructions to the image
1474 1478
 that is created.
... ...
@@ -1477,6 +1481,10 @@ Supported `Dockerfile` instructions:
1477 1477
 
1478 1478
 #### Examples
1479 1479
 
1480
+**Import from a local file archive:**
1481
+
1482
+    $ sudo docker import /local/path/to/exampleimage.tgz exampleimagedir
1483
+
1480 1484
 **Import from a remote location:**
1481 1485
 
1482 1486
 This will create a new untagged image.
... ...
@@ -1,6 +1,9 @@
1 1
 package main
2 2
 
3 3
 import (
4
+	"bufio"
5
+	"io/ioutil"
6
+	"os"
4 7
 	"os/exec"
5 8
 	"strings"
6 9
 
... ...
@@ -50,3 +53,56 @@ func (s *DockerSuite) TestImportBadURL(c *check.C) {
50 50
 		c.Fatalf("expected an error msg but didn't get one:\n%s", out)
51 51
 	}
52 52
 }
53
+
54
+func (s *DockerSuite) TestImportFile(c *check.C) {
55
+	runCmd := exec.Command(dockerBinary, "run", "--name", "test-import", "busybox", "true")
56
+	out, _, err := runCommandWithOutput(runCmd)
57
+	if err != nil {
58
+		c.Fatal("failed to create a container", out, err)
59
+	}
60
+
61
+	temporaryFile, err := ioutil.TempFile("", "exportImportTest")
62
+	if err != nil {
63
+		c.Fatal("failed to create temporary file", "", err)
64
+	}
65
+	defer os.Remove(temporaryFile.Name())
66
+
67
+	runCmd = exec.Command(dockerBinary, "export", "test-import")
68
+	runCmd.Stdout = bufio.NewWriter(temporaryFile)
69
+
70
+	_, err = runCommand(runCmd)
71
+	if err != nil {
72
+		c.Fatal("failed to export a container", out, err)
73
+	}
74
+
75
+	runCmd = exec.Command(dockerBinary, "import", temporaryFile.Name())
76
+	out, _, err = runCommandWithOutput(runCmd)
77
+	if err != nil {
78
+		c.Fatal("failed to import a container", out, err)
79
+	}
80
+
81
+	if n := strings.Count(out, "\n"); n != 1 {
82
+		c.Fatalf("display is messed up: %d '\\n' instead of 1:\n%s", n, out)
83
+	}
84
+	image := strings.TrimSpace(out)
85
+
86
+	runCmd = exec.Command(dockerBinary, "run", "--rm", image, "true")
87
+	out, _, err = runCommandWithOutput(runCmd)
88
+	if err != nil {
89
+		c.Fatal("failed to create a container", out, err)
90
+	}
91
+
92
+	if out != "" {
93
+		c.Fatalf("command output should've been nothing, was %q", out)
94
+	}
95
+
96
+}
97
+
98
+func (s *DockerSuite) TestImportFileNonExistentFile(c *check.C) {
99
+	runCmd := exec.Command(dockerBinary, "import", "example.com/myImage.tar")
100
+	_, exitCode, err := runCommandWithOutput(runCmd)
101
+	if exitCode == 0 || err == nil {
102
+		c.Fatalf("import non-existing file must failed")
103
+	}
104
+
105
+}
... ...
@@ -8,7 +8,7 @@ docker-import - Create an empty filesystem image and import the contents of the
8 8
 **docker import**
9 9
 [**-c**|**--change**[= []**]]
10 10
 [**--help**]
11
-URL|- [REPOSITORY[:TAG]]
11
+file|URL|- [REPOSITORY[:TAG]]
12 12
 
13 13
 # OPTIONS
14 14
 **-c**, **--change**=[]
... ...
@@ -35,6 +35,11 @@ Import to docker via pipe and stdin:
35 35
 
36 36
     # cat exampleimage.tgz | docker import - example/imagelocal
37 37
 
38
+Import to a Docker image from a local file.
39
+
40
+    # docker import /path/to/exampleimage.tgz 
41
+
42
+
38 43
 ## Import from a local file and tag
39 44
 
40 45
 Import to docker via pipe and stdin: