Get container details immediately after getting containers

Previously we separately obtained container details every second,
meaning there was a second of not knowing whether a given container was
using a TTY or not, and that meant we were using the wrong streaming
method for logs, which led to a black screen.
This commit is contained in:
Jesse Duffield 2024-05-26 12:19:17 +10:00
parent a6ade7e78d
commit bca15cf6b1
3 changed files with 30 additions and 14 deletions

View file

@ -140,7 +140,9 @@ func (c *Container) RenderTop(ctx context.Context) (string, error) {
return utils.RenderTable(append([][]string{result.Titles}, result.Processes...)) return utils.RenderTable(append([][]string{result.Titles}, result.Processes...))
} }
// DetailsLoaded tells us whether we have yet loaded the details for a container. Because this is an asynchronous operation, sometimes we have the container before we have its details. // DetailsLoaded tells us whether we have yet loaded the details for a container.
// Sometimes it takes some time for a container to have its details loaded
// after it starts.
func (c *Container) DetailsLoaded() bool { func (c *Container) DetailsLoaded() bool {
return c.Details.ContainerJSONBase != nil return c.Details.ContainerJSONBase != nil
} }

View file

@ -10,6 +10,7 @@ import (
"os" "os"
"os/exec" "os/exec"
"strings" "strings"
"sync"
"time" "time"
cliconfig "github.com/docker/cli/cli/config" cliconfig "github.com/docker/cli/cli/config"
@ -209,7 +210,7 @@ func (c *DockerCommand) GetContainers(existingContainers []*Container) ([]*Conta
for i, container := range containers { for i, container := range containers {
var newContainer *Container var newContainer *Container
// check if we already data stored against the container // check if we already have data stored against the container
for _, existingContainer := range existingContainers { for _, existingContainer := range existingContainers {
if existingContainer.ID == container.ID { if existingContainer.ID == container.ID {
newContainer = existingContainer newContainer = existingContainer
@ -244,6 +245,8 @@ func (c *DockerCommand) GetContainers(existingContainers []*Container) ([]*Conta
ownContainers[i] = newContainer ownContainers[i] = newContainer
} }
c.SetContainerDetails(ownContainers)
return ownContainers, nil return ownContainers, nil
} }
@ -278,24 +281,35 @@ func (c *DockerCommand) GetServices() ([]*Service, error) {
return services, nil return services, nil
} }
// UpdateContainerDetails attaches the details returned from docker inspect to each of the containers func (c *DockerCommand) RefreshContainerDetails(containers []*Container) error {
// this contains a bit more info than what you get from the go-docker client
func (c *DockerCommand) UpdateContainerDetails(containers []*Container) error {
c.ContainerMutex.Lock() c.ContainerMutex.Lock()
defer c.ContainerMutex.Unlock() defer c.ContainerMutex.Unlock()
for _, container := range containers { c.SetContainerDetails(containers)
details, err := c.Client.ContainerInspect(context.Background(), container.ID)
if err != nil {
c.Log.Error(err)
} else {
container.Details = details
}
}
return nil return nil
} }
// Attaches the details returned from docker inspect to each of the containers
// this contains a bit more info than what you get from the go-docker client
func (c *DockerCommand) SetContainerDetails(containers []*Container) {
wg := sync.WaitGroup{}
for _, container := range containers {
container := container
wg.Add(1)
go func() {
details, err := c.Client.ContainerInspect(context.Background(), container.ID)
if err != nil {
c.Log.Error(err)
} else {
container.Details = details
}
wg.Done()
}()
}
wg.Wait()
}
// ViewAllLogs attaches to a subprocess viewing all the logs from docker-compose // ViewAllLogs attaches to a subprocess viewing all the logs from docker-compose
func (c *DockerCommand) ViewAllLogs() (*exec.Cmd, error) { func (c *DockerCommand) ViewAllLogs() (*exec.Cmd, error) {
cmd := c.OSCommand.ExecutableFromString( cmd := c.OSCommand.ExecutableFromString(

View file

@ -294,7 +294,7 @@ func (gui *Gui) setPanels() {
} }
func (gui *Gui) updateContainerDetails() error { func (gui *Gui) updateContainerDetails() error {
return gui.DockerCommand.UpdateContainerDetails(gui.Panels.Containers.List.GetAllItems()) return gui.DockerCommand.RefreshContainerDetails(gui.Panels.Containers.List.GetAllItems())
} }
func (gui *Gui) refresh() { func (gui *Gui) refresh() {