fix this insane CPU usage issue

This commit is contained in:
Jesse Duffield 2019-06-02 13:34:26 +10:00
parent 46d27c477e
commit 159a1883e5
10 changed files with 86 additions and 40 deletions

View file

@ -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

View file

@ -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,
} }
} }

View file

@ -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()

View file

@ -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{

View file

@ -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

View file

@ -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
} }
} }

View file

@ -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

View file

@ -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

View file

@ -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())
} }

View file

@ -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
} }