mirror of https://github.com/docker/cli.git
prevent "docker stats" from hanging if the initial API call fails
When running `docker stats` without a list of containers, `runStats` collects an initial list of containers. If that API call fails, the error is sent to the `closeChan`, however, `closeChan` is non-buffered, and nothing is reading the channel until we received the initial list and start collecting stats. This patch rewrites the code that gets the initial list of containers to return the error if the API call fails. The `getContainerList` closure is also removed and inlined to make the logic somewhat easier to read. Before this patch, the command would hang without producing output; docker stats # hangs; no output With this patch, the error is printed, and the CLI exits: docker stats Error response from daemon: some error occurred echo $? 1 Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
parent
3b57acb236
commit
b642078c87
|
@ -135,24 +135,6 @@ func runStats(ctx context.Context, dockerCLI command.Cli, options *statsOptions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getContainerList simulates creation event for all previously existing
|
|
||||||
// containers (only used when calling `docker stats` without arguments).
|
|
||||||
getContainerList := func() {
|
|
||||||
cs, err := apiClient.ContainerList(ctx, container.ListOptions{
|
|
||||||
All: options.all,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
closeChan <- err
|
|
||||||
}
|
|
||||||
for _, ctr := range cs {
|
|
||||||
s := NewStats(ctr.ID[:12])
|
|
||||||
if cStats.add(s) {
|
|
||||||
waitFirst.Add(1)
|
|
||||||
go collect(ctx, s, apiClient, !options.noStream, waitFirst)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
eventChan := make(chan events.Message)
|
eventChan := make(chan events.Message)
|
||||||
go eh.Watch(eventChan)
|
go eh.Watch(eventChan)
|
||||||
stopped := make(chan struct{})
|
stopped := make(chan struct{})
|
||||||
|
@ -160,17 +142,30 @@ func runStats(ctx context.Context, dockerCLI command.Cli, options *statsOptions)
|
||||||
defer close(stopped)
|
defer close(stopped)
|
||||||
<-started
|
<-started
|
||||||
|
|
||||||
// Start a short-lived goroutine to retrieve the initial list of
|
// Fetch the initial list of containers and collect stats for them.
|
||||||
// containers.
|
// After the initial list was collected, we start listening for events
|
||||||
getContainerList()
|
// to refresh the list of containers.
|
||||||
|
cs, err := apiClient.ContainerList(ctx, container.ListOptions{
|
||||||
|
All: options.all,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, ctr := range cs {
|
||||||
|
s := NewStats(ctr.ID[:12])
|
||||||
|
if cStats.add(s) {
|
||||||
|
waitFirst.Add(1)
|
||||||
|
go collect(ctx, s, apiClient, !options.noStream, waitFirst)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 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)
|
||||||
|
|
Loading…
Reference in New Issue