1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Merge pull request #20835 from cpuguy83/handle_stats_client_errors

Do not remove containers from stats list on err
This commit is contained in:
Doug Davis 2016-04-16 10:20:58 +01:00
commit 5314296c69
4 changed files with 26 additions and 23 deletions

View file

@ -10,6 +10,7 @@ import (
"golang.org/x/net/context" "golang.org/x/net/context"
"github.com/Sirupsen/logrus"
Cli "github.com/docker/docker/cli" Cli "github.com/docker/docker/cli"
"github.com/docker/engine-api/types" "github.com/docker/engine-api/types"
"github.com/docker/engine-api/types/events" "github.com/docker/engine-api/types/events"
@ -169,20 +170,12 @@ func (cli *DockerCli) CmdStats(args ...string) error {
for range time.Tick(500 * time.Millisecond) { for range time.Tick(500 * time.Millisecond) {
printHeader() printHeader()
toRemove := []int{}
cStats.mu.Lock() cStats.mu.Lock()
for i, s := range cStats.cs { for _, s := range cStats.cs {
if err := s.Display(w); err != nil && !*noStream { if err := s.Display(w); err != nil && !*noStream {
toRemove = append(toRemove, i) logrus.Debugf("stats: got error for %s: %v", s.Name, err)
} }
} }
for j := len(toRemove) - 1; j >= 0; j-- {
i := toRemove[j]
cStats.cs = append(cStats.cs[:i], cStats.cs[i+1:]...)
}
if len(cStats.cs) == 0 && !showAll {
return nil
}
cStats.mu.Unlock() cStats.mu.Unlock()
w.Flush() w.Flush()
if *noStream { if *noStream {

View file

@ -2,12 +2,14 @@ package client
import ( import (
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io" "io"
"strings" "strings"
"sync" "sync"
"time" "time"
"github.com/Sirupsen/logrus"
"github.com/docker/engine-api/client" "github.com/docker/engine-api/client"
"github.com/docker/engine-api/types" "github.com/docker/engine-api/types"
"github.com/docker/go-units" "github.com/docker/go-units"
@ -25,7 +27,7 @@ type containerStats struct {
BlockRead float64 BlockRead float64
BlockWrite float64 BlockWrite float64
PidsCurrent uint64 PidsCurrent uint64
mu sync.RWMutex mu sync.Mutex
err error err error
} }
@ -62,6 +64,7 @@ func (s *stats) isKnownContainer(cid string) (int, bool) {
} }
func (s *containerStats) Collect(cli client.APIClient, streamStats bool, waitFirst *sync.WaitGroup) { func (s *containerStats) Collect(cli client.APIClient, streamStats bool, waitFirst *sync.WaitGroup) {
logrus.Debugf("collecting stats for %s", s.Name)
var ( var (
getFirst bool getFirst bool
previousCPU uint64 previousCPU uint64
@ -90,9 +93,11 @@ func (s *containerStats) Collect(cli client.APIClient, streamStats bool, waitFir
go func() { go func() {
for { for {
var v *types.StatsJSON var v *types.StatsJSON
if err := dec.Decode(&v); err != nil { if err := dec.Decode(&v); err != nil {
dec = json.NewDecoder(io.MultiReader(dec.Buffered(), responseBody))
u <- err u <- err
return continue
} }
var memPercent = 0.0 var memPercent = 0.0
@ -139,6 +144,7 @@ func (s *containerStats) Collect(cli client.APIClient, streamStats bool, waitFir
s.BlockRead = 0 s.BlockRead = 0
s.BlockWrite = 0 s.BlockWrite = 0
s.PidsCurrent = 0 s.PidsCurrent = 0
s.err = errors.New("timeout waiting for stats")
s.mu.Unlock() s.mu.Unlock()
// if this is the first stat you get, release WaitGroup // if this is the first stat you get, release WaitGroup
if !getFirst { if !getFirst {
@ -150,8 +156,9 @@ func (s *containerStats) Collect(cli client.APIClient, streamStats bool, waitFir
s.mu.Lock() s.mu.Lock()
s.err = err s.err = err
s.mu.Unlock() s.mu.Unlock()
return continue
} }
s.err = nil
// if this is the first stat you get, release WaitGroup // if this is the first stat you get, release WaitGroup
if !getFirst { if !getFirst {
getFirst = true getFirst = true
@ -165,12 +172,20 @@ func (s *containerStats) Collect(cli client.APIClient, streamStats bool, waitFir
} }
func (s *containerStats) Display(w io.Writer) error { func (s *containerStats) Display(w io.Writer) error {
s.mu.RLock() s.mu.Lock()
defer s.mu.RUnlock() defer s.mu.Unlock()
// NOTE: if you change this format, you must also change the err format below!
format := "%s\t%.2f%%\t%s / %s\t%.2f%%\t%s / %s\t%s / %s\t%d\n"
if s.err != nil { if s.err != nil {
return s.err format = "%s\t%s\t%s / %s\t%s\t%s / %s\t%s / %s\t%s\n"
errStr := "--"
fmt.Fprintf(w, format,
s.Name, errStr, errStr, errStr, errStr, errStr, errStr, errStr, errStr, errStr,
)
err := s.err
return err
} }
fmt.Fprintf(w, "%s\t%.2f%%\t%s / %s\t%.2f%%\t%s / %s\t%s / %s\t%d\n", fmt.Fprintf(w, format,
s.Name, s.Name,
s.CPUPercentage, s.CPUPercentage,
units.BytesSize(s.Memory), units.BytesSize(s.MemoryLimit), units.BytesSize(s.Memory), units.BytesSize(s.MemoryLimit),

View file

@ -2,7 +2,6 @@ package client
import ( import (
"bytes" "bytes"
"sync"
"testing" "testing"
"github.com/docker/engine-api/types" "github.com/docker/engine-api/types"
@ -20,7 +19,6 @@ func TestDisplay(t *testing.T) {
BlockRead: 100 * 1024 * 1024, BlockRead: 100 * 1024 * 1024,
BlockWrite: 800 * 1024 * 1024, BlockWrite: 800 * 1024 * 1024,
PidsCurrent: 1, PidsCurrent: 1,
mu: sync.RWMutex{},
} }
var b bytes.Buffer var b bytes.Buffer
if err := c.Display(&b); err != nil { if err := c.Display(&b); err != nil {

View file

@ -120,10 +120,7 @@ func (s *DockerSuite) TestStatsAllNoStream(c *check.C) {
func (s *DockerSuite) TestStatsAllNewContainersAdded(c *check.C) { func (s *DockerSuite) TestStatsAllNewContainersAdded(c *check.C) {
// Windows does not support stats // Windows does not support stats
// TODO: remove SameHostDaemon testRequires(c, DaemonIsLinux)
// The reason it was added is because, there seems to be some race that makes this test fail
// for remote daemons (namely in the win2lin CI). We highly welcome contributions to fix this.
testRequires(c, DaemonIsLinux, SameHostDaemon)
id := make(chan string) id := make(chan string)
addedChan := make(chan struct{}) addedChan := make(chan struct{})