support custom commands that filter down to service names

This commit is contained in:
Jesse Duffield 2019-06-29 21:29:35 +10:00
parent 4856a3d872
commit 4e86732cf2
6 changed files with 102 additions and 60 deletions

View file

@ -13,6 +13,7 @@ import (
"github.com/fatih/color" "github.com/fatih/color"
"github.com/go-errors/errors" "github.com/go-errors/errors"
"github.com/jesseduffield/lazydocker/pkg/config" "github.com/jesseduffield/lazydocker/pkg/config"
"github.com/jesseduffield/lazydocker/pkg/i18n"
"github.com/jesseduffield/lazydocker/pkg/utils" "github.com/jesseduffield/lazydocker/pkg/utils"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"golang.org/x/xerrors" "golang.org/x/xerrors"
@ -39,6 +40,7 @@ type Container struct {
Details Details Details Details
MonitoringStats bool MonitoringStats bool
DockerCommand LimitedDockerCommand DockerCommand LimitedDockerCommand
Tr *i18n.TranslationSet
} }
// Details is a struct containing what we get back from `docker inspect` on a container // Details is a struct containing what we get back from `docker inspect` on a container
@ -342,7 +344,11 @@ func (c *Container) Restart() error {
func (c *Container) Attach() (*exec.Cmd, error) { func (c *Container) Attach() (*exec.Cmd, error) {
// verify that we can in fact attach to this container // verify that we can in fact attach to this container
if !c.Details.Config.OpenStdin { if !c.Details.Config.OpenStdin {
return nil, errors.New("Container does not support attaching. You must either run the service with the '-it' flag or use `stdin_open: true, tty: true` in the docker-compose.yml file") return nil, errors.New(c.Tr.UnattachableContainerError)
}
if c.Container.State == "exited" {
return nil, errors.New(c.Tr.CannotAttachStoppedContainerError)
} }
cmd := c.OSCommand.PrepareSubProcess("docker", "attach", "--sig-proxy=false", c.ID) cmd := c.OSCommand.PrepareSubProcess("docker", "attach", "--sig-proxy=false", c.ID)

View file

@ -303,6 +303,7 @@ func (c *DockerCommand) GetContainers() ([]*Container, error) {
Log: c.Log, Log: c.Log,
Config: c.Config, Config: c.Config,
DockerCommand: c, DockerCommand: c,
Tr: c.Tr,
} }
} }

View file

