github.com/DaoCloud/dao@v0.0.0-20161212064103-c3dbfd13ee36/api/client/container/attach.go (about)

     1  package container
     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  	"github.com/docker/docker/api/client"
    12  	"github.com/docker/docker/cli"
    13  	"github.com/docker/docker/pkg/signal"
    14  	"github.com/docker/engine-api/types"
    15  	"github.com/spf13/cobra"
    16  )
    17  
    18  type attachOptions struct {
    19  	noStdin    bool
    20  	proxy      bool
    21  	detachKeys string
    22  
    23  	container string
    24  }
    25  
    26  // NewAttachCommand creats a new cobra.Command for `docker attach`
    27  func NewAttachCommand(dockerCli *client.DockerCli) *cobra.Command {
    28  	var opts attachOptions
    29  
    30  	cmd := &cobra.Command{
    31  		Use:   "attach [OPTIONS] CONTAINER",
    32  		Short: "附加到一个运行的容器,包含标准输入,标准输出,标准错误",
    33  		Args:  cli.ExactArgs(1),
    34  		RunE: func(cmd *cobra.Command, args []string) error {
    35  			opts.container = args[0]
    36  			return runAttach(dockerCli, &opts)
    37  		},
    38  	}
    39  
    40  	flags := cmd.Flags()
    41  	flags.BoolVar(&opts.noStdin, "no-stdin", false, "不附加标准输入")
    42  	flags.BoolVar(&opts.proxy, "sig-proxy", true, "代理所有接收到的信号至进程")
    43  	flags.StringVar(&opts.detachKeys, "detach-keys", "", "覆盖从一个容器停止附加的输入键顺序")
    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("您不能附加到一个停止的容器中,请先启动此容器。")
    57  	}
    58  
    59  	if c.State.Paused {
    60  		return fmt.Errorf("您不能附加到一个暂停的容器中,请先启动此容器。")
    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 := getExitCode(dockerCli, 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  }