Browse code

Fixes content-type/length for stats stream=false

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
(cherry picked from commit 855a056af7829504ccc310587445c61d62427b51)

Brian Goff authored on 2015/06/13 00:27:39
Showing 4 changed files
... ...
@@ -575,7 +575,16 @@ func (s *Server) getContainersStats(version version.Version, w http.ResponseWrit
575 575
 		return fmt.Errorf("Missing parameter")
576 576
 	}
577 577
 
578
-	return s.daemon.ContainerStats(vars["name"], boolValueOrDefault(r, "stream", true), ioutils.NewWriteFlusher(w))
578
+	stream := boolValueOrDefault(r, "stream", true)
579
+	var out io.Writer
580
+	if !stream {
581
+		w.Header().Set("Content-Type", "application/json")
582
+		out = w
583
+	} else {
584
+		out = ioutils.NewWriteFlusher(w)
585
+	}
586
+
587
+	return s.daemon.ContainerStats(vars["name"], stream, out)
579 588
 }
580 589
 
581 590
 func (s *Server) getContainersLogs(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
... ...
@@ -15,31 +15,39 @@ func (daemon *Daemon) ContainerStats(name string, stream bool, out io.Writer) er
15 15
 	if err != nil {
16 16
 		return err
17 17
 	}
18
-	var pre_cpu_stats types.CpuStats
19
-	for first_v := range updates {
20
-		first_update := first_v.(*execdriver.ResourceStats)
21
-		first_stats := convertToAPITypes(first_update.Stats)
22
-		pre_cpu_stats = first_stats.CpuStats
23
-		pre_cpu_stats.SystemUsage = first_update.SystemUsage
24
-		break
25
-	}
26
-	enc := json.NewEncoder(out)
27
-	for v := range updates {
18
+
19
+	var preCpuStats types.CpuStats
20
+	getStat := func(v interface{}) *types.Stats {
28 21
 		update := v.(*execdriver.ResourceStats)
29 22
 		ss := convertToAPITypes(update.Stats)
30
-		ss.PreCpuStats = pre_cpu_stats
23
+		ss.PreCpuStats = preCpuStats
31 24
 		ss.MemoryStats.Limit = uint64(update.MemoryLimit)
32 25
 		ss.Read = update.Read
33 26
 		ss.CpuStats.SystemUsage = update.SystemUsage
34
-		pre_cpu_stats = ss.CpuStats
35
-		if err := enc.Encode(ss); err != nil {
27
+		preCpuStats = ss.CpuStats
28
+		return ss
29
+	}
30
+
31
+	enc := json.NewEncoder(out)
32
+
33
+	if !stream {
34
+		// prime the cpu stats so they aren't 0 in the final output
35
+		s := getStat(<-updates)
36
+
37
+		// now pull stats again with the cpu stats primed
38
+		s = getStat(<-updates)
39
+		err := enc.Encode(s)
40
+		daemon.UnsubscribeToContainerStats(name, updates)
41
+		return err
42
+	}
43
+
44
+	for v := range updates {
45
+		s := getStat(v)
46
+		if err := enc.Encode(s); err != nil {
36 47
 			// TODO: handle the specific broken pipe
37 48
 			daemon.UnsubscribeToContainerStats(name, updates)
38 49
 			return err
39 50
 		}
40
-		if !stream {
41
-			break
42
-		}
43 51
 	}
44 52
 	return nil
45 53
 }
46 54
deleted file mode 100644
... ...
@@ -1,48 +0,0 @@
1
-package main
2
-
3
-import (
4
-	"encoding/json"
5
-	"fmt"
6
-	"github.com/docker/docker/api/types"
7
-	"github.com/go-check/check"
8
-	"strings"
9
-	"time"
10
-)
11
-
12
-func (s *DockerSuite) TestCliStatsNoStreamGetCpu(c *check.C) {
13
-	out, _ := dockerCmd(c, "run", "-d", "--cpu-quota=2000", "busybox", "/bin/sh", "-c", "while true;do echo 'Hello';done")
14
-
15
-	id := strings.TrimSpace(out)
16
-	if err := waitRun(id); err != nil {
17
-		c.Fatal(err)
18
-	}
19
-	ch := make(chan error)
20
-	var v *types.Stats
21
-	go func() {
22
-		_, body, err := sockRequestRaw("GET", fmt.Sprintf("/containers/%s/stats?stream=1", id), nil, "")
23
-		if err != nil {
24
-			ch <- err
25
-		}
26
-		dec := json.NewDecoder(body)
27
-		if err := dec.Decode(&v); err != nil {
28
-			ch <- err
29
-		}
30
-		ch <- nil
31
-	}()
32
-	select {
33
-	case e := <-ch:
34
-		if e == nil {
35
-			var cpuPercent = 0.0
36
-			cpuDelta := float64(v.CpuStats.CpuUsage.TotalUsage - v.PreCpuStats.CpuUsage.TotalUsage)
37
-			systemDelta := float64(v.CpuStats.SystemUsage - v.PreCpuStats.SystemUsage)
38
-			cpuPercent = (cpuDelta / systemDelta) * float64(len(v.CpuStats.CpuUsage.PercpuUsage)) * 100.0
39
-			if cpuPercent < 1.8 || cpuPercent > 2.2 {
40
-				c.Fatal("docker stats with no-stream get cpu usage failed")
41
-			}
42
-
43
-		}
44
-	case <-time.After(4 * time.Second):
45
-		c.Fatal("docker stats with no-stream timeout")
46
-	}
47
-
48
-}
49 1
new file mode 100644
... ...
@@ -0,0 +1,35 @@
0
+package main
1
+
2
+import (
3
+	"encoding/json"
4
+	"fmt"
5
+	"strings"
6
+
7
+	"github.com/docker/docker/api/types"
8
+	"github.com/go-check/check"
9
+)
10
+
11
+func (s *DockerSuite) TestCliStatsNoStreamGetCpu(c *check.C) {
12
+	out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "while true;do echo 'Hello'; usleep 100000; done")
13
+
14
+	id := strings.TrimSpace(out)
15
+	err := waitRun(id)
16
+	c.Assert(err, check.IsNil)
17
+
18
+	resp, body, err := sockRequestRaw("GET", fmt.Sprintf("/containers/%s/stats?stream=false", id), nil, "")
19
+	c.Assert(err, check.IsNil)
20
+	c.Assert(resp.ContentLength > 0, check.Equals, true, check.Commentf("should not use chunked encoding"))
21
+	c.Assert(resp.Header.Get("Content-Type"), check.Equals, "application/json")
22
+
23
+	var v *types.Stats
24
+	err = json.NewDecoder(body).Decode(&v)
25
+	c.Assert(err, check.IsNil)
26
+
27
+	var cpuPercent = 0.0
28
+	cpuDelta := float64(v.CpuStats.CpuUsage.TotalUsage - v.PreCpuStats.CpuUsage.TotalUsage)
29
+	systemDelta := float64(v.CpuStats.SystemUsage - v.PreCpuStats.SystemUsage)
30
+	cpuPercent = (cpuDelta / systemDelta) * float64(len(v.CpuStats.CpuUsage.PercpuUsage)) * 100.0
31
+	if cpuPercent == 0 {
32
+		c.Fatalf("docker stats with no-stream get cpu usage failed: was %v", cpuPercent)
33
+	}
34
+}