retry upon losing connection to docker

This commit is contained in:
Jesse Duffield 2022-05-11 00:34:20 +10:00
parent 011824a9b7
commit b5384d6cdb
3 changed files with 70 additions and 33 deletions

View file

@ -130,37 +130,44 @@ func (c *DockerCommand) MonitorContainerStats() {
// MonitorCLIContainerStats monitors a stream of container stats and updates the containers as each new stats object is received // MonitorCLIContainerStats monitors a stream of container stats and updates the containers as each new stats object is received
func (c *DockerCommand) MonitorCLIContainerStats() { func (c *DockerCommand) MonitorCLIContainerStats() {
command := `docker stats --all --no-trunc --format '{{json .}}'` onError := func(err error) {
cmd := c.OSCommand.RunCustomCommand(command)
r, err := cmd.StdoutPipe()
if err != nil {
c.ErrorChan <- err c.ErrorChan <- err
return time.Sleep(2 * time.Second)
} }
_ = cmd.Start() for {
command := `docker stats --all --no-trunc --format '{{json .}}'`
cmd := c.OSCommand.RunCustomCommand(command)
scanner := bufio.NewScanner(r) r, err := cmd.StdoutPipe()
scanner.Split(bufio.ScanLines) if err != nil {
for scanner.Scan() { onError(err)
var stats ContainerCliStat continue
// need to strip ANSI codes because uses escape sequences to clear the screen with each refresh
cleanString := stripansi.Strip(scanner.Text())
if err := json.Unmarshal([]byte(cleanString), &stats); err != nil {
c.ErrorChan <- err
return
} }
c.ContainerMutex.Lock()
for _, container := range c.Containers { _ = cmd.Start()
if container.ID == stats.ID {
container.CLIStats = stats scanner := bufio.NewScanner(r)
scanner.Split(bufio.ScanLines)
for scanner.Scan() {
var stats ContainerCliStat
// need to strip ANSI codes because uses escape sequences to clear the screen with each refresh
cleanString := stripansi.Strip(scanner.Text())
if err := json.Unmarshal([]byte(cleanString), &stats); err != nil {
onError(err)
continue
} }
c.ContainerMutex.Lock()
for _, container := range c.Containers {
if container.ID == stats.ID {
container.CLIStats = stats
}
}
c.ContainerMutex.Unlock()
} }
c.ContainerMutex.Unlock()
}
_ = cmd.Wait() _ = cmd.Wait()
}
} }
// MonitorClientContainerStats is a function // MonitorClientContainerStats is a function

View file

@ -20,20 +20,20 @@ func (gui *Gui) wrappedConfirmationFunction(function func(*gocui.Gui, *gocui.Vie
return err return err
} }
} }
return gui.closeConfirmationPrompt(g) return gui.closeConfirmationPrompt()
} }
} }
func (gui *Gui) closeConfirmationPrompt(g *gocui.Gui) error { func (gui *Gui) closeConfirmationPrompt() error {
view, err := g.View("confirmation") view, err := gui.g.View("confirmation")
if err != nil { if err != nil {
return nil // if it's already been closed we can just return return nil // if it's already been closed we can just return
} }
if err := gui.returnFocus(g, view); err != nil { if err := gui.returnFocus(gui.g, view); err != nil {
panic(err) return err
} }
g.DeleteViewKeybindings("confirmation") gui.g.DeleteViewKeybindings("confirmation")
return g.DeleteView("confirmation") return gui.g.DeleteView("confirmation")
} }
func (gui *Gui) getMessageHeight(wrap bool, message string, width int) int { func (gui *Gui) getMessageHeight(wrap bool, message string, width int) int {
@ -110,7 +110,7 @@ func (gui *Gui) createPopupPanel(g *gocui.Gui, currentView *gocui.View, title, p
g.Update(func(g *gocui.Gui) error { g.Update(func(g *gocui.Gui) error {
// delete the existing confirmation panel if it exists // delete the existing confirmation panel if it exists
if view, _ := g.View("confirmation"); view != nil { if view, _ := g.View("confirmation"); view != nil {
if err := gui.closeConfirmationPrompt(g); err != nil { if err := gui.closeConfirmationPrompt(); err != nil {
gui.Log.Error(err.Error()) gui.Log.Error(err.Error())
} }
} }

View file

@ -302,8 +302,38 @@ func (gui *Gui) refresh() {
} }
func (gui *Gui) listenForEvents(finish chan struct{}, refresh func()) { func (gui *Gui) listenForEvents(finish chan struct{}, refresh func()) {
errorCount := 0
onError := func(err error) {
if err != nil {
gui.ErrorChan <- errors.Errorf("Docker event stream returned error: %s\nRetry count: %d", err.Error(), errorCount)
}
errorCount++
time.Sleep(time.Second * 2)
}
outer:
for { for {
messageChan, errChan := gui.DockerCommand.Client.Events(context.Background(), types.EventsOptions{}) messageChan, errChan := gui.DockerCommand.Client.Events(context.Background(), types.EventsOptions{})
if errorCount > 0 {
select {
case err := <-errChan:
onError(err)
continue outer
default:
// If we're here then we lost connection to docker and we just got it back.
// The reason we do this refresh explicitly is because successfully
// reconnecting with docker does not mean it's going to send us a new
// event any time soon.
// Assuming the confirmation prompt currently holds the given error
_ = gui.closeConfirmationPrompt()
refresh()
errorCount = 0
}
}
for { for {
select { select {
case <-finish: case <-finish:
@ -316,8 +346,8 @@ func (gui *Gui) listenForEvents(finish chan struct{}, refresh func()) {
gui.Log.Infof("received event of type: %s", message.Type) gui.Log.Infof("received event of type: %s", message.Type)
case err := <-errChan: case err := <-errChan:
gui.ErrorChan <- errors.Errorf("Docker event stream returned error: %s", err.Error()) onError(err)
break continue outer
} }
} }
} }