mirror of
https://github.com/jesseduffield/lazydocker.git
synced 2026-07-25 08:31:03 +00:00
wip on projects
This commit is contained in:
parent
099ae97e68
commit
01f9b3973f
7 changed files with 149 additions and 48 deletions
|
|
@ -41,17 +41,30 @@ func NewApp(config *config.AppConfig) (*App, error) {
|
||||||
}
|
}
|
||||||
app.OSCommand = commands.NewOSCommand(app.Log, config)
|
app.OSCommand = commands.NewOSCommand(app.Log, config)
|
||||||
|
|
||||||
// here is the place to make use of the docker-compose.yml file in the current directory
|
|
||||||
|
|
||||||
app.DockerCommand, err = commands.NewDockerCommand(app.Log, app.OSCommand, app.Tr, app.Config, app.ErrorChan)
|
app.DockerCommand, err = commands.NewDockerCommand(app.Log, app.OSCommand, app.Tr, app.Config, app.ErrorChan)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return app, err
|
return app, err
|
||||||
}
|
}
|
||||||
|
|
||||||
app.closers = append(app.closers, app.DockerCommand)
|
app.closers = append(app.closers, app.DockerCommand)
|
||||||
app.Gui, err = gui.NewGui(app.Log, app.DockerCommand, app.OSCommand, app.Tr, config, app.ErrorChan)
|
app.Gui, err = gui.NewGui(app.Log, app.DockerCommand, app.OSCommand, app.Tr, config, app.ErrorChan)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return app, err
|
return app, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// here is the place to make use of the docker-compose.yml file in the current directory
|
||||||
|
err = app.OSCommand.RunCommand(
|
||||||
|
utils.ApplyTemplate(
|
||||||
|
config.UserConfig.CommandTemplates.CheckDockerComposeConfig,
|
||||||
|
app.DockerCommand.NewCommandObject(commands.CommandObject{}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
app.DockerCommand.InDockerComposeProject = false
|
||||||
|
app.Log.Warn(err.Error())
|
||||||
|
} else {
|
||||||
|
app.Gui.State.Project = &commands.Project{Name: app.Gui.GetProjectName()}
|
||||||
|
}
|
||||||
return app, nil
|
return app, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,15 +34,16 @@ const (
|
||||||
|
|
||||||
// DockerCommand is our main docker interface
|
// DockerCommand is our main docker interface
|
||||||
type DockerCommand struct {
|
type DockerCommand struct {
|
||||||
Log *logrus.Entry
|
Log *logrus.Entry
|
||||||
OSCommand *OSCommand
|
OSCommand *OSCommand
|
||||||
Tr *i18n.TranslationSet
|
Tr *i18n.TranslationSet
|
||||||
Config *config.AppConfig
|
Config *config.AppConfig
|
||||||
Client *client.Client
|
Client *client.Client
|
||||||
InDockerComposeProject bool
|
InDockerComposeProject bool
|
||||||
ErrorChan chan error
|
CurrentDockerComposeProject string
|
||||||
ContainerMutex deadlock.Mutex
|
ErrorChan chan error
|
||||||
ServiceMutex deadlock.Mutex
|
ContainerMutex deadlock.Mutex
|
||||||
|
ServiceMutex deadlock.Mutex
|
||||||
|
|
||||||
Closers []io.Closer
|
Closers []io.Closer
|
||||||
}
|
}
|
||||||
|
|
@ -112,17 +113,6 @@ func NewDockerCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.Translat
|
||||||
|
|
||||||
dockerCommand.setDockerComposeCommand(config)
|
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())
|
|
||||||
}
|
|
||||||
|
|
||||||
return dockerCommand, nil
|
return dockerCommand, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -176,7 +166,7 @@ func (c *DockerCommand) CreateClientStatMonitor(container *Container) {
|
||||||
container.MonitoringStats = false
|
container.MonitoringStats = false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *DockerCommand) RefreshContainersAndServices(currentServices []*Service, currentContainers []*Container) ([]*Container, []*Service, error) {
|
func (c *DockerCommand) RefreshContainersAndServices(currentServices []*Service, currentContainers []*Container, currentProject *Project) ([]*Container, []*Service, error) {
|
||||||
c.ServiceMutex.Lock()
|
c.ServiceMutex.Lock()
|
||||||
defer c.ServiceMutex.Unlock()
|
defer c.ServiceMutex.Unlock()
|
||||||
|
|
||||||
|
|
@ -186,17 +176,15 @@ func (c *DockerCommand) RefreshContainersAndServices(currentServices []*Service,
|
||||||
}
|
}
|
||||||
|
|
||||||
var services []*Service
|
var services []*Service
|
||||||
// we only need to get these services once because they won't change in the runtime of the program
|
if currentProject != nil {
|
||||||
if currentServices != nil {
|
services, err = c.GetServicesFromContainers(containers, currentProject)
|
||||||
services = currentServices
|
|
||||||
} else {
|
|
||||||
services, err = c.GetServices()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
c.assignContainersToServices(containers, services)
|
c.assignContainersToServices(containers, services)
|
||||||
|
//c.assignContainersToProjects(containers)
|
||||||
|
}
|
||||||
|
|
||||||
return containers, services, nil
|
return containers, services, nil
|
||||||
}
|
}
|
||||||
|
|
@ -214,6 +202,11 @@ L:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetDockerProjects
|
||||||
|
func (c *DockerCommand) GetDockerProjects() ([]*Project, error) {
|
||||||
|
return []*Project{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetContainers gets the docker containers
|
// GetContainers gets the docker containers
|
||||||
func (c *DockerCommand) GetContainers(existingContainers []*Container) ([]*Container, error) {
|
func (c *DockerCommand) GetContainers(existingContainers []*Container) ([]*Container, error) {
|
||||||
c.ContainerMutex.Lock()
|
c.ContainerMutex.Lock()
|
||||||
|
|
@ -269,6 +262,42 @@ func (c *DockerCommand) GetContainers(existingContainers []*Container) ([]*Conta
|
||||||
return ownContainers, nil
|
return ownContainers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetServicesFromContainers gets services
|
||||||
|
func (c *DockerCommand) GetServicesFromContainers(containers []*Container, currentProject *Project) ([]*Service, error) {
|
||||||
|
var services []*Service
|
||||||
|
|
||||||
|
if currentProject.Name == "" {
|
||||||
|
return services, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, cont := range containers {
|
||||||
|
if cont.ProjectName != currentProject.Name {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
service := &Service{
|
||||||
|
Name: cont.Name,
|
||||||
|
ID: cont.ID,
|
||||||
|
OSCommand: c.OSCommand,
|
||||||
|
Log: c.Log,
|
||||||
|
DockerCommand: c,
|
||||||
|
}
|
||||||
|
services = append(services, service)
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: Should be run once every time the project changes
|
||||||
|
if len(services) == 0 {
|
||||||
|
//If no services are found in running containers fetch services from dockerCompose
|
||||||
|
var err error
|
||||||
|
services, err = c.GetServices()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return services, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetServices gets services
|
// GetServices gets services
|
||||||
func (c *DockerCommand) GetServices() ([]*Service, error) {
|
func (c *DockerCommand) GetServices() ([]*Service, error) {
|
||||||
if !c.InDockerComposeProject {
|
if !c.InDockerComposeProject {
|
||||||
|
|
@ -276,6 +305,7 @@ func (c *DockerCommand) GetServices() ([]*Service, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
composeCommand := c.Config.UserConfig.CommandTemplates.DockerCompose
|
composeCommand := c.Config.UserConfig.CommandTemplates.DockerCompose
|
||||||
|
// TODO: Handle remote docker compose files
|
||||||
output, err := c.OSCommand.RunCommandWithOutput(fmt.Sprintf("%s config --services", composeCommand))
|
output, err := c.OSCommand.RunCommandWithOutput(fmt.Sprintf("%s config --services", composeCommand))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
||||||
|
|
@ -258,6 +258,7 @@ func (gui *Gui) refreshContainersAndServices() error {
|
||||||
containers, services, err := gui.DockerCommand.RefreshContainersAndServices(
|
containers, services, err := gui.DockerCommand.RefreshContainersAndServices(
|
||||||
gui.Panels.Services.List.GetAllItems(),
|
gui.Panels.Services.List.GetAllItems(),
|
||||||
gui.Panels.Containers.List.GetAllItems(),
|
gui.Panels.Containers.List.GetAllItems(),
|
||||||
|
gui.State.Project,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
|
|
@ -87,8 +87,15 @@ type guiState struct {
|
||||||
// Maintains the state of manual filtering i.e. typing in a substring
|
// Maintains the state of manual filtering i.e. typing in a substring
|
||||||
// to filter on in the current panel.
|
// to filter on in the current panel.
|
||||||
Filter filterState
|
Filter filterState
|
||||||
|
|
||||||
|
// Project used for navigation in projects
|
||||||
|
Project *commands.Project
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//type projectState struct {
|
||||||
|
// name string
|
||||||
|
//}
|
||||||
|
|
||||||
type filterState struct {
|
type filterState struct {
|
||||||
// If true then we're either currently inside the filter view
|
// If true then we're either currently inside the filter view
|
||||||
// or we've committed the filter and we're back in the list view
|
// or we've committed the filter and we're back in the list view
|
||||||
|
|
@ -297,7 +304,7 @@ func (gui *Gui) updateContainerDetails() error {
|
||||||
|
|
||||||
func (gui *Gui) refresh() {
|
func (gui *Gui) refresh() {
|
||||||
go func() {
|
go func() {
|
||||||
if err := gui.refreshProject(); err != nil {
|
if err := gui.refreshProjects(); err != nil {
|
||||||
gui.Log.Error(err)
|
gui.Log.Error(err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
|
||||||
|
|
@ -135,6 +135,13 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
||||||
Handler: gui.handleEditConfig,
|
Handler: gui.handleEditConfig,
|
||||||
Description: gui.Tr.EditConfig,
|
Description: gui.Tr.EditConfig,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
ViewName: "project",
|
||||||
|
Key: gocui.KeyCtrlP,
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleCreateProjectMenu,
|
||||||
|
Description: gui.Tr.SwitchProject,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
ViewName: "project",
|
ViewName: "project",
|
||||||
Key: 'o',
|
Key: 'o',
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,9 @@ package gui
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"github.com/docker/docker/api/types/container"
|
||||||
|
"github.com/jesseduffield/lazydocker/pkg/gui/types"
|
||||||
|
"log"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
|
@ -60,35 +63,51 @@ func (gui *Gui) getProjectPanel() *panels.SideListPanel[*commands.Project] {
|
||||||
List: panels.NewFilteredList[*commands.Project](),
|
List: panels.NewFilteredList[*commands.Project](),
|
||||||
View: gui.Views.Project,
|
View: gui.Views.Project,
|
||||||
},
|
},
|
||||||
NoItemsMessage: "",
|
NoItemsMessage: "No docker compose projects found.",
|
||||||
Gui: gui.intoInterface(),
|
Gui: gui.intoInterface(),
|
||||||
|
|
||||||
Sort: func(a *commands.Project, b *commands.Project) bool {
|
Sort: func(a *commands.Project, b *commands.Project) bool {
|
||||||
return false
|
return (gui.State.Project != nil && gui.State.Project.Name == a.Name) || a.Name < b.Name
|
||||||
},
|
},
|
||||||
GetTableCells: presentation.GetProjectDisplayStrings,
|
GetTableCells: presentation.GetProjectDisplayStrings,
|
||||||
// It doesn't make sense to filter a list of only one item.
|
|
||||||
DisableFilter: true,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) refreshProject() error {
|
func (gui *Gui) refreshProjects() error {
|
||||||
gui.Panels.Projects.SetItems([]*commands.Project{{Name: gui.getProjectName()}})
|
containers, err := gui.DockerCommand.Client.ContainerList(context.Background(), container.ListOptions{All: true})
|
||||||
return gui.Panels.Projects.RerenderList()
|
if err != nil {
|
||||||
}
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (gui *Gui) getProjectName() string {
|
projectsMap := make(map[string]*commands.Project)
|
||||||
projectName := path.Base(gui.Config.ProjectDir)
|
|
||||||
if gui.DockerCommand.InDockerComposeProject {
|
for _, container := range containers {
|
||||||
for _, service := range gui.Panels.Services.List.GetAllItems() {
|
if projectName, exists := container.Labels["com.docker.compose.project"]; exists && projectName != "" {
|
||||||
container := service.Container
|
projectsMap[projectName] = &commands.Project{Name: projectName}
|
||||||
if container != nil && container.DetailsLoaded() {
|
|
||||||
return container.Details.Config.Labels["com.docker.compose.project"]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return projectName
|
projectsList := make([]*commands.Project, 0, len(projectsMap))
|
||||||
|
for _, project := range projectsMap {
|
||||||
|
projectsList = append(projectsList, project)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add current's folder project if exists
|
||||||
|
if gui.DockerCommand.InDockerComposeProject {
|
||||||
|
projectsList = append(projectsList, &commands.Project{Name: gui.GetProjectName()})
|
||||||
|
}
|
||||||
|
gui.Panels.Projects.SetItems(projectsList)
|
||||||
|
|
||||||
|
return gui.Panels.Projects.RerenderList()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) GetProjectName() string {
|
||||||
|
if gui.State.Project != nil {
|
||||||
|
return gui.State.Project.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default to the command line argument
|
||||||
|
return path.Base(gui.Config.ProjectDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) renderCredits(_project *commands.Project) tasks.TaskFunc {
|
func (gui *Gui) renderCredits(_project *commands.Project) tasks.TaskFunc {
|
||||||
|
|
@ -180,3 +199,25 @@ func (gui *Gui) handleViewAllLogs(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
|
||||||
return gui.runSubprocess(c)
|
return gui.runSubprocess(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) handleCreateProjectMenu(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
if gui.isPopupPanel(v.Name()) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
testMenuItem := &types.MenuItem{
|
||||||
|
LabelColumns: []string{"t", "test"},
|
||||||
|
OnPress: func() error {
|
||||||
|
log.Println("tested")
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
menuItems := []*types.MenuItem{testMenuItem}
|
||||||
|
|
||||||
|
return gui.Menu(CreateMenuOptions{
|
||||||
|
Title: gui.Tr.MenuTitle,
|
||||||
|
Items: menuItems,
|
||||||
|
HideCancel: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -109,6 +109,7 @@ type TranslationSet struct {
|
||||||
FilterList string
|
FilterList string
|
||||||
OpenInBrowser string
|
OpenInBrowser string
|
||||||
SortContainersByState string
|
SortContainersByState string
|
||||||
|
SwitchProject string
|
||||||
|
|
||||||
LogsTitle string
|
LogsTitle string
|
||||||
ConfigTitle string
|
ConfigTitle string
|
||||||
|
|
@ -216,6 +217,7 @@ func englishSet() TranslationSet {
|
||||||
FilterList: "filter list",
|
FilterList: "filter list",
|
||||||
OpenInBrowser: "open in browser (first port is http)",
|
OpenInBrowser: "open in browser (first port is http)",
|
||||||
SortContainersByState: "sort containers by state",
|
SortContainersByState: "sort containers by state",
|
||||||
|
SwitchProject: "switch project",
|
||||||
|
|
||||||
GlobalTitle: "Global",
|
GlobalTitle: "Global",
|
||||||
MainTitle: "Main",
|
MainTitle: "Main",
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue