From 9421e9b204962f24bf30738f1285c24fb370e449 Mon Sep 17 00:00:00 2001 From: rohitkumar1369 Date: Mon, 16 Jun 2025 20:00:47 +0530 Subject: [PATCH 1/5] handles -n flagin any position and fixes args that need to be passed to kubectl , but get passed to command in exec --- debug/handler.go | 9 ++++- pkg/kube/executor.go | 93 +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 90 insertions(+), 12 deletions(-) diff --git a/debug/handler.go b/debug/handler.go index eb9f59b..347b5c4 100644 --- a/debug/handler.go +++ b/debug/handler.go @@ -71,13 +71,18 @@ func (h *debugHandler) getAuth(r *http.Request, ps httprouter.Params) (*reqAuth, Account: sd.GetAccount(), Partner: sd.GetPartner(), Organization: sd.GetOrganization(), - ProjectID: sd.GetProject().GetList()[0].GetProjectId(), - Project: sd.GetProject().GetList()[0].GetProject(), IsSSOUser: sd.GetIsSsoUser(), Username: sd.GetUsername(), Groups: sd.GetGroups(), } + // Check if project list exists and has items + projectList := sd.GetProject().GetList() + if len(projectList) > 0 { + auth.ProjectID = projectList[0].GetProjectId() + auth.Project = projectList[0].GetProject() + } + return auth, nil } diff --git a/pkg/kube/executor.go b/pkg/kube/executor.go index 5581224..7e759da 100644 --- a/pkg/kube/executor.go +++ b/pkg/kube/executor.go @@ -51,18 +51,36 @@ func NewIOExecutor(rw io.ReadWriter, rows, cols uint16, args []string, event *au var execArgs []string + // Add kubectl flags first + for _, arg := range args { + if strings.TrimSpace(arg) != "" { + execArgs = append(execArgs, arg) + } + } + // appending kubectl commands to execute p, err := shellwords.Parse(s) if err != nil { _log.Error("unable to parse command", zap.Error(err)) return } + + // Add all parsed arguments first execArgs = append(execArgs, p...) - // appending default flags - for _, arg := range args { - if strings.TrimSpace(arg) != "" { - execArgs = append(execArgs, arg) + // Handle namespace flag specially - look for it in any position + for i := 0; i < len(execArgs); i++ { + if execArgs[i] == "-n" && i+1 < len(execArgs) { + // Move namespace flag and its value to the beginning of the command + // after the initial kubectl flags + nsFlag := execArgs[i] + nsValue := execArgs[i+1] + // Remove the flag and value from their current position + execArgs = append(execArgs[:i], execArgs[i+2:]...) + // Insert them after the initial kubectl flags + initialFlagsLen := len(args) + execArgs = append(execArgs[:initialFlagsLen], append([]string{nsFlag, nsValue}, execArgs[initialFlagsLen:]...)...) + break // Only handle the first occurrence of -n } } @@ -83,20 +101,75 @@ func NewIOExecutor(rw io.ReadWriter, rows, cols uint16, args []string, event *au var wg sync.WaitGroup wg.Add(2) + // Create a done channel to signal goroutines to stop + done := make(chan struct{}) + + // Copy from PTY to websocket go func() { defer wg.Done() - _, err := io.Copy(rw, f) - _log.Infow("exited copy from pty", "error", err) + buf := make([]byte, 1024) + for { + select { + case <-done: + return + default: + n, err := f.Read(buf) + if err != nil { + if err != io.EOF { + _log.Infow("error reading from pty", "error", err) + } + return + } + if n > 0 { + if _, err := rw.Write(buf[:n]); err != nil { + _log.Infow("error writing to websocket", "error", err) + return + } + } + } + } }() + + // Copy from websocket to PTY go func() { defer wg.Done() - _, err := io.Copy(f, rw) - _log.Infow("exited copy to pty", "error", err) + buf := make([]byte, 1024) + for { + select { + case <-done: + return + default: + n, err := rw.Read(buf) + if err != nil { + if err != io.EOF { + _log.Infow("error reading from websocket", "error", err) + } + return + } + if n > 0 { + if _, err := f.Write(buf[:n]); err != nil { + _log.Infow("error writing to pty", "error", err) + return + } + } + } + } }() - cmd.Wait() - f.Close() + // Wait for command to complete + err = cmd.Wait() + if err != nil { + _log.Infow("command exited with error", "error", err) + } + + // Signal goroutines to stop + close(done) + + // Wait for goroutines to finish wg.Wait() + + // Close PTY after goroutines are done + f.Close() return } From 4fe064fcc077ebbdd15d685dc5ac65600e6d022b Mon Sep 17 00:00:00 2001 From: rohitkumar1369 Date: Mon, 16 Jun 2025 20:33:02 +0530 Subject: [PATCH 2/5] fix graceful handling of pty exit --- pkg/kube/executor.go | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/pkg/kube/executor.go b/pkg/kube/executor.go index 7e759da..d67f832 100644 --- a/pkg/kube/executor.go +++ b/pkg/kube/executor.go @@ -104,6 +104,15 @@ func NewIOExecutor(rw io.ReadWriter, rows, cols uint16, args []string, event *au // Create a done channel to signal goroutines to stop done := make(chan struct{}) + // Handle cleanup on exit + defer func() { + close(done) + wg.Wait() + f.Close() + // Send a newline to ensure prompt is on a new line + rw.Write([]byte{'\r', '\n'}) + }() + // Copy from PTY to websocket go func() { defer wg.Done() @@ -162,14 +171,6 @@ func NewIOExecutor(rw io.ReadWriter, rows, cols uint16, args []string, event *au _log.Infow("command exited with error", "error", err) } - // Signal goroutines to stop - close(done) - - // Wait for goroutines to finish - wg.Wait() - - // Close PTY after goroutines are done - f.Close() return } From 80486ca694e87c02868df20d76d1c21940b36f15 Mon Sep 17 00:00:00 2001 From: rohitkumar1369 Date: Mon, 16 Jun 2025 20:59:45 +0530 Subject: [PATCH 3/5] fix graceful handling of pty exit --- pkg/kube/executor.go | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/pkg/kube/executor.go b/pkg/kube/executor.go index d67f832..2cabb57 100644 --- a/pkg/kube/executor.go +++ b/pkg/kube/executor.go @@ -8,6 +8,7 @@ import ( "os/exec" "strings" "sync" + "time" "github.com/creack/pty" "github.com/mattn/go-shellwords" @@ -106,9 +107,18 @@ func NewIOExecutor(rw io.ReadWriter, rows, cols uint16, args []string, event *au // Handle cleanup on exit defer func() { + // Signal goroutines to stop first close(done) + // Wait for goroutines to finish wg.Wait() - f.Close() + // Send exit sequence to the PTY + if f != nil { + // Send Ctrl-D to gracefully exit + f.Write([]byte{0x04}) + // Give it a moment to process + time.Sleep(100 * time.Millisecond) + f.Close() + } // Send a newline to ensure prompt is on a new line rw.Write([]byte{'\r', '\n'}) }() @@ -124,7 +134,8 @@ func NewIOExecutor(rw io.ReadWriter, rows, cols uint16, args []string, event *au default: n, err := f.Read(buf) if err != nil { - if err != io.EOF { + // Don't log EOF or I/O errors as they're expected during cleanup + if err != io.EOF && !strings.Contains(err.Error(), "input/output error") { _log.Infow("error reading from pty", "error", err) } return @@ -157,7 +168,10 @@ func NewIOExecutor(rw io.ReadWriter, rows, cols uint16, args []string, event *au } if n > 0 { if _, err := f.Write(buf[:n]); err != nil { - _log.Infow("error writing to pty", "error", err) + // Don't log I/O errors as they're expected during cleanup + if !strings.Contains(err.Error(), "input/output error") { + _log.Infow("error writing to pty", "error", err) + } return } } From 990044c50b49d6febbfeaa0517e240324b69b062 Mon Sep 17 00:00:00 2001 From: rohitkumar1369 Date: Mon, 16 Jun 2025 21:15:50 +0530 Subject: [PATCH 4/5] fix graceful handling of kubectl exit --- pkg/kube/executor.go | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/pkg/kube/executor.go b/pkg/kube/executor.go index 2cabb57..0c2fed1 100644 --- a/pkg/kube/executor.go +++ b/pkg/kube/executor.go @@ -52,13 +52,6 @@ func NewIOExecutor(rw io.ReadWriter, rows, cols uint16, args []string, event *au var execArgs []string - // Add kubectl flags first - for _, arg := range args { - if strings.TrimSpace(arg) != "" { - execArgs = append(execArgs, arg) - } - } - // appending kubectl commands to execute p, err := shellwords.Parse(s) if err != nil { @@ -66,9 +59,16 @@ func NewIOExecutor(rw io.ReadWriter, rows, cols uint16, args []string, event *au return } - // Add all parsed arguments first + // Add command first execArgs = append(execArgs, p...) + // Add kubectl flags after the command + for _, arg := range args { + if strings.TrimSpace(arg) != "" { + execArgs = append(execArgs, arg) + } + } + // Handle namespace flag specially - look for it in any position for i := 0; i < len(execArgs); i++ { if execArgs[i] == "-n" && i+1 < len(execArgs) { @@ -78,9 +78,8 @@ func NewIOExecutor(rw io.ReadWriter, rows, cols uint16, args []string, event *au nsValue := execArgs[i+1] // Remove the flag and value from their current position execArgs = append(execArgs[:i], execArgs[i+2:]...) - // Insert them after the initial kubectl flags - initialFlagsLen := len(args) - execArgs = append(execArgs[:initialFlagsLen], append([]string{nsFlag, nsValue}, execArgs[initialFlagsLen:]...)...) + // Insert them after the command name + execArgs = append(execArgs[:1], append([]string{nsFlag, nsValue}, execArgs[1:]...)...) break // Only handle the first occurrence of -n } } @@ -113,8 +112,8 @@ func NewIOExecutor(rw io.ReadWriter, rows, cols uint16, args []string, event *au wg.Wait() // Send exit sequence to the PTY if f != nil { - // Send Ctrl-D to gracefully exit - f.Write([]byte{0x04}) + // Send exit command to the shell + f.Write([]byte("exit\n")) // Give it a moment to process time.Sleep(100 * time.Millisecond) f.Close() From a4509f25e14f5c0881f0144944e5b93c256580a5 Mon Sep 17 00:00:00 2001 From: rohitkumar1369 Date: Mon, 16 Jun 2025 21:31:08 +0530 Subject: [PATCH 5/5] Revert "fix graceful handling of kubectl exit" This reverts commit 990044c50b49d6febbfeaa0517e240324b69b062. --- pkg/kube/executor.go | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/pkg/kube/executor.go b/pkg/kube/executor.go index 0c2fed1..2cabb57 100644 --- a/pkg/kube/executor.go +++ b/pkg/kube/executor.go @@ -52,6 +52,13 @@ func NewIOExecutor(rw io.ReadWriter, rows, cols uint16, args []string, event *au var execArgs []string + // Add kubectl flags first + for _, arg := range args { + if strings.TrimSpace(arg) != "" { + execArgs = append(execArgs, arg) + } + } + // appending kubectl commands to execute p, err := shellwords.Parse(s) if err != nil { @@ -59,16 +66,9 @@ func NewIOExecutor(rw io.ReadWriter, rows, cols uint16, args []string, event *au return } - // Add command first + // Add all parsed arguments first execArgs = append(execArgs, p...) - // Add kubectl flags after the command - for _, arg := range args { - if strings.TrimSpace(arg) != "" { - execArgs = append(execArgs, arg) - } - } - // Handle namespace flag specially - look for it in any position for i := 0; i < len(execArgs); i++ { if execArgs[i] == "-n" && i+1 < len(execArgs) { @@ -78,8 +78,9 @@ func NewIOExecutor(rw io.ReadWriter, rows, cols uint16, args []string, event *au nsValue := execArgs[i+1] // Remove the flag and value from their current position execArgs = append(execArgs[:i], execArgs[i+2:]...) - // Insert them after the command name - execArgs = append(execArgs[:1], append([]string{nsFlag, nsValue}, execArgs[1:]...)...) + // Insert them after the initial kubectl flags + initialFlagsLen := len(args) + execArgs = append(execArgs[:initialFlagsLen], append([]string{nsFlag, nsValue}, execArgs[initialFlagsLen:]...)...) break // Only handle the first occurrence of -n } } @@ -112,8 +113,8 @@ func NewIOExecutor(rw io.ReadWriter, rows, cols uint16, args []string, event *au wg.Wait() // Send exit sequence to the PTY if f != nil { - // Send exit command to the shell - f.Write([]byte("exit\n")) + // Send Ctrl-D to gracefully exit + f.Write([]byte{0x04}) // Give it a moment to process time.Sleep(100 * time.Millisecond) f.Close()