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
88758de164
commit
f0ffdcd81f
8 changed files with 139 additions and 44 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -1,2 +1,3 @@
|
|||
|
||||
TODO.md
|
||||
TODO.md
|
||||
Lazydocker.code-workspace
|
||||
|
|
@ -15,19 +15,35 @@ import (
|
|||
|
||||
// Container : A docker Container
|
||||
type Container struct {
|
||||
Name string
|
||||
ServiceName string
|
||||
ID string
|
||||
Container types.Container
|
||||
DisplayString string
|
||||
Client *client.Client
|
||||
OSCommand *OSCommand
|
||||
Log *logrus.Entry
|
||||
Name string
|
||||
ServiceName string
|
||||
ContainerNumber string // might make this an int in the future if need be
|
||||
ProjectName string
|
||||
ID string
|
||||
Container types.Container
|
||||
DisplayString string
|
||||
Client *client.Client
|
||||
OSCommand *OSCommand
|
||||
Log *logrus.Entry
|
||||
Stats ContainerCliStat
|
||||
}
|
||||
|
||||
// ContainerCliStat is a stat object returned by the CLI docker stat command
|
||||
type ContainerCliStat struct {
|
||||
BlockIO string `json:"BlockIO"`
|
||||
CPUPerc string `json:"CPUPerc"`
|
||||
Container string `json:"Container"`
|
||||
ID string `json:"ID"`
|
||||
MemPerc string `json:"MemPerc"`
|
||||
MemUsage string `json:"MemUsage"`
|
||||
Name string `json:"Name"`
|
||||
NetIO string `json:"NetIO"`
|
||||
PIDs string `json:"PIDs"`
|
||||
}
|
||||
|
||||
// GetDisplayStrings returns the dispaly string of Container
|
||||
func (c *Container) GetDisplayStrings(isFocused bool) []string {
|
||||
return []string{utils.ColoredString(c.Container.State, c.GetColor()), utils.ColoredString(c.Name, color.FgWhite)}
|
||||
return []string{utils.ColoredString(c.Container.State, c.GetColor()), utils.ColoredString(c.Name, color.FgWhite), c.Stats.CPUPerc}
|
||||
}
|
||||
|
||||
// GetColor Container color
|
||||
|
|
@ -79,8 +95,20 @@ func (c *Container) Restart() error {
|
|||
return c.Client.ContainerRestart(context.Background(), c.ID, nil)
|
||||
}
|
||||
|
||||
// RestartService restarts the container
|
||||
func (c *Container) RestartService() error {
|
||||
templateString := c.OSCommand.Config.GetUserConfig().GetString("commandTemplates.restartService")
|
||||
command := utils.ApplyTemplate(templateString, c)
|
||||
return c.OSCommand.RunCommand(command)
|
||||
}
|
||||
|
||||
// Attach attaches the container
|
||||
func (c *Container) Attach() *exec.Cmd {
|
||||
cmd := c.OSCommand.PrepareSubProcess("docker", "attach", "--sig-proxy=false", c.ID)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Top returns process information
|
||||
func (c *Container) Top() (types.ContainerProcessList, error) {
|
||||
return c.Client.ContainerTop(context.Background(), c.ID, []string{})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package commands
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
|
|
@ -37,6 +38,40 @@ func NewDockerCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.Localize
|
|||
}, nil
|
||||
}
|
||||
|
||||
// UpdateContainerStats takes a slice of containers and returns the same slice but with new stats added
|
||||
// TODO: consider using this for everything stats-related
|
||||
func (c *DockerCommand) UpdateContainerStats(containers []*Container) ([]*Container, error) {
|
||||
// TODO: consider using a stream rather than polling
|
||||
command := `docker stats --all --no-trunc --no-stream --format '{{json .}}'`
|
||||
output, err := c.OSCommand.RunCommandWithOutput(command)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
jsonStats := "[" + strings.Join(
|
||||
strings.Split(
|
||||
strings.Trim(output, "\n"), "\n",
|
||||
), ",",
|
||||
) + "]"
|
||||
|
||||
c.Log.Warn(jsonStats)
|
||||
|
||||
var stats []ContainerCliStat
|
||||
if err := json.Unmarshal([]byte(jsonStats), &stats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, stat := range stats {
|
||||
for _, container := range containers {
|
||||
if container.ID == stat.ID {
|
||||
container.Stats = stat
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return containers, nil
|
||||
}
|
||||
|
||||
// GetContainers returns a slice of docker containers
|
||||
func (c *DockerCommand) GetContainers() ([]*Container, error) {
|
||||
containers, err := c.Client.ContainerList(context.Background(), types.ContainerListOptions{All: true})
|
||||
|
|
@ -47,22 +82,24 @@ func (c *DockerCommand) GetContainers() ([]*Container, error) {
|
|||
ownContainers := make([]*Container, len(containers))
|
||||
|
||||
for i, container := range containers {
|
||||
serviceName, ok := container.Labels["com.docker.compose.service"]
|
||||
if !ok {
|
||||
serviceName = ""
|
||||
c.Log.Warn("Could not get service name from docker container")
|
||||
}
|
||||
ownContainers[i] = &Container{
|
||||
ID: container.ID,
|
||||
Name: strings.TrimLeft(container.Names[0], "/"),
|
||||
ServiceName: serviceName,
|
||||
Container: container,
|
||||
Client: c.Client,
|
||||
OSCommand: c.OSCommand,
|
||||
Log: c.Log,
|
||||
ID: container.ID,
|
||||
Name: strings.TrimLeft(container.Names[0], "/"),
|
||||
ServiceName: container.Labels["com.docker.compose.service"],
|
||||
ProjectName: container.Labels["com.docker.compose.project"],
|
||||
ContainerNumber: container.Labels["com.docker.compose.container"],
|
||||
Container: container,
|
||||
Client: c.Client,
|
||||
OSCommand: c.OSCommand,
|
||||
Log: c.Log,
|
||||
}
|
||||
}
|
||||
|
||||
// ownContainers, err = c.UpdateContainerStats(ownContainers)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return ownContainers, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -248,6 +248,8 @@ update:
|
|||
days: 14 # how often a update is checked for
|
||||
reporting: 'undetermined' # one of: 'on' | 'off' | 'undetermined'
|
||||
confirmOnQuit: false
|
||||
commandTemplates:
|
||||
restartService: "docker-compose restart {{ .ServiceName }}"
|
||||
`)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,9 +8,7 @@ import (
|
|||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
|
|
@ -374,11 +372,14 @@ func (gui *Gui) handleContainerStop(g *gocui.Gui, v *gocui.View) error {
|
|||
}
|
||||
|
||||
return gui.createConfirmationPanel(gui.g, v, gui.Tr.SLocalize("Confirm"), gui.Tr.SLocalize("StopContainer"), func(g *gocui.Gui, v *gocui.View) error {
|
||||
if err := container.Stop(); err != nil {
|
||||
return gui.createErrorPanel(gui.g, err.Error())
|
||||
}
|
||||
return gui.WithWaitingStatus(gui.Tr.SLocalize("StoppingStatus"), func() error {
|
||||
if err := container.Stop(); err != nil {
|
||||
return gui.createErrorPanel(gui.g, err.Error())
|
||||
}
|
||||
|
||||
return gui.refreshContainers()
|
||||
})
|
||||
|
||||
return gui.refreshContainers()
|
||||
}, nil)
|
||||
}
|
||||
|
||||
|
|
@ -405,7 +406,9 @@ func (gui *Gui) handleContainerAttach(g *gocui.Gui, v *gocui.View) error {
|
|||
|
||||
gui.State.Panels.Main.WriterID++
|
||||
|
||||
// Create arbitrary command.
|
||||
// c := container.Attach()
|
||||
|
||||
// // Create arbitrary command.
|
||||
c := exec.Command("bash")
|
||||
|
||||
// Start the command with a pty.
|
||||
|
|
@ -414,20 +417,6 @@ func (gui *Gui) handleContainerAttach(g *gocui.Gui, v *gocui.View) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// Handle pty size.
|
||||
ch := make(chan os.Signal, 1)
|
||||
signal.Notify(ch, syscall.SIGWINCH)
|
||||
go func() {
|
||||
|
||||
for range ch {
|
||||
x, y := gui.getMainView().Size()
|
||||
if err := pty.Setsize(ptmx, &pty.Winsize{Cols: uint16(x), Rows: uint16(y), X: 0, Y: 0}); err != nil {
|
||||
gui.Log.Error(err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
ch <- syscall.SIGWINCH // Initial resize.
|
||||
|
||||
go func() {
|
||||
// Set stdin in raw mode.
|
||||
oldState, err := terminal.MakeRaw(int(os.Stdin.Fd()))
|
||||
|
|
@ -445,7 +434,7 @@ func (gui *Gui) handleContainerAttach(g *gocui.Gui, v *gocui.View) error {
|
|||
gui.State.Panels.Main.ObjectKey = "attached-" + container.ID
|
||||
|
||||
scanner := bufio.NewScanner(ptmx)
|
||||
scanner.Split(bufio.ScanRunes)
|
||||
scanner.Split(bufio.ScanBytes)
|
||||
|
||||
content := ""
|
||||
for scanner.Scan() {
|
||||
|
|
@ -465,3 +454,18 @@ func (gui *Gui) handleContainerAttach(g *gocui.Gui, v *gocui.View) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) handleServiceRestart(g *gocui.Gui, v *gocui.View) error {
|
||||
container, err := gui.getSelectedContainer(g)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return gui.WithWaitingStatus(gui.Tr.SLocalize("RestartingStatus"), func() error {
|
||||
if err := container.RestartService(); err != nil {
|
||||
return gui.createErrorPanel(gui.g, err.Error())
|
||||
}
|
||||
|
||||
return gui.refreshContainers()
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -179,6 +179,13 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
|||
Handler: gui.handleContainerAttach,
|
||||
Description: gui.Tr.SLocalize("attachContainer"),
|
||||
},
|
||||
{
|
||||
ViewName: "containers",
|
||||
Key: 'R',
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleServiceRestart,
|
||||
Description: gui.Tr.SLocalize("restartService"),
|
||||
},
|
||||
{
|
||||
ViewName: "images",
|
||||
Key: '[',
|
||||
|
|
|
|||
|
|
@ -780,6 +780,10 @@ func addEnglish(i18nObject *i18n.Bundle) error {
|
|||
ID: "RestartingStatus",
|
||||
Other: "restarting",
|
||||
},
|
||||
&i18n.Message{
|
||||
ID: "StoppingStatus",
|
||||
Other: "stopping",
|
||||
},
|
||||
&i18n.Message{
|
||||
ID: "removeContainer",
|
||||
Other: "remove container",
|
||||
|
|
@ -792,6 +796,10 @@ func addEnglish(i18nObject *i18n.Bundle) error {
|
|||
ID: "restartContainer",
|
||||
Other: "restart container",
|
||||
},
|
||||
&i18n.Message{
|
||||
ID: "restartService",
|
||||
Other: "restart container's service",
|
||||
},
|
||||
&i18n.Message{
|
||||
ID: "previousContext",
|
||||
Other: "previous context",
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"math"
|
||||
"reflect"
|
||||
"regexp"
|
||||
|
|
@ -288,3 +290,9 @@ func FormatDecimalBytes(b int) string {
|
|||
}
|
||||
return "a lot"
|
||||
}
|
||||
|
||||
func ApplyTemplate(str string, object interface{}) string {
|
||||
var buf bytes.Buffer
|
||||
template.Must(template.New("").Parse(str)).Execute(&buf, object)
|
||||
return buf.String()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue