diff --git a/docs/keybindings/Keybindings_de.md b/docs/keybindings/Keybindings_de.md index dc9dd08e..a37002d1 100644 --- a/docs/keybindings/Keybindings_de.md +++ b/docs/keybindings/Keybindings_de.md @@ -20,7 +20,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct d: entfernen e: hide/show stopped containers p: pause - s: anhalten + s: start/stop r: neustarten a: anbinden m: zeige Protokolle diff --git a/docs/keybindings/Keybindings_en.md b/docs/keybindings/Keybindings_en.md index ae21c4dc..85349bec 100644 --- a/docs/keybindings/Keybindings_en.md +++ b/docs/keybindings/Keybindings_en.md @@ -20,7 +20,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct d: remove e: hide/show stopped containers p: pause - s: stop + s: start/stop r: restart a: attach m: view logs diff --git a/docs/keybindings/Keybindings_es.md b/docs/keybindings/Keybindings_es.md index 6b4eb921..e81c2322 100644 --- a/docs/keybindings/Keybindings_es.md +++ b/docs/keybindings/Keybindings_es.md @@ -20,7 +20,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct d: borrar e: esconder/mostrar contenedores parados p: pausa - s: parar + s: start/stop r: reiniciar a: attach m: ver logs diff --git a/docs/keybindings/Keybindings_fr.md b/docs/keybindings/Keybindings_fr.md index 109078b2..1b04466d 100644 --- a/docs/keybindings/Keybindings_fr.md +++ b/docs/keybindings/Keybindings_fr.md @@ -20,7 +20,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct d: supprimer e: cacher/montrer les conteneurs arrêtés p: pause - s: arrêter + s: start/stop r: redémarrer a: attacher m: voir les enregistrements diff --git a/docs/keybindings/Keybindings_nl.md b/docs/keybindings/Keybindings_nl.md index 24477006..6de93739 100644 --- a/docs/keybindings/Keybindings_nl.md +++ b/docs/keybindings/Keybindings_nl.md @@ -20,7 +20,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct d: verwijder e: verberg gestopte containers p: pause - s: stop + s: start/stop r: herstart a: verbinden m: bekijk logs diff --git a/docs/keybindings/Keybindings_pl.md b/docs/keybindings/Keybindings_pl.md index 2f4cb93f..a80a58f7 100644 --- a/docs/keybindings/Keybindings_pl.md +++ b/docs/keybindings/Keybindings_pl.md @@ -20,7 +20,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct d: usuń e: hide/show stopped containers p: pause - s: zatrzymaj + s: start/stop r: restartuj a: przyczep m: pokaż logi diff --git a/docs/keybindings/Keybindings_pt.md b/docs/keybindings/Keybindings_pt.md index 32a557b5..e20cc4f5 100644 --- a/docs/keybindings/Keybindings_pt.md +++ b/docs/keybindings/Keybindings_pt.md @@ -20,7 +20,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct d: remover e: ocultar/mostrar contêineres parados p: pausar - s: parar + s: start/stop r: reiniciar a: anexar m: ver logs diff --git a/docs/keybindings/Keybindings_tr.md b/docs/keybindings/Keybindings_tr.md index 3192a7c2..0650a45e 100644 --- a/docs/keybindings/Keybindings_tr.md +++ b/docs/keybindings/Keybindings_tr.md @@ -20,7 +20,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct d: kaldır e: hide/show stopped containers p: pause - s: durdur + s: start/stop r: yeniden başlat a: bağlan/iliştir m: kayıt defterini görüntüle diff --git a/docs/keybindings/Keybindings_zh.md b/docs/keybindings/Keybindings_zh.md index 0c75fbcb..5eef4ba9 100644 --- a/docs/keybindings/Keybindings_zh.md +++ b/docs/keybindings/Keybindings_zh.md @@ -20,7 +20,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct d: 移除 e: 隐藏/显示已停止的容器 p: 暂停 - s: 停止 + s: start/stop r: 重新启动 a: attach m: 查看日志 diff --git a/pkg/config/app_config.go b/pkg/config/app_config.go index 0ebc1796..e3bb3d52 100644 --- a/pkg/config/app_config.go +++ b/pkg/config/app_config.go @@ -220,6 +220,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 @@ -402,6 +405,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 a638ee29..f54c90bc 100644 --- a/pkg/gui/containers_panel.go +++ b/pkg/gui/containers_panel.go @@ -359,6 +359,8 @@ func (gui *Gui) handleContainersRemoveMenu(g *gocui.Gui, v *gocui.View) error { }) } +// TODO: do same thing for start +// Fix UI not showing it being paused (it should say unpaused) func (gui *Gui) PauseContainer(container *commands.Container) error { return gui.WithWaitingStatus(gui.Tr.PausingStatus, func() (err error) { if container.Details.State.Paused { @@ -384,12 +386,22 @@ func (gui *Gui) handleContainerPause(g *gocui.Gui, v *gocui.View) error { return gui.PauseContainer(ctr) } -func (gui *Gui) handleContainerStop(g *gocui.Gui, v *gocui.View) error { +func (gui *Gui) handleContainerStartStop(g *gocui.Gui, v *gocui.View) error { ctr, err := gui.Panels.Containers.GetSelectedItem() if err != nil { return nil } + if !(ctr.Container.State == "exited" || ctr.Container.State == "running") { + return gui.createErrorPanel(gui.Tr.CannotStartStop) + } + + if ctr.Container.State == "exited" { + return gui.WithWaitingStatus(gui.Tr.StoppingStatus, func() error { + return ctr.Start() + }) + } + return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.StopContainer, func(g *gocui.Gui, v *gocui.View) error { return gui.WithWaitingStatus(gui.Tr.StoppingStatus, func() error { if err := ctr.Stop(); err != nil { @@ -466,11 +478,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) // attach and return the subprocess error - cmd := gui.OSCommand.ExecutableFromString(resolvedCommand) + cmd := gui.OSCommand.ExecutableFromString(command) return gui.runSubprocess(cmd) } diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go index df706024..0d696a38 100644 --- a/pkg/gui/keybindings.go +++ b/pkg/gui/keybindings.go @@ -210,8 +210,8 @@ func (gui *Gui) GetInitialKeybindings() []*Binding { ViewName: "containers", Key: 's', Modifier: gocui.ModNone, - Handler: gui.handleContainerStop, - Description: gui.Tr.Stop, + Handler: gui.handleContainerStartStop, + Description: gui.Tr.StartStop, }, { ViewName: "containers", diff --git a/pkg/gui/subprocess.go b/pkg/gui/subprocess.go index b41ac475..300bfca4 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,8 @@ 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 +84,6 @@ func (gui *Gui) runCommand(cmd *exec.Cmd, msg string) { cmd.Stderr = io.Discard gui.promptToReturn() + + return err } diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index d86b0c84..28dc3649 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -26,6 +26,7 @@ type TranslationSet struct { CannotAttachStoppedContainerError string CannotAccessDockerSocketError string CannotKillChildError string + CannotStartStop string Donate string Cancel string @@ -59,6 +60,7 @@ type TranslationSet struct { Down string DownWithVolumes string Start string + StartStop string Rebuild string Recreate string PreviousContext string @@ -161,6 +163,7 @@ func englishSet() TranslationSet { CannotAttachStoppedContainerError: "You cannot attach to a stopped container, you need to start it first (which you can actually do with the 'r' key) (yes I'm too lazy to do this automatically for you) (pretty cool that I get to communicate one-on-one with you in the form of an error message though)", CannotAccessDockerSocketError: "Can't access docker socket at: unix:///var/run/docker.sock\nRun lazydocker as root or read https://docs.docker.com/install/linux/linux-postinstall/", CannotKillChildError: "Waited three seconds for child process to stop. There may be an orphan process that continues to run on your system.", + CannotStartStop: "Cannot start stop this container", Donate: "Donate", Confirm: "Confirm", @@ -190,6 +193,7 @@ func englishSet() TranslationSet { Down: "down project", DownWithVolumes: "down project with volumes", Start: "start", + StartStop: "start/stop", Rebuild: "rebuild", Recreate: "recreate", PreviousContext: "previous tab",