Merge pull request #1529 from lifubang/ttyexecresize

fixes 1492: tty initial size error
This commit is contained in:
Silvin Lubecki 2019-02-12 10:31:17 +01:00 committed by GitHub
commit 7f612bfca6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 90 additions and 26 deletions

View File

@ -12,20 +12,24 @@ import (
type fakeClient struct { type fakeClient struct {
client.Client client.Client
inspectFunc func(string) (types.ContainerJSON, error) inspectFunc func(string) (types.ContainerJSON, error)
execInspectFunc func(execID string) (types.ContainerExecInspect, error) execInspectFunc func(execID string) (types.ContainerExecInspect, error)
execCreateFunc func(container string, config types.ExecConfig) (types.IDResponse, 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) createContainerFunc func(config *container.Config,
containerStartFunc func(container string, options types.ContainerStartOptions) error hostConfig *container.HostConfig,
imageCreateFunc func(parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error) networkingConfig *network.NetworkingConfig,
infoFunc func() (types.Info, error) containerName string) (container.ContainerCreateCreatedBody, error)
containerStatPathFunc func(container, path string) (types.ContainerPathStat, error) containerStartFunc func(container string, options types.ContainerStartOptions) error
containerCopyFromFunc func(container, srcPath string) (io.ReadCloser, types.ContainerPathStat, error) imageCreateFunc func(parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error)
logFunc func(string, types.ContainerLogsOptions) (io.ReadCloser, error) infoFunc func() (types.Info, error)
waitFunc func(string) (<-chan container.ContainerWaitOKBody, <-chan error) containerStatPathFunc func(container, path string) (types.ContainerPathStat, error)
containerListFunc func(types.ContainerListOptions) ([]types.Container, error) containerCopyFromFunc func(container, srcPath string) (io.ReadCloser, types.ContainerPathStat, error)
containerExportFunc func(string) (io.ReadCloser, error) logFunc func(string, types.ContainerLogsOptions) (io.ReadCloser, error)
Version string waitFunc func(string) (<-chan container.ContainerWaitOKBody, <-chan error)
containerListFunc func(types.ContainerListOptions) ([]types.Container, error)
containerExportFunc func(string) (io.ReadCloser, error)
containerExecResizeFunc func(id string, options types.ResizeOptions) error
Version string
} }
func (f *fakeClient) ContainerList(_ context.Context, options types.ContainerListOptions) ([]types.Container, error) { func (f *fakeClient) ContainerList(_ context.Context, options types.ContainerListOptions) ([]types.Container, error) {
@ -132,3 +136,10 @@ func (f *fakeClient) ContainerExport(_ context.Context, container string) (io.Re
} }
return nil, nil return nil, nil
} }
func (f *fakeClient) ContainerExecResize(_ context.Context, id string, options types.ResizeOptions) error {
if f.containerExecResizeFunc != nil {
return f.containerExecResizeFunc(id, options)
}
return nil
}

View File

@ -16,9 +16,9 @@ import (
) )
// resizeTtyTo resizes tty to specific height and width // 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 { if height == 0 && width == 0 {
return return nil
} }
options := types.ResizeOptions{ options := types.ResizeOptions{
@ -34,19 +34,42 @@ func resizeTtyTo(ctx context.Context, client client.ContainerAPIClient, id strin
} }
if err != nil { if err != nil {
logrus.Debugf("Error resize: %s", err) logrus.Debugf("Error resize: %s\r", err)
}
return err
}
// 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)
}
// initTtySize is to init the tty's size to the same as the window, if there is an error, it will retry 5 times.
func initTtySize(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
}
if err := rttyFunc(ctx, cli, id, isExec); err != nil {
go func() {
var err error
for retry := 0; retry < 5; retry++ {
time.Sleep(10 * time.Millisecond)
if err = rttyFunc(ctx, cli, id, isExec); err == nil {
break
}
}
if err != nil {
fmt.Fprintln(cli.Err(), "failed to resize tty, using default size")
}
}()
} }
} }
// MonitorTtySize updates the container tty size when the terminal tty changes size // 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 { func MonitorTtySize(ctx context.Context, cli command.Cli, id string, isExec bool) error {
resizeTty := func() { initTtySize(ctx, cli, id, isExec, resizeTty)
height, width := cli.Out().GetTtySize()
resizeTtyTo(ctx, cli.Client(), id, height, width, isExec)
}
resizeTty()
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
go func() { go func() {
prevH, prevW := cli.Out().GetTtySize() prevH, prevW := cli.Out().GetTtySize()
@ -55,7 +78,7 @@ func MonitorTtySize(ctx context.Context, cli command.Cli, id string, isExec bool
h, w := cli.Out().GetTtySize() h, w := cli.Out().GetTtySize()
if prevW != w || prevH != h { if prevW != w || prevH != h {
resizeTty() resizeTty(ctx, cli, id, isExec)
} }
prevH = h prevH = h
prevW = w prevW = w
@ -66,7 +89,7 @@ func MonitorTtySize(ctx context.Context, cli command.Cli, id string, isExec bool
gosignal.Notify(sigchan, signal.SIGWINCH) gosignal.Notify(sigchan, signal.SIGWINCH)
go func() { go func() {
for range sigchan { for range sigchan {
resizeTty() resizeTty(ctx, cli, id, isExec)
} }
}() }()
} }

View File

@ -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 TestInitTtySizeErrors(t *testing.T) {
expectedError := "failed to resize tty, using default size\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})
initTtySize(ctx, cli, "8mm8nn8tt8bb", true, fakeResizeTtyFunc)
time.Sleep(100 * time.Millisecond)
assert.Check(t, is.Equal(expectedError, cli.ErrBuffer().String()))
}