summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVincent Demeester <vincent@sbr.pm>2016-06-14 17:16:59 +0200
committerVincent Demeester <vincent@sbr.pm>2016-06-14 17:16:59 +0200
commit2e6db51de7dc293785520df81c6bb54aa96c84ca (patch)
treeca922eecc630f9baa5e5c18a3f5f438bb3af0625
parentc913dd5f575c877b93a812c8f57dcc212d566b87 (diff)
downloaddocker-2e6db51de7dc293785520df81c6bb54aa96c84ca.tar.gz
Migrate cp command to cobra
Signed-off-by: Vincent Demeester <vincent@sbr.pm>
-rw-r--r--api/client/commands.go1
-rw-r--r--api/client/container/cp.go (renamed from api/client/cp.go)159
-rw-r--r--cli/cobraadaptor/adaptor.go1
-rw-r--r--cli/usage.go1
4 files changed, 83 insertions, 79 deletions
diff --git a/api/client/commands.go b/api/client/commands.go
index 98c7ba4256..ed328b8b57 100644
--- a/api/client/commands.go
+++ b/api/client/commands.go
@@ -3,7 +3,6 @@ package client
// Command returns a cli command handler if one exists
func (cli *DockerCli) Command(name string) func(...string) error {
return map[string]func(...string) error{
- "cp": cli.CmdCp,
"exec": cli.CmdExec,
"info": cli.CmdInfo,
"inspect": cli.CmdInspect,
diff --git a/api/client/cp.go b/api/client/container/cp.go
index ae3c7149f3..6870ef3c18 100644
--- a/api/client/cp.go
+++ b/api/client/container/cp.go
@@ -1,4 +1,4 @@
-package client
+package container
import (
"fmt"
@@ -9,13 +9,20 @@ import (
"golang.org/x/net/context"
- Cli "github.com/docker/docker/cli"
+ "github.com/docker/docker/api/client"
+ "github.com/docker/docker/cli"
"github.com/docker/docker/pkg/archive"
- flag "github.com/docker/docker/pkg/mflag"
"github.com/docker/docker/pkg/system"
"github.com/docker/engine-api/types"
+ "github.com/spf13/cobra"
)
+type copyOptions struct {
+ source string
+ destination string
+ followLink bool
+}
+
type copyDirection int
const (
@@ -28,46 +35,44 @@ type cpConfig struct {
followLink bool
}
-// CmdCp copies files/folders to or from a path in a container.
-//
-// When copying from a container, if DEST_PATH is '-' the data is written as a
-// tar archive file to STDOUT.
-//
-// When copying to a container, if SRC_PATH is '-' the data is read as a tar
-// archive file from STDIN, and the destination CONTAINER:DEST_PATH, must specify
-// a directory.
-//
-// Usage:
-// docker cp CONTAINER:SRC_PATH DEST_PATH|-
-// docker cp SRC_PATH|- CONTAINER:DEST_PATH
-func (cli *DockerCli) CmdCp(args ...string) error {
- cmd := Cli.Subcmd(
- "cp",
- []string{"CONTAINER:SRC_PATH DEST_PATH|-", "SRC_PATH|- CONTAINER:DEST_PATH"},
- strings.Join([]string{
- Cli.DockerCommands["cp"].Description,
+// NewCopyCommand creates a new `docker cp` command
+func NewCopyCommand(dockerCli *client.DockerCli) *cobra.Command {
+ var opts copyOptions
+
+ cmd := &cobra.Command{
+ Use: `cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|-
+ docker cp [OPTIONS] SRC_PATH|- CONTAINER:DEST_PATH`,
+ Short: "Copy files/folders between a container and the local filesystem",
+ Long: strings.Join([]string{
"\nUse '-' as the source to read a tar archive from stdin\n",
"and extract it to a directory destination in a container.\n",
"Use '-' as the destination to stream a tar archive of a\n",
"container source to stdout.",
}, ""),
- true,
- )
+ Args: cli.ExactArgs(2),
+ RunE: func(cmd *cobra.Command, args []string) error {
+ if args[0] == "" {
+ return fmt.Errorf("source can not be empty")
+ }
+ if args[1] == "" {
+ return fmt.Errorf("destination can not be empty")
+ }
+ opts.source = args[0]
+ opts.destination = args[1]
+ return runCopy(dockerCli, opts)
+ },
+ }
- followLink := cmd.Bool([]string{"L", "-follow-link"}, false, "Always follow symbol link in SRC_PATH")
+ flags := cmd.Flags()
- cmd.Require(flag.Exact, 2)
- cmd.ParseFlags(args, true)
+ flags.BoolVarP(&opts.followLink, "follow-link", "L", false, "Always follow symbol link in SRC_PATH")
- if cmd.Arg(0) == "" {
- return fmt.Errorf("source can not be empty")
- }
- if cmd.Arg(1) == "" {
- return fmt.Errorf("destination can not be empty")
- }
+ return cmd
+}
- srcContainer, srcPath := splitCpArg(cmd.Arg(0))
- dstContainer, dstPath := splitCpArg(cmd.Arg(1))
+func runCopy(dockerCli *client.DockerCli, opts copyOptions) error {
+ srcContainer, srcPath := splitCpArg(opts.source)
+ dstContainer, dstPath := splitCpArg(opts.destination)
var direction copyDirection
if srcContainer != "" {
@@ -78,16 +83,16 @@ func (cli *DockerCli) CmdCp(args ...string) error {
}
cpParam := &cpConfig{
- followLink: *followLink,
+ followLink: opts.followLink,
}
ctx := context.Background()
switch direction {
case fromContainer:
- return cli.copyFromContainer(ctx, srcContainer, srcPath, dstPath, cpParam)
+ return copyFromContainer(ctx, dockerCli, srcContainer, srcPath, dstPath, cpParam)
case toContainer:
- return cli.copyToContainer(ctx, srcPath, dstContainer, dstPath, cpParam)
+ return copyToContainer(ctx, dockerCli, srcPath, dstContainer, dstPath, cpParam)
case acrossContainers:
// Copying between containers isn't supported.
return fmt.Errorf("copying between containers is not supported")
@@ -97,39 +102,8 @@ func (cli *DockerCli) CmdCp(args ...string) error {
}
}
-// We use `:` as a delimiter between CONTAINER and PATH, but `:` could also be
-// in a valid LOCALPATH, like `file:name.txt`. We can resolve this ambiguity by
-// requiring a LOCALPATH with a `:` to be made explicit with a relative or
-// absolute path:
-// `/path/to/file:name.txt` or `./file:name.txt`
-//
-// This is apparently how `scp` handles this as well:
-// http://www.cyberciti.biz/faq/rsync-scp-file-name-with-colon-punctuation-in-it/
-//
-// We can't simply check for a filepath separator because container names may
-// have a separator, e.g., "host0/cname1" if container is in a Docker cluster,
-// so we have to check for a `/` or `.` prefix. Also, in the case of a Windows
-// client, a `:` could be part of an absolute Windows path, in which case it
-// is immediately proceeded by a backslash.
-func splitCpArg(arg string) (container, path string) {
- if system.IsAbs(arg) {
- // Explicit local absolute path, e.g., `C:\foo` or `/foo`.
- return "", arg
- }
-
- parts := strings.SplitN(arg, ":", 2)
-
- if len(parts) == 1 || strings.HasPrefix(parts[0], ".") {
- // Either there's no `:` in the arg
- // OR it's an explicit local relative path like `./file:name.txt`.
- return "", arg
- }
-
- return parts[0], parts[1]
-}
-
-func (cli *DockerCli) statContainerPath(ctx context.Context, containerName, path string) (types.ContainerPathStat, error) {
- return cli.client.ContainerStatPath(ctx, containerName, path)
+func statContainerPath(ctx context.Context, dockerCli *client.DockerCli, containerName, path string) (types.ContainerPathStat, error) {
+ return dockerCli.Client().ContainerStatPath(ctx, containerName, path)
}
func resolveLocalPath(localPath string) (absPath string, err error) {
@@ -140,7 +114,7 @@ func resolveLocalPath(localPath string) (absPath string, err error) {
return archive.PreserveTrailingDotOrSeparator(absPath, localPath), nil
}
-func (cli *DockerCli) copyFromContainer(ctx context.Context, srcContainer, srcPath, dstPath string, cpParam *cpConfig) (err error) {
+func copyFromContainer(ctx context.Context, dockerCli *client.DockerCli, srcContainer, srcPath, dstPath string, cpParam *cpConfig) (err error) {
if dstPath != "-" {
// Get an absolute destination path.
dstPath, err = resolveLocalPath(dstPath)
@@ -152,7 +126,7 @@ func (cli *DockerCli) copyFromContainer(ctx context.Context, srcContainer, srcPa
// if client requests to follow symbol link, then must decide target file to be copied
var rebaseName string
if cpParam.followLink {
- srcStat, err := cli.statContainerPath(ctx, srcContainer, srcPath)
+ srcStat, err := statContainerPath(ctx, dockerCli, srcContainer, srcPath)
// If the destination is a symbolic link, we should follow it.
if err == nil && srcStat.Mode&os.ModeSymlink != 0 {
@@ -169,7 +143,7 @@ func (cli *DockerCli) copyFromContainer(ctx context.Context, srcContainer, srcPa
}
- content, stat, err := cli.client.CopyFromContainer(ctx, srcContainer, srcPath)
+ content, stat, err := dockerCli.Client().CopyFromContainer(ctx, srcContainer, srcPath)
if err != nil {
return err
}
@@ -201,7 +175,7 @@ func (cli *DockerCli) copyFromContainer(ctx context.Context, srcContainer, srcPa
return archive.CopyTo(preArchive, srcInfo, dstPath)
}
-func (cli *DockerCli) copyToContainer(ctx context.Context, srcPath, dstContainer, dstPath string, cpParam *cpConfig) (err error) {
+func copyToContainer(ctx context.Context, dockerCli *client.DockerCli, srcPath, dstContainer, dstPath string, cpParam *cpConfig) (err error) {
if srcPath != "-" {
// Get an absolute source path.
srcPath, err = resolveLocalPath(srcPath)
@@ -217,7 +191,7 @@ func (cli *DockerCli) copyToContainer(ctx context.Context, srcPath, dstContainer
// Prepare destination copy info by stat-ing the container path.
dstInfo := archive.CopyInfo{Path: dstPath}
- dstStat, err := cli.statContainerPath(ctx, dstContainer, dstPath)
+ dstStat, err := statContainerPath(ctx, dockerCli, dstContainer, dstPath)
// If the destination is a symbolic link, we should evaluate it.
if err == nil && dstStat.Mode&os.ModeSymlink != 0 {
@@ -229,7 +203,7 @@ func (cli *DockerCli) copyToContainer(ctx context.Context, srcPath, dstContainer
}
dstInfo.Path = linkTarget
- dstStat, err = cli.statContainerPath(ctx, dstContainer, linkTarget)
+ dstStat, err = statContainerPath(ctx, dockerCli, dstContainer, linkTarget)
}
// Ignore any error and assume that the parent directory of the destination
@@ -293,5 +267,36 @@ func (cli *DockerCli) copyToContainer(ctx context.Context, srcPath, dstContainer
AllowOverwriteDirWithFile: false,
}
- return cli.client.CopyToContainer(ctx, dstContainer, resolvedDstPath, content, options)
+ return dockerCli.Client().CopyToContainer(ctx, dstContainer, resolvedDstPath, content, options)
+}
+
+// We use `:` as a delimiter between CONTAINER and PATH, but `:` could also be
+// in a valid LOCALPATH, like `file:name.txt`. We can resolve this ambiguity by
+// requiring a LOCALPATH with a `:` to be made explicit with a relative or
+// absolute path:
+// `/path/to/file:name.txt` or `./file:name.txt`
+//
+// This is apparently how `scp` handles this as well:
+// http://www.cyberciti.biz/faq/rsync-scp-file-name-with-colon-punctuation-in-it/
+//
+// We can't simply check for a filepath separator because container names may
+// have a separator, e.g., "host0/cname1" if container is in a Docker cluster,
+// so we have to check for a `/` or `.` prefix. Also, in the case of a Windows
+// client, a `:` could be part of an absolute Windows path, in which case it
+// is immediately proceeded by a backslash.
+func splitCpArg(arg string) (container, path string) {
+ if system.IsAbs(arg) {
+ // Explicit local absolute path, e.g., `C:\foo` or `/foo`.
+ return "", arg
+ }
+
+ parts := strings.SplitN(arg, ":", 2)
+
+ if len(parts) == 1 || strings.HasPrefix(parts[0], ".") {
+ // Either there's no `:` in the arg
+ // OR it's an explicit local relative path like `./file:name.txt`.
+ return "", arg
+ }
+
+ return parts[0], parts[1]
}
diff --git a/cli/cobraadaptor/adaptor.go b/cli/cobraadaptor/adaptor.go
index 6f1a8876b3..55538dd98f 100644
--- a/cli/cobraadaptor/adaptor.go
+++ b/cli/cobraadaptor/adaptor.go
@@ -44,6 +44,7 @@ func NewCobraAdaptor(clientFlags *cliflags.ClientFlags) CobraAdaptor {
swarm.NewSwarmCommand(dockerCli),
container.NewAttachCommand(dockerCli),
container.NewCommitCommand(dockerCli),
+ container.NewCopyCommand(dockerCli),
container.NewCreateCommand(dockerCli),
container.NewDiffCommand(dockerCli),
container.NewExportCommand(dockerCli),
diff --git a/cli/usage.go b/cli/usage.go
index 3c3b321be6..0e2923740f 100644
--- a/cli/usage.go
+++ b/cli/usage.go
@@ -8,7 +8,6 @@ type Command struct {
// DockerCommandUsage lists the top level docker commands and their short usage
var DockerCommandUsage = []Command{
- {"cp", "Copy files/folders between a container and the local filesystem"},
{"exec", "Run a command in a running container"},
{"info", "Display system-wide information"},
{"inspect", "Return low-level information on a container, image or task"},