mirror of
https://github.com/jesseduffield/lazydocker.git
synced 2026-07-25 00:21:04 +00:00
more stuff
This commit is contained in:
parent
159a1883e5
commit
3cfa4a2bda
7 changed files with 74 additions and 20 deletions
|
|
@ -21,13 +21,15 @@ type App struct {
|
||||||
DockerCommand *commands.DockerCommand
|
DockerCommand *commands.DockerCommand
|
||||||
Gui *gui.Gui
|
Gui *gui.Gui
|
||||||
Tr *i18n.Localizer
|
Tr *i18n.Localizer
|
||||||
|
ErrorChan chan error
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewApp bootstrap a new application
|
// NewApp bootstrap a new application
|
||||||
func NewApp(config *config.AppConfig) (*App, error) {
|
func NewApp(config *config.AppConfig) (*App, error) {
|
||||||
app := &App{
|
app := &App{
|
||||||
closers: []io.Closer{},
|
closers: []io.Closer{},
|
||||||
Config: config,
|
Config: config,
|
||||||
|
ErrorChan: make(chan error),
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
app.Log = log.NewLogger(config, "23432119147a4367abf7c0de2aa99a2d")
|
app.Log = log.NewLogger(config, "23432119147a4367abf7c0de2aa99a2d")
|
||||||
|
|
@ -36,11 +38,11 @@ func NewApp(config *config.AppConfig) (*App, error) {
|
||||||
|
|
||||||
// here is the place to make use of the docker-compose.yml file in the current directory
|
// here is the place to make use of the docker-compose.yml file in the current directory
|
||||||
|
|
||||||
app.DockerCommand, err = commands.NewDockerCommand(app.Log, app.OSCommand, app.Tr, app.Config)
|
app.DockerCommand, err = commands.NewDockerCommand(app.Log, app.OSCommand, app.Tr, app.Config, app.ErrorChan)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return app, err
|
return app, err
|
||||||
}
|
}
|
||||||
app.Gui, err = gui.NewGui(app.Log, app.DockerCommand, app.OSCommand, app.Tr, config)
|
app.Gui, err = gui.NewGui(app.Log, app.DockerCommand, app.OSCommand, app.Tr, config, app.ErrorChan)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return app, err
|
return app, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package commands
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -32,6 +33,12 @@ type Container struct {
|
||||||
Details Details
|
Details Details
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DetailsCollection struct {
|
||||||
|
InspectDetails Details
|
||||||
|
CLIStats ContainerCliStat
|
||||||
|
ClientStats ContainerStats
|
||||||
|
}
|
||||||
|
|
||||||
type Details struct {
|
type Details struct {
|
||||||
ID string `json:"Id"`
|
ID string `json:"Id"`
|
||||||
Created time.Time `json:"Created"`
|
Created time.Time `json:"Created"`
|
||||||
|
|
@ -239,7 +246,31 @@ 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.Stats.CPUPerc}
|
return []string{utils.ColoredString(c.Container.State, c.GetColor()), utils.ColoredString(c.Name, color.FgWhite), c.GetDisplayCPUPerc()}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDisplayCPUPerc colors the cpu percentage based on how extreme it is
|
||||||
|
func (c *Container) GetDisplayCPUPerc() string {
|
||||||
|
if c.Stats.CPUPerc == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
percentage, err := strconv.ParseFloat(strings.TrimSuffix(c.Stats.CPUPerc, "%"), 32)
|
||||||
|
if err != nil {
|
||||||
|
c.Log.Error(err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var clr color.Attribute
|
||||||
|
if percentage > 90 {
|
||||||
|
clr = color.FgRed
|
||||||
|
} else if percentage > 50 {
|
||||||
|
clr = color.FgYellow
|
||||||
|
} else {
|
||||||
|
clr = color.FgWhite
|
||||||
|
}
|
||||||
|
|
||||||
|
return utils.ColoredString(c.Stats.CPUPerc, clr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetColor Container color
|
// GetColor Container color
|
||||||
|
|
@ -301,7 +332,7 @@ func (c *Container) RestartService() error {
|
||||||
// Attach attaches the container
|
// Attach attaches the container
|
||||||
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.AttachStdin {
|
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("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")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,10 +24,11 @@ type DockerCommand struct {
|
||||||
Config *config.AppConfig
|
Config *config.AppConfig
|
||||||
Client *client.Client
|
Client *client.Client
|
||||||
InDockerComposeProject bool
|
InDockerComposeProject bool
|
||||||
|
ErrorChan chan error
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDockerCommand it runs git commands
|
// NewDockerCommand it runs git commands
|
||||||
func NewDockerCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.Localizer, config *config.AppConfig) (*DockerCommand, error) {
|
func NewDockerCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.Localizer, config *config.AppConfig, errorChan chan error) (*DockerCommand, error) {
|
||||||
cli, err := client.NewEnvClient()
|
cli, err := client.NewEnvClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -40,17 +41,19 @@ func NewDockerCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.Localize
|
||||||
Config: config,
|
Config: config,
|
||||||
Client: cli,
|
Client: cli,
|
||||||
InDockerComposeProject: true, // TODO: determine this at startup
|
InDockerComposeProject: true, // TODO: determine this at startup
|
||||||
|
ErrorChan: errorChan,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateContainerStats takes a slice of containers and returns the same slice but with new stats added
|
// UpdateContainerStats takes a slice of containers and returns the same slice but with new stats added
|
||||||
// TODO: consider using this for everything stats-related
|
// TODO: consider using this for everything stats-related
|
||||||
func (c *DockerCommand) UpdateContainerStats(containers []*Container) ([]*Container, error) {
|
func (c *DockerCommand) UpdateContainerStats(containers []*Container) {
|
||||||
// TODO: consider using a stream rather than polling
|
// TODO: consider using a stream rather than polling
|
||||||
command := `docker stats --all --no-trunc --no-stream --format '{{json .}}'`
|
command := `docker stats --all --no-trunc --no-stream --format '{{json .}}'`
|
||||||
output, err := c.OSCommand.RunCommandWithOutput(command)
|
output, err := c.OSCommand.RunCommandWithOutput(command)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
c.ErrorChan <- err
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonStats := "[" + strings.Join(
|
jsonStats := "[" + strings.Join(
|
||||||
|
|
@ -63,7 +66,8 @@ func (c *DockerCommand) UpdateContainerStats(containers []*Container) ([]*Contai
|
||||||
|
|
||||||
var stats []ContainerCliStat
|
var stats []ContainerCliStat
|
||||||
if err := json.Unmarshal([]byte(jsonStats), &stats); err != nil {
|
if err := json.Unmarshal([]byte(jsonStats), &stats); err != nil {
|
||||||
return nil, err
|
c.ErrorChan <- err
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, stat := range stats {
|
for _, stat := range stats {
|
||||||
|
|
@ -74,7 +78,9 @@ func (c *DockerCommand) UpdateContainerStats(containers []*Container) ([]*Contai
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return containers, nil
|
c.Log.Warn("updated containers")
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetContainersAndServices returns a slice of docker containers
|
// GetContainersAndServices returns a slice of docker containers
|
||||||
|
|
@ -147,10 +153,7 @@ func (c *DockerCommand) GetContainers() ([]*Container, error) {
|
||||||
|
|
||||||
c.UpdateContainerDetails(ownContainers)
|
c.UpdateContainerDetails(ownContainers)
|
||||||
|
|
||||||
// ownContainers, err = c.UpdateContainerStats(ownContainers)
|
c.UpdateContainerStats(ownContainers)
|
||||||
// if err != nil {
|
|
||||||
// return nil, err
|
|
||||||
// }
|
|
||||||
|
|
||||||
return ownContainers, nil
|
return ownContainers, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,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.Stats.CPUPerc}
|
return []string{utils.ColoredString(cont.Container.State, cont.GetColor()), utils.ColoredString(s.Name, color.FgWhite), cont.GetDisplayCPUPerc()}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove removes the service's containers
|
// Remove removes the service's containers
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ func (gui *Gui) handleContainerSelect(g *gocui.Gui, v *gocui.View) error {
|
||||||
if err != gui.Errors.ErrNoContainers {
|
if err != gui.Errors.ErrNoContainers {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return gui.renderString(g, "main", gui.Tr.SLocalize("NoChangedContainers"))
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
key := container.ID + "-" + gui.getContainerContexts()[gui.State.Panels.Containers.ContextIndex]
|
key := container.ID + "-" + gui.getContainerContexts()[gui.State.Panels.Containers.ContextIndex]
|
||||||
|
|
@ -171,7 +171,7 @@ func (gui *Gui) renderContainerLogs(mainView *gocui.View, container *commands.Co
|
||||||
mainView.Autoscroll = true
|
mainView.Autoscroll = true
|
||||||
mainView.Title = "Logs"
|
mainView.Title = "Logs"
|
||||||
|
|
||||||
if container.Details.Config.AttachStdin {
|
if container.Details.Config.OpenStdin {
|
||||||
return gui.renderLogsForTTYContainer(mainView, container)
|
return gui.renderLogsForTTYContainer(mainView, container)
|
||||||
}
|
}
|
||||||
return gui.renderLogsForRegularContainer(mainView, container)
|
return gui.renderLogsForRegularContainer(mainView, container)
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,7 @@ type Gui struct {
|
||||||
statusManager *statusManager
|
statusManager *statusManager
|
||||||
waitForIntro sync.WaitGroup
|
waitForIntro sync.WaitGroup
|
||||||
T *tasks.TaskManager
|
T *tasks.TaskManager
|
||||||
|
ErrorChan chan error
|
||||||
}
|
}
|
||||||
|
|
||||||
type servicePanelState struct {
|
type servicePanelState struct {
|
||||||
|
|
@ -118,10 +119,11 @@ type guiState struct {
|
||||||
SubProcessOutput string
|
SubProcessOutput string
|
||||||
MainProcessMutex sync.Mutex
|
MainProcessMutex sync.Mutex
|
||||||
MainProcessChan chan struct{}
|
MainProcessChan chan struct{}
|
||||||
|
Stats map[string]commands.ContainerStats
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewGui builds a new gui handler
|
// NewGui builds a new gui handler
|
||||||
func NewGui(log *logrus.Entry, dockerCommand *commands.DockerCommand, oSCommand *commands.OSCommand, tr *i18n.Localizer, config *config.AppConfig) (*Gui, error) {
|
func NewGui(log *logrus.Entry, dockerCommand *commands.DockerCommand, oSCommand *commands.OSCommand, tr *i18n.Localizer, config *config.AppConfig, errorChan chan error) (*Gui, error) {
|
||||||
|
|
||||||
initialState := guiState{
|
initialState := guiState{
|
||||||
Containers: make([]*commands.Container, 0),
|
Containers: make([]*commands.Container, 0),
|
||||||
|
|
@ -151,11 +153,13 @@ func NewGui(log *logrus.Entry, dockerCommand *commands.DockerCommand, oSCommand
|
||||||
Log: log,
|
Log: log,
|
||||||
DockerCommand: dockerCommand,
|
DockerCommand: dockerCommand,
|
||||||
OSCommand: oSCommand,
|
OSCommand: oSCommand,
|
||||||
|
// TODO: look into this warning
|
||||||
State: initialState,
|
State: initialState,
|
||||||
Config: config,
|
Config: config,
|
||||||
Tr: tr,
|
Tr: tr,
|
||||||
statusManager: &statusManager{},
|
statusManager: &statusManager{},
|
||||||
T: tasks.NewTaskManager(log),
|
T: tasks.NewTaskManager(log),
|
||||||
|
ErrorChan: errorChan,
|
||||||
}
|
}
|
||||||
|
|
||||||
gui.GenerateSentinelErrors()
|
gui.GenerateSentinelErrors()
|
||||||
|
|
@ -535,6 +539,12 @@ func (gui *Gui) Run() error {
|
||||||
gui.goEvery(time.Millisecond*500, gui.refreshContainersAndServices)
|
gui.goEvery(time.Millisecond*500, gui.refreshContainersAndServices)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for err := range gui.ErrorChan {
|
||||||
|
gui.createErrorPanel(gui.g, err.Error())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
g.SetManager(gocui.ManagerFunc(gui.layout), gocui.ManagerFunc(gui.getFocusLayout()))
|
g.SetManager(gocui.ManagerFunc(gui.layout), gocui.ManagerFunc(gui.getFocusLayout()))
|
||||||
|
|
||||||
if err = gui.keybindings(g); err != nil {
|
if err = gui.keybindings(g); err != nil {
|
||||||
|
|
@ -546,7 +556,11 @@ func (gui *Gui) Run() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) reRenderMain() error {
|
func (gui *Gui) reRenderMain() error {
|
||||||
if gui.getMainView().IsTainted() {
|
mainView := gui.getMainView()
|
||||||
|
if mainView == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if mainView.IsTainted() {
|
||||||
gui.g.Update(func(g *gocui.Gui) error {
|
gui.g.Update(func(g *gocui.Gui) error {
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -856,5 +856,9 @@ func addEnglish(i18nObject *i18n.Bundle) error {
|
||||||
ID: "PruningStatus",
|
ID: "PruningStatus",
|
||||||
Other: "pruning",
|
Other: "pruning",
|
||||||
},
|
},
|
||||||
|
&i18n.Message{
|
||||||
|
ID: "StopService",
|
||||||
|
Other: "Are you sure you want to stop this service's containers? (enter/esc)",
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue