mirror of
https://github.com/jesseduffield/lazydocker.git
synced 2024-10-06 01:17:33 +03:00
Merge 30f3619eb9
into de40167712
This commit is contained in:
commit
9f003a9b08
@ -2,6 +2,7 @@ package app
|
||||
|
||||
import (
|
||||
"io"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazydocker/pkg/commands"
|
||||
@ -49,6 +50,15 @@ func NewApp(config *config.AppConfig) (*App, error) {
|
||||
}
|
||||
app.closers = append(app.closers, app.DockerCommand)
|
||||
app.Gui, err = gui.NewGui(app.Log, app.DockerCommand, app.OSCommand, app.Tr, config, app.ErrorChan)
|
||||
|
||||
currentDir := path.Base(app.Config.ProjectDir)
|
||||
for _, project := range app.DockerCommand.GetAllComposeProjects() {
|
||||
if project.WorkingDir == currentDir {
|
||||
app.DockerCommand.SelectedComposeProject = &project
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return app, err
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ type DockerCommand struct {
|
||||
Tr *i18n.TranslationSet
|
||||
Config *config.AppConfig
|
||||
Client *client.Client
|
||||
InDockerComposeProject bool
|
||||
SelectedComposeProject *ComposeProject
|
||||
ErrorChan chan error
|
||||
ContainerMutex deadlock.Mutex
|
||||
ServiceMutex deadlock.Mutex
|
||||
@ -64,6 +64,11 @@ type CommandObject struct {
|
||||
Network *Network
|
||||
}
|
||||
|
||||
type ComposeProject struct {
|
||||
Name string
|
||||
WorkingDir string
|
||||
}
|
||||
|
||||
// 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}
|
||||
@ -109,22 +114,18 @@ func NewDockerCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.Translat
|
||||
Config: config,
|
||||
Client: cli,
|
||||
ErrorChan: errorChan,
|
||||
InDockerComposeProject: true,
|
||||
Closers: []io.Closer{tunnelCloser},
|
||||
}
|
||||
|
||||
dockerCommand.setDockerComposeCommand(config)
|
||||
|
||||
err = osCommand.RunCommand(
|
||||
utils.ApplyTemplate(
|
||||
config.UserConfig.CommandTemplates.CheckDockerComposeConfig,
|
||||
dockerCommand.NewCommandObject(CommandObject{}),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
dockerCommand.InDockerComposeProject = false
|
||||
log.Warn(err.Error())
|
||||
}
|
||||
//dockerCommand.GetAllComposeProjects()
|
||||
//err = osCommand.RunCommand(
|
||||
// utils.ApplyTemplate(
|
||||
// config.UserConfig.CommandTemplates.CheckDockerComposeConfig,
|
||||
// dockerCommand.NewCommandObject(CommandObject{}),
|
||||
// ),
|
||||
//)
|
||||
|
||||
return dockerCommand, nil
|
||||
}
|
||||
@ -145,6 +146,25 @@ func (c *DockerCommand) Close() error {
|
||||
return utils.CloseMany(c.Closers)
|
||||
}
|
||||
|
||||
func (c *DockerCommand) GetAllComposeProjects() map[string]ComposeProject {
|
||||
containers, _ := c.Client.ContainerList(context.Background(), dockerTypes.ContainerListOptions{
|
||||
All: true,
|
||||
})
|
||||
|
||||
projects := make(map[string]ComposeProject)
|
||||
for _, c := range containers {
|
||||
projectName, ok := c.Labels["com.docker.compose.project"]
|
||||
if ok {
|
||||
workingDir, ok := c.Labels["com.docker.compose.project.working_dir"]
|
||||
//configFiles, ok := c.Labels["com.docker.compose.project.config_files"]
|
||||
if ok {
|
||||
projects[projectName] = ComposeProject{Name: projectName, WorkingDir: workingDir}
|
||||
}
|
||||
}
|
||||
}
|
||||
return projects
|
||||
}
|
||||
|
||||
func (c *DockerCommand) CreateClientStatMonitor(container *Container) {
|
||||
container.MonitoringStats = true
|
||||
stream, err := c.Client.ContainerStats(context.Background(), container.ID, true)
|
||||
@ -274,12 +294,12 @@ func (c *DockerCommand) GetContainers(existingContainers []*Container) ([]*Conta
|
||||
|
||||
// GetServices gets services
|
||||
func (c *DockerCommand) GetServices() ([]*Service, error) {
|
||||
if !c.InDockerComposeProject {
|
||||
if c.SelectedComposeProject == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
composeCommand := c.Config.UserConfig.CommandTemplates.DockerCompose
|
||||
output, err := c.OSCommand.RunCommandWithOutput(fmt.Sprintf("%s config --services", composeCommand))
|
||||
output, err := c.OSCommand.RunCommandWithOutputFrom(fmt.Sprintf("%s config --services", composeCommand), c.SelectedComposeProject.WorkingDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -348,11 +368,12 @@ 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(
|
||||
output, err := c.OSCommand.RunCommandWithOutputFrom(
|
||||
utils.ApplyTemplate(
|
||||
c.OSCommand.Config.UserConfig.CommandTemplates.DockerComposeConfig,
|
||||
c.NewCommandObject(CommandObject{}),
|
||||
),
|
||||
c.SelectedComposeProject.WorkingDir,
|
||||
)
|
||||
if err != nil {
|
||||
output = err.Error()
|
||||
|
@ -55,15 +55,21 @@ func (c *OSCommand) SetCommand(cmd func(string, ...string) *exec.Cmd) {
|
||||
c.command = cmd
|
||||
}
|
||||
|
||||
// RunCommandWithOutput wrapper around commands returning their output and error
|
||||
func (c *OSCommand) RunCommandWithOutput(command string) (string, error) {
|
||||
// RunCommandWithOutputFrom wrapper around commands returning their output and error
|
||||
func (c *OSCommand) RunCommandWithOutputFrom(command string, workingDir string) (string, error) {
|
||||
cmd := c.ExecutableFromString(command)
|
||||
before := time.Now()
|
||||
cmd.Dir = workingDir
|
||||
output, err := sanitisedCommandOutput(cmd.Output())
|
||||
c.Log.Warn(fmt.Sprintf("'%s': %s", command, time.Since(before)))
|
||||
return output, err
|
||||
}
|
||||
|
||||
// RunCommandWithOutput wrapper around commands returning their output and error
|
||||
func (c *OSCommand) RunCommandWithOutput(command string) (string, error) {
|
||||
return c.RunCommandWithOutputFrom(command, "")
|
||||
}
|
||||
|
||||
// RunCommandWithOutput wrapper around commands returning their output and error
|
||||
func (c *OSCommand) RunCommandWithOutputContext(ctx context.Context, command string) (string, error) {
|
||||
cmd := c.ExecutableFromStringContext(ctx, command)
|
||||
|
@ -1,5 +1,5 @@
|
||||
package commands
|
||||
|
||||
type Project struct {
|
||||
Name string
|
||||
Compose ComposeProject
|
||||
}
|
||||
|
@ -19,17 +19,20 @@ import (
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
func (gui *Gui) getContainersPanel() *panels.SideListPanel[*commands.Container] {
|
||||
// Standalone containers are containers which are either one-off containers, or whose service is not part of this docker-compose context.
|
||||
isStandaloneContainer := func(container *commands.Container) bool {
|
||||
var isStandaloneContainer = func(container *commands.Container) bool {
|
||||
if container.OneOff || container.ServiceName == "" {
|
||||
return true
|
||||
}
|
||||
|
||||
return !lo.SomeBy(gui.Panels.Services.List.GetAllItems(), func(service *commands.Service) bool {
|
||||
return service.Name == container.ServiceName
|
||||
})
|
||||
}
|
||||
project, _ := container.Container.Labels["com.docker.compose.project"]
|
||||
return project == ""
|
||||
//return !lo.SomeBy(gui.Panels.Services.List.GetAllItems(), func(service *commands.Service) bool {
|
||||
// return service.Name == container.ServiceName
|
||||
//})
|
||||
}
|
||||
|
||||
func (gui *Gui) getContainersPanel() *panels.SideListPanel[*commands.Container] {
|
||||
// Standalone containers are containers which are either one-off containers, or whose service is not part of this docker-compose context.
|
||||
|
||||
return &panels.SideListPanel[*commands.Container]{
|
||||
ContextState: &panels.ContextState[*commands.Container]{
|
||||
@ -255,6 +258,12 @@ func (gui *Gui) refreshContainersAndServices() error {
|
||||
originalSelectedLineIdx := gui.Panels.Services.SelectedIdx
|
||||
selectedService, isServiceSelected := gui.Panels.Services.List.TryGet(originalSelectedLineIdx)
|
||||
|
||||
services, err := gui.DockerCommand.GetServices()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
gui.Panels.Services.List.SetItems(services)
|
||||
|
||||
containers, services, err := gui.DockerCommand.RefreshContainersAndServices(
|
||||
gui.Panels.Services.List.GetAllItems(),
|
||||
gui.Panels.Containers.List.GetAllItems(),
|
||||
@ -283,7 +292,7 @@ func (gui *Gui) refreshContainersAndServices() error {
|
||||
}
|
||||
|
||||
func (gui *Gui) renderContainersAndServices() error {
|
||||
if gui.DockerCommand.InDockerComposeProject {
|
||||
if gui.DockerCommand.SelectedComposeProject != nil {
|
||||
if err := gui.Panels.Services.RerenderList(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -459,7 +459,7 @@ func (gui *Gui) ShouldRefresh(key string) bool {
|
||||
}
|
||||
|
||||
func (gui *Gui) initiallyFocusedViewName() string {
|
||||
if gui.DockerCommand.InDockerComposeProject {
|
||||
if gui.DockerCommand.SelectedComposeProject != nil {
|
||||
return "services"
|
||||
}
|
||||
return "containers"
|
||||
|
@ -129,21 +129,27 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
||||
Handler: gui.handleCustomCommand,
|
||||
},
|
||||
{
|
||||
ViewName: "project",
|
||||
ViewName: "projects",
|
||||
Key: 'e',
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleEditConfig,
|
||||
Description: gui.Tr.EditConfig,
|
||||
},
|
||||
{
|
||||
ViewName: "project",
|
||||
ViewName: "projects",
|
||||
Key: 'o',
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleOpenConfig,
|
||||
Description: gui.Tr.OpenConfig,
|
||||
},
|
||||
{
|
||||
ViewName: "project",
|
||||
ViewName: "projects",
|
||||
Key: gocui.KeySpace,
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleComposeProjectSelect,
|
||||
},
|
||||
{
|
||||
ViewName: "projects",
|
||||
Key: 'm',
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleViewAllLogs,
|
||||
|
@ -3,5 +3,6 @@ package presentation
|
||||
import "github.com/jesseduffield/lazydocker/pkg/commands"
|
||||
|
||||
func GetProjectDisplayStrings(project *commands.Project) []string {
|
||||
return []string{project.Name}
|
||||
// TODO show status up down stop
|
||||
return []string{project.Compose.Name}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ package gui
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"path"
|
||||
"github.com/samber/lo"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
@ -23,7 +23,7 @@ func (gui *Gui) getProjectPanel() *panels.SideListPanel[*commands.Project] {
|
||||
return &panels.SideListPanel[*commands.Project]{
|
||||
ContextState: &panels.ContextState[*commands.Project]{
|
||||
GetMainTabs: func() []panels.MainTab[*commands.Project] {
|
||||
if gui.DockerCommand.InDockerComposeProject {
|
||||
if gui.DockerCommand.SelectedComposeProject != nil {
|
||||
return []panels.MainTab[*commands.Project]{
|
||||
{
|
||||
Key: "logs",
|
||||
@ -52,43 +52,39 @@ func (gui *Gui) getProjectPanel() *panels.SideListPanel[*commands.Project] {
|
||||
}
|
||||
},
|
||||
GetItemContextCacheKey: func(project *commands.Project) string {
|
||||
return "projects-" + project.Name
|
||||
return "projects-" + project.Compose.Name // TODO status
|
||||
},
|
||||
},
|
||||
|
||||
ListPanel: panels.ListPanel[*commands.Project]{
|
||||
List: panels.NewFilteredList[*commands.Project](),
|
||||
View: gui.Views.Project,
|
||||
View: gui.Views.Projects,
|
||||
},
|
||||
NoItemsMessage: "",
|
||||
Gui: gui.intoInterface(),
|
||||
|
||||
Sort: func(a *commands.Project, b *commands.Project) bool {
|
||||
return false
|
||||
return a.Compose.Name < b.Compose.Name
|
||||
},
|
||||
GetTableCells: presentation.GetProjectDisplayStrings,
|
||||
// It doesn't make sense to filter a list of only one item.
|
||||
DisableFilter: true,
|
||||
//DisableFilter: true,
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) refreshProject() error {
|
||||
gui.Panels.Projects.SetItems([]*commands.Project{{Name: gui.getProjectName()}})
|
||||
c := gui.getProjects()
|
||||
list := make([]*commands.Project, len(c))
|
||||
for i, s := range lo.Values(c) {
|
||||
list[i] = &commands.Project{Compose: s}
|
||||
}
|
||||
|
||||
gui.Panels.Projects.SetItems(list)
|
||||
return gui.Panels.Projects.RerenderList()
|
||||
}
|
||||
|
||||
func (gui *Gui) getProjectName() string {
|
||||
projectName := path.Base(gui.Config.ProjectDir)
|
||||
if gui.DockerCommand.InDockerComposeProject {
|
||||
for _, service := range gui.Panels.Services.List.GetAllItems() {
|
||||
container := service.Container
|
||||
if container != nil && container.DetailsLoaded() {
|
||||
return container.Details.Config.Labels["com.docker.compose.project"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return projectName
|
||||
func (gui *Gui) getProjects() map[string]commands.ComposeProject {
|
||||
return gui.DockerCommand.GetAllComposeProjects()
|
||||
}
|
||||
|
||||
func (gui *Gui) renderCredits(_project *commands.Project) tasks.TaskFunc {
|
||||
@ -125,6 +121,7 @@ func (gui *Gui) renderAllLogs(_project *commands.Project) tasks.TaskFunc {
|
||||
gui.DockerCommand.NewCommandObject(commands.CommandObject{}),
|
||||
),
|
||||
)
|
||||
cmd.Dir = _project.Compose.WorkingDir
|
||||
|
||||
cmd.Stdout = gui.Views.Main
|
||||
cmd.Stderr = gui.Views.Main
|
||||
@ -158,6 +155,20 @@ func (gui *Gui) handleEditConfig(g *gocui.Gui, v *gocui.View) error {
|
||||
return gui.editFile(gui.Config.ConfigFilename())
|
||||
}
|
||||
|
||||
func (gui *Gui) handleComposeProjectSelect(g *gocui.Gui, v *gocui.View) error {
|
||||
project, err := gui.Panels.Projects.GetSelectedItem()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
//if gui.DockerCommand.SelectedComposeProject != &project.Compose {
|
||||
gui.DockerCommand.SelectedComposeProject = &project.Compose
|
||||
// gui.renderServiceLogs()
|
||||
return gui.refreshContainersAndServices()
|
||||
//}
|
||||
//return nil
|
||||
}
|
||||
|
||||
func lazydockerTitle() string {
|
||||
return `
|
||||
_ _ _
|
||||
|
@ -77,9 +77,9 @@ func (gui *Gui) getServicesPanel() *panels.SideListPanel[*commands.Service] {
|
||||
GetTableCells: func(service *commands.Service) []string {
|
||||
return presentation.GetServiceDisplayStrings(&gui.Config.UserConfig.Gui, service)
|
||||
},
|
||||
Hide: func() bool {
|
||||
return !gui.DockerCommand.InDockerComposeProject
|
||||
},
|
||||
//Hide: func() bool {
|
||||
// return gui.DockerCommand.SelectedComposeProject == nil
|
||||
//},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@ func hideUnderScores() bool {
|
||||
|
||||
type Views struct {
|
||||
// side panels
|
||||
Project *gocui.View
|
||||
Projects *gocui.View
|
||||
Services *gocui.View
|
||||
Containers *gocui.View
|
||||
Images *gocui.View
|
||||
@ -66,7 +66,7 @@ func (gui *Gui) orderedViewNameMappings() []viewNameMapping {
|
||||
return []viewNameMapping{
|
||||
// first layer. Ordering within this layer does not matter because there are
|
||||
// no overlapping views
|
||||
{viewPtr: &gui.Views.Project, name: "project", autoPosition: true},
|
||||
{viewPtr: &gui.Views.Projects, name: "projects", autoPosition: true},
|
||||
{viewPtr: &gui.Views.Services, name: "services", autoPosition: true},
|
||||
{viewPtr: &gui.Views.Containers, name: "containers", autoPosition: true},
|
||||
{viewPtr: &gui.Views.Images, name: "images", autoPosition: true},
|
||||
@ -118,7 +118,9 @@ func (gui *Gui) createAllViews() error {
|
||||
// when you run a docker container with the -it flags (interactive mode) it adds carriage returns for some reason. This is not docker's fault, it's an os-level default.
|
||||
gui.Views.Main.IgnoreCarriageReturns = true
|
||||
|
||||
gui.Views.Project.Title = gui.Tr.ProjectTitle
|
||||
gui.Views.Projects.Highlight = true
|
||||
gui.Views.Projects.Title = gui.Tr.ProjectTitle
|
||||
gui.Views.Projects.SelBgColor = selectedLineBgColor
|
||||
|
||||
gui.Views.Services.Highlight = true
|
||||
gui.Views.Services.Title = gui.Tr.ServicesTitle
|
||||
@ -126,7 +128,7 @@ func (gui *Gui) createAllViews() error {
|
||||
|
||||
gui.Views.Containers.Highlight = true
|
||||
gui.Views.Containers.SelBgColor = selectedLineBgColor
|
||||
if gui.Config.UserConfig.Gui.ShowAllContainers || !gui.DockerCommand.InDockerComposeProject {
|
||||
if gui.Config.UserConfig.Gui.ShowAllContainers || gui.DockerCommand.SelectedComposeProject == nil {
|
||||
gui.Views.Containers.Title = gui.Tr.ContainersTitle
|
||||
} else {
|
||||
gui.Views.Containers.Title = gui.Tr.StandaloneContainersTitle
|
||||
|
@ -56,7 +56,7 @@ func dutchSet() TranslationSet {
|
||||
|
||||
GlobalTitle: "Globaal",
|
||||
MainTitle: "Hoofd",
|
||||
ProjectTitle: "Project",
|
||||
ProjectTitle: "Projects",
|
||||
ServicesTitle: "Diensten",
|
||||
ContainersTitle: "Containers",
|
||||
StandaloneContainersTitle: "Alleenstaande Containers",
|
||||
|
@ -212,7 +212,7 @@ func englishSet() TranslationSet {
|
||||
|
||||
GlobalTitle: "Global",
|
||||
MainTitle: "Main",
|
||||
ProjectTitle: "Project",
|
||||
ProjectTitle: "Projects",
|
||||
ServicesTitle: "Services",
|
||||
ContainersTitle: "Containers",
|
||||
StandaloneContainersTitle: "Standalone Containers",
|
||||
|
Loading…
Reference in New Issue
Block a user