From be5d78ad10868a8928a9c1f018e8868c3135fcc1 Mon Sep 17 00:00:00 2001 From: Nicolas De Loof Date: Thu, 8 Apr 2021 12:15:39 +0200 Subject: [PATCH] resize terminal and monitor SIGWINCH Signed-off-by: Nicolas De Loof --- local/compose/exec.go | 7 ++++ local/compose/resize.go | 75 +++++++++++++++++++++++++++++++++++++++++ local/compose/run.go | 5 +++ 3 files changed, 87 insertions(+) create mode 100644 local/compose/resize.go diff --git a/local/compose/exec.go b/local/compose/exec.go index e5ddbec3d..d2230085a 100644 --- a/local/compose/exec.go +++ b/local/compose/exec.go @@ -82,6 +82,13 @@ func (s *composeService) Exec(ctx context.Context, project *types.Project, opts } defer resp.Close() + if opts.Tty { + err := s.monitorTTySize(ctx, exec.ID, s.apiClient.ContainerExecResize) + if err != nil { + return err + } + } + readChannel := make(chan error, 10) writeChannel := make(chan error, 10) diff --git a/local/compose/resize.go b/local/compose/resize.go new file mode 100644 index 000000000..8453d4e63 --- /dev/null +++ b/local/compose/resize.go @@ -0,0 +1,75 @@ +/* + Copyright 2020 Docker Compose CLI authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package compose + +import ( + "context" + "os" + gosignal "os/signal" + "runtime" + "time" + + "github.com/buger/goterm" + moby "github.com/docker/docker/api/types" + "github.com/docker/docker/pkg/signal" +) + +func (s *composeService) monitorTTySize(ctx context.Context, container string, resize func(context.Context, string, moby.ResizeOptions) error) error { + err := resize(ctx, container, moby.ResizeOptions{ // nolint:errcheck + Height: uint(goterm.Height()), + Width: uint(goterm.Width()), + }) + if err != nil { + return err + } + + sigchan := make(chan os.Signal, 1) + gosignal.Notify(sigchan, signal.SIGWINCH) + + if runtime.GOOS == "windows" { + // Windows has no SIGWINCH support, so we have to poll tty size ¯\_(ツ)_/¯ + go func() { + prevH := goterm.Height() + prevW := goterm.Width() + for { + time.Sleep(time.Millisecond * 250) + h := goterm.Height() + w := goterm.Width() + if prevW != w || prevH != h { + sigchan <- signal.SIGWINCH + } + prevH = h + prevW = w + } + }() + } + + go func() { + for { + select { + case <-sigchan: + resize(ctx, container, moby.ResizeOptions{ // nolint:errcheck + Height: uint(goterm.Height()), + Width: uint(goterm.Width()), + }) + case <-ctx.Done(): + return + } + } + }() + return nil +} diff --git a/local/compose/run.go b/local/compose/run.go index 71c57672f..e4dc1c7e0 100644 --- a/local/compose/run.go +++ b/local/compose/run.go @@ -97,6 +97,11 @@ func (s *composeService) RunOneOffContainer(ctx context.Context, project *types. return 0, err } + err = s.monitorTTySize(ctx, containerID, s.apiClient.ContainerResize) + if err != nil { + return 0, err + } + statusC, errC := s.apiClient.ContainerWait(context.Background(), oneoffContainer.ID, container.WaitConditionNotRunning) select { case status := <-statusC: