From 31d8c27e704c9dd06ba3a0cccb7427054d9e6ccd Mon Sep 17 00:00:00 2001 From: Daniel Nephin Date: Mon, 29 Aug 2016 12:23:15 -0400 Subject: [PATCH] Move container util methods to the container package. Signed-off-by: Daniel Nephin --- api/client/container/attach.go | 11 ++-- api/client/container/exec.go | 28 ++++++-- api/client/container/run.go | 4 +- api/client/container/start.go | 4 +- api/client/container/tty.go | 103 +++++++++++++++++++++++++++++ api/client/utils.go | 116 --------------------------------- 6 files changed, 136 insertions(+), 130 deletions(-) create mode 100644 api/client/container/tty.go diff --git a/api/client/container/attach.go b/api/client/container/attach.go index 12904f09b9..aea3eec334 100644 --- a/api/client/container/attach.go +++ b/api/client/container/attach.go @@ -46,8 +46,9 @@ func NewAttachCommand(dockerCli *client.DockerCli) *cobra.Command { func runAttach(dockerCli *client.DockerCli, opts *attachOptions) error { ctx := context.Background() + client := dockerCli.Client() - c, err := dockerCli.Client().ContainerInspect(ctx, opts.container) + c, err := client.ContainerInspect(ctx, opts.container) if err != nil { return err } @@ -82,11 +83,11 @@ func runAttach(dockerCli *client.DockerCli, opts *attachOptions) error { } if opts.proxy && !c.Config.Tty { - sigc := dockerCli.ForwardAllSignals(ctx, opts.container) + sigc := ForwardAllSignals(ctx, dockerCli, opts.container) defer signal.StopCatch(sigc) } - resp, errAttach := dockerCli.Client().ContainerAttach(ctx, opts.container, options) + resp, errAttach := 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 @@ -101,11 +102,11 @@ func runAttach(dockerCli *client.DockerCli, opts *attachOptions) error { // 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) + resizeTtyTo(ctx, client, 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 { + if err := MonitorTtySize(ctx, dockerCli, opts.container, false); err != nil { logrus.Debugf("Error monitoring TTY size: %s", err) } } diff --git a/api/client/container/exec.go b/api/client/container/exec.go index 3a83e98689..89338ae061 100644 --- a/api/client/container/exec.go +++ b/api/client/container/exec.go @@ -11,6 +11,8 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/cli" "github.com/docker/docker/pkg/promise" + apiclient "github.com/docker/docker/client" + "github.com/docker/engine-api/types" "github.com/spf13/cobra" ) @@ -66,8 +68,9 @@ func runExec(dockerCli *client.DockerCli, opts *execOptions, container string, e execConfig.DetachKeys = dockerCli.ConfigFile().DetachKeys ctx := context.Background() + client := dockerCli.Client() - response, err := dockerCli.Client().ContainerExecCreate(ctx, container, *execConfig) + response, err := client.ContainerExecCreate(ctx, container, *execConfig) if err != nil { return err } @@ -89,7 +92,7 @@ func runExec(dockerCli *client.DockerCli, opts *execOptions, container string, e Tty: execConfig.Tty, } - if err := dockerCli.Client().ContainerExecStart(ctx, execID, execStartCheck); err != nil { + if err := client.ContainerExecStart(ctx, execID, execStartCheck); err != nil { return err } // For now don't print this - wait for when we support exec wait() @@ -118,7 +121,7 @@ func runExec(dockerCli *client.DockerCli, opts *execOptions, container string, e } } - resp, err := dockerCli.Client().ContainerExecAttach(ctx, execID, *execConfig) + resp, err := client.ContainerExecAttach(ctx, execID, *execConfig) if err != nil { return err } @@ -128,7 +131,7 @@ func runExec(dockerCli *client.DockerCli, opts *execOptions, container string, e }) if execConfig.Tty && dockerCli.In().IsTerminal() { - if err := dockerCli.MonitorTtySize(ctx, execID, true); err != nil { + if err := MonitorTtySize(ctx, dockerCli, execID, true); err != nil { fmt.Fprintf(dockerCli.Err(), "Error monitoring TTY size: %s\n", err) } } @@ -139,7 +142,7 @@ func runExec(dockerCli *client.DockerCli, opts *execOptions, container string, e } var status int - if _, status, err = dockerCli.GetExecExitCode(ctx, execID); err != nil { + if _, status, err = getExecExitCode(ctx, client, execID); err != nil { return err } @@ -150,6 +153,21 @@ func runExec(dockerCli *client.DockerCli, opts *execOptions, container string, e return nil } +// getExecExitCode perform an inspect on the exec command. It returns +// the running state and the exit code. +func getExecExitCode(ctx context.Context, client apiclient.ContainerAPIClient, execID string) (bool, int, error) { + resp, err := client.ContainerExecInspect(ctx, execID) + if err != nil { + // If we can't connect, then the daemon probably died. + if err != apiclient.ErrConnectionFailed { + return false, -1, err + } + return false, -1, nil + } + + return resp.Running, resp.ExitCode, nil +} + // parseExec parses the specified args for the specified command and generates // an ExecConfig from it. func parseExec(opts *execOptions, container string, execCmd []string) (*types.ExecConfig, error) { diff --git a/api/client/container/run.go b/api/client/container/run.go index ac6747526d..5d44ff5ad5 100644 --- a/api/client/container/run.go +++ b/api/client/container/run.go @@ -146,7 +146,7 @@ func runRun(dockerCli *client.DockerCli, flags *pflag.FlagSet, opts *runOptions, return runStartContainerErr(err) } if opts.sigProxy { - sigc := dockerCli.ForwardAllSignals(ctx, createResponse.ID) + sigc := ForwardAllSignals(ctx, dockerCli, createResponse.ID) defer signal.StopCatch(sigc) } var ( @@ -235,7 +235,7 @@ func runRun(dockerCli *client.DockerCli, flags *pflag.FlagSet, opts *runOptions, } if (config.AttachStdin || config.AttachStdout || config.AttachStderr) && config.Tty && dockerCli.Out().IsTerminal() { - if err := dockerCli.MonitorTtySize(ctx, createResponse.ID, false); err != nil { + if err := MonitorTtySize(ctx, dockerCli, createResponse.ID, false); err != nil { fmt.Fprintf(stderr, "Error monitoring TTY size: %s\n", err) } } diff --git a/api/client/container/start.go b/api/client/container/start.go index 50aaa47896..dbd4bca056 100644 --- a/api/client/container/start.go +++ b/api/client/container/start.go @@ -64,7 +64,7 @@ func runStart(dockerCli *client.DockerCli, opts *startOptions) error { // We always use c.ID instead of container to maintain consistency during `docker start` if !c.Config.Tty { - sigc := dockerCli.ForwardAllSignals(ctx, c.ID) + sigc := ForwardAllSignals(ctx, dockerCli, c.ID) defer signal.StopCatch(sigc) } @@ -119,7 +119,7 @@ func runStart(dockerCli *client.DockerCli, opts *startOptions) error { // 5. Wait for attachment to break. if c.Config.Tty && dockerCli.Out().IsTerminal() { - if err := dockerCli.MonitorTtySize(ctx, c.ID, false); err != nil { + if err := MonitorTtySize(ctx, dockerCli, c.ID, false); err != nil { fmt.Fprintf(dockerCli.Err(), "Error monitoring TTY size: %s\n", err) } } diff --git a/api/client/container/tty.go b/api/client/container/tty.go new file mode 100644 index 0000000000..d87be0dcda --- /dev/null +++ b/api/client/container/tty.go @@ -0,0 +1,103 @@ +package container + +import ( + "fmt" + "os" + gosignal "os/signal" + "runtime" + "time" + + "github.com/Sirupsen/logrus" + "github.com/docker/docker/api/client" + "github.com/docker/docker/pkg/signal" + apiclient "github.com/docker/engine-api/client" + "github.com/docker/engine-api/types" + "golang.org/x/net/context" +) + +// ResizeTtyTo resizes tty to specific height and width +func resizeTtyTo(ctx context.Context, client apiclient.ContainerAPIClient, id string, height, width int, isExec bool) { + if height == 0 && width == 0 { + return + } + + options := types.ResizeOptions{ + Height: height, + Width: width, + } + + var err error + if isExec { + err = client.ContainerExecResize(ctx, id, options) + } else { + err = client.ContainerResize(ctx, id, options) + } + + if err != nil { + logrus.Debugf("Error resize: %s", err) + } +} + +// MonitorTtySize updates the container tty size when the terminal tty changes size +func MonitorTtySize(ctx context.Context, cli *client.DockerCli, id string, isExec bool) error { + resizeTty := func() { + height, width := cli.Out().GetTtySize() + resizeTtyTo(ctx, cli.Client(), id, height, width, isExec) + } + + resizeTty() + + if runtime.GOOS == "windows" { + go func() { + prevH, prevW := cli.Out().GetTtySize() + for { + time.Sleep(time.Millisecond * 250) + h, w := cli.Out().GetTtySize() + + if prevW != w || prevH != h { + resizeTty() + } + prevH = h + prevW = w + } + }() + } else { + sigchan := make(chan os.Signal, 1) + gosignal.Notify(sigchan, signal.SIGWINCH) + go func() { + for range sigchan { + resizeTty() + } + }() + } + return nil +} + +// ForwardAllSignals forwards signals to the container +func ForwardAllSignals(ctx context.Context, cli *client.DockerCli, cid string) chan os.Signal { + sigc := make(chan os.Signal, 128) + signal.CatchAll(sigc) + go func() { + for s := range sigc { + if s == signal.SIGCHLD || s == signal.SIGPIPE { + continue + } + var sig string + for sigStr, sigN := range signal.SignalMap { + if sigN == s { + sig = sigStr + break + } + } + if sig == "" { + fmt.Fprintf(cli.Err(), "Unsupported signal: %v. Discarding.\n", s) + continue + } + + if err := cli.Client().ContainerKill(ctx, cid, sig); err != nil { + logrus.Debugf("Error sending signal: %s", err) + } + } + }() + return sigc +} diff --git a/api/client/utils.go b/api/client/utils.go index c20230dd64..f76f536853 100644 --- a/api/client/utils.go +++ b/api/client/utils.go @@ -5,95 +5,10 @@ import ( "io" "io/ioutil" "os" - gosignal "os/signal" "path/filepath" - "runtime" "strings" - "time" - - "golang.org/x/net/context" - - "github.com/Sirupsen/logrus" - "github.com/docker/docker/api/types" - "github.com/docker/docker/client" - "github.com/docker/docker/pkg/signal" - "github.com/docker/engine-api/types" ) -func (cli *DockerCli) resizeTty(ctx context.Context, id string, isExec bool) { - height, width := cli.Out().GetTtySize() - cli.ResizeTtyTo(ctx, id, height, width, isExec) -} - -// 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 { - return - } - - options := types.ResizeOptions{ - Height: height, - Width: width, - } - - var err error - if isExec { - err = cli.client.ContainerExecResize(ctx, id, options) - } else { - err = cli.client.ContainerResize(ctx, id, options) - } - - if err != nil { - logrus.Debugf("Error resize: %s", err) - } -} - -// GetExecExitCode perform an inspect on the exec command. It returns -// the running state and the exit code. -func (cli *DockerCli) GetExecExitCode(ctx context.Context, execID string) (bool, int, error) { - resp, err := cli.client.ContainerExecInspect(ctx, execID) - if err != nil { - // If we can't connect, then the daemon probably died. - if err != client.ErrConnectionFailed { - return false, -1, err - } - return false, -1, nil - } - - return resp.Running, resp.ExitCode, nil -} - -// MonitorTtySize updates the container tty size when the terminal tty changes size -func (cli *DockerCli) MonitorTtySize(ctx context.Context, id string, isExec bool) error { - cli.resizeTty(ctx, id, isExec) - - if runtime.GOOS == "windows" { - go func() { - prevH, prevW := cli.Out().GetTtySize() - for { - time.Sleep(time.Millisecond * 250) - h, w := cli.Out().GetTtySize() - - if prevW != w || prevH != h { - cli.resizeTty(ctx, id, isExec) - } - prevH = h - prevW = w - } - }() - } else { - sigchan := make(chan os.Signal, 1) - gosignal.Notify(sigchan, signal.SIGWINCH) - go func() { - for range sigchan { - cli.resizeTty(ctx, id, isExec) - } - }() - } - return nil -} - // CopyToFile writes the content of the reader to the specified file func CopyToFile(outfile string, r io.Reader) error { tmpFile, err := ioutil.TempFile(filepath.Dir(outfile), ".docker_temp_") @@ -119,37 +34,6 @@ func CopyToFile(outfile string, r io.Reader) error { return nil } -// ForwardAllSignals forwards signals to the container -// TODO: this can be unexported again once all container commands are under -// api/client/container -func (cli *DockerCli) ForwardAllSignals(ctx context.Context, cid string) chan os.Signal { - sigc := make(chan os.Signal, 128) - signal.CatchAll(sigc) - go func() { - for s := range sigc { - if s == signal.SIGCHLD || s == signal.SIGPIPE { - continue - } - var sig string - for sigStr, sigN := range signal.SignalMap { - if sigN == s { - sig = sigStr - break - } - } - if sig == "" { - fmt.Fprintf(cli.err, "Unsupported signal: %v. Discarding.\n", s) - continue - } - - if err := cli.client.ContainerKill(ctx, cid, sig); err != nil { - logrus.Debugf("Error sending signal: %s", err) - } - } - }() - return sigc -} - // capitalizeFirst capitalizes the first character of string func capitalizeFirst(s string) string { switch l := len(s); l {