From 0beb4dd4d4389445f87db99ef56fc25ba9e33833 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20P=C3=89AU?= Date: Sun, 16 Jun 2024 00:16:06 +0200 Subject: [PATCH 1/3] bubble up errors on command running --- pkg/gui/subprocess.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/pkg/gui/subprocess.go b/pkg/gui/subprocess.go index b41ac4757..943b4fe0f 100644 --- a/pkg/gui/subprocess.go +++ b/pkg/gui/subprocess.go @@ -26,7 +26,7 @@ func (gui *Gui) runSubprocessWithMessage(cmd *exec.Cmd, msg string) error { gui.PauseBackgroundThreads = true - gui.runCommand(cmd, msg) + err := gui.runCommand(cmd, msg) if err := gui.g.Resume(); err != nil { return gui.createErrorPanel(err.Error()) @@ -34,7 +34,7 @@ func (gui *Gui) runSubprocessWithMessage(cmd *exec.Cmd, msg string) error { gui.PauseBackgroundThreads = false - return nil + return err } func (gui *Gui) runCommand(cmd *exec.Cmd, msg string) { @@ -58,9 +58,9 @@ func (gui *Gui) runCommand(cmd *exec.Cmd, msg string) { if msg != "" { fmt.Fprintf(os.Stdout, "\n%s\n\n", utils.ColoredString(msg, color.FgGreen)) } - if err := cmd.Run(); err != nil { - // not handling the error explicitly because usually we're going to see it - // in the output anyway + err := cmd.Run() + + if err != nil { gui.Log.Error(err) } @@ -69,4 +69,6 @@ func (gui *Gui) runCommand(cmd *exec.Cmd, msg string) { cmd.Stderr = io.Discard gui.promptToReturn() + + return err } From 6cd2e5625dbc6879ec1695e8f7221d7c7df4e040 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20P=C3=89AU?= Date: Sun, 16 Jun 2024 00:16:13 +0200 Subject: [PATCH 2/3] add silent command running --- pkg/gui/subprocess.go | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/pkg/gui/subprocess.go b/pkg/gui/subprocess.go index 943b4fe0f..05549ce6c 100644 --- a/pkg/gui/subprocess.go +++ b/pkg/gui/subprocess.go @@ -37,7 +37,23 @@ func (gui *Gui) runSubprocessWithMessage(cmd *exec.Cmd, msg string) error { return err } -func (gui *Gui) runCommand(cmd *exec.Cmd, msg string) { +func (gui *Gui) runCommandSilently(cmd *exec.Cmd) error { + stop := make(chan os.Signal, 1) + defer signal.Stop(stop) + + go func() { + signal.Notify(stop, os.Interrupt) + <-stop + + if err := gui.OSCommand.Kill(cmd); err != nil { + gui.Log.Error(err) + } + }() + + return cmd.Run() +} + +func (gui *Gui) runCommand(cmd *exec.Cmd, msg string) error { cmd.Stdout = os.Stdout cmd.Stderr = os.Stdout cmd.Stdin = os.Stdin From 0f8786995e09125c3e7bc8267a9efb456f814d07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20P=C3=89AU?= Date: Sun, 16 Jun 2024 00:16:39 +0200 Subject: [PATCH 3/3] add new config option to add handle preferred shells to execute on the container --- pkg/config/app_config.go | 4 ++++ pkg/gui/containers_panel.go | 27 ++++++++++++++++++++++++--- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/pkg/config/app_config.go b/pkg/config/app_config.go index d35a244d6..c87bad622 100644 --- a/pkg/config/app_config.go +++ b/pkg/config/app_config.go @@ -216,6 +216,9 @@ type CommandTemplatesConfig struct { // ServiceTop is the command for viewing the processes under a given service ServiceTop string `yaml:"serviceTop,omitempty"` + + // List of shells that, in given order, will be tried when attaching to a container. + PreferredExecShells []string `yaml:"preferedExecShell,omitempty"` } // OSConfig contains config on the level of the os @@ -398,6 +401,7 @@ func GetDefaultConfig() UserConfig { DockerComposeConfig: "{{ .DockerCompose }} config", CheckDockerComposeConfig: "{{ .DockerCompose }} config --quiet", ServiceTop: "{{ .DockerCompose }} top {{ .Service.Name }}", + PreferredExecShells: []string{}, }, CustomCommands: CustomCommands{ Containers: []CustomCommand{}, diff --git a/pkg/gui/containers_panel.go b/pkg/gui/containers_panel.go index 24965c6c0..4c60b81fe 100644 --- a/pkg/gui/containers_panel.go +++ b/pkg/gui/containers_panel.go @@ -449,11 +449,32 @@ func (gui *Gui) containerExecShell(container *commands.Container) error { commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{ Container: container, }) + var command string + shell := "" + + preferredExecShells := gui.Config.UserConfig.CommandTemplates.PreferredExecShells + if len(preferredExecShells) > 0 { + for _, preferredExecShell := range preferredExecShells { + command := utils.ApplyTemplate(fmt.Sprintf("docker exec {{ .Container.ID }} which %s", preferredExecShell), commandObject) + + err := gui.runCommandSilently(gui.OSCommand.ExecutableFromString(command)) + if err == nil { + shell = preferredExecShell + break + } + } + } + + // TODO: Use SDK + // Use default implementation in case we cannot fulfill user's preference + if shell == "" { + command = utils.ApplyTemplate("docker exec -it {{ .Container.ID }} /bin/sh -c 'eval $(grep ^$(id -un): /etc/passwd | cut -d : -f 7-)'", commandObject) + } else { + command = utils.ApplyTemplate(fmt.Sprintf("docker exec -it {{ .Container.ID }} %s", shell), commandObject) + } - // TODO: use SDK - resolvedCommand := utils.ApplyTemplate("docker exec -it {{ .Container.ID }} /bin/sh -c 'eval $(grep ^$(id -un): /etc/passwd | cut -d : -f 7-)'", commandObject) + cmd := gui.OSCommand.ExecutableFromString(command) // attach and return the subprocess error - cmd := gui.OSCommand.ExecutableFromString(resolvedCommand) return gui.runSubprocess(cmd) }