fix #11094 - Allow 'docker import' to load from local 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: |