Browse code

Move attach command to cobra.

Signed-off-by: Zhang Wei <zhangwei555@huawei.com>

Zhang Wei authored on 2016/06/06 01:17:39
Showing 7 changed files
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)