Merge pull request #4730 from thaJeztah/fix_stats_hang

prevent "docker stats" from hanging if the initial API call fails
This commit is contained in:
Sebastiaan van Stijn 2023-12-20 17:38:29 +01:00 committed by GitHub
commit 05a2dc401f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 20 additions and 25 deletions

View File

@ -135,14 +135,21 @@ func runStats(ctx context.Context, dockerCLI command.Cli, options *statsOptions)
} }
} }
// getContainerList simulates creation event for all previously existing eventChan := make(chan events.Message)
// containers (only used when calling `docker stats` without arguments). go eh.Watch(eventChan)
getContainerList := func() { stopped := make(chan struct{})
go monitorContainerEvents(started, eventChan, stopped)
defer close(stopped)
<-started
// Fetch the initial list of containers and collect stats for them.
// After the initial list was collected, we start listening for events
// to refresh the list of containers.
cs, err := apiClient.ContainerList(ctx, container.ListOptions{ cs, err := apiClient.ContainerList(ctx, container.ListOptions{
All: options.all, All: options.all,
}) })
if err != nil { if err != nil {
closeChan <- err return err
} }
for _, ctr := range cs { for _, ctr := range cs {
s := NewStats(ctr.ID[:12]) s := NewStats(ctr.ID[:12])
@ -151,26 +158,14 @@ func runStats(ctx context.Context, dockerCLI command.Cli, options *statsOptions)
go collect(ctx, s, apiClient, !options.noStream, waitFirst) go collect(ctx, s, apiClient, !options.noStream, waitFirst)
} }
} }
}
eventChan := make(chan events.Message)
go eh.Watch(eventChan)
stopped := make(chan struct{})
go monitorContainerEvents(started, eventChan, stopped)
defer close(stopped)
<-started
// Start a short-lived goroutine to retrieve the initial list of
// containers.
getContainerList()
// make sure each container get at least one valid stat data // make sure each container get at least one valid stat data
waitFirst.Wait() waitFirst.Wait()
} else { } else {
// Artificially send creation events for the containers we were asked to // Create the list of containers, and start collecting stats for all
// monitor (same code path than we use when monitoring all containers). // containers passed.
for _, name := range options.containers { for _, ctr := range options.containers {
s := NewStats(name) s := NewStats(ctr)
if cStats.add(s) { if cStats.add(s) {
waitFirst.Add(1) waitFirst.Add(1)
go collect(ctx, s, apiClient, !options.noStream, waitFirst) go collect(ctx, s, apiClient, !options.noStream, waitFirst)