Signed-off-by: Zhang Wei <zhangwei555@huawei.com>
| 1 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,113 +0,0 @@ |
| 1 |
-package client |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "fmt" |
|
| 5 |
- "io" |
|
| 6 |
- "net/http/httputil" |
|
| 7 |
- |
|
| 8 |
- "golang.org/x/net/context" |
|
| 9 |
- |
|
| 10 |
- "github.com/Sirupsen/logrus" |
|
| 11 |
- Cli "github.com/docker/docker/cli" |
|
| 12 |
- flag "github.com/docker/docker/pkg/mflag" |
|
| 13 |
- "github.com/docker/docker/pkg/signal" |
|
| 14 |
- "github.com/docker/engine-api/types" |
|
| 15 |
-) |
|
| 16 |
- |
|
| 17 |
-// CmdAttach attaches to a running container. |
|
| 18 |
-// |
|
| 19 |
-// Usage: docker attach [OPTIONS] CONTAINER |
|
| 20 |
-func (cli *DockerCli) CmdAttach(args ...string) error {
|
|
| 21 |
- cmd := Cli.Subcmd("attach", []string{"CONTAINER"}, Cli.DockerCommands["attach"].Description, true)
|
|
| 22 |
- noStdin := cmd.Bool([]string{"-no-stdin"}, false, "Do not attach STDIN")
|
|
| 23 |
- proxy := cmd.Bool([]string{"-sig-proxy"}, true, "Proxy all received signals to the process")
|
|
| 24 |
- detachKeys := cmd.String([]string{"-detach-keys"}, "", "Override the key sequence for detaching a container")
|
|
| 25 |
- |
|
| 26 |
- cmd.Require(flag.Exact, 1) |
|
| 27 |
- |
|
| 28 |
- cmd.ParseFlags(args, true) |
|
| 29 |
- |
|
| 30 |
- ctx := context.Background() |
|
| 31 |
- |
|
| 32 |
- c, err := cli.client.ContainerInspect(ctx, cmd.Arg(0)) |
|
| 33 |
- if err != nil {
|
|
| 34 |
- return err |
|
| 35 |
- } |
|
| 36 |
- |
|
| 37 |
- if !c.State.Running {
|
|
| 38 |
- return fmt.Errorf("You cannot attach to a stopped container, start it first")
|
|
| 39 |
- } |
|
| 40 |
- |
|
| 41 |
- if c.State.Paused {
|
|
| 42 |
- return fmt.Errorf("You cannot attach to a paused container, unpause it first")
|
|
| 43 |
- } |
|
| 44 |
- |
|
| 45 |
- if err := cli.CheckTtyInput(!*noStdin, c.Config.Tty); err != nil {
|
|
| 46 |
- return err |
|
| 47 |
- } |
|
| 48 |
- |
|
| 49 |
- if *detachKeys != "" {
|
|
| 50 |
- cli.configFile.DetachKeys = *detachKeys |
|
| 51 |
- } |
|
| 52 |
- |
|
| 53 |
- container := cmd.Arg(0) |
|
| 54 |
- |
|
| 55 |
- options := types.ContainerAttachOptions{
|
|
| 56 |
- Stream: true, |
|
| 57 |
- Stdin: !*noStdin && c.Config.OpenStdin, |
|
| 58 |
- Stdout: true, |
|
| 59 |
- Stderr: true, |
|
| 60 |
- DetachKeys: cli.configFile.DetachKeys, |
|
| 61 |
- } |
|
| 62 |
- |
|
| 63 |
- var in io.ReadCloser |
|
| 64 |
- if options.Stdin {
|
|
| 65 |
- in = cli.in |
|
| 66 |
- } |
|
| 67 |
- |
|
| 68 |
- if *proxy && !c.Config.Tty {
|
|
| 69 |
- sigc := cli.ForwardAllSignals(ctx, container) |
|
| 70 |
- defer signal.StopCatch(sigc) |
|
| 71 |
- } |
|
| 72 |
- |
|
| 73 |
- resp, errAttach := cli.client.ContainerAttach(ctx, container, options) |
|
| 74 |
- if errAttach != nil && errAttach != httputil.ErrPersistEOF {
|
|
| 75 |
- // ContainerAttach returns an ErrPersistEOF (connection closed) |
|
| 76 |
- // means server met an error and put it in Hijacked connection |
|
| 77 |
- // keep the error and read detailed error message from hijacked connection later |
|
| 78 |
- return errAttach |
|
| 79 |
- } |
|
| 80 |
- defer resp.Close() |
|
| 81 |
- |
|
| 82 |
- if c.Config.Tty && cli.isTerminalOut {
|
|
| 83 |
- height, width := cli.GetTtySize() |
|
| 84 |
- // To handle the case where a user repeatedly attaches/detaches without resizing their |
|
| 85 |
- // terminal, the only way to get the shell prompt to display for attaches 2+ is to artificially |
|
| 86 |
- // resize it, then go back to normal. Without this, every attach after the first will |
|
| 87 |
- // require the user to manually resize or hit enter. |
|
| 88 |
- cli.resizeTtyTo(ctx, cmd.Arg(0), height+1, width+1, false) |
|
| 89 |
- |
|
| 90 |
- // After the above resizing occurs, the call to MonitorTtySize below will handle resetting back |
|
| 91 |
- // to the actual size. |
|
| 92 |
- if err := cli.MonitorTtySize(ctx, cmd.Arg(0), false); err != nil {
|
|
| 93 |
- logrus.Debugf("Error monitoring TTY size: %s", err)
|
|
| 94 |
- } |
|
| 95 |
- } |
|
| 96 |
- if err := cli.HoldHijackedConnection(ctx, c.Config.Tty, in, cli.out, cli.err, resp); err != nil {
|
|
| 97 |
- return err |
|
| 98 |
- } |
|
| 99 |
- |
|
| 100 |
- if errAttach != nil {
|
|
| 101 |
- return errAttach |
|
| 102 |
- } |
|
| 103 |
- |
|
| 104 |
- _, status, err := cli.GetExitCode(ctx, container) |
|
| 105 |
- if err != nil {
|
|
| 106 |
- return err |
|
| 107 |
- } |
|
| 108 |
- if status != 0 {
|
|
| 109 |
- return Cli.StatusError{StatusCode: status}
|
|
| 110 |
- } |
|
| 111 |
- |
|
| 112 |
- return nil |
|
| 113 |
-} |
| ... | ... |
@@ -3,7 +3,6 @@ package client |
| 3 | 3 |
// Command returns a cli command handler if one exists |
| 4 | 4 |
func (cli *DockerCli) Command(name string) func(...string) error {
|
| 5 | 5 |
return map[string]func(...string) error{
|
| 6 |
- "attach": cli.CmdAttach, |
|
| 7 | 6 |
"build": cli.CmdBuild, |
| 8 | 7 |
"commit": cli.CmdCommit, |
| 9 | 8 |
"cp": cli.CmdCp, |
| 10 | 9 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,130 @@ |
| 0 |
+package container |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "io" |
|
| 5 |
+ "net/http/httputil" |
|
| 6 |
+ |
|
| 7 |
+ "golang.org/x/net/context" |
|
| 8 |
+ |
|
| 9 |
+ "github.com/Sirupsen/logrus" |
|
| 10 |
+ "github.com/docker/docker/api/client" |
|
| 11 |
+ "github.com/docker/docker/cli" |
|
| 12 |
+ "github.com/docker/docker/pkg/signal" |
|
| 13 |
+ "github.com/docker/engine-api/types" |
|
| 14 |
+ "github.com/spf13/cobra" |
|
| 15 |
+) |
|
| 16 |
+ |
|
| 17 |
+type attachOptions struct {
|
|
| 18 |
+ noStdin bool |
|
| 19 |
+ proxy bool |
|
| 20 |
+ detachKeys string |
|
| 21 |
+ |
|
| 22 |
+ container string |
|
| 23 |
+} |
|
| 24 |
+ |
|
| 25 |
+// NewAttachCommand creats a new cobra.Command for `docker attach` |
|
| 26 |
+func NewAttachCommand(dockerCli *client.DockerCli) *cobra.Command {
|
|
| 27 |
+ var opts attachOptions |
|
| 28 |
+ |
|
| 29 |
+ cmd := &cobra.Command{
|
|
| 30 |
+ Use: "attach [OPTIONS] CONTAINER", |
|
| 31 |
+ Short: "Attach to a running container", |
|
| 32 |
+ Args: cli.ExactArgs(1), |
|
| 33 |
+ RunE: func(cmd *cobra.Command, args []string) error {
|
|
| 34 |
+ opts.container = args[0] |
|
| 35 |
+ return runAttach(dockerCli, &opts) |
|
| 36 |
+ }, |
|
| 37 |
+ } |
|
| 38 |
+ cmd.SetFlagErrorFunc(flagErrorFunc) |
|
| 39 |
+ |
|
| 40 |
+ flags := cmd.Flags() |
|
| 41 |
+ flags.BoolVar(&opts.noStdin, "no-stdin", false, "Do not attach STDIN") |
|
| 42 |
+ flags.BoolVar(&opts.proxy, "sig-proxy", true, "Proxy all received signals to the process") |
|
| 43 |
+ flags.StringVar(&opts.detachKeys, "detach-keys", "", "Override the key sequence for detaching a container") |
|
| 44 |
+ return cmd |
|
| 45 |
+} |
|
| 46 |
+ |
|
| 47 |
+func runAttach(dockerCli *client.DockerCli, opts *attachOptions) error {
|
|
| 48 |
+ ctx := context.Background() |
|
| 49 |
+ |
|
| 50 |
+ c, err := dockerCli.Client().ContainerInspect(ctx, opts.container) |
|
| 51 |
+ if err != nil {
|
|
| 52 |
+ return err |
|
| 53 |
+ } |
|
| 54 |
+ |
|
| 55 |
+ if !c.State.Running {
|
|
| 56 |
+ return fmt.Errorf("You cannot attach to a stopped container, start it first")
|
|
| 57 |
+ } |
|
| 58 |
+ |
|
| 59 |
+ if c.State.Paused {
|
|
| 60 |
+ return fmt.Errorf("You cannot attach to a paused container, unpause it first")
|
|
| 61 |
+ } |
|
| 62 |
+ |
|
| 63 |
+ if err := dockerCli.CheckTtyInput(!opts.noStdin, c.Config.Tty); err != nil {
|
|
| 64 |
+ return err |
|
| 65 |
+ } |
|
| 66 |
+ |
|
| 67 |
+ if opts.detachKeys != "" {
|
|
| 68 |
+ dockerCli.ConfigFile().DetachKeys = opts.detachKeys |
|
| 69 |
+ } |
|
| 70 |
+ |
|
| 71 |
+ options := types.ContainerAttachOptions{
|
|
| 72 |
+ Stream: true, |
|
| 73 |
+ Stdin: !opts.noStdin && c.Config.OpenStdin, |
|
| 74 |
+ Stdout: true, |
|
| 75 |
+ Stderr: true, |
|
| 76 |
+ DetachKeys: dockerCli.ConfigFile().DetachKeys, |
|
| 77 |
+ } |
|
| 78 |
+ |
|
| 79 |
+ var in io.ReadCloser |
|
| 80 |
+ if options.Stdin {
|
|
| 81 |
+ in = dockerCli.In() |
|
| 82 |
+ } |
|
| 83 |
+ |
|
| 84 |
+ if opts.proxy && !c.Config.Tty {
|
|
| 85 |
+ sigc := dockerCli.ForwardAllSignals(ctx, opts.container) |
|
| 86 |
+ defer signal.StopCatch(sigc) |
|
| 87 |
+ } |
|
| 88 |
+ |
|
| 89 |
+ resp, errAttach := dockerCli.Client().ContainerAttach(ctx, opts.container, options) |
|
| 90 |
+ if errAttach != nil && errAttach != httputil.ErrPersistEOF {
|
|
| 91 |
+ // ContainerAttach returns an ErrPersistEOF (connection closed) |
|
| 92 |
+ // means server met an error and put it in Hijacked connection |
|
| 93 |
+ // keep the error and read detailed error message from hijacked connection later |
|
| 94 |
+ return errAttach |
|
| 95 |
+ } |
|
| 96 |
+ defer resp.Close() |
|
| 97 |
+ |
|
| 98 |
+ if c.Config.Tty && dockerCli.IsTerminalOut() {
|
|
| 99 |
+ height, width := dockerCli.GetTtySize() |
|
| 100 |
+ // To handle the case where a user repeatedly attaches/detaches without resizing their |
|
| 101 |
+ // terminal, the only way to get the shell prompt to display for attaches 2+ is to artificially |
|
| 102 |
+ // resize it, then go back to normal. Without this, every attach after the first will |
|
| 103 |
+ // require the user to manually resize or hit enter. |
|
| 104 |
+ dockerCli.ResizeTtyTo(ctx, opts.container, height+1, width+1, false) |
|
| 105 |
+ |
|
| 106 |
+ // After the above resizing occurs, the call to MonitorTtySize below will handle resetting back |
|
| 107 |
+ // to the actual size. |
|
| 108 |
+ if err := dockerCli.MonitorTtySize(ctx, opts.container, false); err != nil {
|
|
| 109 |
+ logrus.Debugf("Error monitoring TTY size: %s", err)
|
|
| 110 |
+ } |
|
| 111 |
+ } |
|
| 112 |
+ if err := dockerCli.HoldHijackedConnection(ctx, c.Config.Tty, in, dockerCli.Out(), dockerCli.Err(), resp); err != nil {
|
|
| 113 |
+ return err |
|
| 114 |
+ } |
|
| 115 |
+ |
|
| 116 |
+ if errAttach != nil {
|
|
| 117 |
+ return errAttach |
|
| 118 |
+ } |
|
| 119 |
+ |
|
| 120 |
+ _, status, err := dockerCli.GetExitCode(ctx, opts.container) |
|
| 121 |
+ if err != nil {
|
|
| 122 |
+ return err |
|
| 123 |
+ } |
|
| 124 |
+ if status != 0 {
|
|
| 125 |
+ return cli.StatusError{StatusCode: status}
|
|
| 126 |
+ } |
|
| 127 |
+ |
|
| 128 |
+ return nil |
|
| 129 |
+} |
| ... | ... |
@@ -62,10 +62,12 @@ func (cli *DockerCli) RegistryAuthenticationPrivilegedFunc(index *registrytypes. |
| 62 | 62 |
|
| 63 | 63 |
func (cli *DockerCli) resizeTty(ctx context.Context, id string, isExec bool) {
|
| 64 | 64 |
height, width := cli.GetTtySize() |
| 65 |
- cli.resizeTtyTo(ctx, id, height, width, isExec) |
|
| 65 |
+ cli.ResizeTtyTo(ctx, id, height, width, isExec) |
|
| 66 | 66 |
} |
| 67 | 67 |
|
| 68 |
-func (cli *DockerCli) resizeTtyTo(ctx context.Context, id string, height, width int, isExec bool) {
|
|
| 68 |
+// ResizeTtyTo resizes tty to specific height and width |
|
| 69 |
+// TODO: this can be unexported again once all container related commands move to package container |
|
| 70 |
+func (cli *DockerCli) ResizeTtyTo(ctx context.Context, id string, height, width int, isExec bool) {
|
|
| 69 | 71 |
if height == 0 && width == 0 {
|
| 70 | 72 |
return |
| 71 | 73 |
} |
| ... | ... |
@@ -34,6 +34,7 @@ func NewCobraAdaptor(clientFlags *cliflags.ClientFlags) CobraAdaptor {
|
| 34 | 34 |
rootCmd.SetFlagErrorFunc(cli.FlagErrorFunc) |
| 35 | 35 |
rootCmd.SetOutput(stdout) |
| 36 | 36 |
rootCmd.AddCommand( |
| 37 |
+ container.NewAttachCommand(dockerCli), |
|
| 37 | 38 |
container.NewCreateCommand(dockerCli), |
| 38 | 39 |
container.NewDiffCommand(dockerCli), |
| 39 | 40 |
container.NewExportCommand(dockerCli), |
| ... | ... |
@@ -8,7 +8,6 @@ type Command struct {
|
| 8 | 8 |
|
| 9 | 9 |
// DockerCommandUsage lists the top level docker commands and their short usage |
| 10 | 10 |
var DockerCommandUsage = []Command{
|
| 11 |
- {"attach", "Attach to a running container"},
|
|
| 12 | 11 |
{"build", "Build an image from a Dockerfile"},
|
| 13 | 12 |
{"commit", "Create a new image from a container's changes"},
|
| 14 | 13 |
{"cp", "Copy files/folders between a container and the local filesystem"},
|
| ... | ... |
@@ -150,7 +150,7 @@ func (s *DockerSuite) TestRunAttachDetachFromFlag(c *check.C) {
|
| 150 | 150 |
|
| 151 | 151 |
dockerCmd(c, "run", "--name", name, "-itd", "busybox", "cat") |
| 152 | 152 |
|
| 153 |
- cmd := exec.Command(dockerBinary, "attach", "--detach-keys='ctrl-a,a'", name) |
|
| 153 |
+ cmd := exec.Command(dockerBinary, "attach", "--detach-keys=ctrl-a,a", name) |
|
| 154 | 154 |
stdout, err := cmd.StdoutPipe() |
| 155 | 155 |
if err != nil {
|
| 156 | 156 |
c.Fatal(err) |
| ... | ... |
@@ -210,7 +210,7 @@ func (s *DockerSuite) TestRunAttachDetachFromInvalidFlag(c *check.C) {
|
| 210 | 210 |
c.Assert(waitRun(name), check.IsNil) |
| 211 | 211 |
|
| 212 | 212 |
// specify an invalid detach key, container will ignore it and use default |
| 213 |
- cmd := exec.Command(dockerBinary, "attach", "--detach-keys='ctrl-A,a'", name) |
|
| 213 |
+ cmd := exec.Command(dockerBinary, "attach", "--detach-keys=ctrl-A,a", name) |
|
| 214 | 214 |
stdout, err := cmd.StdoutPipe() |
| 215 | 215 |
if err != nil {
|
| 216 | 216 |
c.Fatal(err) |
| ... | ... |
@@ -348,7 +348,7 @@ func (s *DockerSuite) TestRunAttachDetachKeysOverrideConfig(c *check.C) {
|
| 348 | 348 |
name := "attach-detach" |
| 349 | 349 |
dockerCmd(c, "run", "--name", name, "-itd", "busybox", "cat") |
| 350 | 350 |
|
| 351 |
- cmd := exec.Command(dockerBinary, "attach", "--detach-keys='ctrl-a,a'", name) |
|
| 351 |
+ cmd := exec.Command(dockerBinary, "attach", "--detach-keys=ctrl-a,a", name) |
|
| 352 | 352 |
stdout, err := cmd.StdoutPipe() |
| 353 | 353 |
if err != nil {
|
| 354 | 354 |
c.Fatal(err) |
| ... | ... |
@@ -408,7 +408,7 @@ func (s *DockerSuite) TestRunAttachInvalidDetachKeySequencePreserved(c *check.C) |
| 408 | 408 |
|
| 409 | 409 |
dockerCmd(c, "run", "--name", name, "-itd", "busybox", "cat") |
| 410 | 410 |
|
| 411 |
- cmd := exec.Command(dockerBinary, "attach", "--detach-keys='a,b,c'", name) |
|
| 411 |
+ cmd := exec.Command(dockerBinary, "attach", "--detach-keys=a,b,c", name) |
|
| 412 | 412 |
stdout, err := cmd.StdoutPipe() |
| 413 | 413 |
if err != nil {
|
| 414 | 414 |
c.Fatal(err) |