Skip to content

Commit 991fa23

Browse files
authored
add fields definition for DockerStat API of Docker host uses cgroup2 (#922)
* chore: add fields definition for DockerStat API of Docker host uses cgroup2 Docker Engine 20.10.0+ uses cgroup2 instead of cgroup by default when available (cf. moby/moby#40846 ). Using cgroup2 varies the output of DockerStats API. In particular, there are many new fields on Stats.MemoryStats.Stats. Therefore this commit adds the fields for new fields on cgroup2 to `Stats` struct. * chore: removed extra commas
1 parent fbff592 commit 991fa23

File tree

2 files changed

+310
-1
lines changed

2 files changed

+310
-1
lines changed

container_stats.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,30 @@ type Stats struct {
5555
TotalPgpgin uint64 `json:"total_pgpgin,omitempty" yaml:"total_pgpgin,omitempty" toml:"total_pgpgin,omitempty"`
5656
HierarchicalMemswLimit uint64 `json:"hierarchical_memsw_limit,omitempty" yaml:"hierarchical_memsw_limit,omitempty" toml:"hierarchical_memsw_limit,omitempty"`
5757
Swap uint64 `json:"swap,omitempty" yaml:"swap,omitempty" toml:"swap,omitempty"`
58+
Anon uint64 `json:"anon,omitempty" yaml:"anon,omitempty" toml:"anon,omitempty"`
59+
AnonThp uint64 `json:"anon_thp,omitempty" yaml:"anon_thp,omitempty" toml:"anon_thp,omitempty"`
60+
File uint64 `json:"file,omitempty" yaml:"file,omitempty" toml:"file,omitempty"`
61+
FileDirty uint64 `json:"file_dirty,omitempty" yaml:"file_dirty,omitempty" toml:"file_dirty,omitempty"`
62+
FileMapped uint64 `json:"file_mapped,omitempty" yaml:"file_mapped,omitempty" toml:"file_mapped,omitempty"`
63+
FileWriteback uint64 `json:"file_writeback,omitempty" yaml:"file_writeback,omitempty" toml:"file_writeback,omitempty"`
64+
KernelStack uint64 `json:"kernel_stack,omitempty" yaml:"kernel_stack,omitempty" toml:"kernel_stack,omitempty"`
65+
Pgactivate uint64 `json:"pgactivate,omitempty" yaml:"pgactivate,omitempty" toml:"pgactivate,omitempty"`
66+
Pgdeactivate uint64 `json:"pgdeactivate,omitempty" yaml:"pgdeactivate,omitempty" toml:"pgdeactivate,omitempty"`
67+
Pglazyfree uint64 `json:"pglazyfree,omitempty" yaml:"pglazyfree,omitempty" toml:"pglazyfree,omitempty"`
68+
Pglazyfreed uint64 `json:"pglazyfreed,omitempty" yaml:"pglazyfreed,omitempty" toml:"pglazyfreed,omitempty"`
69+
Pgrefill uint64 `json:"pgrefill,omitempty" yaml:"pgrefill,omitempty" toml:"pgrefill,omitempty"`
70+
Pgscan uint64 `json:"pgscan,omitempty" yaml:"pgscan,omitempty" toml:"pgscan,omitempty"`
71+
Pgsteal uint64 `json:"pgsteal,omitempty" yaml:"pgsteal,omitempty" toml:"pgsteal,omitempty"`
72+
Shmem uint64 `json:"shmem,omitempty" yaml:"shmem,omitempty" toml:"shmem,omitempty"`
73+
Slab uint64 `json:"slab,omitempty" yaml:"slab,omitempty" toml:"slab,omitempty"`
74+
SlabReclaimable uint64 `json:"slab_reclaimable,omitempty" yaml:"slab_reclaimable,omitempty" toml:"slab_reclaimable,omitempty"`
75+
SlabUnreclaimable uint64 `json:"slab_unreclaimable,omitempty" yaml:"slab_unreclaimable,omitempty" toml:"slab_unreclaimable,omitempty"`
76+
Sock uint64 `json:"sock,omitempty" yaml:"sock,omitempty" toml:"sock,omitempty"`
77+
ThpCollapseAlloc uint64 `json:"thp_collapse_alloc,omitempty" yaml:"thp_collapse_alloc,omitempty" toml:"thp_collapse_alloc,omitempty"`
78+
ThpFaultAlloc uint64 `json:"thp_fault_alloc,omitempty" yaml:"thp_fault_alloc,omitempty" toml:"thp_fault_alloc,omitempty"`
79+
WorkingsetActivate uint64 `json:"workingset_activate,omitempty" yaml:"workingset_activate,omitempty" toml:"workingset_activate,omitempty"`
80+
WorkingsetNodereclaim uint64 `json:"workingset_nodereclaim,omitempty" yaml:"workingset_nodereclaim,omitempty" toml:"workingset_nodereclaim,omitempty"`
81+
WorkingsetRefault uint64 `json:"workingset_refault,omitempty" yaml:"workingset_refault,omitempty" toml:"workingset_refault,omitempty"`
5882
} `json:"stats,omitempty" yaml:"stats,omitempty" toml:"stats,omitempty"`
5983
MaxUsage uint64 `json:"max_usage,omitempty" yaml:"max_usage,omitempty" toml:"max_usage,omitempty"`
6084
Usage uint64 `json:"usage,omitempty" yaml:"usage,omitempty" toml:"usage,omitempty"`

container_stats_test.go

Lines changed: 286 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ import (
99
"testing"
1010
)
1111

12-
func TestStats(t *testing.T) {
12+
// The test for Docker Stat API of the host uses cgroup
13+
func TestStatsV1(t *testing.T) {
1314
t.Parallel()
1415
jsonStats1 := `{
1516
"read" : "2015-01-08T22:57:31.547920715Z",
@@ -321,6 +322,290 @@ func TestStats(t *testing.T) {
321322
}
322323
}
323324

325+
// The test for Docker Stat API of the host uses cgroup2
326+
func TestStatsV2(t *testing.T) {
327+
t.Parallel()
328+
jsonStats1 := `{
329+
"read": "2022-05-18T14:37:18.175989615Z",
330+
"preread": "2022-05-18T14:37:17.171501052Z",
331+
"pids_stats": {
332+
"current": 29,
333+
"limit": 76948
334+
},
335+
"blkio_stats": {
336+
"io_service_bytes_recursive": [
337+
{
338+
"major": 259,
339+
"minor": 0,
340+
"op": "read",
341+
"value": 11390976
342+
},
343+
{
344+
"major": 259,
345+
"minor": 0,
346+
"op": "write",
347+
"value": 0
348+
}
349+
],
350+
"io_serviced_recursive": null,
351+
"io_queue_recursive": null,
352+
"io_service_time_recursive": null,
353+
"io_wait_time_recursive": null,
354+
"io_merged_recursive": null,
355+
"io_time_recursive": null,
356+
"sectors_recursive": null
357+
},
358+
"num_procs": 0,
359+
"storage_stats": {},
360+
"cpu_stats": {
361+
"cpu_usage": {
362+
"total_usage": 185266562000,
363+
"usage_in_kernelmode": 37912635000,
364+
"usage_in_usermode": 147353926000
365+
},
366+
"system_cpu_usage": 26707255190000000,
367+
"online_cpus": 24,
368+
"throttling_data": {
369+
"periods": 0,
370+
"throttled_periods": 0,
371+
"throttled_time": 0
372+
}
373+
},
374+
"precpu_stats": {
375+
"cpu_usage": {
376+
"total_usage": 185266562000,
377+
"usage_in_kernelmode": 37912635000,
378+
"usage_in_usermode": 147353926000
379+
},
380+
"system_cpu_usage": 26707231080000000,
381+
"online_cpus": 24,
382+
"throttling_data": {
383+
"periods": 0,
384+
"throttled_periods": 0,
385+
"throttled_time": 0
386+
}
387+
},
388+
"memory_stats": {
389+
"usage": 28557312,
390+
"stats": {
391+
"active_anon": 4096,
392+
"active_file": 7446528,
393+
"anon": 16572416,
394+
"anon_thp": 0,
395+
"file": 10829824,
396+
"file_dirty": 0,
397+
"file_mapped": 9740288,
398+
"file_writeback": 0,
399+
"inactive_anon": 8069120,
400+
"inactive_file": 11882496,
401+
"kernel_stack": 475136,
402+
"pgactivate": 241,
403+
"pgdeactivate": 253,
404+
"pgfault": 7714,
405+
"pglazyfree": 3042,
406+
"pglazyfreed": 967,
407+
"pgmajfault": 155,
408+
"pgrefill": 301,
409+
"pgscan": 1802,
410+
"pgsteal": 1100,
411+
"shmem": 0,
412+
"slab": 488920,
413+
"slab_reclaimable": 159664,
414+
"slab_unreclaimable": 329256,
415+
"sock": 0,
416+
"thp_collapse_alloc": 0,
417+
"thp_fault_alloc": 0,
418+
"unevictable": 0,
419+
"workingset_activate": 0,
420+
"workingset_nodereclaim": 0,
421+
"workingset_refault": 0
422+
},
423+
"limit": 67353382912
424+
},
425+
"networks": {
426+
"eth0": {
427+
"rx_bytes": 96802652,
428+
"rx_packets": 623704,
429+
"rx_errors": 0,
430+
"rx_dropped": 0,
431+
"tx_bytes": 16597749,
432+
"tx_packets": 91982,
433+
"tx_errors": 0,
434+
"tx_dropped": 0
435+
}
436+
}
437+
}`
438+
// 1 second later, shmem is 100
439+
jsonStats2 := `{
440+
"read": "2022-05-18T14:37:18.175989615Z",
441+
"preread": "2022-05-18T14:37:17.171501052Z",
442+
"pids_stats": {
443+
"current": 29,
444+
"limit": 76948
445+
},
446+
"blkio_stats": {
447+
"io_service_bytes_recursive": [
448+
{
449+
"major": 259,
450+
"minor": 0,
451+
"op": "read",
452+
"value": 11390976
453+
},
454+
{
455+
"major": 259,
456+
"minor": 0,
457+
"op": "write",
458+
"value": 0
459+
}
460+
],
461+
"io_serviced_recursive": null,
462+
"io_queue_recursive": null,
463+
"io_service_time_recursive": null,
464+
"io_wait_time_recursive": null,
465+
"io_merged_recursive": null,
466+
"io_time_recursive": null,
467+
"sectors_recursive": null
468+
},
469+
"num_procs": 0,
470+
"storage_stats": {},
471+
"cpu_stats": {
472+
"cpu_usage": {
473+
"total_usage": 185266562000,
474+
"usage_in_kernelmode": 37912635000,
475+
"usage_in_usermode": 147353926000
476+
},
477+
"system_cpu_usage": 26707255190000000,
478+
"online_cpus": 24,
479+
"throttling_data": {
480+
"periods": 0,
481+
"throttled_periods": 0,
482+
"throttled_time": 0
483+
}
484+
},
485+
"precpu_stats": {
486+
"cpu_usage": {
487+
"total_usage": 185266562000,
488+
"usage_in_kernelmode": 37912635000,
489+
"usage_in_usermode": 147353926000
490+
},
491+
"system_cpu_usage": 26707231080000000,
492+
"online_cpus": 24,
493+
"throttling_data": {
494+
"periods": 0,
495+
"throttled_periods": 0,
496+
"throttled_time": 0
497+
}
498+
},
499+
"memory_stats": {
500+
"usage": 28557312,
501+
"stats": {
502+
"active_anon": 4096,
503+
"active_file": 7446528,
504+
"anon": 16572416,
505+
"anon_thp": 0,
506+
"file": 10829824,
507+
"file_dirty": 0,
508+
"file_mapped": 9740288,
509+
"file_writeback": 0,
510+
"inactive_anon": 8069120,
511+
"inactive_file": 11882496,
512+
"kernel_stack": 475136,
513+
"pgactivate": 241,
514+
"pgdeactivate": 253,
515+
"pgfault": 7714,
516+
"pglazyfree": 3042,
517+
"pglazyfreed": 967,
518+
"pgmajfault": 155,
519+
"pgrefill": 301,
520+
"pgscan": 1802,
521+
"pgsteal": 1100,
522+
"shmem": 100,
523+
"slab": 488920,
524+
"slab_reclaimable": 159664,
525+
"slab_unreclaimable": 329256,
526+
"sock": 0,
527+
"thp_collapse_alloc": 0,
528+
"thp_fault_alloc": 0,
529+
"unevictable": 0,
530+
"workingset_activate": 0,
531+
"workingset_nodereclaim": 0,
532+
"workingset_refault": 0
533+
},
534+
"limit": 67353382912
535+
},
536+
"networks": {
537+
"eth0": {
538+
"rx_bytes": 96802652,
539+
"rx_packets": 623704,
540+
"rx_errors": 0,
541+
"rx_dropped": 0,
542+
"tx_bytes": 16597749,
543+
"tx_packets": 91982,
544+
"tx_errors": 0,
545+
"tx_dropped": 0
546+
}
547+
}
548+
}`
549+
var expected1 Stats
550+
var expected2 Stats
551+
err := json.Unmarshal([]byte(jsonStats1), &expected1)
552+
if err != nil {
553+
t.Fatal(err)
554+
}
555+
err = json.Unmarshal([]byte(jsonStats2), &expected2)
556+
if err != nil {
557+
t.Fatal(err)
558+
}
559+
id := "4fa6e0f0"
560+
561+
var req http.Request
562+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
563+
w.Header().Set("Content-Type", "application/json")
564+
w.Write([]byte(jsonStats1))
565+
w.Write([]byte(jsonStats2))
566+
req = *r
567+
}))
568+
defer server.Close()
569+
client, _ := NewClient(server.URL)
570+
client.SkipServerVersionCheck = true
571+
errC := make(chan error, 1)
572+
statsC := make(chan *Stats)
573+
done := make(chan bool)
574+
defer close(done)
575+
go func() {
576+
errC <- client.Stats(StatsOptions{ID: id, Stats: statsC, Stream: true, Done: done})
577+
close(errC)
578+
}()
579+
var resultStats []*Stats
580+
for {
581+
stats, ok := <-statsC
582+
if !ok {
583+
break
584+
}
585+
resultStats = append(resultStats, stats)
586+
}
587+
err = <-errC
588+
if err != nil {
589+
t.Fatal(err)
590+
}
591+
if len(resultStats) != 2 {
592+
t.Fatalf("Stats: Expected 2 results. Got %d.", len(resultStats))
593+
}
594+
if !reflect.DeepEqual(resultStats[0], &expected1) {
595+
t.Errorf("Stats: Expected:\n%+v\nGot:\n%+v", expected1, resultStats[0])
596+
}
597+
if !reflect.DeepEqual(resultStats[1], &expected2) {
598+
t.Errorf("Stats: Expected:\n%+v\nGot:\n%+v", expected2, resultStats[1])
599+
}
600+
if req.Method != http.MethodGet {
601+
t.Errorf("Stats: wrong HTTP method. Want GET. Got %s.", req.Method)
602+
}
603+
u, _ := url.Parse(client.getURL("/containers/" + id + "/stats"))
604+
if req.URL.Path != u.Path {
605+
t.Errorf("Stats: wrong HTTP path. Want %q. Got %q.", u.Path, req.URL.Path)
606+
}
607+
}
608+
324609
func TestStatsContainerNotFound(t *testing.T) {
325610
t.Parallel()
326611
client := newTestClient(&FakeRoundTripper{message: "no such container", status: http.StatusNotFound})

0 commit comments

Comments
 (0)