better support for custom commands

This commit is contained in:
Jesse Duffield 2019-06-10 20:14:44 +10:00
parent b02c92da33
commit 3e49e299b9
9 changed files with 166 additions and 83 deletions

View file

@ -36,6 +36,7 @@ type Container struct {
StatHistory []RecordedStats
Details Details
MonitoringStats bool
DockerCommand LimitedDockerCommand
}
// RecordedStats contains both the container stats we've received from docker, and our own derived stats from those container stats. When configuring a graph, you're basically specifying the path of a value in this struct
@ -354,13 +355,6 @@ 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.UserConfig.CommandTemplates.RestartService
command := utils.ApplyTemplate(templateString, c)
return c.OSCommand.RunCommand(command)
}
// Attach attaches the container
func (c *Container) Attach() (*exec.Cmd, error) {
// verify that we can in fact attach to this container
@ -394,7 +388,10 @@ func (c *Container) EraseOldHistory() {
// ViewLogs attaches to a subprocess viewing the container's logs
func (c *Container) ViewLogs() (*exec.Cmd, error) {
templateString := c.OSCommand.Config.UserConfig.CommandTemplates.ViewContainerLogs
command := utils.ApplyTemplate(templateString, c)
command := utils.ApplyTemplate(
templateString,
c.DockerCommand.NewCommandObject(CommandObject{Container: c}),
)
cmd := c.OSCommand.ExecutableFromString(command)
c.OSCommand.PrepareForChildren(cmd)
@ -407,3 +404,17 @@ func (c *DockerCommand) PruneContainers() error {
_, err := c.Client.ContainersPrune(context.Background(), filters.Args{})
return err
}
// TTYLogsCommand returns a command for obtaining the logs from a TTY container
func (c *Container) TTYLogsCommand() *exec.Cmd {
command := utils.ApplyTemplate(
c.Config.UserConfig.CommandTemplates.ContainerTTYLogs,
c.DockerCommand.NewCommandObject(CommandObject{Container: c}),
)
return c.OSCommand.RunCustomCommand(command)
}
// Inspect returns details about the container
func (c *Container) Inspect() (types.ContainerJSON, error) {
return c.Client.ContainerInspect(context.Background(), c.ID)
}

View file

@ -14,6 +14,7 @@ import (
"github.com/acarl005/stripansi"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
"github.com/imdario/mergo"
"github.com/jesseduffield/lazydocker/pkg/config"
"github.com/jesseduffield/lazydocker/pkg/i18n"
"github.com/jesseduffield/lazydocker/pkg/utils"
@ -39,6 +40,25 @@ type DockerCommand struct {
Volumes []*Volume
}
// LimitedDockerCommand is a stripped-down DockerCommand with just the methods the container/service/image might need
type LimitedDockerCommand interface {
NewCommandObject(CommandObject) CommandObject
}
// CommandObject is what we pass to our template resolvers when we are running a custom command. We do not guarantee that all fields will be populated: just the ones that make sense for the current context
type CommandObject struct {
DockerCompose string
Service *Service
Container *Container
}
// NewCommandObject takes a command object and returns a default command object with the passed command object merged in
func (c *DockerCommand) NewCommandObject(obj CommandObject) CommandObject {
defaultObj := CommandObject{DockerCompose: c.Config.UserConfig.CommandTemplates.DockerCompose}
mergo.Merge(&defaultObj, obj)
return defaultObj
}
// NewDockerCommand it runs git commands
func NewDockerCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.TranslationSet, config *config.AppConfig, errorChan chan error) (*DockerCommand, error) {
cli, err := client.NewEnvClient()
@ -46,22 +66,35 @@ func NewDockerCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.Translat
return nil, err
}
inDockerComposeProject := true
err = osCommand.RunCommand(config.UserConfig.CommandTemplates.CheckDockerComposeConfig)
if err != nil {
inDockerComposeProject = false
log.Warn(err.Error())
}
return &DockerCommand{
dockerCommand := &DockerCommand{
Log: log,
OSCommand: osCommand,
Tr: tr,
Config: config,
Client: cli,
InDockerComposeProject: inDockerComposeProject,
ErrorChan: errorChan,
}, nil
InDockerComposeProject: true,
}
command := utils.ApplyTemplate(
config.UserConfig.CommandTemplates.CheckDockerComposeConfig,
dockerCommand.NewCommandObject(CommandObject{}),
)
log.Warn(command)
err = osCommand.RunCommand(
utils.ApplyTemplate(
config.UserConfig.CommandTemplates.CheckDockerComposeConfig,
dockerCommand.NewCommandObject(CommandObject{}),
),
)
if err != nil {
dockerCommand.InDockerComposeProject = false
log.Warn(err.Error())
}
return dockerCommand, nil
}
// MonitorContainerStats is a function
@ -263,11 +296,12 @@ func (c *DockerCommand) GetContainers() ([]*Container, error) {
// initialise the container if it's completely new
if newContainer == nil {
newContainer = &Container{
ID: container.ID,
Client: c.Client,
OSCommand: c.OSCommand,
Log: c.Log,
Config: c.Config,
ID: container.ID,
Client: c.Client,
OSCommand: c.OSCommand,
Log: c.Log,
Config: c.Config,
DockerCommand: c,
}
}
@ -310,10 +344,11 @@ func (c *DockerCommand) GetServices() ([]*Service, error) {
for i, str := range lines {
arr := strings.Split(str, " ")
services[i] = &Service{
Name: arr[0],
ID: arr[1],
OSCommand: c.OSCommand,
Log: c.Log,
Name: arr[0],
ID: arr[1],
OSCommand: c.OSCommand,
Log: c.Log,
DockerCommand: c,
}
}
@ -353,7 +388,13 @@ func (c *DockerCommand) UpdateContainerDetails() error {
// ViewAllLogs attaches to a subprocess viewing all the logs from docker-compose
func (c *DockerCommand) ViewAllLogs() (*exec.Cmd, error) {
cmd := c.OSCommand.ExecutableFromString(c.OSCommand.Config.UserConfig.CommandTemplates.ViewAllLogs)
cmd := c.OSCommand.ExecutableFromString(
utils.ApplyTemplate(
c.OSCommand.Config.UserConfig.CommandTemplates.ViewAllLogs,
c.NewCommandObject(CommandObject{}),
),
)
c.OSCommand.PrepareForChildren(cmd)
return cmd, nil
@ -361,7 +402,13 @@ func (c *DockerCommand) ViewAllLogs() (*exec.Cmd, error) {
// DockerComposeConfig returns the result of 'docker-compose config'
func (c *DockerCommand) DockerComposeConfig() string {
output, err := c.OSCommand.RunCommandWithOutput(c.OSCommand.Config.UserConfig.CommandTemplates.DockerComposeConfig)
output, err := c.OSCommand.RunCommandWithOutput(
utils.ApplyTemplate(
c.OSCommand.Config.UserConfig.CommandTemplates.DockerComposeConfig,
c.NewCommandObject(CommandObject{}),
),
)
if err != nil {
output = err.Error()
}

View file

@ -14,13 +14,14 @@ import (
// Image : A docker Image
type Image struct {
Name string
Tag string
ID string
Image types.ImageSummary
Client *client.Client
OSCommand *OSCommand
Log *logrus.Entry
Name string
Tag string
ID string
Image types.ImageSummary
Client *client.Client
OSCommand *OSCommand
Log *logrus.Entry
DockerCommand LimitedDockerCommand
}
// GetDisplayStrings returns the display string of Image
@ -99,9 +100,6 @@ func (i *Image) RenderHistory() (string, error) {
return utils.RenderList(layers, utils.WithHeader([]string{"ID", "TAG", "SIZE", "COMMAND"}))
}
// GetImages returns a slice of docker images
func (c *DockerCommand) GetImages() ([]*Image, error) {
images, err := c.Client.ImageList(context.Background(), types.ImageListOptions{})
@ -127,13 +125,14 @@ func (c *DockerCommand) GetImages() ([]*Image, error) {
}
ownImages[i] = &Image{
ID: image.ID,
Name: nameParts[0],
Tag: tag,
Image: image,
Client: c.Client,
OSCommand: c.OSCommand,
Log: c.Log,
ID: image.ID,
Name: nameParts[0],
Tag: tag,
Image: image,
Client: c.Client,
OSCommand: c.OSCommand,
Log: c.Log,
DockerCommand: c,
}
}

View file

@ -11,11 +11,12 @@ import (
// Service : A docker Service
type Service struct {
Name string
ID string
OSCommand *OSCommand
Log *logrus.Entry
Container *Container
Name string
ID string
OSCommand *OSCommand
Log *logrus.Entry
Container *Container
DockerCommand LimitedDockerCommand
}
// GetDisplayStrings returns the dispaly string of Container
@ -37,14 +38,20 @@ func (s *Service) Remove(options types.ContainerRemoveOptions) error {
// Stop stops the service's containers
func (s *Service) Stop() error {
templateString := s.OSCommand.Config.UserConfig.CommandTemplates.StopService
command := utils.ApplyTemplate(templateString, s)
command := utils.ApplyTemplate(
templateString,
s.DockerCommand.NewCommandObject(CommandObject{Service: s}),
)
return s.OSCommand.RunCommand(command)
}
// Restart restarts the service
func (s *Service) Restart() error {
templateString := s.OSCommand.Config.UserConfig.CommandTemplates.RestartService
command := utils.ApplyTemplate(templateString, s)
command := utils.ApplyTemplate(
templateString,
s.DockerCommand.NewCommandObject(CommandObject{Service: s}),
)
return s.OSCommand.RunCommand(command)
}
@ -61,7 +68,10 @@ func (s *Service) Top() (types.ContainerProcessList, error) {
// ViewLogs attaches to a subprocess viewing the service's logs
func (s *Service) ViewLogs() (*exec.Cmd, error) {
templateString := s.OSCommand.Config.UserConfig.CommandTemplates.ViewServiceLogs
command := utils.ApplyTemplate(templateString, s)
command := utils.ApplyTemplate(
templateString,
s.DockerCommand.NewCommandObject(CommandObject{Service: s}),
)
cmd := s.OSCommand.ExecutableFromString(command)
s.OSCommand.PrepareForChildren(cmd)

View file

@ -12,11 +12,12 @@ import (
// Volume : A docker Volume
type Volume struct {
Name string
Volume *types.Volume
Client *client.Client
OSCommand *OSCommand
Log *logrus.Entry
Name string
Volume *types.Volume
Client *client.Client
OSCommand *OSCommand
Log *logrus.Entry
DockerCommand LimitedDockerCommand
}
// GetDisplayStrings returns the dispaly string of Container
@ -41,11 +42,12 @@ func (c *DockerCommand) RefreshVolumes() error {
for i, volume := range volumes {
ownVolumes[i] = &Volume{
Name: volume.Name,
Volume: volume,
Client: c.Client,
OSCommand: c.OSCommand,
Log: c.Log,
Name: volume.Name,
Volume: volume,
Client: c.Client,
OSCommand: c.OSCommand,
Log: c.Log,
DockerCommand: c,
}
}

View file

@ -164,18 +164,18 @@ func GetDefaultConfig() UserConfig {
Reporting: "undetermined",
ConfirmOnQuit: false,
CommandTemplates: CommandTemplatesConfig{
RestartService: "docker-compose restart {{ .Name }}",
RebuildService: "docker-compose up -d --build {{ .Name }}",
DockerCompose: "docker-compose",
StopService: "docker-compose stop {{ .Name }}",
ServiceLogs: "docker-compose logs {{ .Name }}",
AllLogs: "docker-compose logs --tail=100",
ViewAllLogs: "docker-compose logs",
DockerComposeConfig: "docker-compose config",
CheckDockerComposeConfig: "docker-compose config --quiet",
ContainerLogs: "docker logs --timestamps --follow --tail=100 {{ .ID }}",
ViewContainerLogs: "docker logs --timestamps --follow --tail=100 {{ .ID }}",
ContainerTTYLogs: "docker logs --follow --tail=100 {{ .ID }}",
RestartService: "{{ .DockerCompose }} restart {{ .Service.Name }}",
RebuildService: "{{ .DockerCompose }} up -d --build {{ .Service.Name }}",
StopService: "{{ .DockerCompose }} stop {{ .Service.Name }}",
ServiceLogs: "{{ .DockerCompose }} logs {{ .Service.Name }}",
AllLogs: "{{ .DockerCompose }} logs --tail=100",
ViewAllLogs: "{{ .DockerCompose }} logs",
DockerComposeConfig: "{{ .DockerCompose }} config",
CheckDockerComposeConfig: "{{ .DockerCompose }} config --quiet",
ContainerLogs: "docker logs --timestamps --follow --tail=100 {{ .Container.ID }}",
ViewContainerLogs: "docker logs --timestamps --follow --tail=100 {{ .Container.ID }}",
ContainerTTYLogs: "docker logs --follow --tail=100 {{ .Container.ID }}",
},
OS: GetPlatformDefaultConfig(),
Update: UpdateConfig{

View file

@ -2,7 +2,6 @@ package gui
import (
"bufio"
"context"
"encoding/json"
"fmt"
"os/exec"
@ -190,8 +189,7 @@ func (gui *Gui) renderLogs(container *commands.Container, template string, setup
gui.T.NewTickerTask(time.Millisecond*200, nil, func(stop, notifyStopped chan struct{}) {
gui.clearMainView()
command := utils.ApplyTemplate(gui.Config.UserConfig.CommandTemplates.ContainerTTYLogs, container)
cmd := gui.OSCommand.RunCustomCommand(command)
cmd := container.TTYLogsCommand()
setup(cmd)
@ -213,7 +211,7 @@ func (gui *Gui) renderLogs(container *commands.Container, template string, setup
case <-stop:
return
default:
result, err := gui.DockerCommand.Client.ContainerInspect(context.Background(), container.ID)
result, err := container.Inspect()
if err != nil {
// if we get an error, then the container has probably been removed so we'll get out of here
gui.Log.Error(err)

View file

@ -284,12 +284,18 @@ func (gui *Gui) handleServiceRestartMenu(g *gocui.Gui, v *gocui.View) error {
return nil
}
rebuildCommand := utils.ApplyTemplate(gui.Config.UserConfig.CommandTemplates.RebuildService, service)
rebuildCommand := utils.ApplyTemplate(
gui.Config.UserConfig.CommandTemplates.RebuildService,
service,
)
options := []*commandOption{
{
description: gui.Tr.Restart,
command: utils.ApplyTemplate(gui.Config.UserConfig.CommandTemplates.RestartService, service),
command: utils.ApplyTemplate(
gui.Config.UserConfig.CommandTemplates.RestartService,
gui.DockerCommand.NewCommandObject(commands.CommandObject{Service: service}),
),
f: func() error {
return gui.WithWaitingStatus(gui.Tr.RestartingStatus, func() error {
if err := service.Restart(); err != nil {
@ -302,7 +308,10 @@ func (gui *Gui) handleServiceRestartMenu(g *gocui.Gui, v *gocui.View) error {
},
{
description: gui.Tr.Rebuild,
command: utils.ApplyTemplate(gui.Config.UserConfig.CommandTemplates.RebuildService, service),
command: utils.ApplyTemplate(
gui.Config.UserConfig.CommandTemplates.RebuildService,
gui.DockerCommand.NewCommandObject(commands.CommandObject{Service: service}),
),
f: func() error {
gui.SubProcess = gui.OSCommand.RunCustomCommand(rebuildCommand)
return gui.Errors.ErrSubProcess

View file

@ -6,6 +6,8 @@ import (
"github.com/go-errors/errors"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazydocker/pkg/commands"
"github.com/jesseduffield/lazydocker/pkg/utils"
)
func (gui *Gui) getStatusContexts() []string {
@ -105,7 +107,12 @@ func (gui *Gui) renderAllLogs() error {
gui.clearMainView()
cmd := gui.OSCommand.RunCustomCommand(gui.Config.UserConfig.CommandTemplates.AllLogs)
cmd := gui.OSCommand.RunCustomCommand(
utils.ApplyTemplate(
gui.Config.UserConfig.CommandTemplates.AllLogs,
gui.DockerCommand.NewCommandObject(commands.CommandObject{}),
),
)
cmd.Stdout = mainView
cmd.Stderr = mainView