mirror of
https://github.com/jesseduffield/lazydocker.git
synced 2026-07-25 08:31:03 +00:00
allow config option to only show standalone containers
This commit is contained in:
parent
61a10f5cc7
commit
a27b429807
6 changed files with 110 additions and 99 deletions
|
|
@ -33,6 +33,8 @@ type DockerCommand struct {
|
||||||
ServiceMutex sync.Mutex
|
ServiceMutex sync.Mutex
|
||||||
Services []*Service
|
Services []*Service
|
||||||
Containers []*Container
|
Containers []*Container
|
||||||
|
// DisplayContainers is the array of containers we will display in the containers panel. If Gui.ShowAllContainers is false, this will only be those containers which aren't based on a service. This reduces clutter and duplication in the UI
|
||||||
|
DisplayContainers []*Container
|
||||||
Images []*Image
|
Images []*Image
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -166,19 +168,13 @@ func (c *DockerCommand) GetContainersAndServices() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// find out which services have corresponding containers and assign them
|
c.assignContainersToServices(containers, services)
|
||||||
for _, service := range services {
|
|
||||||
foundMatch := false
|
var displayContainers []*Container
|
||||||
for _, container := range containers {
|
if c.Config.UserConfig.Gui.ShowAllContainers {
|
||||||
if container.ServiceID != "" && container.ServiceID == service.ID {
|
displayContainers = containers
|
||||||
foundMatch = true
|
} else {
|
||||||
service.Container = container
|
displayContainers = c.obtainStandaloneContainers(containers, services)
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !foundMatch {
|
|
||||||
service.Container = nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// sort services first by whether they have a linked container, and second by alphabetical order
|
// sort services first by whether they have a linked container, and second by alphabetical order
|
||||||
|
|
@ -196,10 +192,39 @@ func (c *DockerCommand) GetContainersAndServices() error {
|
||||||
|
|
||||||
c.Containers = containers
|
c.Containers = containers
|
||||||
c.Services = services
|
c.Services = services
|
||||||
|
c.DisplayContainers = displayContainers
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *DockerCommand) assignContainersToServices(containers []*Container, services []*Service) {
|
||||||
|
L:
|
||||||
|
for _, service := range services {
|
||||||
|
for _, container := range containers {
|
||||||
|
if container.ServiceID != "" && container.ServiceID == service.ID {
|
||||||
|
service.Container = container
|
||||||
|
continue L
|
||||||
|
}
|
||||||
|
}
|
||||||
|
service.Container = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DockerCommand) obtainStandaloneContainers(containers []*Container, services []*Service) []*Container {
|
||||||
|
standaloneContainers := []*Container{}
|
||||||
|
L:
|
||||||
|
for _, container := range containers {
|
||||||
|
for _, service := range services {
|
||||||
|
if container.ServiceID != "" && container.ServiceID == service.ID {
|
||||||
|
continue L
|
||||||
|
}
|
||||||
|
}
|
||||||
|
standaloneContainers = append(standaloneContainers, container)
|
||||||
|
}
|
||||||
|
|
||||||
|
return standaloneContainers
|
||||||
|
}
|
||||||
|
|
||||||
// GetContainers gets the docker containers
|
// GetContainers gets the docker containers
|
||||||
func (c *DockerCommand) GetContainers() ([]*Container, error) {
|
func (c *DockerCommand) GetContainers() ([]*Container, error) {
|
||||||
c.ContainerMutex.Lock()
|
c.ContainerMutex.Lock()
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,7 @@ type GuiConfig struct {
|
||||||
ScrollPastBottom bool `yaml:"scrollPastBottom,omitempty"`
|
ScrollPastBottom bool `yaml:"scrollPastBottom,omitempty"`
|
||||||
MouseEvents bool `yaml:"mouseEvents,omitempty"`
|
MouseEvents bool `yaml:"mouseEvents,omitempty"`
|
||||||
Theme ThemeConfig `yaml:"theme,omitempty"`
|
Theme ThemeConfig `yaml:"theme,omitempty"`
|
||||||
|
ShowAllContainers bool `yaml:"showAllContainers,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CommandTemplatesConfig struct {
|
type CommandTemplatesConfig struct {
|
||||||
|
|
@ -139,6 +140,7 @@ type StatsConfig struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDefaultConfig returns the application default configuration
|
// GetDefaultConfig returns the application default configuration
|
||||||
|
// NOTE: do not default a boolean to true, because false is the boolean zero value and this will be ignored when parsing the user's config
|
||||||
func GetDefaultConfig() UserConfig {
|
func GetDefaultConfig() UserConfig {
|
||||||
return UserConfig{
|
return UserConfig{
|
||||||
Gui: GuiConfig{
|
Gui: GuiConfig{
|
||||||
|
|
@ -150,6 +152,7 @@ func GetDefaultConfig() UserConfig {
|
||||||
InactiveBorderColor: []string{"white"},
|
InactiveBorderColor: []string{"white"},
|
||||||
OptionsTextColor: []string{"blue"},
|
OptionsTextColor: []string{"blue"},
|
||||||
},
|
},
|
||||||
|
ShowAllContainers: false,
|
||||||
},
|
},
|
||||||
Reporting: "undetermined",
|
Reporting: "undetermined",
|
||||||
ConfirmOnQuit: false,
|
ConfirmOnQuit: false,
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ func (gui *Gui) getSelectedContainer(g *gocui.Gui) (*commands.Container, error)
|
||||||
return &commands.Container{}, gui.Errors.ErrNoContainers
|
return &commands.Container{}, gui.Errors.ErrNoContainers
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.DockerCommand.Containers[selectedLine], nil
|
return gui.DockerCommand.DisplayContainers[selectedLine], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleContainersFocus(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleContainersFocus(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
|
@ -42,7 +42,7 @@ func (gui *Gui) handleContainersFocus(g *gocui.Gui, v *gocui.View) error {
|
||||||
prevSelectedLine := gui.State.Panels.Containers.SelectedLine
|
prevSelectedLine := gui.State.Panels.Containers.SelectedLine
|
||||||
newSelectedLine := cy - oy
|
newSelectedLine := cy - oy
|
||||||
|
|
||||||
if newSelectedLine > len(gui.DockerCommand.Containers)-1 || len(utils.Decolorise(gui.DockerCommand.Containers[newSelectedLine].Name)) < cx {
|
if newSelectedLine > len(gui.DockerCommand.DisplayContainers)-1 || len(utils.Decolorise(gui.DockerCommand.DisplayContainers[newSelectedLine].Name)) < cx {
|
||||||
return gui.handleContainerSelect(gui.g, v)
|
return gui.handleContainerSelect(gui.g, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -75,7 +75,7 @@ func (gui *Gui) handleContainerSelect(g *gocui.Gui, v *gocui.View) error {
|
||||||
gui.State.Panels.Main.ObjectKey = key
|
gui.State.Panels.Main.ObjectKey = key
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := gui.focusPoint(0, gui.State.Panels.Containers.SelectedLine, len(gui.DockerCommand.Containers), v); err != nil {
|
if err := gui.focusPoint(0, gui.State.Panels.Containers.SelectedLine, len(gui.DockerCommand.DisplayContainers), v); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -147,15 +147,45 @@ func (gui *Gui) renderContainerLogs(mainView *gocui.View, container *commands.Co
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) renderLogsForRegularContainer(container *commands.Container) error {
|
func (gui *Gui) renderLogsForRegularContainer(container *commands.Container) error {
|
||||||
|
gui.renderLogs(container, gui.Config.UserConfig.CommandTemplates.ContainerLogs, func(cmd *exec.Cmd) {
|
||||||
mainView := gui.getMainView()
|
mainView := gui.getMainView()
|
||||||
go gui.T.NewTickerTask(time.Millisecond*200, nil, func(stop, notifyStopped chan struct{}) {
|
|
||||||
|
|
||||||
gui.clearMainView()
|
|
||||||
|
|
||||||
cmd := gui.OSCommand.RunCustomCommand(utils.ApplyTemplate(gui.Config.UserConfig.CommandTemplates.ContainerLogs, container))
|
|
||||||
|
|
||||||
cmd.Stdout = mainView
|
cmd.Stdout = mainView
|
||||||
cmd.Stderr = mainView
|
cmd.Stderr = mainView
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) renderLogsForTTYContainer(container *commands.Container) error {
|
||||||
|
gui.renderLogs(container, gui.Config.UserConfig.CommandTemplates.ContainerTTYLogs, func(cmd *exec.Cmd) {
|
||||||
|
// for some reason just saying cmd.Stdout = mainView does not work here as it does for non-tty containers, so we feed it through line by line
|
||||||
|
r, err := cmd.StdoutPipe()
|
||||||
|
if err != nil {
|
||||||
|
gui.ErrorChan <- err
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
mainView := gui.getMainView()
|
||||||
|
s := bufio.NewScanner(r)
|
||||||
|
s.Split(bufio.ScanLines)
|
||||||
|
for s.Scan() {
|
||||||
|
// I might put a check on the stopped channel here. Would mean more code duplication though
|
||||||
|
mainView.Write(append(s.Bytes(), '\n'))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) renderLogs(container *commands.Container, template string, setup func(*exec.Cmd)) {
|
||||||
|
gui.T.NewTickerTask(time.Millisecond*200, nil, func(stop, notifyStopped chan struct{}) {
|
||||||
|
gui.clearMainView()
|
||||||
|
|
||||||
|
command := utils.ApplyTemplate(gui.Config.UserConfig.CommandTemplates.ContainerTTYLogs, container)
|
||||||
|
cmd := gui.OSCommand.RunCustomCommand(command)
|
||||||
|
|
||||||
|
setup(cmd)
|
||||||
|
|
||||||
cmd.Start()
|
cmd.Start()
|
||||||
|
|
||||||
|
|
@ -187,59 +217,6 @@ func (gui *Gui) renderLogsForRegularContainer(container *commands.Container) err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) runProcessWithLock(cmd *exec.Cmd) {
|
|
||||||
go gui.T.NewTask(func(stop chan struct{}) {
|
|
||||||
gui.clearMainView()
|
|
||||||
cmd.Start()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
<-stop
|
|
||||||
cmd.Process.Kill()
|
|
||||||
}()
|
|
||||||
|
|
||||||
cmd.Wait()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) renderLogsForTTYContainer(container *commands.Container) error {
|
|
||||||
mainView := gui.getMainView()
|
|
||||||
gui.T.NewTickerTask(time.Millisecond*200, nil, func(stop, notifyStopped chan struct{}) {
|
|
||||||
gui.clearMainView()
|
|
||||||
|
|
||||||
command := utils.ApplyTemplate(gui.Config.UserConfig.CommandTemplates.ContainerTTYLogs, container)
|
|
||||||
cmd := gui.OSCommand.RunCustomCommand(command)
|
|
||||||
|
|
||||||
// for some reason just saying cmd.Stdout = mainView does not work here as it does for non-tty containers, so we feed it through line by line
|
|
||||||
r, err := cmd.StdoutPipe()
|
|
||||||
if err != nil {
|
|
||||||
gui.ErrorChan <- err
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
s := bufio.NewScanner(r)
|
|
||||||
s.Split(bufio.ScanLines)
|
|
||||||
for s.Scan() {
|
|
||||||
// I might put a check on the stopped channel here. Would mean more code duplication though
|
|
||||||
mainView.Write(append(s.Bytes(), '\n'))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
cmd.Start()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
<-stop
|
|
||||||
cmd.Process.Kill()
|
|
||||||
return
|
|
||||||
}()
|
|
||||||
|
|
||||||
cmd.Wait()
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) refreshContainersAndServices() error {
|
func (gui *Gui) refreshContainersAndServices() error {
|
||||||
|
|
@ -275,11 +252,11 @@ func (gui *Gui) refreshContainersAndServices() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(gui.DockerCommand.Containers) > 0 && gui.State.Panels.Containers.SelectedLine == -1 {
|
if len(gui.DockerCommand.DisplayContainers) > 0 && gui.State.Panels.Containers.SelectedLine == -1 {
|
||||||
gui.State.Panels.Containers.SelectedLine = 0
|
gui.State.Panels.Containers.SelectedLine = 0
|
||||||
}
|
}
|
||||||
if len(gui.DockerCommand.Containers)-1 < gui.State.Panels.Containers.SelectedLine {
|
if len(gui.DockerCommand.DisplayContainers)-1 < gui.State.Panels.Containers.SelectedLine {
|
||||||
gui.State.Panels.Containers.SelectedLine = len(gui.DockerCommand.Containers) - 1
|
gui.State.Panels.Containers.SelectedLine = len(gui.DockerCommand.DisplayContainers) - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// doing the exact same thing for services
|
// doing the exact same thing for services
|
||||||
|
|
@ -293,7 +270,7 @@ func (gui *Gui) refreshContainersAndServices() error {
|
||||||
gui.g.Update(func(g *gocui.Gui) error {
|
gui.g.Update(func(g *gocui.Gui) error {
|
||||||
containersView.Clear()
|
containersView.Clear()
|
||||||
isFocused := gui.g.CurrentView().Name() == "containers"
|
isFocused := gui.g.CurrentView().Name() == "containers"
|
||||||
list, err := utils.RenderList(gui.DockerCommand.Containers, utils.IsFocused(isFocused))
|
list, err := utils.RenderList(gui.DockerCommand.DisplayContainers, utils.IsFocused(isFocused))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -337,7 +314,7 @@ func (gui *Gui) handleContainersNextLine(g *gocui.Gui, v *gocui.View) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
panelState := gui.State.Panels.Containers
|
panelState := gui.State.Panels.Containers
|
||||||
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.DockerCommand.Containers), false)
|
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.DockerCommand.DisplayContainers), false)
|
||||||
|
|
||||||
return gui.handleContainerSelect(gui.g, v)
|
return gui.handleContainerSelect(gui.g, v)
|
||||||
}
|
}
|
||||||
|
|
@ -348,7 +325,7 @@ func (gui *Gui) handleContainersPrevLine(g *gocui.Gui, v *gocui.View) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
panelState := gui.State.Panels.Containers
|
panelState := gui.State.Panels.Containers
|
||||||
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.DockerCommand.Containers), true)
|
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.DockerCommand.DisplayContainers), true)
|
||||||
|
|
||||||
return gui.handleContainerSelect(gui.g, v)
|
return gui.handleContainerSelect(gui.g, v)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -164,7 +164,11 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
containersView.Highlight = true
|
containersView.Highlight = true
|
||||||
|
if gui.Config.UserConfig.Gui.ShowAllContainers {
|
||||||
containersView.Title = gui.Tr.ContainersTitle
|
containersView.Title = gui.Tr.ContainersTitle
|
||||||
|
} else {
|
||||||
|
containersView.Title = gui.Tr.StandaloneContainersTitle
|
||||||
|
}
|
||||||
containersView.FgColor = gocui.ColorWhite
|
containersView.FgColor = gocui.ColorWhite
|
||||||
|
|
||||||
gui.focusPoint(0, gui.State.Panels.Containers.SelectedLine, len(gui.DockerCommand.Containers), containersView)
|
gui.focusPoint(0, gui.State.Panels.Containers.SelectedLine, len(gui.DockerCommand.Containers), containersView)
|
||||||
|
|
|
||||||
|
|
@ -57,16 +57,16 @@ func (gui *Gui) runCommand() error {
|
||||||
gui.Log.Error(err)
|
gui.Log.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gui.SubProcess.Stdin = nil
|
||||||
|
gui.SubProcess.Stdout = ioutil.Discard
|
||||||
|
gui.SubProcess.Stderr = ioutil.Discard
|
||||||
|
gui.SubProcess = nil
|
||||||
|
|
||||||
signal.Stop(c)
|
signal.Stop(c)
|
||||||
|
|
||||||
fmt.Fprintf(os.Stdout, "\n%s", utils.ColoredString(gui.Tr.PressEnterToReturn, color.FgGreen))
|
fmt.Fprintf(os.Stdout, "\n%s", utils.ColoredString(gui.Tr.PressEnterToReturn, color.FgGreen))
|
||||||
|
|
||||||
fmt.Scanln() // wait for enter press
|
fmt.Scanln() // wait for enter press
|
||||||
|
|
||||||
gui.SubProcess.Stdout = ioutil.Discard
|
|
||||||
gui.SubProcess.Stderr = ioutil.Discard
|
|
||||||
gui.SubProcess.Stdin = nil
|
|
||||||
gui.SubProcess = nil
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ type TranslationSet struct {
|
||||||
ViewLogs string
|
ViewLogs string
|
||||||
ServicesTitle string
|
ServicesTitle string
|
||||||
ContainersTitle string
|
ContainersTitle string
|
||||||
|
StandaloneContainersTitle string
|
||||||
ImagesTitle string
|
ImagesTitle string
|
||||||
NoContainers string
|
NoContainers string
|
||||||
NoImages string
|
NoImages string
|
||||||
|
|
@ -100,6 +101,7 @@ func englishSet() TranslationSet {
|
||||||
StatusTitle: "Status",
|
StatusTitle: "Status",
|
||||||
ServicesTitle: "Services",
|
ServicesTitle: "Services",
|
||||||
ContainersTitle: "Containers",
|
ContainersTitle: "Containers",
|
||||||
|
StandaloneContainersTitle: "Standalone Containers",
|
||||||
ImagesTitle: "Images",
|
ImagesTitle: "Images",
|
||||||
CustomCommandTitle: "Custom Command:",
|
CustomCommandTitle: "Custom Command:",
|
||||||
ErrorTitle: "Error",
|
ErrorTitle: "Error",
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue