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 }