much more stuff

This commit is contained in:
Jesse Duffield 2019-06-09 00:08:22 +10:00
parent 9a5a01dfbd
commit b65df625f4
8 changed files with 151 additions and 7 deletions

View file

@ -254,7 +254,17 @@ type ContainerCliStat struct {
// GetDisplayStrings returns the dispaly string of Container // GetDisplayStrings returns the dispaly string of Container
func (c *Container) GetDisplayStrings(isFocused bool) []string { func (c *Container) GetDisplayStrings(isFocused bool) []string {
return []string{utils.ColoredString(c.Container.State, c.GetColor()), utils.ColoredString(c.Name, color.FgWhite), c.GetDisplayCPUPerc()} return []string{c.GetDisplayStatus(), utils.ColoredString(c.Name, color.FgWhite), c.GetDisplayCPUPerc()}
}
// GetDisplayStatus returns the colored status of the container
func (c *Container) GetDisplayStatus() string {
state := c.Container.State
if c.Container.State == "exited" {
state += " (" + strconv.Itoa(c.Details.State.ExitCode) + ")"
}
return utils.ColoredString(state, c.GetColor())
} }
// GetDisplayCPUPerc colors the cpu percentage based on how extreme it is // GetDisplayCPUPerc colors the cpu percentage based on how extreme it is
@ -292,6 +302,9 @@ func (c *Container) ProducingLogs() bool {
func (c *Container) GetColor() color.Attribute { func (c *Container) GetColor() color.Attribute {
switch c.Container.State { switch c.Container.State {
case "exited": case "exited":
if c.Details.State.ExitCode == 0 {
return color.FgBlue
}
return color.FgRed return color.FgRed
case "created": case "created":
return color.FgCyan return color.FgCyan

View file

@ -9,6 +9,7 @@ import (
"regexp" "regexp"
"strings" "strings"
"sync" "sync"
"syscall"
"time" "time"
"github.com/go-errors/errors" "github.com/go-errors/errors"
@ -366,3 +367,13 @@ func (c *OSCommand) PipeCommands(commandStrings ...string) error {
} }
return nil return nil
} }
// Kill kills a process. If the process has Setpgid == true, then we have anticipated that it might spawn its own child processes, so we've given it a process group ID (PGID) equal to its process id (PID) and given its child processes will inherit the PGID, we can kill that group, rather than killing the process itself.
func (c *OSCommand) Kill(cmd *exec.Cmd) error {
if cmd.SysProcAttr != nil && cmd.SysProcAttr.Setpgid == true {
// minus sign means we're talking about a PGID as opposed to a PID
return syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
}
return cmd.Process.Kill()
}

View file

@ -27,7 +27,7 @@ func (s *Service) GetDisplayStrings(isFocused bool) []string {
} }
cont := s.Container cont := s.Container
return []string{utils.ColoredString(cont.Container.State, cont.GetColor()), utils.ColoredString(s.Name, color.FgWhite), cont.GetDisplayCPUPerc()} return []string{cont.GetDisplayStatus(), utils.ColoredString(s.Name, color.FgWhite), cont.GetDisplayCPUPerc()}
} }
// Remove removes the service's containers // Remove removes the service's containers
@ -68,7 +68,6 @@ func (s *Service) ViewLogs() (*exec.Cmd, error) {
// so long as this is commented in, the child process does not receive the interrupt // so long as this is commented in, the child process does not receive the interrupt
cmd.SysProcAttr = &syscall.SysProcAttr{ cmd.SysProcAttr = &syscall.SysProcAttr{
Setpgid: true, Setpgid: true,
Pgid: 0,
} }
return cmd, nil return cmd, nil

View file

@ -110,6 +110,7 @@ type CommandTemplatesConfig struct {
RebuildService string `yaml:"rebuildService,omitempty"` RebuildService string `yaml:"rebuildService,omitempty"`
ContainerLogs string `yaml:"containerLogs,omitempty"` ContainerLogs string `yaml:"containerLogs,omitempty"`
ContainerTTYLogs string `yaml:"containerTTYLogs,omitempty"` ContainerTTYLogs string `yaml:"containerTTYLogs,omitempty"`
AllLogs string `yaml:"allLogs,omitempty"`
} }
type OSConfig struct { type OSConfig struct {
@ -164,6 +165,7 @@ func GetDefaultConfig() UserConfig {
ServiceLogs: "apdev logs {{ .Name }}", ServiceLogs: "apdev logs {{ .Name }}",
ContainerLogs: "docker logs --timestamps --follow --tail=100 {{ .ID }}", ContainerLogs: "docker logs --timestamps --follow --tail=100 {{ .ID }}",
ContainerTTYLogs: "docker logs --follow --tail=100 {{ .ID }}", ContainerTTYLogs: "docker logs --follow --tail=100 {{ .ID }}",
AllLogs: "apdev logs --tail=100",
}, },
OS: GetPlatformDefaultConfig(), OS: GetPlatformDefaultConfig(),
Update: UpdateConfig{ Update: UpdateConfig{

View file

@ -77,6 +77,10 @@ type containerPanelState struct {
ContextIndex int // for specifying if you are looking at logs/stats/config/etc ContextIndex int // for specifying if you are looking at logs/stats/config/etc
} }
type statusState struct {
ContextIndex int // for specifying if you are looking at credits/logs
}
type menuPanelState struct { type menuPanelState struct {
SelectedLine int SelectedLine int
} }
@ -96,6 +100,7 @@ type panelStates struct {
Menu *menuPanelState Menu *menuPanelState
Main *mainPanelState Main *mainPanelState
Images *imagePanelState Images *imagePanelState
Status *statusState
} }
type guiState struct { type guiState struct {
@ -121,6 +126,7 @@ func NewGui(log *logrus.Entry, dockerCommand *commands.DockerCommand, oSCommand
Main: &mainPanelState{ Main: &mainPanelState{
ObjectKey: "", ObjectKey: "",
}, },
Status: &statusState{ContextIndex: 0},
}, },
} }

View file

@ -133,6 +133,20 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Handler: gui.handleOpenConfig, Handler: gui.handleOpenConfig,
Description: gui.Tr.OpenConfig, Description: gui.Tr.OpenConfig,
}, },
{
ViewName: "status",
Key: '[',
Modifier: gocui.ModNone,
Handler: gui.handleStatusPrevContext,
Description: gui.Tr.PreviousContext,
},
{
ViewName: "status",
Key: ']',
Modifier: gocui.ModNone,
Handler: gui.handleStatusNextContext,
Description: gui.Tr.NextContext,
},
{ {
ViewName: "menu", ViewName: "menu",
Key: gocui.KeyEsc, Key: gocui.KeyEsc,

View file

@ -3,10 +3,16 @@ package gui
import ( import (
"fmt" "fmt"
"strings" "strings"
"syscall"
"github.com/go-errors/errors"
"github.com/jesseduffield/gocui" "github.com/jesseduffield/gocui"
) )
func (gui *Gui) getStatusContexts() []string {
return []string{"logs", "credits"}
}
func (gui *Gui) refreshStatus() error { func (gui *Gui) refreshStatus() error {
v := gui.getStatusView() v := gui.getStatusView()
@ -28,6 +34,36 @@ func (gui *Gui) handleStatusSelect(g *gocui.Gui, v *gocui.View) error {
return err return err
} }
key := gui.getStatusContexts()[gui.State.Panels.Status.ContextIndex]
if gui.State.Panels.Main.ObjectKey == key {
return nil
} else {
gui.State.Panels.Main.ObjectKey = key
}
gui.clearMainView()
switch gui.getStatusContexts()[gui.State.Panels.Status.ContextIndex] {
case "credits":
if err := gui.renderCredits(); err != nil {
return err
}
case "logs":
if err := gui.renderAllLogs(); err != nil {
return err
}
default:
return errors.New("Unknown context for status panel")
}
return nil
}
func (gui *Gui) renderCredits() error {
mainView := gui.getMainView()
mainView.Autoscroll = false
mainView.Title = "about"
dashboardString := strings.Join( dashboardString := strings.Join(
[]string{ []string{
lazydockerTitle(), lazydockerTitle(),
@ -37,7 +73,40 @@ func (gui *Gui) handleStatusSelect(g *gocui.Gui, v *gocui.View) error {
"Raise an Issue: https://github.com/jesseduffield/lazydocker/issues", "Raise an Issue: https://github.com/jesseduffield/lazydocker/issues",
}, "\n\n") }, "\n\n")
return gui.renderString(g, "main", dashboardString) go gui.T.NewTask(func(stop chan struct{}) {
gui.renderString(gui.g, "main", dashboardString)
})
return nil
}
func (gui *Gui) renderAllLogs() error {
mainView := gui.getMainView()
mainView.Autoscroll = true
mainView.Title = "logs"
go gui.T.NewTask(func(stop chan struct{}) {
gui.clearMainView()
cmd := gui.OSCommand.RunCustomCommand(gui.Config.UserConfig.CommandTemplates.AllLogs)
cmd.Stdout = mainView
cmd.Stderr = mainView
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
cmd.Start()
go func() {
<-stop
if err := gui.OSCommand.Kill(cmd); err != nil {
gui.Log.Error(err)
}
}()
cmd.Wait()
})
return nil
} }
func (gui *Gui) handleOpenConfig(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleOpenConfig(g *gocui.Gui, v *gocui.View) error {
@ -60,3 +129,29 @@ func lazydockerTitle() string {
|___/ |___/
` `
} }
func (gui *Gui) handleStatusPrevContext(g *gocui.Gui, v *gocui.View) error {
contexts := gui.getStatusContexts()
if gui.State.Panels.Status.ContextIndex >= len(contexts)-1 {
gui.State.Panels.Status.ContextIndex = 0
} else {
gui.State.Panels.Status.ContextIndex++
}
gui.handleStatusSelect(gui.g, v)
return nil
}
func (gui *Gui) handleStatusNextContext(g *gocui.Gui, v *gocui.View) error {
contexts := gui.getStatusContexts()
if gui.State.Panels.Status.ContextIndex <= 0 {
gui.State.Panels.Status.ContextIndex = len(contexts) - 1
} else {
gui.State.Panels.Status.ContextIndex--
}
gui.handleStatusSelect(gui.g, v)
return nil
}

View file

@ -21,6 +21,10 @@ func (gui *Gui) RunWithSubprocesses() error {
if err == gocui.ErrQuit { if err == gocui.ErrQuit {
break break
} else if err == gui.Errors.ErrSubProcess { } else if err == gui.Errors.ErrSubProcess {
// preparing the state for when we return
gui.State.PreviousView = gui.currentViewName()
gui.State.Panels.Main.ObjectKey = ""
if err := gui.runCommand(); err != nil { if err := gui.runCommand(); err != nil {
return err return err
} }
@ -33,8 +37,6 @@ func (gui *Gui) RunWithSubprocesses() error {
} }
func (gui *Gui) runCommand() error { func (gui *Gui) runCommand() error {
gui.State.PreviousView = gui.currentViewName()
gui.SubProcess.Stdout = os.Stdout gui.SubProcess.Stdout = os.Stdout
gui.SubProcess.Stderr = os.Stdout gui.SubProcess.Stderr = os.Stdout
gui.SubProcess.Stdin = os.Stdin gui.SubProcess.Stdin = os.Stdin
@ -46,7 +48,9 @@ func (gui *Gui) runCommand() error {
<-c <-c
signal.Stop(c) signal.Stop(c)
gui.SubProcess.Process.Kill() if err := gui.OSCommand.Kill(gui.SubProcess); err != nil {
gui.Log.Error(err)
}
}() }()
fmt.Fprintf(os.Stdout, "\n%s\n\n", utils.ColoredString("+ "+strings.Join(gui.SubProcess.Args, " "), color.FgBlue)) fmt.Fprintf(os.Stdout, "\n%s\n\n", utils.ColoredString("+ "+strings.Join(gui.SubProcess.Args, " "), color.FgBlue))