Browse code

Merge pull request #10766 from cpuguy83/stats_pull_once

Allow pulling stats once and disconnecting.

Tibor Vass authored on 2015/05/05 14:28:01
Showing 11 changed files
... ...
@@ -4,6 +4,7 @@ import (
4 4
 	"encoding/json"
5 5
 	"fmt"
6 6
 	"io"
7
+	"net/url"
7 8
 	"sort"
8 9
 	"strings"
9 10
 	"sync"
... ...
@@ -27,8 +28,14 @@ type containerStats struct {
27 27
 	err              error
28 28
 }
29 29
 
30
-func (s *containerStats) Collect(cli *DockerCli) {
31
-	stream, _, err := cli.call("GET", "/containers/"+s.Name+"/stats", nil, nil)
30
+func (s *containerStats) Collect(cli *DockerCli, streamStats bool) {
31
+	v := url.Values{}
32
+	if streamStats {
33
+		v.Set("stream", "1")
34
+	} else {
35
+		v.Set("stream", "0")
36
+	}
37
+	stream, _, err := cli.call("GET", "/containers/"+s.Name+"/stats?"+v.Encode(), nil, nil)
32 38
 	if err != nil {
33 39
 		s.err = err
34 40
 		return
... ...
@@ -67,6 +74,9 @@ func (s *containerStats) Collect(cli *DockerCli) {
67 67
 			previousCPU = v.CpuStats.CpuUsage.TotalUsage
68 68
 			previousSystem = v.CpuStats.SystemUsage
69 69
 			u <- nil
70
+			if !streamStats {
71
+				return
72
+			}
70 73
 		}
71 74
 	}()
72 75
 	for {
... ...
@@ -87,6 +97,9 @@ func (s *containerStats) Collect(cli *DockerCli) {
87 87
 				return
88 88
 			}
89 89
 		}
90
+		if !streamStats {
91
+			return
92
+		}
90 93
 	}
91 94
 }
92 95
 
... ...
@@ -112,6 +125,7 @@ func (s *containerStats) Display(w io.Writer) error {
112 112
 // Usage: docker stats CONTAINER [CONTAINER...]
113 113
 func (cli *DockerCli) CmdStats(args ...string) error {
114 114
 	cmd := cli.Subcmd("stats", "CONTAINER [CONTAINER...]", "Display a live stream of one or more containers' resource usage statistics", true)
115
+	noStream := cmd.Bool([]string{"-no-stream"}, false, "Disable streaming stats and only pull the first result")
115 116
 	cmd.Require(flag.Min, 1)
116 117
 	cmd.ParseFlags(args, true)
117 118
 
... ...
@@ -122,14 +136,16 @@ func (cli *DockerCli) CmdStats(args ...string) error {
122 122
 		w      = tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
123 123
 	)
124 124
 	printHeader := func() {
125
-		io.WriteString(cli.out, "\033[2J")
126
-		io.WriteString(cli.out, "\033[H")
125
+		if !*noStream {
126
+			fmt.Fprint(cli.out, "\033[2J")
127
+			fmt.Fprint(cli.out, "\033[H")
128
+		}
127 129
 		io.WriteString(w, "CONTAINER\tCPU %\tMEM USAGE/LIMIT\tMEM %\tNET I/O\n")
128 130
 	}
129 131
 	for _, n := range names {
130 132
 		s := &containerStats{Name: n}
131 133
 		cStats = append(cStats, s)
132
-		go s.Collect(cli)
134
+		go s.Collect(cli, !*noStream)
133 135
 	}
134 136
 	// do a quick pause so that any failed connections for containers that do not exist are able to be
135 137
 	// evicted before we display the initial or default values.
... ...
@@ -149,7 +165,7 @@ func (cli *DockerCli) CmdStats(args ...string) error {
149 149
 		printHeader()
150 150
 		toRemove := []int{}
151 151
 		for i, s := range cStats {
152
-			if err := s.Display(w); err != nil {
152
+			if err := s.Display(w); err != nil && !*noStream {
153 153
 				toRemove = append(toRemove, i)
154 154
 			}
155 155
 		}
... ...
@@ -161,6 +177,9 @@ func (cli *DockerCli) CmdStats(args ...string) error {
161 161
 			return nil
162 162
 		}
163 163
 		w.Flush()
164
+		if *noStream {
165
+			break
166
+		}
164 167
 	}
165 168
 	return nil
166 169
 }
... ...
@@ -581,7 +581,7 @@ func (s *Server) getContainersStats(version version.Version, w http.ResponseWrit
581 581
 		return fmt.Errorf("Missing parameter")
582 582
 	}
583 583
 
584
-	return s.daemon.ContainerStats(vars["name"], utils.NewWriteFlusher(w))
584
+	return s.daemon.ContainerStats(vars["name"], boolValue(r, "stream"), utils.NewWriteFlusher(w))
585 585
 }
586 586
 
587 587
 func (s *Server) getContainersLogs(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
... ...
@@ -1011,7 +1011,7 @@ _docker_start() {
1011 1011
 _docker_stats() {
1012 1012
 	case "$cur" in
1013 1013
 		-*)
1014
-			COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
1014
+			COMPREPLY=( $( compgen -W "--no-stream --help" -- "$cur" ) )
1015 1015
 			;;
1016 1016
 		*)
1017 1017
 			__docker_containers_running
... ...
@@ -16,7 +16,7 @@
16 16
 
17 17
 function __fish_docker_no_subcommand --description 'Test if docker has yet to be given the subcommand'
18 18
     for i in (commandline -opc)
19
-        if contains -- $i attach build commit cp create diff events exec export history images import info inspect kill load login logout logs pause port ps pull push rename restart rm rmi run save search start stop tag top unpause version wait
19
+        if contains -- $i attach build commit cp create diff events exec export history images import info inspect kill load login logout logs pause port ps pull push rename restart rm rmi run save search start stop tag top unpause version wait stats
20 20
             return 1
21 21
         end
22 22
     end
... ...
@@ -362,6 +362,7 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from start' -a '(__fish_prin
362 362
 # stats
363 363
 complete -c docker -f -n '__fish_docker_no_subcommand' -a stats -d "Display a live stream of one or more containers' resource usage statistics"
364 364
 complete -c docker -A -f -n '__fish_seen_subcommand_from stats' -l help -d 'Print usage'
365
+complete -c docker -A -f -n '__fish_seen_subcommand_from stats' -l no-stream -d 'Disable streaming stats and only pull the first result'
365 366
 complete -c docker -A -f -n '__fish_seen_subcommand_from stats' -a '(__fish_print_docker_containers running)' -d "Container"
366 367
 
367 368
 # stop
... ...
@@ -326,6 +326,7 @@ __docker_subcommand () {
326 326
             ;;
327 327
         (stats)
328 328
             _arguments \
329
+                '--no-stream[Disable streaming stats and only pull the first result]' \
329 330
                 '*:containers:__docker_runningcontainers'
330 331
             ;;
331 332
         (rm)
... ...
@@ -10,7 +10,7 @@ import (
10 10
 	"github.com/docker/libcontainer/cgroups"
11 11
 )
12 12
 
13
-func (daemon *Daemon) ContainerStats(name string, out io.Writer) error {
13
+func (daemon *Daemon) ContainerStats(name string, stream bool, out io.Writer) error {
14 14
 	updates, err := daemon.SubscribeToContainerStats(name)
15 15
 	if err != nil {
16 16
 		return err
... ...
@@ -27,6 +27,9 @@ func (daemon *Daemon) ContainerStats(name string, out io.Writer) error {
27 27
 			daemon.UnsubscribeToContainerStats(name, updates)
28 28
 			return err
29 29
 		}
30
+		if !stream {
31
+			break
32
+		}
30 33
 	}
31 34
 	return nil
32 35
 }
... ...
@@ -17,6 +17,9 @@ Display a live stream of one or more containers' resource usage statistics
17 17
 **--help**
18 18
   Print usage statement
19 19
 
20
+**--no-stream**="false"
21
+  Disable streaming stats and only pull the first result
22
+
20 23
 # EXAMPLES
21 24
 
22 25
 Run **docker stats** with multiple containers.
... ...
@@ -46,6 +46,11 @@ You can still call an old version of the API using
46 46
 
47 47
 ### What's new
48 48
 
49
+`GET /containers/(id)/stats`
50
+
51
+**New!**
52
+You can now supply a `stream` bool to get only one set of stats and
53
+disconnect
49 54
 
50 55
 ## v1.18
51 56
 
... ...
@@ -647,6 +647,10 @@ This endpoint returns a live stream of a container's resource usage statistics.
647 647
            }
648 648
         }
649 649
 
650
+Query Parameters:
651
+
652
+-   **stream** – 1/True/true or 0/False/false, pull stats once then disconnect. Default true
653
+
650 654
 Status Codes:
651 655
 
652 656
 -   **200** – no error
... ...
@@ -2394,6 +2394,7 @@ more details on finding shared images from the command line.
2394 2394
     Display a live stream of one or more containers' resource usage statistics
2395 2395
 
2396 2396
       --help=false       Print usage
2397
+      --no-stream=false  Disable streaming stats and only pull the first result
2397 2398
 
2398 2399
 Running `docker stats` on multiple containers
2399 2400
 
2400 2401
new file mode 100644
... ...
@@ -0,0 +1,36 @@
0
+package main
1
+
2
+import (
3
+	"os/exec"
4
+	"strings"
5
+	"time"
6
+
7
+	"github.com/go-check/check"
8
+)
9
+
10
+func (s *DockerSuite) TestCliStatsNoStream(c *check.C) {
11
+	out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "-d", "busybox", "top"))
12
+	if err != nil {
13
+		c.Fatalf("Error on container creation: %v, output: %s", err, out)
14
+	}
15
+	id := strings.TrimSpace(out)
16
+	if err := waitRun(id); err != nil {
17
+		c.Fatalf("error waiting for container to start: %v", err)
18
+	}
19
+
20
+	statsCmd := exec.Command(dockerBinary, "stats", "--no-stream", id)
21
+	chErr := make(chan error)
22
+	go func() {
23
+		chErr <- statsCmd.Run()
24
+	}()
25
+
26
+	select {
27
+	case err := <-chErr:
28
+		if err != nil {
29
+			c.Fatalf("Error running stats: %v", err)
30
+		}
31
+	case <-time.After(2 * time.Second):
32
+		statsCmd.Process.Kill()
33
+		c.Fatalf("stats did not return immediately when not streaming")
34
+	}
35
+}