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",