@ -183,11 +183,17 @@ type CustomCommands struct {
// CustomCommand is a template for a command we want to run against a service or container // CustomCommand is a template for a command we want to run against a service or container
type CustomCommand struct { type CustomCommand struct {
// Name is the name of the command, purely for visual display
Name string `yaml:"name"`
// Attach tells us whether to switch to a subprocess to interact with the called program, or just read its output. If Attach is set to false, the command will run in the background. I'm open to the idea of having a third option where the output plays in the main panel. // Attach tells us whether to switch to a subprocess to interact with the called program, or just read its output. If Attach is set to false, the command will run in the background. I'm open to the idea of having a third option where the output plays in the main panel.
Attach bool Attach bool `yaml:"attach"`
// Command is the command we want to run. We can use the go templates here as well. One example might be `{{ .DockerCompose }} exec {{ .Service.Name }} /bin/sh` // Command is the command we want to run. We can use the go templates here as well. One example might be `{{ .DockerCompose }} exec {{ .Service.Name }} /bin/sh`
Command string Command string `yaml:"command"`
// ServiceNames is used to restrict this command to just one or more services. An example might be 'rails migrate' for your rails api service(s). This field has no effect on customcommands under the 'communications' part of the customCommand config.
ServiceNames []string `yaml:"serviceNames"`
} }
// GetDefaultConfig returns the application default configuration // GetDefaultConfig returns the application default configuration
@ -226,8 +232,9 @@ func GetDefaultConfig() UserConfig {
CustomCommands: CustomCommands{ CustomCommands: CustomCommands{
Containers: []CustomCommand{ Containers: []CustomCommand{
{ {
Attach: true, Name: "bash",
Command: "docker exec -it {{ .Container.ID }} /bin/sh", Command: "docker exec -it {{ .Container.ID }} /bin/sh",
Attach: true,
}, },
}, },
Services: []CustomCommand{}, Services: []CustomCommand{},

View file

@ -1,6 +1,7 @@
package gui package gui
import ( import (
"github.com/fatih/color"
"github.com/jesseduffield/lazydocker/pkg/commands" "github.com/jesseduffield/lazydocker/pkg/commands"
"github.com/jesseduffield/lazydocker/pkg/config" "github.com/jesseduffield/lazydocker/pkg/config"
"github.com/jesseduffield/lazydocker/pkg/utils" "github.com/jesseduffield/lazydocker/pkg/utils"
@ -10,13 +11,14 @@ type customCommandOption struct {
customCommand config.CustomCommand customCommand config.CustomCommand
description string description string
command string command string
name string
runCommand bool runCommand bool
attach bool attach bool
} }
// GetDisplayStrings is a function. // GetDisplayStrings is a function.
func (r *customCommandOption) GetDisplayStrings(isFocused bool) []string { func (r *customCommandOption) GetDisplayStrings(isFocused bool) []string {
return []string{r.description} return []string{r.name, utils.ColoredString(r.description, color.FgCyan)}
} }
func (gui *Gui) createCustomCommandMenu(customCommands []config.CustomCommand, commandObject commands.CommandObject) error { func (gui *Gui) createCustomCommandMenu(customCommands []config.CustomCommand, commandObject commands.CommandObject) error {
@ -30,11 +32,12 @@ func (gui *Gui) createCustomCommandMenu(customCommands []config.CustomCommand, c
command: resolvedCommand, command: resolvedCommand,
runCommand: true, runCommand: true,
attach: command.Attach, attach: command.Attach,
name: command.Name,
} }
} }
options[len(options)-1] = &customCommandOption{ options[len(options)-1] = &customCommandOption{
description: gui.Tr.Cancel, name: gui.Tr.Cancel,
runCommand: false, runCommand: false,
} }
handleMenuPress := func(index int) error { handleMenuPress := func(index int) error {

View file

@ -8,6 +8,7 @@ import (
"github.com/go-errors/errors" "github.com/go-errors/errors"
"github.com/jesseduffield/gocui" "github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazydocker/pkg/commands" "github.com/jesseduffield/lazydocker/pkg/commands"
"github.com/jesseduffield/lazydocker/pkg/config"
"github.com/jesseduffield/lazydocker/pkg/utils" "github.com/jesseduffield/lazydocker/pkg/utils"
) )
@ -376,7 +377,25 @@ func (gui *Gui) handleServicesCustomCommand(g *gocui.Gui, v *gocui.View) error {
Container: service.Container, Container: service.Container,
}) })
customCommands := gui.Config.UserConfig.CustomCommands.Services var customCommands []config.CustomCommand
customServiceCommands := gui.Config.UserConfig.CustomCommands.Services
// we only include service commands if they have no serviceNames defined or if our service happens to be one of the serviceNames defined
L:
for _, cmd := range customServiceCommands {
if len(cmd.ServiceNames) == 0 {
customCommands = append(customCommands, cmd)
continue L
}
for _, serviceName := range cmd.ServiceNames {
if serviceName == service.Name {
// appending these to the top given they're more likely to be selected
customCommands = append([]config.CustomCommand{cmd}, customCommands...)
continue L
}
}
}
if service.Container != nil { if service.Container != nil {
customCommands = append(customCommands, gui.Config.UserConfig.CustomCommands.Containers...) customCommands = append(customCommands, gui.Config.UserConfig.CustomCommands.Containers...)
} }

View file

@ -23,54 +23,57 @@ type TranslationSet struct {
ConfirmQuit string ConfirmQuit string
ErrorOccurred string ErrorOccurred string
ConnectionFailed string ConnectionFailed string
Donate string UnattachableContainerError string
Cancel string CannotAttachStoppedContainerError string
CustomCommandTitle string
Remove string Donate string
ForceRemove string Cancel string
RemoveWithVolumes string CustomCommandTitle string
MustForceToRemoveContainer string Remove string
Confirm string ForceRemove string
Return string RemoveWithVolumes string
FocusMain string MustForceToRemoveContainer string
StopContainer string Confirm string
RestartingStatus string Return string
StoppingStatus string FocusMain string
RemovingStatus string StopContainer string
RunningCustomCommandStatus string RestartingStatus string
RemoveService string StoppingStatus string
Stop string RemovingStatus string
Restart string RunningCustomCommandStatus string
Rebuild string RemoveService string
Recreate string Stop string
PreviousContext string Restart string
NextContext string Rebuild string
Attach string Recreate string
ViewLogs string PreviousContext string
ServicesTitle string NextContext string
ContainersTitle string Attach string
StandaloneContainersTitle string ViewLogs string
TopTitle string ServicesTitle string
ImagesTitle string ContainersTitle string
VolumesTitle string StandaloneContainersTitle string
NoContainers string TopTitle string
NoContainer string ImagesTitle string
NoImages string VolumesTitle string
NoVolumes string NoContainers string
RemoveImage string NoContainer string
RemoveVolume string NoImages string
RemoveWithoutPrune string NoVolumes string
PruneImages string RemoveImage string
PruneContainers string RemoveVolume string
PruneVolumes string RemoveWithoutPrune string
ConfirmPruneContainers string PruneImages string
ConfirmPruneImages string PruneContainers string
ConfirmPruneVolumes string PruneVolumes string
PruningStatus string ConfirmPruneContainers string
StopService string ConfirmPruneImages string
PressEnterToReturn string ConfirmPruneVolumes string
ViewRestartOptions string PruningStatus string
RunCustomCommand string StopService string
PressEnterToReturn string
ViewRestartOptions string
RunCustomCommand string
LogsTitle string LogsTitle string
ConfigTitle string ConfigTitle string
@ -91,10 +94,13 @@ func englishSet() TranslationSet {
RunningSubprocess: "running subprocess", RunningSubprocess: "running subprocess",
NoViewMachingNewLineFocusedSwitchStatement: "No view matching newLineFocused switch statement", NoViewMachingNewLineFocusedSwitchStatement: "No view matching newLineFocused switch statement",
ErrorOccurred: "An error occurred! Please create an issue at https://github.com/jesseduffield/lazydocker/issues", ErrorOccurred: "An error occurred! Please create an issue at https://github.com/jesseduffield/lazydocker/issues",
ConnectionFailed: "connection to docker client failed. You may need to restart the docker client", ConnectionFailed: "connection to docker client failed. You may need to restart the docker client",
Donate: "Donate", UnattachableContainerError: "Container does not support attaching. You must either run the service with the '-it' flag or use `stdin_open: true, tty: true` in the docker-compose.yml file",
Confirm: "Confirm", 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)",
Donate: "Donate",
Confirm: "Confirm",
Return: "return", Return: "return",
FocusMain: "focus main panel", FocusMain: "focus main panel",