Fixes content-type/length for stats stream=false
Signed-off-by: Brian Goff <cpuguy83@gmail.com>
(cherry picked from commit 855a056af7829504ccc310587445c61d62427b51)
diff --git a/api/server/server.go b/api/server/server.go
index bb3a09e..7a0ff4f 100644
--- a/api/server/server.go
+++ b/api/server/server.go
@@ -575,7 +575,16 @@
return fmt.Errorf("Missing parameter")
}
- return s.daemon.ContainerStats(vars["name"], boolValueOrDefault(r, "stream", true), ioutils.NewWriteFlusher(w))
+ stream := boolValueOrDefault(r, "stream", true)
+ var out io.Writer
+ if !stream {
+ w.Header().Set("Content-Type", "application/json")
+ out = w
+ } else {
+ out = ioutils.NewWriteFlusher(w)
+ }
+
+ return s.daemon.ContainerStats(vars["name"], stream, out)
}
func (s *Server) getContainersLogs(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
diff --git a/daemon/stats.go b/daemon/stats.go
index 960ba4a..6a49172 100644
--- a/daemon/stats.go
+++ b/daemon/stats.go
@@ -15,31 +15,39 @@
if err != nil {
return err
}
- var pre_cpu_stats types.CpuStats
- for first_v := range updates {
- first_update := first_v.(*execdriver.ResourceStats)
- first_stats := convertToAPITypes(first_update.Stats)
- pre_cpu_stats = first_stats.CpuStats
- pre_cpu_stats.SystemUsage = first_update.SystemUsage
- break
- }
- enc := json.NewEncoder(out)
- for v := range updates {
+
+ var preCpuStats types.CpuStats
+ getStat := func(v interface{}) *types.Stats {
update := v.(*execdriver.ResourceStats)
ss := convertToAPITypes(update.Stats)
- ss.PreCpuStats = pre_cpu_stats
+ ss.PreCpuStats = preCpuStats
ss.MemoryStats.Limit = uint64(update.MemoryLimit)
ss.Read = update.Read
ss.CpuStats.SystemUsage = update.SystemUsage
- pre_cpu_stats = ss.CpuStats
- if err := enc.Encode(ss); err != nil {
+ preCpuStats = ss.CpuStats
+ return ss
+ }
+
+ enc := json.NewEncoder(out)
+
+ if !stream {
+ // prime the cpu stats so they aren't 0 in the final output
+ s := getStat(<-updates)
+
+ // now pull stats again with the cpu stats primed
+ s = getStat(<-updates)
+ err := enc.Encode(s)
+ daemon.UnsubscribeToContainerStats(name, updates)
+ return err
+ }
+
+ for v := range updates {
+ s := getStat(v)
+ if err := enc.Encode(s); err != nil {
// TODO: handle the specific broken pipe
daemon.UnsubscribeToContainerStats(name, updates)
return err
}
- if !stream {
- break
- }
}
return nil
}
diff --git a/integration-cli/docker_api_stats.go b/integration-cli/docker_api_stats.go
deleted file mode 100644
index 41d2f86..0000000
--- a/integration-cli/docker_api_stats.go
+++ /dev/null
@@ -1,48 +0,0 @@
-package main
-
-import (
- "encoding/json"
- "fmt"
- "github.com/docker/docker/api/types"
- "github.com/go-check/check"
- "strings"
- "time"
-)
-
-func (s *DockerSuite) TestCliStatsNoStreamGetCpu(c *check.C) {
- out, _ := dockerCmd(c, "run", "-d", "--cpu-quota=2000", "busybox", "/bin/sh", "-c", "while true;do echo 'Hello';done")
-
- id := strings.TrimSpace(out)
- if err := waitRun(id); err != nil {
- c.Fatal(err)
- }
- ch := make(chan error)
- var v *types.Stats
- go func() {
- _, body, err := sockRequestRaw("GET", fmt.Sprintf("/containers/%s/stats?stream=1", id), nil, "")
- if err != nil {
- ch <- err
- }
- dec := json.NewDecoder(body)
- if err := dec.Decode(&v); err != nil {
- ch <- err
- }
- ch <- nil
- }()
- select {
- case e := <-ch:
- if e == nil {
- var cpuPercent = 0.0
- cpuDelta := float64(v.CpuStats.CpuUsage.TotalUsage - v.PreCpuStats.CpuUsage.TotalUsage)
- systemDelta := float64(v.CpuStats.SystemUsage - v.PreCpuStats.SystemUsage)
- cpuPercent = (cpuDelta / systemDelta) * float64(len(v.CpuStats.CpuUsage.PercpuUsage)) * 100.0
- if cpuPercent < 1.8 || cpuPercent > 2.2 {
- c.Fatal("docker stats with no-stream get cpu usage failed")
- }
-
- }
- case <-time.After(4 * time.Second):
- c.Fatal("docker stats with no-stream timeout")
- }
-
-}
diff --git a/integration-cli/docker_api_stats_test.go b/integration-cli/docker_api_stats_test.go
new file mode 100644
index 0000000..6a143fe
--- /dev/null
+++ b/integration-cli/docker_api_stats_test.go
@@ -0,0 +1,35 @@
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "strings"
+
+ "github.com/docker/docker/api/types"
+ "github.com/go-check/check"
+)
+
+func (s *DockerSuite) TestCliStatsNoStreamGetCpu(c *check.C) {
+ out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "while true;do echo 'Hello'; usleep 100000; done")
+
+ id := strings.TrimSpace(out)
+ err := waitRun(id)
+ c.Assert(err, check.IsNil)
+
+ resp, body, err := sockRequestRaw("GET", fmt.Sprintf("/containers/%s/stats?stream=false", id), nil, "")
+ c.Assert(err, check.IsNil)
+ c.Assert(resp.ContentLength > 0, check.Equals, true, check.Commentf("should not use chunked encoding"))
+ c.Assert(resp.Header.Get("Content-Type"), check.Equals, "application/json")
+
+ var v *types.Stats
+ err = json.NewDecoder(body).Decode(&v)
+ c.Assert(err, check.IsNil)
+
+ var cpuPercent = 0.0
+ cpuDelta := float64(v.CpuStats.CpuUsage.TotalUsage - v.PreCpuStats.CpuUsage.TotalUsage)
+ systemDelta := float64(v.CpuStats.SystemUsage - v.PreCpuStats.SystemUsage)
+ cpuPercent = (cpuDelta / systemDelta) * float64(len(v.CpuStats.CpuUsage.PercpuUsage)) * 100.0
+ if cpuPercent == 0 {
+ c.Fatalf("docker stats with no-stream get cpu usage failed: was %v", cpuPercent)
+ }
+}