testing for #17549
Signed-off-by: Donald Huang <don.hcd@gmail.com>
| ... | ... |
@@ -12,9 +12,12 @@ import ( |
| 12 | 12 |
|
| 13 | 13 |
"github.com/docker/docker/api/types" |
| 14 | 14 |
"github.com/docker/docker/pkg/integration/checker" |
| 15 |
+ "github.com/docker/docker/pkg/version" |
|
| 15 | 16 |
"github.com/go-check/check" |
| 16 | 17 |
) |
| 17 | 18 |
|
| 19 |
+var expectedNetworkInterfaceStats = strings.Split("rx_bytes rx_dropped rx_errors rx_packets tx_bytes tx_dropped tx_errors tx_packets", " ")
|
|
| 20 |
+ |
|
| 18 | 21 |
func (s *DockerSuite) TestApiStatsNoStreamGetCpu(c *check.C) {
|
| 19 | 22 |
testRequires(c, DaemonIsLinux) |
| 20 | 23 |
out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "while true;do echo 'Hello'; usleep 100000; done") |
| ... | ... |
@@ -122,6 +125,28 @@ func (s *DockerSuite) TestApiStatsNetworkStats(c *check.C) {
|
| 122 | 122 |
check.Commentf("Reported less Txbytes than expected. Expected >= %d. Found %d. %s", expRxPkts, postRxPackets, pingouts))
|
| 123 | 123 |
} |
| 124 | 124 |
|
| 125 |
+func (s *DockerSuite) TestApiStatsNetworkStatsVersioning(c *check.C) {
|
|
| 126 |
+ testRequires(c, SameHostDaemon) |
|
| 127 |
+ testRequires(c, DaemonIsLinux) |
|
| 128 |
+ // Run container for 30 secs |
|
| 129 |
+ out, _ := dockerCmd(c, "run", "-d", "busybox", "top") |
|
| 130 |
+ id := strings.TrimSpace(out) |
|
| 131 |
+ c.Assert(waitRun(id), checker.IsNil) |
|
| 132 |
+ |
|
| 133 |
+ for i := 17; i <= 21; i++ {
|
|
| 134 |
+ apiVersion := fmt.Sprintf("v1.%d", i)
|
|
| 135 |
+ for _, statsJSONBlob := range getVersionedStats(c, id, 3, apiVersion) {
|
|
| 136 |
+ if version.Version(apiVersion).LessThan("v1.21") {
|
|
| 137 |
+ c.Assert(jsonBlobHasLTv121NetworkStats(statsJSONBlob), checker.Equals, true, |
|
| 138 |
+ check.Commentf("Stats JSON blob from API %s %#v does not look like a <v1.21 API stats structure", apiVersion, statsJSONBlob))
|
|
| 139 |
+ } else {
|
|
| 140 |
+ c.Assert(jsonBlobHasGTE121NetworkStats(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 |
+ } |
|
| 143 |
+ } |
|
| 144 |
+ } |
|
| 145 |
+} |
|
| 146 |
+ |
|
| 125 | 147 |
func getNetworkStats(c *check.C, id string) map[string]types.NetworkStats {
|
| 126 | 148 |
var st *types.StatsJSON |
| 127 | 149 |
|
| ... | ... |
@@ -135,6 +160,67 @@ func getNetworkStats(c *check.C, id string) map[string]types.NetworkStats {
|
| 135 | 135 |
return st.Networks |
| 136 | 136 |
} |
| 137 | 137 |
|
| 138 |
+// getVersionedNetworkStats returns a slice of numStats stats results for the |
|
| 139 |
+// container with id id using an API call with version apiVersion. Since the |
|
| 140 |
+// stats result type differs between API versions, we simply return |
|
| 141 |
+// []map[string]interface{}.
|
|
| 142 |
+func getVersionedStats(c *check.C, id string, numStats int, apiVersion string) []map[string]interface{} {
|
|
| 143 |
+ stats := make([]map[string]interface{}, numStats)
|
|
| 144 |
+ |
|
| 145 |
+ requestPath := fmt.Sprintf("/%s/containers/%s/stats?stream=true", apiVersion, id)
|
|
| 146 |
+ _, body, err := sockRequestRaw("GET", requestPath, nil, "")
|
|
| 147 |
+ c.Assert(err, checker.IsNil) |
|
| 148 |
+ defer body.Close() |
|
| 149 |
+ |
|
| 150 |
+ statsDecoder := json.NewDecoder(body) |
|
| 151 |
+ for i := range stats {
|
|
| 152 |
+ err = statsDecoder.Decode(&stats[i]) |
|
| 153 |
+ c.Assert(err, checker.IsNil, check.Commentf("failed to decode %dth stat: %s", i, err))
|
|
| 154 |
+ } |
|
| 155 |
+ |
|
| 156 |
+ return stats |
|
| 157 |
+} |
|
| 158 |
+ |
|
| 159 |
+func jsonBlobHasLTv121NetworkStats(blob map[string]interface{}) bool {
|
|
| 160 |
+ networkStatsIntfc, ok := blob["network"] |
|
| 161 |
+ if !ok {
|
|
| 162 |
+ return false |
|
| 163 |
+ } |
|
| 164 |
+ networkStats, ok := networkStatsIntfc.(map[string]interface{})
|
|
| 165 |
+ if !ok {
|
|
| 166 |
+ return false |
|
| 167 |
+ } |
|
| 168 |
+ for _, expectedKey := range expectedNetworkInterfaceStats {
|
|
| 169 |
+ if _, ok := networkStats[expectedKey]; !ok {
|
|
| 170 |
+ return false |
|
| 171 |
+ } |
|
| 172 |
+ } |
|
| 173 |
+ return true |
|
| 174 |
+} |
|
| 175 |
+ |
|
| 176 |
+func jsonBlobHasGTE121NetworkStats(blob map[string]interface{}) bool {
|
|
| 177 |
+ networksStatsIntfc, ok := blob["networks"] |
|
| 178 |
+ if !ok {
|
|
| 179 |
+ return false |
|
| 180 |
+ } |
|
| 181 |
+ networksStats, ok := networksStatsIntfc.(map[string]interface{})
|
|
| 182 |
+ if !ok {
|
|
| 183 |
+ return false |
|
| 184 |
+ } |
|
| 185 |
+ for _, networkInterfaceStatsIntfc := range networksStats {
|
|
| 186 |
+ networkInterfaceStats, ok := networkInterfaceStatsIntfc.(map[string]interface{})
|
|
| 187 |
+ if !ok {
|
|
| 188 |
+ return false |
|
| 189 |
+ } |
|
| 190 |
+ for _, expectedKey := range expectedNetworkInterfaceStats {
|
|
| 191 |
+ if _, ok := networkInterfaceStats[expectedKey]; !ok {
|
|
| 192 |
+ return false |
|
| 193 |
+ } |
|
| 194 |
+ } |
|
| 195 |
+ } |
|
| 196 |
+ return true |
|
| 197 |
+} |
|
| 198 |
+ |
|
| 138 | 199 |
func (s *DockerSuite) TestApiStatsContainerNotFound(c *check.C) {
|
| 139 | 200 |
testRequires(c, DaemonIsLinux) |
| 140 | 201 |
|