From 06fd12fd62e735258dddcd4e8184331c3692e21d Mon Sep 17 00:00:00 2001 From: Lifubang Date: Wed, 19 Sep 2018 14:46:32 +0800 Subject: [PATCH] fixes tty size error Signed-off-by: Lifubang code optimization Signed-off-by: Lifubang fixes no such exec error when tty resize Signed-off-by: Lifubang --- cli/command/container/client_test.go | 38 +++++++++++++------- cli/command/container/tty.go | 53 +++++++++++++++++++++------- cli/command/container/tty_test.go | 30 ++++++++++++++++ 3 files changed, 96 insertions(+), 25 deletions(-) create mode 100644 cli/command/container/tty_test.go diff --git a/cli/command/container/client_test.go b/cli/command/container/client_test.go index a2c39bc6ff81..128a8bd5f836 100644 --- a/cli/command/container/client_test.go +++ b/cli/command/container/client_test.go @@ -12,19 +12,24 @@ import ( type fakeClient struct { client.Client - inspectFunc func(string) (types.ContainerJSON, error) - execInspectFunc func(execID string) (types.ContainerExecInspect, error) - execCreateFunc func(container string, config types.ExecConfig) (types.IDResponse, error) - createContainerFunc func(config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, containerName string) (container.ContainerCreateCreatedBody, error) - containerStartFunc func(container string, options types.ContainerStartOptions) error - imageCreateFunc func(parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error) - infoFunc func() (types.Info, error) - containerStatPathFunc func(container, path string) (types.ContainerPathStat, error) - containerCopyFromFunc func(container, srcPath string) (io.ReadCloser, types.ContainerPathStat, error) - logFunc func(string, types.ContainerLogsOptions) (io.ReadCloser, error) - waitFunc func(string) (<-chan container.ContainerWaitOKBody, <-chan error) - containerListFunc func(types.ContainerListOptions) ([]types.Container, error) - Version string + inspectFunc func(string) (types.ContainerJSON, error) + execInspectFunc func(execID string) (types.ContainerExecInspect, error) + execCreateFunc func(container string, config types.ExecConfig) (types.IDResponse, error) + containerStartFunc func(container string, options types.ContainerStartOptions) error + imageCreateFunc func(parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error) + infoFunc func() (types.Info, error) + containerStatPathFunc func(container, path string) (types.ContainerPathStat, error) + containerCopyFromFunc func(container, srcPath string) (io.ReadCloser, types.ContainerPathStat, error) + logFunc func(string, types.ContainerLogsOptions) (io.ReadCloser, error) + waitFunc func(string) (<-chan container.ContainerWaitOKBody, <-chan error) + containerListFunc func(types.ContainerListOptions) ([]types.Container, error) + containerExecResizeFunc func(id string, options types.ResizeOptions) error + createContainerFunc func( + config *container.Config, + hostConfig *container.HostConfig, + networkingConfig *network.NetworkingConfig, + containerName string) (container.ContainerCreateCreatedBody, error) + Version string } func (f *fakeClient) ContainerList(_ context.Context, options types.ContainerListOptions) ([]types.Container, error) { @@ -124,3 +129,10 @@ func (f *fakeClient) ContainerStart(_ context.Context, container string, options } return nil } + +func (f *fakeClient) ContainerExecResize(_ context.Context, id string, options types.ResizeOptions) error { + if f.containerExecResizeFunc != nil { + return f.containerExecResizeFunc(id, options) + } + return nil +} diff --git a/cli/command/container/tty.go b/cli/command/container/tty.go index cb49ded8ef4f..28614ca2fc32 100644 --- a/cli/command/container/tty.go +++ b/cli/command/container/tty.go @@ -16,9 +16,9 @@ import ( ) // resizeTtyTo resizes tty to specific height and width -func resizeTtyTo(ctx context.Context, client client.ContainerAPIClient, id string, height, width uint, isExec bool) { +func resizeTtyTo(ctx context.Context, client client.ContainerAPIClient, id string, height, width uint, isExec bool) error { if height == 0 && width == 0 { - return + return nil } options := types.ResizeOptions{ @@ -34,19 +34,48 @@ func resizeTtyTo(ctx context.Context, client client.ContainerAPIClient, id strin } if err != nil { - logrus.Debugf("Error resize: %s", err) + logrus.Debugf("Error resize: %s\r", err) } + return err } -// MonitorTtySize updates the container tty size when the terminal tty changes size -func MonitorTtySize(ctx context.Context, cli command.Cli, id string, isExec bool) error { - resizeTty := func() { - height, width := cli.Out().GetTtySize() - resizeTtyTo(ctx, cli.Client(), id, height, width, isExec) - } +// resizeTty is to resize the tty with cli out's tty size +func resizeTty(ctx context.Context, cli command.Cli, id string, isExec bool) error { + height, width := cli.Out().GetTtySize() + return resizeTtyTo(ctx, cli.Client(), id, height, width, isExec) +} - resizeTty() +// safeResizeTty is to resize the tty's size safely +func safeResizeTty(ctx context.Context, cli command.Cli, id string, isExec bool, resizeTtyFunc func(ctx context.Context, cli command.Cli, id string, isExec bool) error) { + rttyFunc := resizeTtyFunc + if rttyFunc == nil { + rttyFunc = resizeTty + } + err := rttyFunc(ctx, cli, id, isExec) + if err != nil { + retryTimes := 5 + retry := 0 + go func() { + for { + time.Sleep(10 * time.Millisecond) + err = rttyFunc(ctx, cli, id, isExec) + if err != nil { + if retry < retryTimes { + retry++ + continue + } else { + fmt.Fprintln(cli.Err(), "Resize tty error, we'll use a default size. You can resize the tty size manually.") + } + } + break + } + }() + } +} +// MonitorTtySize updates the container tty size when the terminal tty changes size +func MonitorTtySize(ctx context.Context, cli command.Cli, id string, isExec bool) error { + safeResizeTty(ctx, cli, id, isExec, nil) if runtime.GOOS == "windows" { go func() { prevH, prevW := cli.Out().GetTtySize() @@ -55,7 +84,7 @@ func MonitorTtySize(ctx context.Context, cli command.Cli, id string, isExec bool h, w := cli.Out().GetTtySize() if prevW != w || prevH != h { - resizeTty() + resizeTty(ctx, cli, id, isExec) } prevH = h prevW = w @@ -66,7 +95,7 @@ func MonitorTtySize(ctx context.Context, cli command.Cli, id string, isExec bool gosignal.Notify(sigchan, signal.SIGWINCH) go func() { for range sigchan { - resizeTty() + resizeTty(ctx, cli, id, isExec) } }() } diff --git a/cli/command/container/tty_test.go b/cli/command/container/tty_test.go new file mode 100644 index 000000000000..f84cddfec96f --- /dev/null +++ b/cli/command/container/tty_test.go @@ -0,0 +1,30 @@ +package container + +import ( + "context" + "testing" + "time" + + "github.com/docker/cli/cli/command" + "github.com/docker/cli/internal/test" + "github.com/docker/docker/api/types" + "github.com/pkg/errors" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" +) + +func TestSafeResizeTtyErrors(t *testing.T) { + expectedError := "Resize tty error, we'll use a default size. You can resize the tty size manually.\n" + fakeContainerExecResizeFunc := func(id string, options types.ResizeOptions) error { + return errors.Errorf("Error response from daemon: no such exec") + } + fakeResizeTtyFunc := func(ctx context.Context, cli command.Cli, id string, isExec bool) error { + height, width := uint(1024), uint(768) + return resizeTtyTo(ctx, cli.Client(), id, height, width, isExec) + } + ctx := context.Background() + cli := test.NewFakeCli(&fakeClient{containerExecResizeFunc: fakeContainerExecResizeFunc}) + safeResizeTty(ctx, cli, "8mm8nn8tt8bb", true, fakeResizeTtyFunc) + time.Sleep(100 * time.Millisecond) + assert.Check(t, is.Equal(expectedError, cli.ErrBuffer().String())) +}