more progress

pull/1/head
Jesse Duffield 5 years ago
parent 88758de164
commit f0ffdcd81f

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…
Cancel
Save