1
16
17 package stats
18
19 import (
20 "reflect"
21 "testing"
22 "time"
23
24 "github.com/Microsoft/hcsshim"
25 cadvisorapiv2 "github.com/google/cadvisor/info/v2"
26 "github.com/stretchr/testify/assert"
27 "k8s.io/apimachinery/pkg/api/resource"
28 v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29 "k8s.io/apimachinery/pkg/types"
30 runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
31 statsapi "k8s.io/kubelet/pkg/apis/stats/v1alpha1"
32 kubecontainertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
33 "k8s.io/kubernetes/pkg/kubelet/kuberuntime"
34 "k8s.io/kubernetes/pkg/volume"
35 testingclock "k8s.io/utils/clock/testing"
36 )
37
38 type fakeNetworkStatsProvider struct {
39 containers []containerStats
40 }
41
42 type containerStats struct {
43 container hcsshim.ContainerProperties
44 hcsStats []hcsshim.NetworkStats
45 }
46
47 func (s fakeNetworkStatsProvider) GetHNSEndpointStats(endpointName string) (*hcsshim.HNSEndpointStats, error) {
48 eps := hcsshim.HNSEndpointStats{}
49 for _, c := range s.containers {
50 for _, stat := range c.hcsStats {
51 if endpointName == stat.InstanceId {
52 eps = hcsshim.HNSEndpointStats{
53 EndpointID: stat.EndpointId,
54 BytesSent: stat.BytesSent,
55 BytesReceived: stat.BytesReceived,
56 PacketsReceived: stat.PacketsReceived,
57 PacketsSent: stat.PacketsSent,
58 }
59 }
60 }
61 }
62
63 return &eps, nil
64 }
65
66 func (s fakeNetworkStatsProvider) HNSListEndpointRequest() ([]hcsshim.HNSEndpoint, error) {
67 uniqueEndpoints := map[string]*hcsshim.HNSEndpoint{}
68
69 for _, c := range s.containers {
70 for _, stat := range c.hcsStats {
71 e, found := uniqueEndpoints[stat.EndpointId]
72 if found {
73
74 e.SharedContainers = append(e.SharedContainers, c.container.ID)
75 continue
76 }
77
78 uniqueEndpoints[stat.EndpointId] = &hcsshim.HNSEndpoint{
79 Name: stat.EndpointId,
80 Id: stat.EndpointId,
81 SharedContainers: []string{c.container.ID},
82 }
83 }
84 }
85
86 eps := []hcsshim.HNSEndpoint{}
87 for _, ep := range uniqueEndpoints {
88 eps = append(eps, *ep)
89 }
90
91 return eps, nil
92 }
93
94 func Test_criStatsProvider_listContainerNetworkStats(t *testing.T) {
95 fakeClock := testingclock.NewFakeClock(time.Time{})
96 tests := []struct {
97 name string
98 fields fakeNetworkStatsProvider
99 want map[string]*statsapi.NetworkStats
100 wantErr bool
101 skipped bool
102 }{
103 {
104 name: "basic example",
105 fields: fakeNetworkStatsProvider{
106 containers: []containerStats{
107 {
108 container: hcsshim.ContainerProperties{
109 ID: "c1",
110 }, hcsStats: []hcsshim.NetworkStats{
111 {
112 BytesReceived: 1,
113 BytesSent: 10,
114 EndpointId: "test",
115 InstanceId: "test",
116 },
117 },
118 },
119 {
120 container: hcsshim.ContainerProperties{
121 ID: "c2",
122 }, hcsStats: []hcsshim.NetworkStats{
123 {
124 BytesReceived: 2,
125 BytesSent: 20,
126 EndpointId: "test2",
127 InstanceId: "test2",
128 },
129 },
130 },
131 },
132 },
133 want: map[string]*statsapi.NetworkStats{
134 "c1": {
135 Time: v1.NewTime(fakeClock.Now()),
136 InterfaceStats: statsapi.InterfaceStats{
137 Name: "test",
138 RxBytes: toP(1),
139 TxBytes: toP(10),
140 },
141 Interfaces: []statsapi.InterfaceStats{
142 {
143 Name: "test",
144 RxBytes: toP(1),
145
146 TxBytes: toP(10),
147 },
148 },
149 },
150 "c2": {
151 Time: v1.Time{},
152 InterfaceStats: statsapi.InterfaceStats{
153 Name: "test2",
154 RxBytes: toP(2),
155 TxBytes: toP(20),
156 },
157 Interfaces: []statsapi.InterfaceStats{
158 {
159 Name: "test2",
160 RxBytes: toP(2),
161 TxBytes: toP(20),
162 },
163 },
164 },
165 },
166 wantErr: false,
167 },
168 {
169 name: "multiple containers same endpoint",
170 fields: fakeNetworkStatsProvider{
171 containers: []containerStats{
172 {
173 container: hcsshim.ContainerProperties{
174 ID: "c1",
175 }, hcsStats: []hcsshim.NetworkStats{
176 {
177 BytesReceived: 1,
178 BytesSent: 10,
179 EndpointId: "test",
180 InstanceId: "test",
181 },
182 },
183 },
184 {
185 container: hcsshim.ContainerProperties{
186 ID: "c2",
187 }, hcsStats: []hcsshim.NetworkStats{
188 {
189 BytesReceived: 2,
190 BytesSent: 20,
191 EndpointId: "test2",
192 InstanceId: "test2",
193 },
194 },
195 },
196 {
197 container: hcsshim.ContainerProperties{
198 ID: "c3",
199 }, hcsStats: []hcsshim.NetworkStats{
200 {
201 BytesReceived: 3,
202 BytesSent: 30,
203 EndpointId: "test2",
204 InstanceId: "test3",
205 },
206 },
207 },
208 },
209 },
210 want: map[string]*statsapi.NetworkStats{
211 "c1": {
212 Time: v1.NewTime(fakeClock.Now()),
213 InterfaceStats: statsapi.InterfaceStats{
214 Name: "test",
215 RxBytes: toP(1),
216 TxBytes: toP(10),
217 },
218 Interfaces: []statsapi.InterfaceStats{
219 {
220 Name: "test",
221 RxBytes: toP(1),
222
223 TxBytes: toP(10),
224 },
225 },
226 },
227 "c2": {
228 Time: v1.Time{},
229 InterfaceStats: statsapi.InterfaceStats{
230 Name: "test2",
231 RxBytes: toP(2),
232 TxBytes: toP(20),
233 },
234 Interfaces: []statsapi.InterfaceStats{
235 {
236 Name: "test2",
237 RxBytes: toP(2),
238 TxBytes: toP(20),
239 },
240 },
241 },
242 "c3": {
243 Time: v1.Time{},
244 InterfaceStats: statsapi.InterfaceStats{
245 Name: "test2",
246 RxBytes: toP(2),
247 TxBytes: toP(20),
248 },
249 Interfaces: []statsapi.InterfaceStats{
250 {
251 Name: "test2",
252 RxBytes: toP(2),
253 TxBytes: toP(20),
254 },
255 },
256 },
257 },
258 wantErr: false,
259 },
260 {
261 name: "multiple stats instances of same interface only picks up first",
262 fields: fakeNetworkStatsProvider{
263 containers: []containerStats{
264 {
265 container: hcsshim.ContainerProperties{
266 ID: "c1",
267 }, hcsStats: []hcsshim.NetworkStats{
268 {
269 BytesReceived: 1,
270 BytesSent: 10,
271 EndpointId: "test",
272 InstanceId: "test",
273 },
274 {
275 BytesReceived: 3,
276 BytesSent: 30,
277 EndpointId: "test",
278 InstanceId: "test3",
279 },
280 },
281 },
282 {
283 container: hcsshim.ContainerProperties{
284 ID: "c2",
285 }, hcsStats: []hcsshim.NetworkStats{
286 {
287 BytesReceived: 2,
288 BytesSent: 20,
289 EndpointId: "test2",
290 InstanceId: "test2",
291 },
292 },
293 },
294 },
295 },
296 want: map[string]*statsapi.NetworkStats{
297 "c1": {
298 Time: v1.NewTime(fakeClock.Now()),
299 InterfaceStats: statsapi.InterfaceStats{
300 Name: "test",
301 RxBytes: toP(1),
302 TxBytes: toP(10),
303 },
304 Interfaces: []statsapi.InterfaceStats{
305 {
306 Name: "test",
307 RxBytes: toP(1),
308
309 TxBytes: toP(10),
310 },
311 },
312 },
313 "c2": {
314 Time: v1.Time{},
315 InterfaceStats: statsapi.InterfaceStats{
316 Name: "test2",
317 RxBytes: toP(2),
318 TxBytes: toP(20),
319 },
320 Interfaces: []statsapi.InterfaceStats{
321 {
322 Name: "test2",
323 RxBytes: toP(2),
324 TxBytes: toP(20),
325 },
326 },
327 },
328 },
329 wantErr: false,
330 },
331 {
332 name: "multiple endpoints per container",
333 fields: fakeNetworkStatsProvider{
334 containers: []containerStats{
335 {
336 container: hcsshim.ContainerProperties{
337 ID: "c1",
338 }, hcsStats: []hcsshim.NetworkStats{
339 {
340 BytesReceived: 1,
341 BytesSent: 10,
342 EndpointId: "test",
343 InstanceId: "test",
344 },
345 {
346 BytesReceived: 3,
347 BytesSent: 30,
348 EndpointId: "test3",
349 InstanceId: "test3",
350 },
351 },
352 },
353 {
354 container: hcsshim.ContainerProperties{
355 ID: "c2",
356 }, hcsStats: []hcsshim.NetworkStats{
357 {
358 BytesReceived: 2,
359 BytesSent: 20,
360 EndpointId: "test2",
361 InstanceId: "test2",
362 },
363 },
364 },
365 },
366 },
367 want: map[string]*statsapi.NetworkStats{
368 "c1": {
369 Time: v1.NewTime(fakeClock.Now()),
370 InterfaceStats: statsapi.InterfaceStats{
371 Name: "test",
372 RxBytes: toP(1),
373 TxBytes: toP(10),
374 },
375 Interfaces: []statsapi.InterfaceStats{
376 {
377 Name: "test",
378 RxBytes: toP(1),
379
380 TxBytes: toP(10),
381 },
382 {
383 Name: "test3",
384 RxBytes: toP(3),
385
386 TxBytes: toP(30),
387 },
388 },
389 },
390 "c2": {
391 Time: v1.Time{},
392 InterfaceStats: statsapi.InterfaceStats{
393 Name: "test2",
394 RxBytes: toP(2),
395 TxBytes: toP(20),
396 },
397 Interfaces: []statsapi.InterfaceStats{
398 {
399 Name: "test2",
400 RxBytes: toP(2),
401 TxBytes: toP(20),
402 },
403 },
404 },
405 },
406 wantErr: false,
407 skipped: true,
408 },
409 }
410 for _, tt := range tests {
411 t.Run(tt.name, func(t *testing.T) {
412
413 if tt.skipped {
414 t.Skip("Test temporarily skipped.")
415 }
416 p := &criStatsProvider{
417 windowsNetworkStatsProvider: fakeNetworkStatsProvider{
418 containers: tt.fields.containers,
419 },
420 clock: fakeClock,
421 }
422 got, err := p.listContainerNetworkStats()
423 if (err != nil) != tt.wantErr {
424 t.Errorf("listContainerNetworkStats() error = %v, wantErr %v", err, tt.wantErr)
425 return
426 }
427 if !reflect.DeepEqual(got, tt.want) {
428 t.Errorf("listContainerNetworkStats() got = %v, want %v", got, tt.want)
429 }
430 })
431 }
432 }
433
434 func toP(i uint64) *uint64 {
435 return &i
436 }
437
438 func Test_criStatsProvider_makeWinContainerStats(t *testing.T) {
439 fakeClock := testingclock.NewFakeClock(time.Time{})
440 containerStartTime := fakeClock.Now()
441
442 cpuUsageTimestamp := int64(555555)
443 cpuUsageNanoSeconds := uint64(0x123456)
444 cpuUsageNanoCores := uint64(0x4000)
445 memoryUsageTimestamp := int64(666666)
446 memoryUsageWorkingSetBytes := uint64(0x11223344)
447 memoryUsageAvailableBytes := uint64(0x55667788)
448 memoryUsagePageFaults := uint64(200)
449 logStatsUsed := uint64(5000)
450 logStatsInodesUsed := uint64(5050)
451
452
453 c0LogStats := &volume.Metrics{
454 Used: resource.NewQuantity(int64(logStatsUsed), resource.BinarySI),
455 InodesUsed: resource.NewQuantity(int64(logStatsInodesUsed), resource.BinarySI),
456 }
457 fakeStats := map[string]*volume.Metrics{
458 kuberuntime.BuildContainerLogsDirectory(testPodLogDirectory, "sb0-ns", "sb0-name", types.UID("sb0-uid"), "c0"): c0LogStats,
459 }
460 fakeOS := &kubecontainertest.FakeOS{}
461 fakeHostStatsProvider := NewFakeHostStatsProviderWithData(fakeStats, fakeOS)
462
463 p := &criStatsProvider{
464 clock: fakeClock,
465 hostStatsProvider: fakeHostStatsProvider,
466 }
467
468 inputStats := &runtimeapi.WindowsContainerStats{
469 Attributes: &runtimeapi.ContainerAttributes{
470 Metadata: &runtimeapi.ContainerMetadata{
471 Name: "c0",
472 },
473 },
474 Cpu: &runtimeapi.WindowsCpuUsage{
475 Timestamp: cpuUsageTimestamp,
476 UsageCoreNanoSeconds: &runtimeapi.UInt64Value{
477 Value: cpuUsageNanoSeconds,
478 },
479 UsageNanoCores: &runtimeapi.UInt64Value{
480 Value: cpuUsageNanoCores,
481 },
482 },
483 Memory: &runtimeapi.WindowsMemoryUsage{
484 Timestamp: memoryUsageTimestamp,
485 AvailableBytes: &runtimeapi.UInt64Value{
486 Value: memoryUsageAvailableBytes,
487 },
488 WorkingSetBytes: &runtimeapi.UInt64Value{
489 Value: memoryUsageWorkingSetBytes,
490 },
491 PageFaults: &runtimeapi.UInt64Value{
492 Value: memoryUsagePageFaults,
493 },
494 },
495 }
496
497 inputContainer := &runtimeapi.Container{
498 CreatedAt: containerStartTime.Unix(),
499 Metadata: &runtimeapi.ContainerMetadata{
500 Name: "c0",
501 },
502 }
503
504 inputRootFsInfo := &cadvisorapiv2.FsInfo{}
505
506
507 inputPodSandboxMetadata := &runtimeapi.PodSandboxMetadata{
508 Namespace: "sb0-ns",
509 Name: "sb0-name",
510 Uid: "sb0-uid",
511 }
512
513 got, err := p.makeWinContainerStats(inputStats, inputContainer, inputRootFsInfo, make(map[runtimeapi.FilesystemIdentifier]*cadvisorapiv2.FsInfo), inputPodSandboxMetadata)
514
515 expected := &statsapi.ContainerStats{
516 Name: "c0",
517 StartTime: v1.NewTime(time.Unix(0, containerStartTime.Unix())),
518 CPU: &statsapi.CPUStats{
519 Time: v1.NewTime(time.Unix(0, cpuUsageTimestamp)),
520 UsageNanoCores: toP(cpuUsageNanoCores),
521 UsageCoreNanoSeconds: toP(cpuUsageNanoSeconds),
522 },
523 Memory: &statsapi.MemoryStats{
524 Time: v1.NewTime(time.Unix(0, memoryUsageTimestamp)),
525 AvailableBytes: toP(memoryUsageAvailableBytes),
526 WorkingSetBytes: toP(memoryUsageWorkingSetBytes),
527 PageFaults: toP(memoryUsagePageFaults),
528 },
529 Rootfs: &statsapi.FsStats{},
530 Logs: &statsapi.FsStats{
531 Time: c0LogStats.Time,
532 UsedBytes: toP(logStatsUsed),
533 InodesUsed: toP(logStatsInodesUsed),
534 },
535 }
536
537 if err != nil {
538 t.Fatalf("makeWinContainerStats() error = %v, expected no error", err)
539 }
540
541 if !reflect.DeepEqual(got.CPU, expected.CPU) {
542 t.Errorf("makeWinContainerStats() CPU = %v, expected %v", got.CPU, expected.CPU)
543 }
544
545 if !reflect.DeepEqual(got.Memory, expected.Memory) {
546 t.Errorf("makeWinContainerStats() Memory = %v, want %v", got.Memory, expected.Memory)
547 }
548
549 if !reflect.DeepEqual(got.Rootfs, expected.Rootfs) {
550 t.Errorf("makeWinContainerStats() Rootfs = %v, want %v", got.Rootfs, expected.Rootfs)
551 }
552
553
554 assert.Equal(t, *got.Logs.UsedBytes, logStatsUsed, "Logs.UsedBytes does not match expected value")
555 assert.Equal(t, *got.Logs.InodesUsed, logStatsInodesUsed, "Logs.InodesUsed does not match expected value")
556 }
557
View as plain text