|
@@ -13,6 +13,7 @@ import (
|
|
|
"os"
|
|
|
"os/exec"
|
|
|
"path"
|
|
|
+ "path/filepath"
|
|
|
"runtime"
|
|
|
"strconv"
|
|
|
"strings"
|
|
@@ -36,6 +37,10 @@ import (
|
|
|
"github.com/dotcloud/docker/utils/filters"
|
|
|
)
|
|
|
|
|
|
+const (
|
|
|
+ tarHeaderSize = 512
|
|
|
+)
|
|
|
+
|
|
|
func (cli *DockerCli) CmdHelp(args ...string) error {
|
|
|
if len(args) > 0 {
|
|
|
method, exists := cli.getMethod(args[0])
|
|
@@ -51,7 +56,7 @@ func (cli *DockerCli) CmdHelp(args ...string) error {
|
|
|
{"attach", "Attach to a running container"},
|
|
|
{"build", "Build an image from a Dockerfile"},
|
|
|
{"commit", "Create a new image from a container's changes"},
|
|
|
- {"cp", "Copy files/folders from the containers filesystem to the host path"},
|
|
|
+ {"cp", "Copy files/folders from a container's filesystem to the host path"},
|
|
|
{"diff", "Inspect changes on a container's filesystem"},
|
|
|
{"events", "Get real time events from the server"},
|
|
|
{"export", "Stream the contents of a container as a tar archive"},
|
|
@@ -62,25 +67,25 @@ func (cli *DockerCli) CmdHelp(args ...string) error {
|
|
|
{"inspect", "Return low-level information on a container"},
|
|
|
{"kill", "Kill a running container"},
|
|
|
{"load", "Load an image from a tar archive"},
|
|
|
- {"login", "Register or Login to the docker registry server"},
|
|
|
+ {"login", "Register or log in to the Docker registry server"},
|
|
|
{"logs", "Fetch the logs of a container"},
|
|
|
- {"port", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT"},
|
|
|
+ {"port", "Lookup the public-facing port that is NAT-ed to PRIVATE_PORT"},
|
|
|
{"pause", "Pause all processes within a container"},
|
|
|
{"ps", "List containers"},
|
|
|
- {"pull", "Pull an image or a repository from the docker registry server"},
|
|
|
- {"push", "Push an image or a repository to the docker registry server"},
|
|
|
+ {"pull", "Pull an image or a repository from a Docker registry server"},
|
|
|
+ {"push", "Push an image or a repository to a Docker registry server"},
|
|
|
{"restart", "Restart a running container"},
|
|
|
{"rm", "Remove one or more containers"},
|
|
|
{"rmi", "Remove one or more images"},
|
|
|
{"run", "Run a command in a new container"},
|
|
|
{"save", "Save an image to a tar archive"},
|
|
|
- {"search", "Search for an image in the docker index"},
|
|
|
+ {"search", "Search for an image on the Docker Hub"},
|
|
|
{"start", "Start a stopped container"},
|
|
|
{"stop", "Stop a running container"},
|
|
|
{"tag", "Tag an image into a repository"},
|
|
|
{"top", "Lookup the running processes of a container"},
|
|
|
{"unpause", "Unpause a paused container"},
|
|
|
- {"version", "Show the docker version information"},
|
|
|
+ {"version", "Show the Docker version information"},
|
|
|
{"wait", "Block until a container stops, then print its exit code"},
|
|
|
} {
|
|
|
help += fmt.Sprintf(" %-10.10s%s\n", command[0], command[1])
|
|
@@ -113,13 +118,22 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
|
|
_, err = exec.LookPath("git")
|
|
|
hasGit := err == nil
|
|
|
if cmd.Arg(0) == "-" {
|
|
|
- // As a special case, 'docker build -' will build from an empty context with the
|
|
|
- // contents of stdin as a Dockerfile
|
|
|
- dockerfile, err := ioutil.ReadAll(cli.in)
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
+ // As a special case, 'docker build -' will build from either an empty context with the
|
|
|
+ // contents of stdin as a Dockerfile, or a tar-ed context from stdin.
|
|
|
+ buf := bufio.NewReader(cli.in)
|
|
|
+ magic, err := buf.Peek(tarHeaderSize)
|
|
|
+ if err != nil && err != io.EOF {
|
|
|
+ return fmt.Errorf("failed to peek context header from STDIN: %v", err)
|
|
|
+ }
|
|
|
+ if !archive.IsArchive(magic) {
|
|
|
+ dockerfile, err := ioutil.ReadAll(buf)
|
|
|
+ if err != nil {
|
|
|
+ return fmt.Errorf("failed to read Dockerfile from STDIN: %v", err)
|
|
|
+ }
|
|
|
+ context, err = archive.Generate("Dockerfile", string(dockerfile))
|
|
|
+ } else {
|
|
|
+ context = ioutil.NopCloser(buf)
|
|
|
}
|
|
|
- context, err = archive.Generate("Dockerfile", string(dockerfile))
|
|
|
} else if utils.IsURL(cmd.Arg(0)) && (!utils.IsGIT(cmd.Arg(0)) || !hasGit) {
|
|
|
isRemote = true
|
|
|
} else {
|
|
@@ -150,7 +164,25 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
|
|
if err = utils.ValidateContextDirectory(root); err != nil {
|
|
|
return fmt.Errorf("Error checking context is accessible: '%s'. Please check permissions and try again.", err)
|
|
|
}
|
|
|
- context, err = archive.Tar(root, archive.Uncompressed)
|
|
|
+ options := &archive.TarOptions{
|
|
|
+ Compression: archive.Uncompressed,
|
|
|
+ }
|
|
|
+ if ignore, err := ioutil.ReadFile(path.Join(root, ".dockerignore")); err != nil && !os.IsNotExist(err) {
|
|
|
+ return fmt.Errorf("Error reading .dockerignore: '%s'", err)
|
|
|
+ } else if err == nil {
|
|
|
+ for _, pattern := range strings.Split(string(ignore), "\n") {
|
|
|
+ ok, err := filepath.Match(pattern, "Dockerfile")
|
|
|
+ if err != nil {
|
|
|
+ utils.Errorf("Bad .dockerignore pattern: '%s', error: %s", pattern, err)
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ if ok {
|
|
|
+ return fmt.Errorf("Dockerfile was excluded by .dockerignore pattern '%s'", pattern)
|
|
|
+ }
|
|
|
+ options.Excludes = append(options.Excludes, pattern)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ context, err = archive.TarWithOptions(root, options)
|
|
|
}
|
|
|
var body io.Reader
|
|
|
// Setup an upload progress bar
|
|
@@ -216,7 +248,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
|
|
|
|
|
// 'docker login': login / register a user to registry service.
|
|
|
func (cli *DockerCli) CmdLogin(args ...string) error {
|
|
|
- cmd := cli.Subcmd("login", "[OPTIONS] [SERVER]", "Register or Login to a docker registry server, if no server is specified \""+registry.IndexServerAddress()+"\" is the default.")
|
|
|
+ cmd := cli.Subcmd("login", "[OPTIONS] [SERVER]", "Register or log in to a Docker registry server, if no server is specified \""+registry.IndexServerAddress()+"\" is the default.")
|
|
|
|
|
|
var username, password, email string
|
|
|
|
|
@@ -342,7 +374,7 @@ func (cli *DockerCli) CmdWait(args ...string) error {
|
|
|
|
|
|
// 'docker version': show version information
|
|
|
func (cli *DockerCli) CmdVersion(args ...string) error {
|
|
|
- cmd := cli.Subcmd("version", "", "Show the docker version information.")
|
|
|
+ cmd := cli.Subcmd("version", "", "Show the Docker version information.")
|
|
|
if err := cmd.Parse(args); err != nil {
|
|
|
return nil
|
|
|
}
|
|
@@ -439,6 +471,9 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
|
|
|
if initPath := remoteInfo.Get("InitPath"); initPath != "" {
|
|
|
fmt.Fprintf(cli.out, "Init Path: %s\n", initPath)
|
|
|
}
|
|
|
+ if len(remoteInfo.GetList("Sockets")) != 0 {
|
|
|
+ fmt.Fprintf(cli.out, "Sockets: %v\n", remoteInfo.GetList("Sockets"))
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
if len(remoteInfo.GetList("IndexServerAddress")) != 0 {
|
|
@@ -462,8 +497,8 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
|
|
|
}
|
|
|
|
|
|
func (cli *DockerCli) CmdStop(args ...string) error {
|
|
|
- cmd := cli.Subcmd("stop", "[OPTIONS] CONTAINER [CONTAINER...]", "Stop a running container (Send SIGTERM, and then SIGKILL after grace period)")
|
|
|
- nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Number of seconds to wait for the container to stop before killing it.")
|
|
|
+ cmd := cli.Subcmd("stop", "[OPTIONS] CONTAINER [CONTAINER...]", "Stop a running container by sending SIGTERM and then SIGKILL after a grace period")
|
|
|
+ nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Number of seconds to wait for the container to stop before killing it. Default is 10 seconds.")
|
|
|
if err := cmd.Parse(args); err != nil {
|
|
|
return nil
|
|
|
}
|
|
@@ -490,7 +525,7 @@ func (cli *DockerCli) CmdStop(args ...string) error {
|
|
|
|
|
|
func (cli *DockerCli) CmdRestart(args ...string) error {
|
|
|
cmd := cli.Subcmd("restart", "[OPTIONS] CONTAINER [CONTAINER...]", "Restart a running container")
|
|
|
- nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Number of seconds to try to stop for before killing the container. Once killed it will then be restarted. Default=10")
|
|
|
+ nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Number of seconds to try to stop for before killing the container. Once killed it will then be restarted. Default is 10 seconds.")
|
|
|
if err := cmd.Parse(args); err != nil {
|
|
|
return nil
|
|
|
}
|
|
@@ -547,8 +582,8 @@ func (cli *DockerCli) CmdStart(args ...string) error {
|
|
|
tty bool
|
|
|
|
|
|
cmd = cli.Subcmd("start", "CONTAINER [CONTAINER...]", "Restart a stopped container")
|
|
|
- attach = cmd.Bool([]string{"a", "-attach"}, false, "Attach container's stdout/stderr and forward all signals to the process")
|
|
|
- openStdin = cmd.Bool([]string{"i", "-interactive"}, false, "Attach container's stdin")
|
|
|
+ attach = cmd.Bool([]string{"a", "-attach"}, false, "Attach container's STDOUT and STDERR and forward all signals to the process")
|
|
|
+ openStdin = cmd.Bool([]string{"i", "-interactive"}, false, "Attach container's STDIN")
|
|
|
)
|
|
|
|
|
|
if err := cmd.Parse(args); err != nil {
|
|
@@ -679,7 +714,7 @@ func (cli *DockerCli) CmdPause(args ...string) error {
|
|
|
}
|
|
|
|
|
|
func (cli *DockerCli) CmdInspect(args ...string) error {
|
|
|
- cmd := cli.Subcmd("inspect", "CONTAINER|IMAGE [CONTAINER|IMAGE...]", "Return low-level information on a container/image")
|
|
|
+ cmd := cli.Subcmd("inspect", "CONTAINER|IMAGE [CONTAINER|IMAGE...]", "Return low-level information on a container or image")
|
|
|
tmplStr := cmd.String([]string{"f", "#format", "-format"}, "", "Format the output using the given go template.")
|
|
|
if err := cmd.Parse(args); err != nil {
|
|
|
return nil
|
|
@@ -759,7 +794,7 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
|
|
|
}
|
|
|
|
|
|
func (cli *DockerCli) CmdTop(args ...string) error {
|
|
|
- cmd := cli.Subcmd("top", "CONTAINER [ps OPTIONS]", "Lookup the running processes of a container")
|
|
|
+ cmd := cli.Subcmd("top", "CONTAINER [ps OPTIONS]", "Display the running processes of a container")
|
|
|
if err := cmd.Parse(args); err != nil {
|
|
|
return nil
|
|
|
}
|
|
@@ -794,7 +829,7 @@ func (cli *DockerCli) CmdTop(args ...string) error {
|
|
|
}
|
|
|
|
|
|
func (cli *DockerCli) CmdPort(args ...string) error {
|
|
|
- cmd := cli.Subcmd("port", "CONTAINER PRIVATE_PORT", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT")
|
|
|
+ cmd := cli.Subcmd("port", "CONTAINER PRIVATE_PORT", "Lookup the public-facing port that is NAT-ed to PRIVATE_PORT")
|
|
|
if err := cmd.Parse(args); err != nil {
|
|
|
return nil
|
|
|
}
|
|
@@ -842,7 +877,7 @@ func (cli *DockerCli) CmdPort(args ...string) error {
|
|
|
func (cli *DockerCli) CmdRmi(args ...string) error {
|
|
|
var (
|
|
|
cmd = cli.Subcmd("rmi", "IMAGE [IMAGE...]", "Remove one or more images")
|
|
|
- force = cmd.Bool([]string{"f", "-force"}, false, "Force")
|
|
|
+ force = cmd.Bool([]string{"f", "-force"}, false, "Force removal of the image")
|
|
|
noprune = cmd.Bool([]string{"-no-prune"}, false, "Do not delete untagged parents")
|
|
|
)
|
|
|
if err := cmd.Parse(args); err != nil {
|
|
@@ -945,7 +980,7 @@ func (cli *DockerCli) CmdHistory(args ...string) error {
|
|
|
|
|
|
func (cli *DockerCli) CmdRm(args ...string) error {
|
|
|
cmd := cli.Subcmd("rm", "[OPTIONS] CONTAINER [CONTAINER...]", "Remove one or more containers")
|
|
|
- v := cmd.Bool([]string{"v", "-volumes"}, false, "Remove the volumes associated to the container")
|
|
|
+ v := cmd.Bool([]string{"v", "-volumes"}, false, "Remove the volumes associated with the container")
|
|
|
link := cmd.Bool([]string{"l", "#link", "-link"}, false, "Remove the specified link and not the underlying container")
|
|
|
force := cmd.Bool([]string{"f", "-force"}, false, "Force removal of running container")
|
|
|
|
|
@@ -982,7 +1017,7 @@ func (cli *DockerCli) CmdRm(args ...string) error {
|
|
|
|
|
|
// 'docker kill NAME' kills a running container
|
|
|
func (cli *DockerCli) CmdKill(args ...string) error {
|
|
|
- cmd := cli.Subcmd("kill", "[OPTIONS] CONTAINER [CONTAINER...]", "Kill a running container (send SIGKILL, or specified signal)")
|
|
|
+ cmd := cli.Subcmd("kill", "[OPTIONS] CONTAINER [CONTAINER...]", "Kill a running container using SIGKILL or a specified signal")
|
|
|
signal := cmd.String([]string{"s", "-signal"}, "KILL", "Signal to send to the container")
|
|
|
|
|
|
if err := cmd.Parse(args); err != nil {
|
|
@@ -1114,7 +1149,7 @@ func (cli *DockerCli) CmdPush(args ...string) error {
|
|
|
|
|
|
func (cli *DockerCli) CmdPull(args ...string) error {
|
|
|
cmd := cli.Subcmd("pull", "NAME[:TAG]", "Pull an image or a repository from the registry")
|
|
|
- tag := cmd.String([]string{"#t", "#-tag"}, "", "Download tagged image in repository")
|
|
|
+ tag := cmd.String([]string{"#t", "#-tag"}, "", "Download tagged image in a repository")
|
|
|
if err := cmd.Parse(args); err != nil {
|
|
|
return nil
|
|
|
}
|
|
@@ -1503,25 +1538,21 @@ func (cli *DockerCli) CmdPs(args ...string) error {
|
|
|
|
|
|
func (cli *DockerCli) CmdCommit(args ...string) error {
|
|
|
cmd := cli.Subcmd("commit", "[OPTIONS] CONTAINER [REPOSITORY[:TAG]]", "Create a new image from a container's changes")
|
|
|
+ flPause := cmd.Bool([]string{"p", "-pause"}, true, "Pause container during commit")
|
|
|
flComment := cmd.String([]string{"m", "-message"}, "", "Commit message")
|
|
|
- flAuthor := cmd.String([]string{"a", "#author", "-author"}, "", "Author (eg. \"John Hannibal Smith <hannibal@a-team.com>\"")
|
|
|
+ flAuthor := cmd.String([]string{"a", "#author", "-author"}, "", "Author (e.g., \"John Hannibal Smith <hannibal@a-team.com>\")")
|
|
|
// FIXME: --run is deprecated, it will be replaced with inline Dockerfile commands.
|
|
|
- flConfig := cmd.String([]string{"#run", "#-run"}, "", "this option is deprecated and will be removed in a future version in favor of inline Dockerfile-compatible commands")
|
|
|
+ flConfig := cmd.String([]string{"#run", "#-run"}, "", "This option is deprecated and will be removed in a future version in favor of inline Dockerfile-compatible commands")
|
|
|
if err := cmd.Parse(args); err != nil {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
- var name, repository, tag string
|
|
|
-
|
|
|
- if cmd.NArg() == 3 {
|
|
|
- fmt.Fprintf(cli.err, "[DEPRECATED] The format 'CONTAINER [REPOSITORY [TAG]]' as been deprecated. Please use CONTAINER [REPOSITORY[:TAG]]\n")
|
|
|
- name, repository, tag = cmd.Arg(0), cmd.Arg(1), cmd.Arg(2)
|
|
|
- } else {
|
|
|
- name = cmd.Arg(0)
|
|
|
+ var (
|
|
|
+ name = cmd.Arg(0)
|
|
|
repository, tag = utils.ParseRepositoryTag(cmd.Arg(1))
|
|
|
- }
|
|
|
+ )
|
|
|
|
|
|
- if name == "" {
|
|
|
+ if name == "" || len(cmd.Args()) > 2 {
|
|
|
cmd.Usage()
|
|
|
return nil
|
|
|
}
|
|
@@ -1539,6 +1570,11 @@ func (cli *DockerCli) CmdCommit(args ...string) error {
|
|
|
v.Set("tag", tag)
|
|
|
v.Set("comment", *flComment)
|
|
|
v.Set("author", *flAuthor)
|
|
|
+
|
|
|
+ if *flPause != true {
|
|
|
+ v.Set("pause", "0")
|
|
|
+ }
|
|
|
+
|
|
|
var (
|
|
|
config *runconfig.Config
|
|
|
env engine.Env
|
|
@@ -1657,6 +1693,7 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
|
|
|
cmd = cli.Subcmd("logs", "CONTAINER", "Fetch the logs of a container")
|
|
|
follow = cmd.Bool([]string{"f", "-follow"}, false, "Follow log output")
|
|
|
times = cmd.Bool([]string{"t", "-timestamps"}, false, "Show timestamps")
|
|
|
+ tail = cmd.String([]string{"-tail"}, "all", "Output the specified number of lines at the end of logs (defaults to all logs)")
|
|
|
)
|
|
|
|
|
|
if err := cmd.Parse(args); err != nil {
|
|
@@ -1690,6 +1727,7 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
|
|
|
if *follow {
|
|
|
v.Set("follow", "1")
|
|
|
}
|
|
|
+ v.Set("tail", *tail)
|
|
|
|
|
|
return cli.streamHelper("GET", "/containers/"+name+"/logs?"+v.Encode(), env.GetSubEnv("Config").GetBool("Tty"), nil, cli.out, cli.err, nil)
|
|
|
}
|
|
@@ -1697,8 +1735,8 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
|
|
|
func (cli *DockerCli) CmdAttach(args ...string) error {
|
|
|
var (
|
|
|
cmd = cli.Subcmd("attach", "[OPTIONS] CONTAINER", "Attach to a running container")
|
|
|
- noStdin = cmd.Bool([]string{"#nostdin", "-no-stdin"}, false, "Do not attach stdin")
|
|
|
- proxy = cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxify all received signal to the process (even in non-tty mode)")
|
|
|
+ noStdin = cmd.Bool([]string{"#nostdin", "-no-stdin"}, false, "Do not attach STDIN")
|
|
|
+ proxy = cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxify all received signals to the process (even in non-TTY mode). SIGCHLD is not proxied.")
|
|
|
)
|
|
|
|
|
|
if err := cmd.Parse(args); err != nil {
|
|
@@ -1769,11 +1807,11 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
|
|
|
}
|
|
|
|
|
|
func (cli *DockerCli) CmdSearch(args ...string) error {
|
|
|
- cmd := cli.Subcmd("search", "TERM", "Search the docker index for images")
|
|
|
+ cmd := cli.Subcmd("search", "TERM", "Search the Docker Hub for images")
|
|
|
noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
|
|
|
trusted := cmd.Bool([]string{"#t", "#trusted", "#-trusted"}, false, "Only show trusted builds")
|
|
|
automated := cmd.Bool([]string{"-automated"}, false, "Only show automated builds")
|
|
|
- stars := cmd.Int([]string{"s", "#stars", "-stars"}, 0, "Only displays with at least xxx stars")
|
|
|
+ stars := cmd.Int([]string{"s", "#stars", "-stars"}, 0, "Only displays with at least x stars")
|
|
|
if err := cmd.Parse(args); err != nil {
|
|
|
return nil
|
|
|
}
|
|
@@ -1829,21 +1867,15 @@ func (cli *DockerCli) CmdTag(args ...string) error {
|
|
|
if err := cmd.Parse(args); err != nil {
|
|
|
return nil
|
|
|
}
|
|
|
- if cmd.NArg() != 2 && cmd.NArg() != 3 {
|
|
|
+ if cmd.NArg() != 2 {
|
|
|
cmd.Usage()
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
- var repository, tag string
|
|
|
-
|
|
|
- if cmd.NArg() == 3 {
|
|
|
- fmt.Fprintf(cli.err, "[DEPRECATED] The format 'IMAGE [REPOSITORY [TAG]]' as been deprecated. Please use IMAGE [REGISTRYHOST/][USERNAME/]NAME[:TAG]]\n")
|
|
|
- repository, tag = cmd.Arg(1), cmd.Arg(2)
|
|
|
- } else {
|
|
|
+ var (
|
|
|
repository, tag = utils.ParseRepositoryTag(cmd.Arg(1))
|
|
|
- }
|
|
|
-
|
|
|
- v := url.Values{}
|
|
|
+ v = url.Values{}
|
|
|
+ )
|
|
|
|
|
|
//Check if the given image name can be resolved
|
|
|
if _, _, err := registry.ResolveRepositoryName(repository); err != nil {
|
|
@@ -1906,7 +1938,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
|
|
}
|
|
|
if cidFileInfo.Size() == 0 {
|
|
|
if err := os.Remove(hostConfig.ContainerIDFile); err != nil {
|
|
|
- fmt.Printf("failed to remove CID file '%s': %s \n", hostConfig.ContainerIDFile, err)
|
|
|
+ fmt.Printf("failed to remove Container ID file '%s': %s \n", hostConfig.ContainerIDFile, err)
|
|
|
}
|
|
|
}
|
|
|
}()
|
|
@@ -2156,7 +2188,7 @@ func (cli *DockerCli) CmdCp(args ...string) error {
|
|
|
}
|
|
|
|
|
|
func (cli *DockerCli) CmdSave(args ...string) error {
|
|
|
- cmd := cli.Subcmd("save", "IMAGE", "Save an image to a tar archive (streamed to stdout by default)")
|
|
|
+ cmd := cli.Subcmd("save", "IMAGE", "Save an image to a tar archive (streamed to STDOUT by default)")
|
|
|
outfile := cmd.String([]string{"o", "-output"}, "", "Write to an file, instead of STDOUT")
|
|
|
|
|
|
if err := cmd.Parse(args); err != nil {
|