allow config option to only show standalone containers

This commit is contained in:
Jesse Duffield 2019-06-08 21:09:53 +10:00
parent 61a10f5cc7
commit a27b429807
6 changed files with 110 additions and 99 deletions

View file

@ -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()

View file

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

View file

@ -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)
} }

View file

@ -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)

View file

@ -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
} }

View file

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