github.com/reds/docker@v1.11.2-rc1/integration-cli/docker_api_stats_test.go (about)

     1  package main
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net/http"
     7  	"os/exec"
     8  	"runtime"
     9  	"strconv"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/docker/docker/pkg/integration/checker"
    14  	"github.com/docker/docker/pkg/version"
    15  	"github.com/docker/engine-api/types"
    16  	"github.com/go-check/check"
    17  )
    18  
    19  var expectedNetworkInterfaceStats = strings.Split("rx_bytes rx_dropped rx_errors rx_packets tx_bytes tx_dropped tx_errors tx_packets", " ")
    20  
    21  func (s *DockerSuite) TestApiStatsNoStreamGetCpu(c *check.C) {
    22  	testRequires(c, DaemonIsLinux)
    23  	out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "while true;do echo 'Hello'; usleep 100000; done")
    24  
    25  	id := strings.TrimSpace(out)
    26  	c.Assert(waitRun(id), checker.IsNil)
    27  
    28  	resp, body, err := sockRequestRaw("GET", fmt.Sprintf("/containers/%s/stats?stream=false", id), nil, "")
    29  	c.Assert(err, checker.IsNil)
    30  	c.Assert(resp.StatusCode, checker.Equals, http.StatusOK)
    31  	c.Assert(resp.Header.Get("Content-Type"), checker.Equals, "application/json")
    32  
    33  	var v *types.Stats
    34  	err = json.NewDecoder(body).Decode(&v)
    35  	c.Assert(err, checker.IsNil)
    36  	body.Close()
    37  
    38  	var cpuPercent = 0.0
    39  	cpuDelta := float64(v.CPUStats.CPUUsage.TotalUsage - v.PreCPUStats.CPUUsage.TotalUsage)
    40  	systemDelta := float64(v.CPUStats.SystemUsage - v.PreCPUStats.SystemUsage)
    41  	cpuPercent = (cpuDelta / systemDelta) * float64(len(v.CPUStats.CPUUsage.PercpuUsage)) * 100.0
    42  
    43  	c.Assert(cpuPercent, check.Not(checker.Equals), 0.0, check.Commentf("docker stats with no-stream get cpu usage failed: was %v", cpuPercent))
    44  }
    45  
    46  func (s *DockerSuite) TestApiStatsStoppedContainerInGoroutines(c *check.C) {
    47  	testRequires(c, DaemonIsLinux)
    48  	out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "echo 1")
    49  	id := strings.TrimSpace(out)
    50  
    51  	getGoRoutines := func() int {
    52  		_, body, err := sockRequestRaw("GET", fmt.Sprintf("/info"), nil, "")
    53  		c.Assert(err, checker.IsNil)
    54  		info := types.Info{}
    55  		err = json.NewDecoder(body).Decode(&info)
    56  		c.Assert(err, checker.IsNil)
    57  		body.Close()
    58  		return info.NGoroutines
    59  	}
    60  
    61  	// When the HTTP connection is closed, the number of goroutines should not increase.
    62  	routines := getGoRoutines()
    63  	_, body, err := sockRequestRaw("GET", fmt.Sprintf("/containers/%s/stats", id), nil, "")
    64  	c.Assert(err, checker.IsNil)
    65  	body.Close()
    66  
    67  	t := time.After(30 * time.Second)
    68  	for {
    69  		select {
    70  		case <-t:
    71  			c.Assert(getGoRoutines(), checker.LessOrEqualThan, routines)
    72  			return
    73  		default:
    74  			if n := getGoRoutines(); n <= routines {
    75  				return
    76  			}
    77  			time.Sleep(200 * time.Millisecond)
    78  		}
    79  	}
    80  }
    81  
    82  func (s *DockerSuite) TestApiStatsNetworkStats(c *check.C) {
    83  	testRequires(c, SameHostDaemon)
    84  	testRequires(c, DaemonIsLinux)
    85  
    86  	out, _ := runSleepingContainer(c)
    87  	id := strings.TrimSpace(out)
    88  	c.Assert(waitRun(id), checker.IsNil)
    89  
    90  	// Retrieve the container address
    91  	contIP := findContainerIP(c, id, "bridge")
    92  	numPings := 4
    93  
    94  	var preRxPackets uint64
    95  	var preTxPackets uint64
    96  	var postRxPackets uint64
    97  	var postTxPackets uint64
    98  
    99  	// Get the container networking stats before and after pinging the container
   100  	nwStatsPre := getNetworkStats(c, id)
   101  	for _, v := range nwStatsPre {
   102  		preRxPackets += v.RxPackets
   103  		preTxPackets += v.TxPackets
   104  	}
   105  
   106  	countParam := "-c"
   107  	if runtime.GOOS == "windows" {
   108  		countParam = "-n" // Ping count parameter is -n on Windows
   109  	}
   110  	pingout, err := exec.Command("ping", contIP, countParam, strconv.Itoa(numPings)).Output()
   111  	pingouts := string(pingout[:])
   112  	c.Assert(err, checker.IsNil)
   113  	nwStatsPost := getNetworkStats(c, id)
   114  	for _, v := range nwStatsPost {
   115  		postRxPackets += v.RxPackets
   116  		postTxPackets += v.TxPackets
   117  	}
   118  
   119  	// Verify the stats contain at least the expected number of packets (account for ARP)
   120  	expRxPkts := 1 + preRxPackets + uint64(numPings)
   121  	expTxPkts := 1 + preTxPackets + uint64(numPings)
   122  	c.Assert(postTxPackets, checker.GreaterOrEqualThan, expTxPkts,
   123  		check.Commentf("Reported less TxPackets than expected. Expected >= %d. Found %d. %s", expTxPkts, postTxPackets, pingouts))
   124  	c.Assert(postRxPackets, checker.GreaterOrEqualThan, expRxPkts,
   125  		check.Commentf("Reported less Txbytes than expected. Expected >= %d. Found %d. %s", expRxPkts, postRxPackets, pingouts))
   126  }
   127  
   128  func (s *DockerSuite) TestApiStatsNetworkStatsVersioning(c *check.C) {
   129  	testRequires(c, SameHostDaemon)
   130  	testRequires(c, DaemonIsLinux)
   131  
   132  	out, _ := runSleepingContainer(c)
   133  	id := strings.TrimSpace(out)
   134  	c.Assert(waitRun(id), checker.IsNil)
   135  
   136  	for i := 17; i <= 21; i++ {
   137  		apiVersion := fmt.Sprintf("v1.%d", i)
   138  		statsJSONBlob := getVersionedStats(c, id, apiVersion)
   139  		if version.Version(apiVersion).LessThan("v1.21") {
   140  			c.Assert(jsonBlobHasLTv121NetworkStats(statsJSONBlob), checker.Equals, true,
   141  				check.Commentf("Stats JSON blob from API %s %#v does not look like a <v1.21 API stats structure", apiVersion, statsJSONBlob))
   142  		} else {
   143  			c.Assert(jsonBlobHasGTE121NetworkStats(statsJSONBlob), checker.Equals, true,
   144  				check.Commentf("Stats JSON blob from API %s %#v does not look like a >=v1.21 API stats structure", apiVersion, statsJSONBlob))
   145  		}
   146  	}
   147  }
   148  
   149  func getNetworkStats(c *check.C, id string) map[string]types.NetworkStats {
   150  	var st *types.StatsJSON
   151  
   152  	_, body, err := sockRequestRaw("GET", fmt.Sprintf("/containers/%s/stats?stream=false", id), nil, "")
   153  	c.Assert(err, checker.IsNil)
   154  
   155  	err = json.NewDecoder(body).Decode(&st)
   156  	c.Assert(err, checker.IsNil)
   157  	body.Close()
   158  
   159  	return st.Networks
   160  }
   161  
   162  // getVersionedStats returns stats result for the
   163  // container with id using an API call with version apiVersion. Since the
   164  // stats result type differs between API versions, we simply return
   165  // map[string]interface{}.
   166  func getVersionedStats(c *check.C, id string, apiVersion string) map[string]interface{} {
   167  	stats := make(map[string]interface{})
   168  
   169  	_, body, err := sockRequestRaw("GET", fmt.Sprintf("/%s/containers/%s/stats?stream=false", apiVersion, id), nil, "")
   170  	c.Assert(err, checker.IsNil)
   171  	defer body.Close()
   172  
   173  	err = json.NewDecoder(body).Decode(&stats)
   174  	c.Assert(err, checker.IsNil, check.Commentf("failed to decode stat: %s", err))
   175  
   176  	return stats
   177  }
   178  
   179  func jsonBlobHasLTv121NetworkStats(blob map[string]interface{}) bool {
   180  	networkStatsIntfc, ok := blob["network"]
   181  	if !ok {
   182  		return false
   183  	}
   184  	networkStats, ok := networkStatsIntfc.(map[string]interface{})
   185  	if !ok {
   186  		return false
   187  	}
   188  	for _, expectedKey := range expectedNetworkInterfaceStats {
   189  		if _, ok := networkStats[expectedKey]; !ok {
   190  			return false
   191  		}
   192  	}
   193  	return true
   194  }
   195  
   196  func jsonBlobHasGTE121NetworkStats(blob map[string]interface{}) bool {
   197  	networksStatsIntfc, ok := blob["networks"]
   198  	if !ok {
   199  		return false
   200  	}
   201  	networksStats, ok := networksStatsIntfc.(map[string]interface{})
   202  	if !ok {
   203  		return false
   204  	}
   205  	for _, networkInterfaceStatsIntfc := range networksStats {
   206  		networkInterfaceStats, ok := networkInterfaceStatsIntfc.(map[string]interface{})
   207  		if !ok {
   208  			return false
   209  		}
   210  		for _, expectedKey := range expectedNetworkInterfaceStats {
   211  			if _, ok := networkInterfaceStats[expectedKey]; !ok {
   212  				return false
   213  			}
   214  		}
   215  	}
   216  	return true
   217  }
   218  
   219  func (s *DockerSuite) TestApiStatsContainerNotFound(c *check.C) {
   220  	testRequires(c, DaemonIsLinux)
   221  
   222  	status, _, err := sockRequest("GET", "/containers/nonexistent/stats", nil)
   223  	c.Assert(err, checker.IsNil)
   224  	c.Assert(status, checker.Equals, http.StatusNotFound)
   225  
   226  	status, _, err = sockRequest("GET", "/containers/nonexistent/stats?stream=0", nil)
   227  	c.Assert(err, checker.IsNil)
   228  	c.Assert(status, checker.Equals, http.StatusNotFound)
   229  }
   230  
   231  func (s *DockerSuite) TestApiStatsContainerGetMemoryLimit(c *check.C) {
   232  	testRequires(c, DaemonIsLinux)
   233  
   234  	resp, body, err := sockRequestRaw("GET", "/info", nil, "application/json")
   235  	c.Assert(err, checker.IsNil)
   236  	c.Assert(resp.StatusCode, checker.Equals, http.StatusOK)
   237  	var info types.Info
   238  	err = json.NewDecoder(body).Decode(&info)
   239  	c.Assert(err, checker.IsNil)
   240  	body.Close()
   241  
   242  	// don't set a memory limit, the memory limit should be system memory
   243  	conName := "foo"
   244  	dockerCmd(c, "run", "-d", "--name", conName, "busybox", "top")
   245  	c.Assert(waitRun(conName), checker.IsNil)
   246  
   247  	resp, body, err = sockRequestRaw("GET", fmt.Sprintf("/containers/%s/stats?stream=false", conName), nil, "")
   248  	c.Assert(err, checker.IsNil)
   249  	c.Assert(resp.StatusCode, checker.Equals, http.StatusOK)
   250  	c.Assert(resp.Header.Get("Content-Type"), checker.Equals, "application/json")
   251  
   252  	var v *types.Stats
   253  	err = json.NewDecoder(body).Decode(&v)
   254  	c.Assert(err, checker.IsNil)
   255  	body.Close()
   256  	c.Assert(fmt.Sprintf("%d", v.MemoryStats.Limit), checker.Equals, fmt.Sprintf("%d", info.MemTotal))
   257  }