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:
commit
5314296c69
4 changed files with 26 additions and 23 deletions
|
@ -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 {
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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{})
|
||||||
|
|
Loading…
Add table
Reference in a new issue