mirror of
https://github.com/jesseduffield/lazydocker.git
synced 2026-07-21 23:01:03 +00:00
more progress
This commit is contained in:
parent
bfa4d24810
commit
2d858955e6
7 changed files with 171 additions and 10 deletions
|
|
@ -17,9 +17,7 @@ type Container struct {
|
|||
|
||||
// GetDisplayStrings returns the dispaly string of Container
|
||||
func (c *Container) GetDisplayStrings(isFocused bool) []string {
|
||||
displayName := utils.ColoredString(c.Name, c.GetColor())
|
||||
|
||||
return []string{c.ServiceName, displayName}
|
||||
return []string{utils.ColoredString(c.Container.State, c.GetColor()), utils.ColoredString(c.Name, color.FgWhite)}
|
||||
}
|
||||
|
||||
// GetColor Container color
|
||||
|
|
|
|||
|
|
@ -152,8 +152,6 @@ func (s *ContainerStats) CalculateContainerMemoryUsage() float64 {
|
|||
|
||||
// RenderStats returns a string containing the rendered stats of the container
|
||||
func (s *ContainerStats) RenderStats(viewWidth int, cpuUsageHistory []float64, memoryUsageHistory []float64) (string, error) {
|
||||
percentMemory := s.CalculateContainerMemoryUsage()
|
||||
memoryUsageHistory = append(memoryUsageHistory, percentMemory)
|
||||
memoryGraph := asciigraph.Plot(
|
||||
memoryUsageHistory,
|
||||
asciigraph.Height(10),
|
||||
|
|
@ -163,22 +161,20 @@ func (s *ContainerStats) RenderStats(viewWidth int, cpuUsageHistory []float64, m
|
|||
asciigraph.Caption(
|
||||
fmt.Sprintf(
|
||||
"%.2f%% Memory (%s/%s)",
|
||||
percentMemory,
|
||||
memoryUsageHistory[len(memoryUsageHistory)-1],
|
||||
formatBinaryBytes(s.MemoryStats.Usage),
|
||||
formatBinaryBytes(int(s.MemoryStats.Limit)),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
percentageCPU := s.CalculateContainerCPUPercentage()
|
||||
cpuUsageHistory = append(cpuUsageHistory, percentageCPU)
|
||||
cpuGraph := asciigraph.Plot(
|
||||
cpuUsageHistory,
|
||||
asciigraph.Height(10),
|
||||
asciigraph.Width(viewWidth-10),
|
||||
asciigraph.Min(0),
|
||||
asciigraph.Max(100),
|
||||
asciigraph.Caption(fmt.Sprintf("%.2f%% CPU", percentageCPU)),
|
||||
asciigraph.Caption(fmt.Sprintf("%.2f%% CPU", cpuUsageHistory[len(cpuUsageHistory)-1])),
|
||||
)
|
||||
|
||||
pidsCount := fmt.Sprintf("PIDs: %d", s.PidsStats.Current)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/jesseduffield/lazydocker/pkg/config"
|
||||
"github.com/jesseduffield/lazydocker/pkg/i18n"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// DockerCommand is our main git interface
|
||||
|
|
@ -60,3 +61,54 @@ func (c *DockerCommand) GetContainers() ([]*Container, error) {
|
|||
|
||||
return ownContainers, nil
|
||||
}
|
||||
|
||||
type removeContainerConfig struct {
|
||||
// RemoveVolumes removes any volumes attached to the container
|
||||
RemoveVolumes bool
|
||||
|
||||
// Force forces the container to be removed, even if it's running
|
||||
Force bool
|
||||
}
|
||||
|
||||
type RemoveContainerOption func(c *removeContainerConfig)
|
||||
|
||||
// RemoveVolumes is an option to remove volumes attached to the container
|
||||
func RemoveVolumes(c *removeContainerConfig) {
|
||||
c.RemoveVolumes = true
|
||||
}
|
||||
|
||||
// Force is an option to remove volumes attached to the container
|
||||
func Force(c *removeContainerConfig) {
|
||||
c.Force = true
|
||||
}
|
||||
|
||||
// MustStopContainer tells us that we must stop the container before removing it
|
||||
const MustStopContainer = iota
|
||||
|
||||
// RemoveContainer removes a container
|
||||
func (c *DockerCommand) RemoveContainer(containerID string, options ...RemoveContainerOption) error {
|
||||
config := &removeContainerConfig{}
|
||||
for _, option := range options {
|
||||
option(config)
|
||||
}
|
||||
flags := ""
|
||||
if config.RemoveVolumes {
|
||||
flags += " --volumes "
|
||||
}
|
||||
if config.Force {
|
||||
flags += " --force "
|
||||
}
|
||||
|
||||
err := c.OSCommand.RunCommand(fmt.Sprintf("docker rm %s %s", flags, containerID))
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "Stop the container before attempting removal or force remove") {
|
||||
return ComplexError{
|
||||
Code: MustStopContainer,
|
||||
Message: err.Error(),
|
||||
frame: xerrors.Caller(1),
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
package commands
|
||||
|
||||
import "github.com/go-errors/errors"
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// WrapError wraps an error for the sake of showing a stack trace at the top level
|
||||
// the go-errors package, for some reason, does not return nil when you try to wrap
|
||||
|
|
@ -12,3 +17,23 @@ func WrapError(err error) error {
|
|||
|
||||
return errors.Wrap(err, 0)
|
||||
}
|
||||
|
||||
// ComplexError an error which carries a code so that calling code has an easier job to do
|
||||
// adapted from https://medium.com/yakka/better-go-error-handling-with-xerrors-1987650e0c79
|
||||
type ComplexError struct {
|
||||
Message string
|
||||
Code int
|
||||
frame xerrors.Frame
|
||||
}
|
||||
|
||||
func (ce ComplexError) FormatError(p xerrors.Printer) error {
|
||||
p.Printf("%d %s", ce.Code, ce.Message)
|
||||
ce.frame.Format(p)
|
||||
return nil
|
||||
}
|
||||
func (ce ComplexError) Format(f fmt.State, c rune) {
|
||||
xerrors.FormatError(ce, f, c)
|
||||
}
|
||||
func (ce ComplexError) Error() string {
|
||||
return fmt.Sprint(ce)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,10 +8,12 @@ import (
|
|||
"os/exec"
|
||||
"time"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazydocker/pkg/commands"
|
||||
"github.com/jesseduffield/lazydocker/pkg/utils"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// list panel functions
|
||||
|
|
@ -130,10 +132,12 @@ func (gui *Gui) renderStats(mainView *gocui.View, container *commands.Container,
|
|||
var stats commands.ContainerStats
|
||||
json.Unmarshal(data, &stats)
|
||||
|
||||
cpuUsageHistory = append(cpuUsageHistory, stats.CalculateContainerCPUPercentage())
|
||||
if len(cpuUsageHistory) >= 20 {
|
||||
cpuUsageHistory = cpuUsageHistory[1:]
|
||||
}
|
||||
|
||||
memoryUsageHistory = append(memoryUsageHistory, stats.CalculateContainerMemoryUsage())
|
||||
if len(memoryUsageHistory) >= 20 {
|
||||
memoryUsageHistory = memoryUsageHistory[1:]
|
||||
}
|
||||
|
|
@ -281,3 +285,66 @@ func (gui *Gui) handleContainersNextContext(g *gocui.Gui, v *gocui.View) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
type removeOption struct {
|
||||
description string
|
||||
command string
|
||||
configOptions []commands.RemoveContainerOption
|
||||
runCommand bool
|
||||
}
|
||||
|
||||
// GetDisplayStrings is a function.
|
||||
func (r *removeOption) GetDisplayStrings(isFocused bool) []string {
|
||||
return []string{r.description, color.New(color.FgRed).Sprint(r.command)}
|
||||
}
|
||||
|
||||
func (gui *Gui) handleContainersRemoveMenu(g *gocui.Gui, v *gocui.View) error {
|
||||
container, err := gui.getSelectedContainer(g)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
options := []*removeOption{
|
||||
{
|
||||
description: gui.Tr.SLocalize("remove"),
|
||||
command: "docker rm " + container.ID[1:10],
|
||||
runCommand: true,
|
||||
},
|
||||
{
|
||||
description: gui.Tr.SLocalize("removeWithVolumes"),
|
||||
command: "docker rm --volumes " + container.ID[1:10],
|
||||
configOptions: []commands.RemoveContainerOption{commands.RemoveVolumes},
|
||||
runCommand: true,
|
||||
},
|
||||
{
|
||||
description: gui.Tr.SLocalize("cancel"),
|
||||
runCommand: false,
|
||||
},
|
||||
}
|
||||
|
||||
handleMenuPress := func(index int) error {
|
||||
if !options[index].runCommand {
|
||||
return nil
|
||||
}
|
||||
configOptions := options[index].configOptions
|
||||
if cerr := gui.DockerCommand.RemoveContainer(container.ID, configOptions...); cerr != nil {
|
||||
var originalErr commands.ComplexError
|
||||
if xerrors.As(cerr, &originalErr) {
|
||||
if originalErr.Code == commands.MustStopContainer {
|
||||
return gui.createConfirmationPanel(gui.g, v, gui.Tr.SLocalize("Confirm"), gui.Tr.SLocalize("mustForceToRemove"), func(g *gocui.Gui, v *gocui.View) error {
|
||||
if err := gui.DockerCommand.RemoveContainer(container.ID, append(configOptions, commands.Force)...); err != nil {
|
||||
return err
|
||||
}
|
||||
return gui.refreshContainers()
|
||||
}, nil)
|
||||
}
|
||||
} else {
|
||||
return gui.createErrorPanel(gui.g, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return gui.refreshContainers()
|
||||
}
|
||||
|
||||
return gui.createMenu("", options, len(options), handleMenuPress)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -148,6 +148,11 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
|||
Key: ']',
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleContainersNextContext,
|
||||
}, {
|
||||
ViewName: "containers",
|
||||
Key: 'd',
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleContainersRemoveMenu,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -760,5 +760,23 @@ func addEnglish(i18nObject *i18n.Bundle) error {
|
|||
ID: "resetTo",
|
||||
Other: `reset to`,
|
||||
},
|
||||
|
||||
// ALL LAZYDOCKER STUFF BELOW:
|
||||
&i18n.Message{
|
||||
ID: "remove",
|
||||
Other: `remove`,
|
||||
},
|
||||
&i18n.Message{
|
||||
ID: "removeWithVolumes",
|
||||
Other: `remove with volumes`,
|
||||
},
|
||||
&i18n.Message{
|
||||
ID: "mustForceToRemove",
|
||||
Other: "You cannot remove a running container unless you force it. Do you want to force it?",
|
||||
},
|
||||
&i18n.Message{
|
||||
ID: "Confirm",
|
||||
Other: "Confirm",
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue