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) } diff --git a/pkg/gui/subprocess.go b/pkg/gui/subprocess.go index b41ac4757..05549ce6c 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,10 +34,26 @@ 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) { +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 @@ -58,9 +74,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 +85,6 @@ func (gui *Gui) runCommand(cmd *exec.Cmd, msg string) { cmd.Stderr = io.Discard gui.promptToReturn() + + return err }