derailed / k9s

🐶 Kubernetes CLI To Manage Your Clusters In Style!
https://k9scli.io
Apache License 2.0
27.04k stars 1.7k forks source link

shell initial window dimensions are still wrong #1267

Open movsb opened 3 years ago

movsb commented 3 years ago

Describe the bug

Can refer to this k8s issue: https://github.com/kubernetes/kubernetes/issues/47990

Still not fixed, both on My MacOS and Ubuntu.

To Reproduce

After being shell into a pod, the dimensions of the window size is fixed to 80x24, which is not initially equal to my terminal window size 195x41.

Now type in the command line, characters are wrapped into the same line and overwrite previous output. (See those zeros.)

dev:k9s (master) → stty size
41 195
dev:k9s (master) → kubectl exec -it hello-57f9ff8548-dnpm2 -- bash
root@hello-57f9ff8548-dnpm2:/# tput cols
80
root@hello-57f9ff8548-dnpm2:/# tput lines
24
000000^Clo-57f9ff8548-dnpm2:/# 0000000000000000000000000000000000000000000000000 
root@hello-57f9ff8548-dnpm2:/# exit

Expected behavior A clear and concise description of what you expected to happen.

If I use env to pass in the COLUMNS and LINES environment variables, it's fixed. (Suggested by this )

dev:k9s (master) → kubectl exec -it hello-57f9ff8548-dnpm2 -- env COLUMNS=195 LINES=41 bash
root@hello-57f9ff8548-dnpm2:/# tput cols
195
root@hello-57f9ff8548-dnpm2:/# tput lines
41
root@hello-57f9ff8548-dnpm2:/# 0000000000000000000000000000000000000000000000000000000000000000000000000^C
root@hello-57f9ff8548-dnpm2:/# exit

Screenshots If applicable, add screenshots to help explain your problem.

Versions (please complete the following information):

Additional context

Resizing the terminal window manually can fix this too.

How I fixed it locally

diff --git a/internal/config/k9s.go b/internal/config/k9s.go
index c84c0ece..c3739b12 100644
--- a/internal/config/k9s.go
+++ b/internal/config/k9s.go
@@ -19,6 +19,7 @@ type K9s struct {
    Crumbsless        bool                `yaml:"crumbsless"`
    ReadOnly          bool                `yaml:"readOnly"`
    NoIcons           bool                `yaml:"noIcons"`
+   FixWindowSize     bool                `yaml:"fixWindowSize"`
    Logger            *Logger             `yaml:"logger"`
    CurrentContext    string              `yaml:"currentContext"`
    CurrentCluster    string              `yaml:"currentCluster"`
diff --git a/internal/view/pod.go b/internal/view/pod.go
index 342e58f7..33e9d22c 100644
--- a/internal/view/pod.go
+++ b/internal/view/pod.go
@@ -4,6 +4,8 @@ import (
    "context"
    "errors"
    "fmt"
+   "os"
+   "os/exec"

    "github.com/derailed/k9s/internal"
    "github.com/derailed/k9s/internal/client"
@@ -265,7 +267,17 @@ func shellIn(a *App, fqn, co string) {
    if err != nil {
        log.Warn().Err(err).Msgf("os detect failed")
    }
-   args := computeShellArgs(fqn, co, a.Conn().Config().Flags().KubeConfig, os)
+
+   env := make(map[string]string)
+
+   if a.Config.K9s.FixWindowSize {
+       if cols, lines, ok := getWindowSize(); ok {
+           env["COLUMNS"] = fmt.Sprint(cols)
+           env["LINES"] = fmt.Sprint(lines)
+       }
+   }
+
+   args := computeShellArgs(fqn, co, a.Conn().Config().Flags().KubeConfig, os, env)

    c := color.New(color.BgGreen).Add(color.FgBlack).Add(color.Bold)
    if !runK(a, shellOpts{clear: true, banner: c.Sprintf(bannerFmt, fqn, co), args: args}) {
@@ -273,6 +285,15 @@ func shellIn(a *App, fqn, co string) {
    }
 }

+func getWindowSize() (cols int, lines int, ok bool) {
+   cmd := exec.Command("stty", "size")
+   cmd.Stdin = os.Stdin
+   out, _ := cmd.Output()
+   n, err := fmt.Sscanf(string(out), "%d %d", &lines, &cols)
+   ok = n == 2 && err == nil && cols > 0 && lines > 0
+   return
+}
+
 func containerAttachIn(a *App, comp model.Component, path, co string) error {
    if co != "" {
        resumeAttachIn(a, comp, path, co)
@@ -315,12 +336,19 @@ func attachIn(a *App, path, co string) {
    }
 }

-func computeShellArgs(path, co string, kcfg *string, os string) []string {
+func computeShellArgs(path, co string, kcfg *string, os string, env map[string]string) []string {
    args := buildShellArgs("exec", path, co, kcfg)
    if os == windowsOS {
        return append(args, "--", powerShell)
    }
-   return append(args, "--", "sh", "-c", shellCheck)
+   args = append(args, "--")
+   if len(env) > 0 {
+       args = append(args, "env")
+       for k, v := range env {
+           args = append(args, fmt.Sprintf("%s=%s", k, v))
+       }
+   }
+   return append(args, "sh", "-c", shellCheck)
 }

 func buildShellArgs(cmd, path, co string, kcfg *string) []string {
diff --git a/internal/view/pod_int_test.go b/internal/view/pod_int_test.go
index e359924b..9bde1cca 100644
--- a/internal/view/pod_int_test.go
+++ b/internal/view/pod_int_test.go
@@ -55,7 +55,7 @@ func TestComputeShellArgs(t *testing.T) {
    for k := range uu {
        u := uu[k]
        t.Run(k, func(t *testing.T) {
-           args := computeShellArgs(u.fqn, u.co, u.cfg, u.os)
+           args := computeShellArgs(u.fqn, u.co, u.cfg, u.os, nil)
            assert.Equal(t, u.e, strings.Join(args, " "))
        })
    }
derailed commented 3 years ago

@movsb Thank you for reporting this! Could you file a PR on this issue? Thank you!

movsb commented 3 years ago

@derailed Will do it after the Chinese National Day. Thank you.✈️

cdcrothers commented 2 years ago

+1

Steps to reproduce issue:

kubetcl exec -it pod-name bash
echo 'a really long line of text that overflows onto second line'
# copy the multi-line text in terminal
# expected: clipboard contains a single line copied
# actual: clipboard contains newline where the line break occurred