@@ -12,9 +12,12 @@ import (
1212
1313 "github.com/docker/docker/api/types"
1414 "github.com/docker/docker/pkg/integration/checker"
15+ "github.com/docker/docker/pkg/version"
1516 "github.com/go-check/check"
1617)
1718
19+ var expectedNetworkInterfaceStats = strings .Split ("rx_bytes rx_dropped rx_errors rx_packets tx_bytes tx_dropped tx_errors tx_packets" , " " )
20+
1821func (s * DockerSuite ) TestApiStatsNoStreamGetCpu (c * check.C ) {
1922 testRequires (c , DaemonIsLinux )
2023 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) {
122125 check .Commentf ("Reported less Txbytes than expected. Expected >= %d. Found %d. %s" , expRxPkts , postRxPackets , pingouts ))
123126}
124127
128+ func (s * DockerSuite ) TestApiStatsNetworkStatsVersioning (c * check.C ) {
129+ testRequires (c , SameHostDaemon )
130+ testRequires (c , DaemonIsLinux )
131+ // Run container for 30 secs
132+ out , _ := dockerCmd (c , "run" , "-d" , "busybox" , "top" )
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+ for _ , statsJSONBlob := range getVersionedStats (c , id , 3 , 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+
125150func getNetworkStats (c * check.C , id string ) map [string ]types.NetworkStats {
126151 var st * types.StatsJSON
127152
@@ -135,6 +160,67 @@ func getNetworkStats(c *check.C, id string) map[string]types.NetworkStats {
135160 return st .Networks
136161}
137162
163+ // getVersionedNetworkStats returns a slice of numStats stats results for the
164+ // container with id id using an API call with version apiVersion. Since the
165+ // stats result type differs between API versions, we simply return
166+ // []map[string]interface{}.
167+ func getVersionedStats (c * check.C , id string , numStats int , apiVersion string ) []map [string ]interface {} {
168+ stats := make ([]map [string ]interface {}, numStats )
169+
170+ requestPath := fmt .Sprintf ("/%s/containers/%s/stats?stream=true" , apiVersion , id )
171+ _ , body , err := sockRequestRaw ("GET" , requestPath , nil , "" )
172+ c .Assert (err , checker .IsNil )
173+ defer body .Close ()
174+
175+ statsDecoder := json .NewDecoder (body )
176+ for i := range stats {
177+ err = statsDecoder .Decode (& stats [i ])
178+ c .Assert (err , checker .IsNil , check .Commentf ("failed to decode %dth stat: %s" , i , err ))
179+ }
180+
181+ return stats
182+ }
183+
184+ func jsonBlobHasLTv121NetworkStats (blob map [string ]interface {}) bool {
185+ networkStatsIntfc , ok := blob ["network" ]
186+ if ! ok {
187+ return false
188+ }
189+ networkStats , ok := networkStatsIntfc .(map [string ]interface {})
190+ if ! ok {
191+ return false
192+ }
193+ for _ , expectedKey := range expectedNetworkInterfaceStats {
194+ if _ , ok := networkStats [expectedKey ]; ! ok {
195+ return false
196+ }
197+ }
198+ return true
199+ }
200+
201+ func jsonBlobHasGTE121NetworkStats (blob map [string ]interface {}) bool {
202+ networksStatsIntfc , ok := blob ["networks" ]
203+ if ! ok {
204+ return false
205+ }
206+ networksStats , ok := networksStatsIntfc .(map [string ]interface {})
207+ if ! ok {
208+ return false
209+ }
210+ for _ , networkInterfaceStatsIntfc := range networksStats {
211+ networkInterfaceStats , ok := networkInterfaceStatsIntfc .(map [string ]interface {})
212+ if ! ok {
213+ return false
214+ }
215+ for _ , expectedKey := range expectedNetworkInterfaceStats {
216+ if _ , ok := networkInterfaceStats [expectedKey ]; ! ok {
217+ return false
218+ }
219+ }
220+ }
221+ return true
222+ }
223+
138224func (s * DockerSuite ) TestApiStatsContainerNotFound (c * check.C ) {
139225 testRequires (c , DaemonIsLinux )
140226
0 commit comments