Browse code

Modified `docker export` to allow an --output flag

Copied code from CmdSave into CmdExport. This should work, not an expert in the API calls being made. But it does make more sense to have a consistent export/save flag.

Signed-off-by: Joseph Kern <joseph.a.kern@gmail.com>

checkpoint before edits on the export functions

Signed-off-by: Joseph Kern <joseph.a.kern@gmail.com>

Added an --output flag to docker export and created tests.

Signed-off-by: Joseph Kern <joseph.a.kern@gmail.com>

White space cleanup.

Signed-off-by: Joseph Kern <joseph.a.kern@gmail.com>

Docker-DCO-1.1-Signed-off-by: Joseph Kern <jkern@semafour.net> (github: jfrazelle)

checkpoint before edits on the export functions

Signed-off-by: Joseph Kern <joseph.a.kern@gmail.com>

White space cleanup.

Signed-off-by: Joseph Kern <joseph.a.kern@gmail.com>

Added text to reflect a new output option for the export command.

Signed-off-by: Joseph Kern <joseph.a.kern@gmail.com>

Whitespace clean up

Signed-off-by: Joseph Kern <joseph.a.kern@gmail.com>

Added man page documentation for the new --output flag in export

Signed-off-by: Joseph Kern <joseph.a.kern@gmail.com>

Joseph Kern authored on 2014/12/19 19:25:33
Showing 4 changed files
... ...
@@ -1872,14 +1872,40 @@ func (cli *DockerCli) CmdEvents(args ...string) error {
1872 1872
 }
1873 1873
 
