mirror of
https://github.com/jesseduffield/lazydocker.git
synced 2026-07-25 08:31:03 +00:00
fix this insane CPU usage issue
This commit is contained in:
parent
46d27c477e
commit
159a1883e5
10 changed files with 86 additions and 40 deletions
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
|
"github.com/go-errors/errors"
|
||||||
"github.com/jesseduffield/lazydocker/pkg/utils"
|
"github.com/jesseduffield/lazydocker/pkg/utils"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
|
|
@ -298,9 +299,14 @@ func (c *Container) RestartService() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attach attaches the container
|
// Attach attaches the container
|
||||||
func (c *Container) Attach() *exec.Cmd {
|
func (c *Container) Attach() (*exec.Cmd, error) {
|
||||||
|
// verify that we can in fact attach to this container
|
||||||
|
if !c.Details.Config.AttachStdin {
|
||||||
|
return nil, errors.New("Container does not support attaching. You must either run the service with the '-it' flag or use `stdin_open: true, tty: true` in the docker-compose.yml file")
|
||||||
|
}
|
||||||
|
|
||||||
cmd := c.OSCommand.PrepareSubProcess("docker", "attach", "--sig-proxy=false", c.ID)
|
cmd := c.OSCommand.PrepareSubProcess("docker", "attach", "--sig-proxy=false", c.ID)
|
||||||
return cmd
|
return cmd, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Top returns process information
|
// Top returns process information
|
||||||
|
|
|
||||||
|
|
@ -78,16 +78,22 @@ func (c *DockerCommand) UpdateContainerStats(containers []*Container) ([]*Contai
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetContainersAndServices returns a slice of docker containers
|
// GetContainersAndServices returns a slice of docker containers
|
||||||
func (c *DockerCommand) GetContainersAndServices() ([]*Container, []*Service, error) {
|
func (c *DockerCommand) GetContainersAndServices(currentServices []*Service) ([]*Container, []*Service, error) {
|
||||||
|
|
||||||
containers, err := c.GetContainers()
|
containers, err := c.GetContainers()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
services, err := c.GetServices()
|
var services []*Service
|
||||||
if err != nil {
|
// we only need to get these services once because they won't change in the runtime of the program
|
||||||
return nil, nil, err
|
if currentServices != nil {
|
||||||
|
services = currentServices
|
||||||
|
} else {
|
||||||
|
services, err = c.GetServices()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// find out which services have corresponding containers and assign them
|
// find out which services have corresponding containers and assign them
|
||||||
|
|
@ -170,8 +176,10 @@ func (c *DockerCommand) GetServices() ([]*Service, error) {
|
||||||
for i, str := range lines {
|
for i, str := range lines {
|
||||||
arr := strings.Split(str, " ")
|
arr := strings.Split(str, " ")
|
||||||
services[i] = &Service{
|
services[i] = &Service{
|
||||||
Name: arr[0],
|
Name: arr[0],
|
||||||
ID: arr[1],
|
ID: arr[1],
|
||||||
|
OSCommand: c.OSCommand,
|
||||||
|
Log: c.Log,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,11 @@ import (
|
||||||
|
|
||||||
// Service : A docker Service
|
// Service : A docker Service
|
||||||
type Service struct {
|
type Service struct {
|
||||||
Name string
|
Name string
|
||||||
ID string
|
ID string
|
||||||
DisplayString string
|
OSCommand *OSCommand
|
||||||
OSCommand *OSCommand
|
Log *logrus.Entry
|
||||||
Log *logrus.Entry
|
Container *Container
|
||||||
Container *Container
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDisplayStrings returns the dispaly string of Container
|
// GetDisplayStrings returns the dispaly string of Container
|
||||||
|
|
@ -50,7 +49,7 @@ func (s *Service) Restart() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attach attaches to the service
|
// Attach attaches to the service
|
||||||
func (s *Service) Attach() *exec.Cmd {
|
func (s *Service) Attach() (*exec.Cmd, error) {
|
||||||
// TODO: if you have a custom command for attaching to a service here is the place to use it
|
// TODO: if you have a custom command for attaching to a service here is the place to use it
|
||||||
|
|
||||||
return s.Container.Attach()
|
return s.Container.Attach()
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,7 @@ type CommandTemplatesConfig struct {
|
||||||
RestartService string `yaml:"restartService,omitempty"`
|
RestartService string `yaml:"restartService,omitempty"`
|
||||||
DockerCompose string `yaml:"dockerCompose,omitempty"`
|
DockerCompose string `yaml:"dockerCompose,omitempty"`
|
||||||
StopService string `yaml:"stopService,omitempty"`
|
StopService string `yaml:"stopService,omitempty"`
|
||||||
|
ServiceLogs string `yaml:"serviceLogs,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type OSConfig struct {
|
type OSConfig struct {
|
||||||
|
|
@ -132,6 +133,7 @@ func GetDefaultConfig() UserConfig {
|
||||||
RestartService: "docker-compose restart {{ .Name }}",
|
RestartService: "docker-compose restart {{ .Name }}",
|
||||||
DockerCompose: "apdev compose",
|
DockerCompose: "apdev compose",
|
||||||
StopService: "apdev stop {{ .Name }}",
|
StopService: "apdev stop {{ .Name }}",
|
||||||
|
ServiceLogs: "apdev logs {{ .Name }}",
|
||||||
},
|
},
|
||||||
OS: GetPlatformDefaultConfig(),
|
OS: GetPlatformDefaultConfig(),
|
||||||
Update: UpdateConfig{
|
Update: UpdateConfig{
|
||||||
|
|
|
||||||
|
|
@ -220,13 +220,11 @@ func (gui *Gui) renderLogsForTTYContainer(mainView *gocui.View, container *comma
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
s := bufio.NewScanner(r)
|
||||||
s := bufio.NewScanner(r)
|
s.Split(bufio.ScanLines)
|
||||||
s.Split(bufio.ScanLines)
|
for s.Scan() {
|
||||||
for s.Scan() {
|
// I might put a check on the stopped channel here. Would mean more code duplication though
|
||||||
// I might put a check on the stopped channel here. Would mean more code duplication though
|
mainView.Write(append(s.Bytes(), '\n'))
|
||||||
mainView.Write(append(s.Bytes(), '\n'))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
@ -297,7 +295,7 @@ func (gui *Gui) refreshContainersAndServices() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) refreshStateContainersAndServices() error {
|
func (gui *Gui) refreshStateContainersAndServices() error {
|
||||||
containers, services, err := gui.DockerCommand.GetContainersAndServices()
|
containers, services, err := gui.DockerCommand.GetContainersAndServices(gui.State.Services)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -457,7 +455,10 @@ func (gui *Gui) handleContainerAttach(g *gocui.Gui, v *gocui.View) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
c := container.Attach()
|
c, err := container.Attach()
|
||||||
|
if err != nil {
|
||||||
|
return gui.createErrorPanel(gui.g, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
gui.SubProcess = c
|
gui.SubProcess = c
|
||||||
return gui.Errors.ErrSubProcess
|
return gui.Errors.ErrSubProcess
|
||||||
|
|
|
||||||
|
|
@ -155,7 +155,7 @@ func NewGui(log *logrus.Entry, dockerCommand *commands.DockerCommand, oSCommand
|
||||||
Config: config,
|
Config: config,
|
||||||
Tr: tr,
|
Tr: tr,
|
||||||
statusManager: &statusManager{},
|
statusManager: &statusManager{},
|
||||||
T: tasks.NewTaskManager(),
|
T: tasks.NewTaskManager(log),
|
||||||
}
|
}
|
||||||
|
|
||||||
gui.GenerateSentinelErrors()
|
gui.GenerateSentinelErrors()
|
||||||
|
|
@ -415,7 +415,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := gui.switchFocus(gui.g, nil, gui.getContainersView()); err != nil {
|
if err := gui.switchFocus(gui.g, nil, gui.getServicesView()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,16 @@ func (gui *Gui) renderServiceStats(mainView *gocui.View, service *commands.Servi
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) renderServiceLogs(mainView *gocui.View, service *commands.Service) error {
|
func (gui *Gui) renderServiceLogs(mainView *gocui.View, service *commands.Service) error {
|
||||||
return nil
|
service, err := gui.getSelectedService(gui.g)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if service.Container == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return gui.renderContainerLogs(gui.getMainView(), service.Container)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleServicesNextLine(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleServicesNextLine(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
|
@ -255,7 +264,10 @@ func (gui *Gui) handleServiceAttach(g *gocui.Gui, v *gocui.View) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
c := service.Attach()
|
c, err := service.Attach()
|
||||||
|
if err != nil {
|
||||||
|
return gui.createErrorPanel(gui.g, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
gui.SubProcess = c
|
gui.SubProcess = c
|
||||||
return gui.Errors.ErrSubProcess
|
return gui.Errors.ErrSubProcess
|
||||||
|
|
|
||||||
|
|
@ -7,15 +7,10 @@ import (
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (gui *Gui) refreshStatus(g *gocui.Gui) error {
|
func (gui *Gui) refreshStatus() error {
|
||||||
v, err := g.View("status")
|
v := gui.getStatusView()
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
gui.g.Update(func(*gocui.Gui) error {
|
||||||
}
|
|
||||||
// for some reason if this isn't wrapped in an update the clear seems to
|
|
||||||
// be applied after the other things or something like that; the panel's
|
|
||||||
// contents end up cleared
|
|
||||||
g.Update(func(*gocui.Gui) error {
|
|
||||||
v.Clear()
|
v.Clear()
|
||||||
fmt.Fprint(v, "lazydocker")
|
fmt.Fprint(v, "lazydocker")
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,10 @@ func (gui *Gui) refreshSidePanels(g *gocui.Gui) error {
|
||||||
if err := gui.refreshImages(); err != nil {
|
if err := gui.refreshImages(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := gui.refreshStatus(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -245,6 +249,11 @@ func (gui *Gui) getMainView() *gocui.View {
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) getStatusView() *gocui.View {
|
||||||
|
v, _ := gui.g.View("status")
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
func (gui *Gui) trimmedContent(v *gocui.View) string {
|
func (gui *Gui) trimmedContent(v *gocui.View) string {
|
||||||
return strings.TrimSpace(v.Buffer())
|
return strings.TrimSpace(v.Buffer())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,38 @@
|
||||||
package tasks
|
package tasks
|
||||||
|
|
||||||
import "sync"
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
type TaskManager struct {
|
type TaskManager struct {
|
||||||
waitingTasks []*Task
|
waitingTasks []*Task
|
||||||
currentTask *Task
|
currentTask *Task
|
||||||
waitingMutex sync.Mutex
|
waitingMutex sync.Mutex
|
||||||
|
Log *logrus.Entry
|
||||||
}
|
}
|
||||||
|
|
||||||
type Task struct {
|
type Task struct {
|
||||||
stop chan struct{}
|
stop chan struct{}
|
||||||
notifyStopped chan struct{}
|
notifyStopped chan struct{}
|
||||||
|
Log *logrus.Entry
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTaskManager() *TaskManager {
|
func NewTaskManager(log *logrus.Entry) *TaskManager {
|
||||||
return &TaskManager{}
|
return &TaskManager{Log: log}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TaskManager) NewTask(f func(stop chan struct{})) error {
|
func (t *TaskManager) NewTask(f func(stop chan struct{})) error {
|
||||||
|
t.Log.Warn("new task")
|
||||||
|
|
||||||
t.waitingMutex.Lock()
|
t.waitingMutex.Lock()
|
||||||
defer t.waitingMutex.Unlock()
|
defer t.waitingMutex.Unlock()
|
||||||
|
|
||||||
|
t.Log.Warn("locked mutex")
|
||||||
|
|
||||||
if t.currentTask != nil {
|
if t.currentTask != nil {
|
||||||
|
t.Log.Warn("about to ask current task to stop")
|
||||||
t.currentTask.Stop()
|
t.currentTask.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -31,9 +42,11 @@ func (t *TaskManager) NewTask(f func(stop chan struct{})) error {
|
||||||
t.currentTask = &Task{
|
t.currentTask = &Task{
|
||||||
stop: stop,
|
stop: stop,
|
||||||
notifyStopped: notifyStopped,
|
notifyStopped: notifyStopped,
|
||||||
|
Log: t.Log,
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
t.Log.Warn("running new task")
|
||||||
f(stop)
|
f(stop)
|
||||||
notifyStopped <- struct{}{}
|
notifyStopped <- struct{}{}
|
||||||
}()
|
}()
|
||||||
|
|
@ -44,5 +57,6 @@ func (t *TaskManager) NewTask(f func(stop chan struct{})) error {
|
||||||
func (t *Task) Stop() {
|
func (t *Task) Stop() {
|
||||||
t.stop <- struct{}{}
|
t.stop <- struct{}{}
|
||||||
<-t.notifyStopped
|
<-t.notifyStopped
|
||||||
|
t.Log.Warn("task successfully stopped")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue