This commit is contained in:
Jesse Duffield 2019-06-23 12:27:31 +10:00
parent 18dc37f822
commit ee688746f0
13 changed files with 104 additions and 60 deletions

View file

@ -195,16 +195,10 @@ type Details struct {
App struct {
} `json:"/app"`
} `json:"Volumes"`
WorkingDir string `json:"WorkingDir"`
Entrypoint interface{} `json:"Entrypoint"`
OnBuild interface{} `json:"OnBuild"`
Labels struct {
ComDockerComposeOneoff string `json:"com.docker.compose.oneoff"`
ComDockerComposeProject string `json:"com.docker.compose.project"`
ComDockerComposeService string `json:"com.docker.compose.service"`
ComDockerComposeSlug string `json:"com.docker.compose.slug"`
ComDockerComposeVersion string `json:"com.docker.compose.version"`
} `json:"Labels"`
WorkingDir string `json:"WorkingDir"`
Entrypoint interface{} `json:"Entrypoint"`
OnBuild interface{} `json:"OnBuild"`
Labels map[string]string `json:"Labels"`
} `json:"Config"`
NetworkSettings struct {
Bridge string `json:"Bridge"`

View file

@ -196,7 +196,7 @@ func GetDefaultConfig() UserConfig {
Containers: []CustomCommand{
{
Attach: true,
Command: "docker exec -it {{ .Container.ID }} /bin/bash",
Command: "docker exec -it {{ .Container.ID }} /bin/sh",
},
},
Services: []CustomCommand{},

View file

@ -3,6 +3,7 @@ package gui
import (
"encoding/json"
"fmt"
"strings"
"time"
"github.com/docker/docker/api/types"
@ -70,10 +71,8 @@ func (gui *Gui) handleContainerSelect(g *gocui.Gui, v *gocui.View) error {
}
key := "containers-" + container.ID + "-" + gui.getContainerContexts()[gui.State.Panels.Containers.ContextIndex]
if gui.State.Panels.Main.ObjectKey == key {
if !gui.shouldRefresh(key) {
return nil
} else {
gui.State.Panels.Main.ObjectKey = key
}
if err := gui.focusPoint(0, gui.State.Panels.Containers.SelectedLine, len(gui.DockerCommand.DisplayContainers), v); err != nil {
@ -115,13 +114,36 @@ func (gui *Gui) renderContainerConfig(container *commands.Container) error {
mainView.Autoscroll = false
mainView.Wrap = true
padding := 15
output := ""
output += utils.WithPadding("ID: ", padding) + container.ID + "\n"
output += utils.WithPadding("Name: ", padding) + container.Name + "\n"
output += utils.WithPadding("Command: ", padding) + strings.Join(append([]string{container.Details.Path}, container.Details.Args...), " ") + "\n"
output += utils.WithPadding("Labels: ", padding) + utils.FormatMap(padding, container.Details.Config.Labels)
output += "\n"
output += utils.WithPadding("Mounts: ", padding)
if len(container.Details.Mounts) > 0 {
output += "\n"
for _, mount := range container.Details.Mounts {
if mount.Type == "volume" {
output += fmt.Sprintf("%s%s %s\n", strings.Repeat(" ", padding), utils.ColoredString(mount.Type+":", color.FgYellow), mount.Name)
} else {
output += fmt.Sprintf("%s%s %s:%s\n", strings.Repeat(" ", padding), utils.ColoredString(mount.Type+":", color.FgYellow), mount.Source, mount.Destination)
}
}
} else {
output += "none\n"
}
data, err := json.MarshalIndent(&container.Details, "", " ")
if err != nil {
return err
}
output += fmt.Sprintf("\nFull details:\n\n%s", string(data))
return gui.T.NewTask(func(stop chan struct{}) {
gui.renderString(gui.g, "main", string(data))
gui.renderString(gui.g, "main", output)
})
}

View file

@ -26,7 +26,7 @@ func (gui *Gui) createCustomCommandMenu(customCommands []config.CustomCommand, c
options[i] = &customCommandOption{
customCommand: command,
description: resolvedCommand,
description: utils.WithShortSha(resolvedCommand),
command: resolvedCommand,
runCommand: true,
attach: command.Attach,

View file

@ -90,6 +90,7 @@ type menuPanelState struct {
}
type mainPanelState struct {
// ObjectKey tells us what context we are in. For example, if we are looking at the logs of a particular service in the services panel this key might be 'services-<service id>-logs'. The key is made so that if something changes which might require us to re-run the logs command or run a different command, the key will be different, and we'll then know to do whatever is required. Object key probably isn't the best name for this but Context is already used to refer to tabs. Maybe I should just call them tabs.
ObjectKey string
}
@ -278,6 +279,7 @@ func (gui *Gui) Run() error {
if strings.Contains(err.Error(), "No such container") {
// this happens all the time when e.g. restarting containers so we won't worry about it
gui.Log.Warn(err)
continue
}
gui.createErrorPanel(gui.g, err.Error())
}
@ -368,3 +370,12 @@ func (gui *Gui) handleCustomCommand(g *gocui.Gui, v *gocui.View) error {
return gui.Errors.ErrSubProcess
})
}
func (gui *Gui) shouldRefresh(key string) bool {
if gui.State.Panels.Main.ObjectKey == key {
return false
}
gui.State.Panels.Main.ObjectKey = key
return true
}

View file

@ -70,10 +70,8 @@ func (gui *Gui) handleImageSelect(g *gocui.Gui, v *gocui.View) error {
}
key := "images-" + Image.ID + "-" + gui.getImageContexts()[gui.State.Panels.Images.ContextIndex]
if gui.State.Panels.Main.ObjectKey == key {
if !gui.shouldRefresh(key) {
return nil
} else {
gui.State.Panels.Main.ObjectKey = key
}
if err := gui.focusPoint(0, gui.State.Panels.Images.SelectedLine, len(gui.DockerCommand.Images), v); err != nil {
@ -234,16 +232,18 @@ func (gui *Gui) handleImagesRemoveMenu(g *gocui.Gui, v *gocui.View) error {
return nil
}
shortSha := Image.ID[7:17]
options := []*removeImageOption{
{
description: gui.Tr.Remove,
command: "docker image rm " + Image.ID[1:20],
command: "docker image rm " + shortSha,
configOptions: types.ImageRemoveOptions{PruneChildren: true},
runCommand: true,
},
{
description: gui.Tr.RemoveWithoutPrune,
command: "docker image rm --no-prune " + Image.ID[1:20],
command: "docker image rm --no-prune " + shortSha,
configOptions: types.ImageRemoveOptions{PruneChildren: false},
runCommand: true,
},

View file

@ -68,11 +68,10 @@ func (gui *Gui) handleServiceSelect(g *gocui.Gui, v *gocui.View) error {
if service.Container != nil {
containerID = service.Container.ID
}
key := "services-" + service.ID + "-" + containerID + "-" + gui.getServiceContexts()[gui.State.Panels.Services.ContextIndex]
if gui.State.Panels.Main.ObjectKey == key {
if !gui.shouldRefresh(key) {
return nil
} else {
gui.State.Panels.Main.ObjectKey = key
}
if err := gui.focusPoint(0, gui.State.Panels.Services.SelectedLine, len(gui.DockerCommand.Services), v); err != nil {

View file

@ -47,10 +47,8 @@ func (gui *Gui) handleStatusSelect(g *gocui.Gui, v *gocui.View) error {
}
key := gui.getStatusContexts()[gui.State.Panels.Status.ContextIndex]
if gui.State.Panels.Main.ObjectKey == key {
if !gui.shouldRefresh(key) {
return nil
} else {
gui.State.Panels.Main.ObjectKey = key
}
gui.clearMainView()

View file

@ -70,7 +70,9 @@ func (gui *Gui) runCommand() error {
fmt.Fprintf(os.Stdout, "\n%s", utils.ColoredString(gui.Tr.PressEnterToReturn, color.FgGreen))
fmt.Scanln() // wait for enter press
if _, err := fmt.Scanln(); err != nil { // wait for enter press
gui.Log.Error(err)
}
return nil
}

View file

@ -78,6 +78,10 @@ func (gui *Gui) resetMainView() {
}
func (gui *Gui) newLineFocused(v *gocui.View) error {
if v == nil {
return nil
}
switch v.Name() {
case "menu":
return gui.handleMenuSelect(gui.g, v)

View file

@ -2,7 +2,6 @@ package gui
import (
"fmt"
"strings"
"github.com/fatih/color"
"github.com/go-errors/errors"
@ -68,10 +67,8 @@ func (gui *Gui) handleVolumeSelect(g *gocui.Gui, v *gocui.View) error {
}
key := "volumes-" + volume.Name + "-" + gui.getVolumeContexts()[gui.State.Panels.Volumes.ContextIndex]
if gui.State.Panels.Main.ObjectKey == key {
if !gui.shouldRefresh(key) {
return nil
} else {
gui.State.Panels.Main.ObjectKey = key
}
if err := gui.focusPoint(0, gui.State.Panels.Volumes.SelectedLine, len(gui.DockerCommand.Volumes), v); err != nil {
@ -105,31 +102,14 @@ func (gui *Gui) renderVolumeConfig(mainView *gocui.View, volume *commands.Volume
output += utils.WithPadding("Driver: ", padding) + volume.Volume.Driver + "\n"
output += utils.WithPadding("Scope: ", padding) + volume.Volume.Scope + "\n"
output += utils.WithPadding("Mountpoint: ", padding) + volume.Volume.Mountpoint + "\n"
output += utils.WithPadding("Labels: ", padding)
if len(volume.Volume.Labels) > 0 {
output += "\n"
for k, v := range volume.Volume.Labels {
output += formatMapItem(padding, k, v)
}
} else {
output += "none\n"
}
output += utils.WithPadding("Labels: ", padding) + utils.FormatMap(padding, volume.Volume.Labels) + "\n"
output += utils.WithPadding("Options: ", padding) + utils.FormatMap(padding, volume.Volume.Options) + "\n"
output += "\n" + utils.WithPadding("Options: ", padding)
if len(volume.Volume.Options) > 0 {
output += "\n"
for k, v := range volume.Volume.Options {
output += formatMapItem(padding, k, v)
}
} else {
output += "none\n"
}
output += "\n" + utils.WithPadding("Status: ", padding)
output += utils.WithPadding("Status: ", padding)
if volume.Volume.Status != nil {
output += "\n"
for k, v := range volume.Volume.Status {
output += formatMapItem(padding, k, v)
output += utils.FormatMapItem(padding, k, v)
}
} else {
output += "n/a"
@ -144,10 +124,6 @@ func (gui *Gui) renderVolumeConfig(mainView *gocui.View, volume *commands.Volume
})
}
func formatMapItem(padding int, k string, v interface{}) string {
return fmt.Sprintf("%s%s %v\n", strings.Repeat(" ", padding), utils.ColoredString(k+":", color.FgYellow), utils.ColoredString(fmt.Sprintf("%v", v), color.FgMagenta))
}
func (gui *Gui) refreshVolumes() error {
volumesView := gui.getVolumesView()
if volumesView == nil {
@ -252,13 +228,13 @@ func (gui *Gui) handleVolumesRemoveMenu(g *gocui.Gui, v *gocui.View) error {
options := []*removeVolumeOption{
{
description: gui.Tr.Remove,
command: "docker volume rm " + volume.Name,
command: utils.WithShortSha("docker volume rm " + volume.Name),
force: false,
runCommand: true,
},
{
description: gui.Tr.ForceRemove,
command: "docker volume rm --force " + volume.Name,
command: utils.WithShortSha("docker volume rm --force " + volume.Name),
force: true,
runCommand: true,
},

View file

@ -87,7 +87,7 @@ func (t *TaskManager) NewTickerTask(duration time.Duration, before func(stop cha
}
tickChan := time.NewTicker(duration)
// calling f first so that we're not waiting for the first tick
// f(stop, notifyStopped)
f(stop, notifyStopped)
for {
select {
case <-notifyStopped:

View file

@ -8,6 +8,7 @@ import (
"math"
"reflect"
"regexp"
"sort"
"strings"
"time"
@ -355,3 +356,40 @@ func GetColorAttribute(key string) color.Attribute {
}
return color.FgWhite
}
// WithShortSha returns a command but with a shorter SHA. in the terminal we're all used to 10 character SHAs but under the hood they're actually 64 characters long. No need including all the characters when we're just displaying a command
func WithShortSha(str string) string {
split := strings.Split(str, " ")
for i, word := range split {
// good enough proxy for now
if len(word) == 64 {
split[i] = word[0:10]
}
}
return strings.Join(split, " ")
}
// FormatMapItem is for displaying items in a map
func FormatMapItem(padding int, k string, v interface{}) string {
return fmt.Sprintf("%s%s %v\n", strings.Repeat(" ", padding), ColoredString(k+":", color.FgYellow), ColoredString(fmt.Sprintf("%v", v), color.FgWhite))
}
// FormatMap is for displaying a map
func FormatMap(padding int, m map[string]string) string {
if len(m) == 0 {
return "none\n"
}
output := "\n"
keys := make([]string, 0, len(m))
for key := range m {
keys = append(keys, key)
}
sort.Strings(keys)
for _, key := range keys {
output += FormatMapItem(padding, key, m[key])
}
return output
}