mirror of https://github.com/docker/docs.git
Move attach command to cobra.
Signed-off-by: Zhang Wei <zhangwei555@huawei.com>
This commit is contained in:
parent
07a7c0632f
commit
eceb8625a9
|
@ -1,113 +0,0 @@
|
||||||
package client
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net/http/httputil"
|
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
|
||||||
Cli "github.com/docker/docker/cli"
|
|
||||||
flag "github.com/docker/docker/pkg/mflag"
|
|
||||||
"github.com/docker/docker/pkg/signal"
|
|
||||||
"github.com/docker/engine-api/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CmdAttach attaches to a running container.
|
|
||||||
//
|
|
||||||
// Usage: docker attach [OPTIONS] CONTAINER
|
|
||||||
func (cli *DockerCli) CmdAttach(args ...string) error {
|
|
||||||
cmd := Cli.Subcmd("attach", []string{"CONTAINER"}, Cli.DockerCommands["attach"].Description, true)
|
|
||||||
noStdin := cmd.Bool([]string{"-no-stdin"}, false, "Do not attach STDIN")
|
|
||||||
proxy := cmd.Bool([]string{"-sig-proxy"}, true, "Proxy all received signals to the process")
|
|
||||||
detachKeys := cmd.String([]string{"-detach-keys"}, "", "Override the key sequence for detaching a container")
|
|
||||||
|
|
||||||
cmd.Require(flag.Exact, 1)
|
|
||||||
|
|
||||||
cmd.ParseFlags(args, true)
|
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
c, err := cli.client.ContainerInspect(ctx, cmd.Arg(0))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !c.State.Running {
|
|
||||||
return fmt.Errorf("You cannot attach to a stopped container, start it first")
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.State.Paused {
|
|
||||||
return fmt.Errorf("You cannot attach to a paused container, unpause it first")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := cli.CheckTtyInput(!*noStdin, c.Config.Tty); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if *detachKeys != "" {
|
|
||||||
cli.configFile.DetachKeys = *detachKeys
|
|
||||||
}
|
|
||||||
|
|
||||||
container := cmd.Arg(0)
|
|
||||||
|
|
||||||
options := types.ContainerAttachOptions{
|
|
||||||
Stream: true,
|
|
||||||
Stdin: !*noStdin && c.Config.OpenStdin,
|
|
||||||
Stdout: true,
|
|
||||||
Stderr: true,
|
|
||||||
DetachKeys: cli.configFile.DetachKeys,
|
|
||||||
}
|
|
||||||
|
|
||||||
var in io.ReadCloser
|
|
||||||
if options.Stdin {
|
|
||||||
in = cli.in
|
|
||||||
}
|
|
||||||
|
|
||||||
if *proxy && !c.Config.Tty {
|
|
||||||
sigc := cli.ForwardAllSignals(ctx, container)
|
|
||||||
defer signal.StopCatch(sigc)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, errAttach := cli.client.ContainerAttach(ctx, container, options)
|
|
||||||
if errAttach != nil && errAttach != httputil.ErrPersistEOF {
|
|
||||||
// ContainerAttach returns an ErrPersistEOF (connection closed)
|
|
||||||
// means server met an error and put it in Hijacked connection
|
|
||||||
// keep the error and read detailed error message from hijacked connection later
|
|
||||||
return errAttach
|
|
||||||
}
|
|
||||||
defer resp.Close()
|
|
||||||
|
|
||||||
if c.Config.Tty && cli.isTerminalOut {
|
|
||||||
height, width := cli.GetTtySize()
|
|
||||||
// To handle the case where a user repeatedly attaches/detaches without resizing their
|
|
||||||
// terminal, the only way to get the shell prompt to display for attaches 2+ is to artificially
|
|
||||||
// resize it, then go back to normal. Without this, every attach after the first will
|
|
||||||
// require the user to manually resize or hit enter.
|
|
||||||
cli.resizeTtyTo(ctx, cmd.Arg(0), height+1, width+1, false)
|
|
||||||
|
|
||||||
// After the above resizing occurs, the call to MonitorTtySize below will handle resetting back
|
|
||||||
// to the actual size.
|
|
||||||
if err := cli.MonitorTtySize(ctx, cmd.Arg(0), false); err != nil {
|
|
||||||
logrus.Debugf("Error monitoring TTY size: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := cli.HoldHijackedConnection(ctx, c.Config.Tty, in, cli.out, cli.err, resp); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if errAttach != nil {
|
|
||||||
return errAttach
|
|
||||||
}
|
|
||||||
|
|
||||||
_, status, err := cli.GetExitCode(ctx, container)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if status != 0 {
|
|
||||||
return Cli.StatusError{StatusCode: status}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -3,7 +3,6 @@ package client
|
||||||
// Command returns a cli command handler if one exists
|
// Command returns a cli command handler if one exists
|
||||||
func (cli *DockerCli) Command(name string) func(...string) error {
|
func (cli *DockerCli) Command(name string) func(...string) error {
|
||||||
return map[string]func(...string) error{
|
return map[string]func(...string) error{
|
||||||
"attach": cli.CmdAttach,
|
|
||||||
"build": cli.CmdBuild,
|
"build": cli.CmdBuild,
|
||||||
"commit": cli.CmdCommit,
|
"commit": cli.CmdCommit,
|
||||||
"cp": cli.CmdCp,
|
"cp": cli.CmdCp,
|
||||||
|
|
|
@ -0,0 +1,130 @@
|
||||||
|
package container
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http/httputil"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/docker/api/client"
|
||||||
|
"github.com/docker/docker/cli"
|
||||||
|
"github.com/docker/docker/pkg/signal"
|
||||||
|
"github.com/docker/engine-api/types"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
type attachOptions struct {
|
||||||
|
noStdin bool
|
||||||
|
proxy bool
|
||||||
|
detachKeys string
|
||||||
|
|
||||||
|
container string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAttachCommand creats a new cobra.Command for `docker attach`
|
||||||
|
func NewAttachCommand(dockerCli *client.DockerCli) *cobra.Command {
|
||||||
|
var opts attachOptions
|
||||||
|
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "attach [OPTIONS] CONTAINER",
|
||||||
|
Short: "Attach to a running container",
|
||||||
|
Args: cli.ExactArgs(1),
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
opts.container = args[0]
|
||||||
|
return runAttach(dockerCli, &opts)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
cmd.SetFlagErrorFunc(flagErrorFunc)
|
||||||
|
|
||||||
|
flags := cmd.Flags()
|
||||||
|
flags.BoolVar(&opts.noStdin, "no-stdin", false, "Do not attach STDIN")
|
||||||
|
flags.BoolVar(&opts.proxy, "sig-proxy", true, "Proxy all received signals to the process")
|
||||||
|
flags.StringVar(&opts.detachKeys, "detach-keys", "", "Override the key sequence for detaching a container")
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func runAttach(dockerCli *client.DockerCli, opts *attachOptions) error {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
c, err := dockerCli.Client().ContainerInspect(ctx, opts.container)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !c.State.Running {
|
||||||
|
return fmt.Errorf("You cannot attach to a stopped container, start it first")
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.State.Paused {
|
||||||
|
return fmt.Errorf("You cannot attach to a paused container, unpause it first")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := dockerCli.CheckTtyInput(!opts.noStdin, c.Config.Tty); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.detachKeys != "" {
|
||||||
|
dockerCli.ConfigFile().DetachKeys = opts.detachKeys
|
||||||
|
}
|
||||||
|
|
||||||
|
options := types.ContainerAttachOptions{
|
||||||
|
Stream: true,
|
||||||
|
Stdin: !opts.noStdin && c.Config.OpenStdin,
|
||||||
|
Stdout: true,
|
||||||
|
Stderr: true,
|
||||||
|
DetachKeys: dockerCli.ConfigFile().DetachKeys,
|
||||||
|
}
|
||||||
|
|
||||||
|
var in io.ReadCloser
|
||||||
|
if options.Stdin {
|
||||||
|
in = dockerCli.In()
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.proxy && !c.Config.Tty {
|
||||||
|
sigc := dockerCli.ForwardAllSignals(ctx, opts.container)
|
||||||
|
defer signal.StopCatch(sigc)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, errAttach := dockerCli.Client().ContainerAttach(ctx, opts.container, options)
|
||||||
|
if errAttach != nil && errAttach != httputil.ErrPersistEOF {
|
||||||
|
// ContainerAttach returns an ErrPersistEOF (connection closed)
|
||||||
|
// means server met an error and put it in Hijacked connection
|
||||||
|
// keep the error and read detailed error message from hijacked connection later
|
||||||
|
return errAttach
|
||||||
|
}
|
||||||
|
defer resp.Close()
|
||||||
|
|
||||||
|
if c.Config.Tty && dockerCli.IsTerminalOut() {
|
||||||
|
height, width := dockerCli.GetTtySize()
|
||||||
|
// To handle the case where a user repeatedly attaches/detaches without resizing their
|
||||||
|
// terminal, the only way to get the shell prompt to display for attaches 2+ is to artificially
|
||||||
|
// resize it, then go back to normal. Without this, every attach after the first will
|
||||||
|
// require the user to manually resize or hit enter.
|
||||||
|
dockerCli.ResizeTtyTo(ctx, opts.container, height+1, width+1, false)
|
||||||
|
|
||||||
|
// After the above resizing occurs, the call to MonitorTtySize below will handle resetting back
|
||||||
|
// to the actual size.
|
||||||
|
if err := dockerCli.MonitorTtySize(ctx, opts.container, false); err != nil {
|
||||||
|
logrus.Debugf("Error monitoring TTY size: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := dockerCli.HoldHijackedConnection(ctx, c.Config.Tty, in, dockerCli.Out(), dockerCli.Err(), resp); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if errAttach != nil {
|
||||||
|
return errAttach
|
||||||
|
}
|
||||||
|
|
||||||
|
_, status, err := dockerCli.GetExitCode(ctx, opts.container)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if status != 0 {
|
||||||
|
return cli.StatusError{StatusCode: status}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -62,10 +62,12 @@ func (cli *DockerCli) RegistryAuthenticationPrivilegedFunc(index *registrytypes.
|
||||||
|
|
||||||
func (cli *DockerCli) resizeTty(ctx context.Context, id string, isExec bool) {
|
func (cli *DockerCli) resizeTty(ctx context.Context, id string, isExec bool) {
|
||||||
height, width := cli.GetTtySize()
|
height, width := cli.GetTtySize()
|
||||||
cli.resizeTtyTo(ctx, id, height, width, isExec)
|
cli.ResizeTtyTo(ctx, id, height, width, isExec)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cli *DockerCli) resizeTtyTo(ctx context.Context, id string, height, width int, isExec bool) {
|
// ResizeTtyTo resizes tty to specific height and width
|
||||||
|
// TODO: this can be unexported again once all container related commands move to package container
|
||||||
|
func (cli *DockerCli) ResizeTtyTo(ctx context.Context, id string, height, width int, isExec bool) {
|
||||||
if height == 0 && width == 0 {
|
if height == 0 && width == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ func NewCobraAdaptor(clientFlags *cliflags.ClientFlags) CobraAdaptor {
|
||||||
rootCmd.SetFlagErrorFunc(cli.FlagErrorFunc)
|
rootCmd.SetFlagErrorFunc(cli.FlagErrorFunc)
|
||||||
rootCmd.SetOutput(stdout)
|
rootCmd.SetOutput(stdout)
|
||||||
rootCmd.AddCommand(
|
rootCmd.AddCommand(
|
||||||
|
container.NewAttachCommand(dockerCli),
|
||||||
container.NewCreateCommand(dockerCli),
|
container.NewCreateCommand(dockerCli),
|
||||||
container.NewDiffCommand(dockerCli),
|
container.NewDiffCommand(dockerCli),
|
||||||
container.NewExportCommand(dockerCli),
|
container.NewExportCommand(dockerCli),
|
||||||
|
|
|
@ -8,7 +8,6 @@ type Command struct {
|
||||||
|
|
||||||
// DockerCommandUsage lists the top level docker commands and their short usage
|
// DockerCommandUsage lists the top level docker commands and their short usage
|
||||||
var DockerCommandUsage = []Command{
|
var DockerCommandUsage = []Command{
|
||||||
{"attach", "Attach to a running container"},
|
|
||||||
{"build", "Build an image from a Dockerfile"},
|
{"build", "Build an image from a Dockerfile"},
|
||||||
{"commit", "Create a new image from a container's changes"},
|
{"commit", "Create a new image from a container's changes"},
|
||||||
{"cp", "Copy files/folders between a container and the local filesystem"},
|
{"cp", "Copy files/folders between a container and the local filesystem"},
|
||||||
|
|
|
@ -150,7 +150,7 @@ func (s *DockerSuite) TestRunAttachDetachFromFlag(c *check.C) {
|
||||||
|
|
||||||
dockerCmd(c, "run", "--name", name, "-itd", "busybox", "cat")
|
dockerCmd(c, "run", "--name", name, "-itd", "busybox", "cat")
|
||||||
|
|
||||||
cmd := exec.Command(dockerBinary, "attach", "--detach-keys='ctrl-a,a'", name)
|
cmd := exec.Command(dockerBinary, "attach", "--detach-keys=ctrl-a,a", name)
|
||||||
stdout, err := cmd.StdoutPipe()
|
stdout, err := cmd.StdoutPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Fatal(err)
|
c.Fatal(err)
|
||||||
|
@ -210,7 +210,7 @@ func (s *DockerSuite) TestRunAttachDetachFromInvalidFlag(c *check.C) {
|
||||||
c.Assert(waitRun(name), check.IsNil)
|
c.Assert(waitRun(name), check.IsNil)
|
||||||
|
|
||||||
// specify an invalid detach key, container will ignore it and use default
|
// specify an invalid detach key, container will ignore it and use default
|
||||||
cmd := exec.Command(dockerBinary, "attach", "--detach-keys='ctrl-A,a'", name)
|
cmd := exec.Command(dockerBinary, "attach", "--detach-keys=ctrl-A,a", name)
|
||||||
stdout, err := cmd.StdoutPipe()
|
stdout, err := cmd.StdoutPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Fatal(err)
|
c.Fatal(err)
|
||||||
|
@ -348,7 +348,7 @@ func (s *DockerSuite) TestRunAttachDetachKeysOverrideConfig(c *check.C) {
|
||||||
name := "attach-detach"
|
name := "attach-detach"
|
||||||
dockerCmd(c, "run", "--name", name, "-itd", "busybox", "cat")
|
dockerCmd(c, "run", "--name", name, "-itd", "busybox", "cat")
|
||||||
|
|
||||||
cmd := exec.Command(dockerBinary, "attach", "--detach-keys='ctrl-a,a'", name)
|
cmd := exec.Command(dockerBinary, "attach", "--detach-keys=ctrl-a,a", name)
|
||||||
stdout, err := cmd.StdoutPipe()
|
stdout, err := cmd.StdoutPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Fatal(err)
|
c.Fatal(err)
|
||||||
|
@ -408,7 +408,7 @@ func (s *DockerSuite) TestRunAttachInvalidDetachKeySequencePreserved(c *check.C)
|
||||||
|
|
||||||
dockerCmd(c, "run", "--name", name, "-itd", "busybox", "cat")
|
dockerCmd(c, "run", "--name", name, "-itd", "busybox", "cat")
|
||||||
|
|
||||||
cmd := exec.Command(dockerBinary, "attach", "--detach-keys='a,b,c'", name)
|
cmd := exec.Command(dockerBinary, "attach", "--detach-keys=a,b,c", name)
|
||||||
stdout, err := cmd.StdoutPipe()
|
stdout, err := cmd.StdoutPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Fatal(err)
|
c.Fatal(err)
|
||||||
|
|
Loading…
Reference in New Issue