1874 1874
 func (cli *DockerCli) CmdExport(args ...string) error {
1875
-	cmd := cli.Subcmd("export", "CONTAINER", "Export the contents of a filesystem as a tar archive to STDOUT", true)
1875
+	cmd := cli.Subcmd("export", "CONTAINER", "Export a filesystem as a tar archive (streamed to STDOUT by default)", true)
1876
+	outfile := cmd.String([]string{"o", "-output"}, "", "Write to a file, instead of STDOUT")
1876 1877
 	cmd.Require(flag.Exact, 1)
1877 1878
 
1878 1879
 	utils.ParseFlags(cmd, args, true)
1879 1880
 
1880
-	if err := cli.stream("GET", "/containers/"+cmd.Arg(0)+"/export", nil, cli.out, nil); err != nil {
1881
-		return err
1881
+	var (
1882
+		output io.Writer = cli.out
1883
+		err    error
1884
+	)
1885
+	if *outfile != "" {
1886
+		output, err = os.Create(*outfile)
1887
+		if err != nil {
1888
+			return err
1889
+		}
1890
+	} else if cli.isTerminalOut {
1891
+		return errors.New("Cowardly refusing to save to a terminal. Use the -o flag or redirect.")
1892
+	}
1893
+
1894
+	if len(cmd.Args()) == 1 {
1895
+		image := cmd.Arg(0)
1896
+		if err := cli.stream("GET", "/containers/"+image+"/export", nil, output, nil); err != nil {
1897
+			return err
1898
+		}
1899
+	} else {
1900
+		v := url.Values{}
1901
+		for _, arg := range cmd.Args() {
1902
+			v.Add("names", arg)
1903
+		}
1904
+		if err := cli.stream("GET", "/containers/get?"+v.Encode(), nil, output, nil); err != nil {
1905
+			return err
1906
+		}
1882 1907
 	}
1908
+
1883 1909
 	return nil
1884 1910
 }
1885 1911
 
... ...
@@ -14,17 +14,24 @@ Export the contents of a container's filesystem using the full or shortened
14 14
 container ID or container name. The output is exported to STDOUT and can be
15 15
 redirected to a tar file.
16 16
 
17
+Stream to a file instead of STDOUT by using **-o**.
18
+
17 19
 # OPTIONS
18 20
 **--help**
19 21
   Print usage statement
22
+**-o**, **--output**=""
23
+   Write to a file, instead of STDOUT
20 24
 
21 25
 # EXAMPLES
22 26
 Export the contents of the container called angry_bell to a tar file
23
-called test.tar:
27
+called angry_bell.tar:
24 28
 
25
-    # docker export angry_bell > test.tar
26
-    # ls *.tar
27
-    test.tar
29
+    # docker export angry_bell > angry_bell.tar
30
+    # docker export --output=angry_bell-latest.tar angry_bell
31
+    # ls -sh angry_bell.tar
32
+    321M angry_bell.tar
33
+    # ls -sh angry_bell-latest.tar
34
+    321M angry_bell-latest.tar
28 35
 
29 36
 # See also
30 37
 **docker-import(1)** to create an empty filesystem image
... ...
@@ -34,3 +41,4 @@ and import the contents of the tarball into it, then optionally tag it.
34 34
 April 2014, Originally compiled by William Henry (whenry at redhat dot com)
35 35
 based on docker.com source material and internal work.
36 36
 June 2014, updated by Sven Dowideit <SvenDowideit@home.org.au>
37
+Janurary 2015, updated by Joseph Kern (josephakern at gmail dot com)
... ...
@@ -1066,14 +1066,22 @@ This will create a new Bash session in the container `ubuntu_bash`.
1066 1066
 
1067 1067
 ## export
1068 1068
 
1069
-    Usage: docker export CONTAINER
1069
+    Usage: docker export [OPTIONS] CONTAINER
1070 1070
 
1071
-    Export the contents of a filesystem as a tar archive to STDOUT
1071
+    Export the contents of a filesystem to a tar archive (streamed to STDOUT by default)
1072 1072
 
1073
-For example:
1073
+      -o, --output=""    Write to a file, instead of STDOUT
1074
+
1075
+      Produces a tarred repository to the standard output stream.
1076
+
1077
+   For example:
1074 1078
 
1075 1079
     $ sudo docker export red_panda > latest.tar
1076 1080
 
1081
+   Or
1082
+
1083
+    $ sudo docker export --output="latest.tar" red_panda
1084
+
1077 1085
 > **Note:**
1078 1086
 > `docker export` does not export the contents of volumes associated with the
1079 1087
 > container. If a volume is mounted on top of an existing directory in the
... ...
@@ -1,6 +1,8 @@
1 1
 package main
2 2
 
3 3
 import (
4
+	"fmt"
5
+	"os"
4 6
 	"os/exec"
5 7
 	"strings"
6 8
 	"testing"
... ...
@@ -47,3 +49,49 @@ func TestExportContainerAndImportImage(t *testing.T) {
47 47
 	logDone("export - export a container")
48 48
 	logDone("import - import an image")
49 49
 }
50
+
51
+// Used to test output flag in the export command
52
+func TestExportContainerWithOutputAndImportImage(t *testing.T) {
53
+	runCmd := exec.Command(dockerBinary, "run", "-d", "busybox", "true")
54
+	out, _, err := runCommandWithOutput(runCmd)
55
+	if err != nil {
56
+		t.Fatal("failed to create a container", out, err)
57
+	}
58
+
59
+	cleanedContainerID := stripTrailingCharacters(out)
60
+
61
+	inspectCmd := exec.Command(dockerBinary, "inspect", cleanedContainerID)
62
+	out, _, err = runCommandWithOutput(inspectCmd)
63
+	if err != nil {
64
+		t.Fatalf("output should've been a container id: %s %s ", cleanedContainerID, err)
65
+	}
66
+
67
+	exportCmdTemplate := `%v export --output=/tmp/testexp.tar %v`
68
+	exportCmdFinal := fmt.Sprintf(exportCmdTemplate, dockerBinary, cleanedContainerID)
69
+	exportCmd := exec.Command(exportCmdFinal)
70
+	if out, _, err = runCommandWithOutput(exportCmd); err != nil {
71
+		t.Fatalf("failed to export container: %s, %v", out, err)
72
+	}
73
+
74
+	importCmdFinal := `cat /tmp/testexp.tar | docker import - repo/testexp:v1`
75
+	importCmd := exec.Command(importCmdFinal)
76
+	out, _, err = runCommandWithOutput(importCmd)
77
+	if err != nil {
78
+		t.Fatalf("failed to import image: %s, %v", out, err)
79
+	}
80
+
81
+	cleanedImageID := stripTrailingCharacters(out)
82
+
83
+	inspectCmd = exec.Command(dockerBinary, "inspect", cleanedImageID)
84
+	if out, _, err = runCommandWithOutput(inspectCmd); err != nil {
85
+		t.Fatalf("output should've been an image id: %s, %v", out, err)
86
+	}
87
+
88
+	deleteContainer(cleanedContainerID)
89
+	deleteImages("repo/testexp:v1")
90
+
91
+	os.Remove("/tmp/testexp.tar")
92
+
93
+	logDone("export - export a container with output flag")
94
+	logDone("import - import an image with output flag")
95
+}