mirror of
https://github.com/jesseduffield/lazydocker.git
synced 2026-07-24 16:11:04 +00:00
WIP on subprocesses
This commit is contained in:
parent
54a5890485
commit
f0f96de2b5
7 changed files with 146 additions and 60 deletions
|
|
@ -2,6 +2,7 @@ package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
|
|
@ -59,3 +60,17 @@ func (s *Service) Attach() (*exec.Cmd, error) {
|
||||||
func (s *Service) Top() (types.ContainerProcessList, error) {
|
func (s *Service) Top() (types.ContainerProcessList, error) {
|
||||||
return s.Container.Top()
|
return s.Container.Top()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
|
||||||
|
cmd := s.OSCommand.ExecutableFromString(command)
|
||||||
|
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||||
|
Setpgid: true,
|
||||||
|
Pgid: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmd, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -101,10 +101,11 @@ type GuiConfig struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type CommandTemplatesConfig struct {
|
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"`
|
ServiceLogs string `yaml:"serviceLogs,omitempty"`
|
||||||
|
ViewServiceLogs string `yaml:"viewServiceLogs,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type OSConfig struct {
|
type OSConfig struct {
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,15 @@
|
||||||
package gui
|
package gui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"math"
|
"math"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
// "io"
|
// "io"
|
||||||
// "io/ioutil"
|
// "io/ioutil"
|
||||||
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fatih/color"
|
|
||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
|
|
||||||
// "strings"
|
// "strings"
|
||||||
|
|
@ -568,51 +563,6 @@ func (gui *Gui) reRenderMain() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunWithSubprocesses loops, instantiating a new gocui.Gui with each iteration
|
|
||||||
// if the error returned from a run is a ErrSubProcess, it runs the subprocess
|
|
||||||
// otherwise it handles the error, possibly by quitting the application
|
|
||||||
func (gui *Gui) RunWithSubprocesses() error {
|
|
||||||
for {
|
|
||||||
if err := gui.Run(); err != nil {
|
|
||||||
if err == gocui.ErrQuit {
|
|
||||||
break
|
|
||||||
} else if err == gui.Errors.ErrSubProcess {
|
|
||||||
if err := gui.runCommand(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) runCommand() error {
|
|
||||||
gui.SubProcess.Stdout = os.Stdout
|
|
||||||
gui.SubProcess.Stderr = os.Stdout
|
|
||||||
gui.SubProcess.Stdin = os.Stdin
|
|
||||||
|
|
||||||
fmt.Fprintf(os.Stdout, "\n%s\n\n", utils.ColoredString("+ "+strings.Join(gui.SubProcess.Args, " "), color.FgBlue))
|
|
||||||
|
|
||||||
if err := gui.SubProcess.Run(); err != nil {
|
|
||||||
// not handling the error explicitly because usually we're going to see it
|
|
||||||
// in the output anyway
|
|
||||||
gui.Log.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
gui.SubProcess.Stdout = ioutil.Discard
|
|
||||||
gui.SubProcess.Stderr = ioutil.Discard
|
|
||||||
gui.SubProcess.Stdin = nil
|
|
||||||
gui.SubProcess = nil
|
|
||||||
|
|
||||||
// fmt.Fprintf(os.Stdout, "\n%s", utils.ColoredString(gui.Tr.SLocalize("pressEnterToReturn"), color.FgGreen))
|
|
||||||
|
|
||||||
// fmt.Scanln() // wait for enter press
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) quit(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) quit(g *gocui.Gui, v *gocui.View) error {
|
||||||
if gui.Config.UserConfig.ConfirmOnQuit {
|
if gui.Config.UserConfig.ConfirmOnQuit {
|
||||||
return gui.createConfirmationPanel(g, v, "", gui.Tr.SLocalize("ConfirmQuit"), func(g *gocui.Gui, v *gocui.View) error {
|
return gui.createConfirmationPanel(g, v, "", gui.Tr.SLocalize("ConfirmQuit"), func(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
|
|
||||||
|
|
@ -64,12 +64,14 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
||||||
Key: 'q',
|
Key: 'q',
|
||||||
Modifier: gocui.ModNone,
|
Modifier: gocui.ModNone,
|
||||||
Handler: gui.quit,
|
Handler: gui.quit,
|
||||||
}, {
|
},
|
||||||
ViewName: "",
|
// {
|
||||||
Key: gocui.KeyCtrlC,
|
// ViewName: "",
|
||||||
Modifier: gocui.ModNone,
|
// Key: gocui.KeyCtrlC,
|
||||||
Handler: gui.quit,
|
// Modifier: gocui.ModNone,
|
||||||
}, {
|
// Handler: gui.quit,
|
||||||
|
// },
|
||||||
|
{
|
||||||
ViewName: "",
|
ViewName: "",
|
||||||
Key: gocui.KeyEsc,
|
Key: gocui.KeyEsc,
|
||||||
Modifier: gocui.ModNone,
|
Modifier: gocui.ModNone,
|
||||||
|
|
@ -201,6 +203,13 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
||||||
Handler: gui.handleServiceAttach,
|
Handler: gui.handleServiceAttach,
|
||||||
Description: gui.Tr.SLocalize("attach"),
|
Description: gui.Tr.SLocalize("attach"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
ViewName: "services",
|
||||||
|
Key: 'm',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleServiceViewLogs,
|
||||||
|
Description: gui.Tr.SLocalize("viewLogs"),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
ViewName: "services",
|
ViewName: "services",
|
||||||
Key: '[',
|
Key: '[',
|
||||||
|
|
|
||||||
|
|
@ -278,3 +278,18 @@ func (gui *Gui) handleServiceAttach(g *gocui.Gui, v *gocui.View) error {
|
||||||
gui.SubProcess = c
|
gui.SubProcess = c
|
||||||
return gui.Errors.ErrSubProcess
|
return gui.Errors.ErrSubProcess
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) handleServiceViewLogs(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
service, err := gui.getSelectedService()
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := service.ViewLogs()
|
||||||
|
if err != nil {
|
||||||
|
return gui.createErrorPanel(gui.g, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
gui.SubProcess = c
|
||||||
|
return gui.Errors.ErrSubProcess
|
||||||
|
}
|
||||||
|
|
|
||||||
92
pkg/gui/subprocess.go
Normal file
92
pkg/gui/subprocess.go
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
package gui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/fatih/color"
|
||||||
|
"github.com/jesseduffield/gocui"
|
||||||
|
"github.com/jesseduffield/lazydocker/pkg/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RunWithSubprocesses loops, instantiating a new gocui.Gui with each iteration
|
||||||
|
// if the error returned from a run is a ErrSubProcess, it runs the subprocess
|
||||||
|
// otherwise it handles the error, possibly by quitting the application
|
||||||
|
func (gui *Gui) RunWithSubprocesses() error {
|
||||||
|
for {
|
||||||
|
if err := gui.Run(); err != nil {
|
||||||
|
if err == gocui.ErrQuit {
|
||||||
|
break
|
||||||
|
} else if err == gui.Errors.ErrSubProcess {
|
||||||
|
if err := gui.runCommand(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) runCommand() error {
|
||||||
|
gui.SubProcess.Stdout = os.Stdout
|
||||||
|
gui.SubProcess.Stderr = os.Stdout
|
||||||
|
// gui.SubProcess.Stdin = os.Stdin
|
||||||
|
|
||||||
|
reader := bufio.NewReader(os.Stdin)
|
||||||
|
|
||||||
|
c := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(c, os.Interrupt)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
<-c
|
||||||
|
gui.SubProcess.Process.Kill()
|
||||||
|
}()
|
||||||
|
|
||||||
|
w, err := gui.SubProcess.StdinPipe()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
input, err := reader.ReadByte()
|
||||||
|
gui.Log.Warn("byte received")
|
||||||
|
gui.Log.Warn(input)
|
||||||
|
if input == 3 {
|
||||||
|
gui.SubProcess.Process.Kill()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil && err == io.EOF {
|
||||||
|
gui.SubProcess.Process.Kill()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
w.Write([]byte{input})
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
fmt.Fprintf(os.Stdout, "\n%s\n\n", utils.ColoredString("+ "+strings.Join(gui.SubProcess.Args, " "), color.FgBlue))
|
||||||
|
|
||||||
|
if err := gui.SubProcess.Run(); err != nil {
|
||||||
|
// not handling the error explicitly because usually we're going to see it
|
||||||
|
// in the output anyway
|
||||||
|
gui.Log.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
gui.SubProcess.Stdout = ioutil.Discard
|
||||||
|
gui.SubProcess.Stderr = ioutil.Discard
|
||||||
|
gui.SubProcess.Stdin = nil
|
||||||
|
gui.SubProcess = nil
|
||||||
|
|
||||||
|
// fmt.Fprintf(os.Stdout, "\n%s", utils.ColoredString(gui.Tr.SLocalize("pressEnterToReturn"), color.FgGreen))
|
||||||
|
|
||||||
|
// fmt.Scanln() // wait for enter press
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -816,6 +816,10 @@ func addEnglish(i18nObject *i18n.Bundle) error {
|
||||||
ID: "attach",
|
ID: "attach",
|
||||||
Other: "attach",
|
Other: "attach",
|
||||||
},
|
},
|
||||||
|
&i18n.Message{
|
||||||
|
ID: "viewLogs",
|
||||||
|
Other: "view logs",
|
||||||
|
},
|
||||||
&i18n.Message{
|
&i18n.Message{
|
||||||
ID: "ServicesTitle",
|
ID: "ServicesTitle",
|
||||||
Other: "Services",
|
Other: "Services",
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue