Просмотр исходного кода

Allow pulling stats once and disconnecting.

Adds a `stream` query param to the stats API which allows API users to
only collect one stats entry and disconnect instead of keeping the
connection alive to stream more stats.

Also adds a `--no-stream` flag to `docker stats` which does the same

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
Brian Goff 10 лет назад
Родитель
Сommit
f3023a93d1

+ 25 - 6
api/client/stats.go

@@ -4,6 +4,7 @@ import (
 	"encoding/json"
 	"encoding/json"
 	"fmt"
 	"fmt"
 	"io"
 	"io"
+	"net/url"
 	"sort"
 	"sort"
 	"strings"
 	"strings"
 	"sync"
 	"sync"
@@ -27,8 +28,14 @@ type containerStats struct {
 	err              error
 	err              error
 }
 }
 
 
-func (s *containerStats) Collect(cli *DockerCli) {
-	stream, _, err := cli.call("GET", "/containers/"+s.Name+"/stats", nil, nil)
+func (s *containerStats) Collect(cli *DockerCli, streamStats bool) {
+	v := url.Values{}
+	if streamStats {
+		v.Set("stream", "1")
+	} else {
+		v.Set("stream", "0")
+	}
+	stream, _, err := cli.call("GET", "/containers/"+s.Name+"/stats?"+v.Encode(), nil, nil)
 	if err != nil {
 	if err != nil {
 		s.err = err
 		s.err = err
 		return
 		return
@@ -67,6 +74,9 @@ func (s *containerStats) Collect(cli *DockerCli) {
 			previousCPU = v.CpuStats.CpuUsage.TotalUsage
 			previousCPU = v.CpuStats.CpuUsage.TotalUsage
 			previousSystem = v.CpuStats.SystemUsage
 			previousSystem = v.CpuStats.SystemUsage
 			u <- nil
 			u <- nil
+			if !streamStats {
+				return
+			}
 		}
 		}
 	}()
 	}()
 	for {
 	for {
@@ -87,6 +97,9 @@ func (s *containerStats) Collect(cli *DockerCli) {
 				return
 				return
 			}
 			}
 		}
 		}
+		if !streamStats {
+			return
+		}
 	}
 	}
 }
 }
 
 
@@ -112,6 +125,7 @@ func (s *containerStats) Display(w io.Writer) error {
 // Usage: docker stats CONTAINER [CONTAINER...]
 // Usage: docker stats CONTAINER [CONTAINER...]
 func (cli *DockerCli) CmdStats(args ...string) error {
 func (cli *DockerCli) CmdStats(args ...string) error {
 	cmd := cli.Subcmd("stats", "CONTAINER [CONTAINER...]", "Display a live stream of one or more containers' resource usage statistics", true)
 	cmd := cli.Subcmd("stats", "CONTAINER [CONTAINER...]", "Display a live stream of one or more containers' resource usage statistics", true)
+	noStream := cmd.Bool([]string{"-no-stream"}, false, "Disable streaming stats and only pull the first result")
 	cmd.Require(flag.Min, 1)
 	cmd.Require(flag.Min, 1)
 	cmd.ParseFlags(args, true)
 	cmd.ParseFlags(args, true)
 
 
@@ -122,14 +136,16 @@ func (cli *DockerCli) CmdStats(args ...string) error {
 		w      = tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
 		w      = tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
 	)
 	)
 	printHeader := func() {
 	printHeader := func() {
-		io.WriteString(cli.out, "\033[2J")
-		io.WriteString(cli.out, "\033[H")
+		if !*noStream {
+			fmt.Fprint(cli.out, "\033[2J")
+			fmt.Fprint(cli.out, "\033[H")
+		}
 		io.WriteString(w, "CONTAINER\tCPU %\tMEM USAGE/LIMIT\tMEM %\tNET I/O\n")
 		io.WriteString(w, "CONTAINER\tCPU %\tMEM USAGE/LIMIT\tMEM %\tNET I/O\n")
 	}
 	}
 	for _, n := range names {
 	for _, n := range names {
 		s := &containerStats{Name: n}
 		s := &containerStats{Name: n}
 		cStats = append(cStats, s)
 		cStats = append(cStats, s)
-		go s.Collect(cli)
+		go s.Collect(cli, !*noStream)
 	}
 	}
 	// do a quick pause so that any failed connections for containers that do not exist are able to be
 	// do a quick pause so that any failed connections for containers that do not exist are able to be
 	// evicted before we display the initial or default values.
 	// evicted before we display the initial or default values.
@@ -149,7 +165,7 @@ func (cli *DockerCli) CmdStats(args ...string) error {
 		printHeader()
 		printHeader()
 		toRemove := []int{}
 		toRemove := []int{}
 		for i, s := range cStats {
 		for i, s := range cStats {
-			if err := s.Display(w); err != nil {
+			if err := s.Display(w); err != nil && !*noStream {
 				toRemove = append(toRemove, i)
 				toRemove = append(toRemove, i)
 			}
 			}
 		}
 		}
@@ -161,6 +177,9 @@ func (cli *DockerCli) CmdStats(args ...string) error {
 			return nil
 			return nil
 		}
 		}
 		w.Flush()
 		w.Flush()
+		if *noStream {
+			break
+		}
 	}
 	}
 	return nil
 	return nil
 }
 }

+ 1 - 1
api/server/server.go

@@ -611,7 +611,7 @@ func (s *Server) getContainersStats(eng *engine.Engine, version version.Version,
 		return fmt.Errorf("Missing parameter")
 		return fmt.Errorf("Missing parameter")
 	}
 	}
 
 
-	return s.daemon.ContainerStats(vars["name"], utils.NewWriteFlusher(w))
+	return s.daemon.ContainerStats(vars["name"], boolValue(r, "stream"), utils.NewWriteFlusher(w))
 }
 }
 
 
 func (s *Server) getContainersLogs(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 func (s *Server) getContainersLogs(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {

+ 1 - 1
contrib/completion/bash/docker

@@ -1003,7 +1003,7 @@ _docker_start() {
 _docker_stats() {
 _docker_stats() {
 	case "$cur" in
 	case "$cur" in
 		-*)
 		-*)
-			COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
+			COMPREPLY=( $( compgen -W "--no-stream --help" -- "$cur" ) )
 			;;
 			;;
 		*)
 		*)
 			__docker_containers_running
 			__docker_containers_running

+ 2 - 1
contrib/completion/fish/docker.fish

@@ -16,7 +16,7 @@
 
 
 function __fish_docker_no_subcommand --description 'Test if docker has yet to be given the subcommand'
 function __fish_docker_no_subcommand --description 'Test if docker has yet to be given the subcommand'
     for i in (commandline -opc)
     for i in (commandline -opc)
-        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
+        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
             return 1
             return 1
         end
         end
     end
     end
@@ -361,6 +361,7 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from start' -a '(__fish_prin
 # stats
 # stats
 complete -c docker -f -n '__fish_docker_no_subcommand' -a stats -d "Display a live stream of one or more containers' resource usage statistics"
 complete -c docker -f -n '__fish_docker_no_subcommand' -a stats -d "Display a live stream of one or more containers' resource usage statistics"
 complete -c docker -A -f -n '__fish_seen_subcommand_from stats' -l help -d 'Print usage'
 complete -c docker -A -f -n '__fish_seen_subcommand_from stats' -l help -d 'Print usage'
+complete -c docker -A -f -n '__fish_seen_subcommand_from stats' -l no-stream -d 'Disable streaming stats and only pull the first result'
 complete -c docker -A -f -n '__fish_seen_subcommand_from stats' -a '(__fish_print_docker_containers running)' -d "Container"
 complete -c docker -A -f -n '__fish_seen_subcommand_from stats' -a '(__fish_print_docker_containers running)' -d "Container"
 
 
 # stop
 # stop

+ 1 - 0
contrib/completion/zsh/_docker

@@ -326,6 +326,7 @@ __docker_subcommand () {
             ;;
             ;;
         (stats)
         (stats)
             _arguments \
             _arguments \
+                '--no-stream[Disable streaming stats and only pull the first result]' \
                 '*:containers:__docker_runningcontainers'
                 '*:containers:__docker_runningcontainers'
             ;;
             ;;
         (rm)
         (rm)

+ 4 - 1
daemon/stats.go

@@ -10,7 +10,7 @@ import (
 	"github.com/docker/libcontainer/cgroups"
 	"github.com/docker/libcontainer/cgroups"
 )
 )
 
 
-func (daemon *Daemon) ContainerStats(name string, out io.Writer) error {
+func (daemon *Daemon) ContainerStats(name string, stream bool, out io.Writer) error {
 	updates, err := daemon.SubscribeToContainerStats(name)
 	updates, err := daemon.SubscribeToContainerStats(name)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -27,6 +27,9 @@ func (daemon *Daemon) ContainerStats(name string, out io.Writer) error {
 			daemon.UnsubscribeToContainerStats(name, updates)
 			daemon.UnsubscribeToContainerStats(name, updates)
 			return err
 			return err
 		}
 		}
+		if !stream {
+			break
+		}
 	}
 	}
 	return nil
 	return nil
 }
 }

+ 3 - 0
docs/man/docker-stats.1.md

@@ -17,6 +17,9 @@ Display a live stream of one or more containers' resource usage statistics
 **--help**
 **--help**
   Print usage statement
   Print usage statement
 
 
+**--no-stream**="false"
+  Disable streaming stats and only pull the first result
+
 # EXAMPLES
 # EXAMPLES
 
 
 Run **docker stats** with multiple containers.
 Run **docker stats** with multiple containers.

+ 5 - 0
docs/sources/reference/api/docker_remote_api.md

@@ -46,6 +46,11 @@ You can still call an old version of the API using
 
 
 ### What's new
 ### What's new
 
 
+`GET /containers/(id)/stats`
+
+**New!**
+You can now supply a `stream` bool to get only one set of stats and
+disconnect
 
 
 ## v1.18
 ## v1.18
 
 

+ 4 - 0
docs/sources/reference/api/docker_remote_api_v1.19.md

@@ -644,6 +644,10 @@ This endpoint returns a live stream of a container's resource usage statistics.
            }
            }
         }
         }
 
 
+Query Parameters:
+
+-   **stream** – 1/True/true or 0/False/false, pull stats once then disconnect. Default true
+
 Status Codes:
 Status Codes:
 
 
 -   **200** – no error
 -   **200** – no error

+ 1 - 0
docs/sources/reference/commandline/cli.md

@@ -2377,6 +2377,7 @@ more details on finding shared images from the command line.
     Display a live stream of one or more containers' resource usage statistics
     Display a live stream of one or more containers' resource usage statistics
 
 
       --help=false       Print usage
       --help=false       Print usage
+      --no-stream=false  Disable streaming stats and only pull the first result
 
 
 Running `docker stats` on multiple containers
 Running `docker stats` on multiple containers
 
 

+ 36 - 0
integration-cli/docker_cli_stats_test.go

@@ -0,0 +1,36 @@
+package main
+
+import (
+	"os/exec"
+	"strings"
+	"time"
+
+	"github.com/go-check/check"
+)
+
+func (s *DockerSuite) TestCliStatsNoStream(c *check.C) {
+	out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "-d", "busybox", "top"))
+	if err != nil {
+		c.Fatalf("Error on container creation: %v, output: %s", err, out)
+	}
+	id := strings.TrimSpace(out)
+	if err := waitRun(id); err != nil {
+		c.Fatalf("error waiting for container to start: %v", err)
+	}
+
+	statsCmd := exec.Command(dockerBinary, "stats", "--no-stream", id)
+	chErr := make(chan error)
+	go func() {
+		chErr <- statsCmd.Run()
+	}()
+
+	select {
+	case err := <-chErr:
+		if err != nil {
+			c.Fatalf("Error running stats: %v", err)
+		}
+	case <-time.After(2 * time.Second):
+		statsCmd.Process.Kill()
+		c.Fatalf("stats did not return immediately when not streaming")
+	}
+}