1
16
17 package collectors
18
19 import (
20 "context"
21 "fmt"
22 "strings"
23 "testing"
24 "time"
25
26 "github.com/golang/mock/gomock"
27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28 "k8s.io/component-base/metrics/testutil"
29 statsapi "k8s.io/kubelet/pkg/apis/stats/v1alpha1"
30 summaryprovidertest "k8s.io/kubernetes/pkg/kubelet/server/stats/testing"
31 )
32
33 func TestCollectResourceMetrics(t *testing.T) {
34
35 staticTimestamp := time.Unix(0, 1624396278302091597)
36 testTime := metav1.NewTime(staticTimestamp)
37 interestedMetrics := []string{
38 "scrape_error",
39 "resource_scrape_error",
40 "node_cpu_usage_seconds_total",
41 "node_memory_working_set_bytes",
42 "node_swap_usage_bytes",
43 "container_cpu_usage_seconds_total",
44 "container_memory_working_set_bytes",
45 "container_swap_usage_bytes",
46 "container_start_time_seconds",
47 "pod_cpu_usage_seconds_total",
48 "pod_memory_working_set_bytes",
49 "pod_swap_usage_bytes",
50 }
51 mockCtrl := gomock.NewController(t)
52 defer mockCtrl.Finish()
53
54 tests := []struct {
55 name string
56 summary *statsapi.Summary
57 summaryErr error
58 expectedMetrics string
59 }{
60 {
61 name: "error getting summary",
62 summary: nil,
63 summaryErr: fmt.Errorf("failed to get summary"),
64 expectedMetrics: `
65 # HELP scrape_error [ALPHA] 1 if there was an error while getting container metrics, 0 otherwise
66 # TYPE scrape_error gauge
67 scrape_error 1
68 # HELP resource_scrape_error [STABLE] 1 if there was an error while getting container metrics, 0 otherwise
69 # TYPE resource_scrape_error gauge
70 resource_scrape_error 1
71 `,
72 },
73 {
74 name: "arbitrary node metrics",
75 summary: &statsapi.Summary{
76 Node: statsapi.NodeStats{
77 CPU: &statsapi.CPUStats{
78 Time: testTime,
79 UsageCoreNanoSeconds: uint64Ptr(10000000000),
80 },
81 Memory: &statsapi.MemoryStats{
82 Time: testTime,
83 WorkingSetBytes: uint64Ptr(1000),
84 },
85 Swap: &statsapi.SwapStats{
86 Time: testTime,
87 SwapUsageBytes: uint64Ptr(500),
88 },
89 },
90 },
91 summaryErr: nil,
92 expectedMetrics: `
93 # HELP node_cpu_usage_seconds_total [STABLE] Cumulative cpu time consumed by the node in core-seconds
94 # TYPE node_cpu_usage_seconds_total counter
95 node_cpu_usage_seconds_total 10 1624396278302
96 # HELP node_memory_working_set_bytes [STABLE] Current working set of the node in bytes
97 # TYPE node_memory_working_set_bytes gauge
98 node_memory_working_set_bytes 1000 1624396278302
99 # HELP node_swap_usage_bytes [ALPHA] Current swap usage of the node in bytes. Reported only on non-windows systems
100 # TYPE node_swap_usage_bytes gauge
101 node_swap_usage_bytes 500 1624396278302
102 # HELP scrape_error [ALPHA] 1 if there was an error while getting container metrics, 0 otherwise
103 # TYPE scrape_error gauge
104 scrape_error 0
105 # HELP resource_scrape_error [STABLE] 1 if there was an error while getting container metrics, 0 otherwise
106 # TYPE resource_scrape_error gauge
107 resource_scrape_error 0
108 `,
109 },
110 {
111 name: "nil node metrics",
112 summary: &statsapi.Summary{
113 Node: statsapi.NodeStats{
114 CPU: &statsapi.CPUStats{
115 Time: testTime,
116 UsageCoreNanoSeconds: nil,
117 },
118 Memory: &statsapi.MemoryStats{
119 Time: testTime,
120 WorkingSetBytes: nil,
121 },
122 },
123 },
124 summaryErr: nil,
125 expectedMetrics: `
126 # HELP scrape_error [ALPHA] 1 if there was an error while getting container metrics, 0 otherwise
127 # TYPE scrape_error gauge
128 scrape_error 0
129 # HELP resource_scrape_error [STABLE] 1 if there was an error while getting container metrics, 0 otherwise
130 # TYPE resource_scrape_error gauge
131 resource_scrape_error 0
132 `,
133 },
134 {
135 name: "arbitrary container metrics for different container, pods and namespaces",
136 summary: &statsapi.Summary{
137 Pods: []statsapi.PodStats{
138 {
139 PodRef: statsapi.PodReference{
140 Name: "pod_a",
141 Namespace: "namespace_a",
142 },
143 Containers: []statsapi.ContainerStats{
144 {
145 Name: "container_a",
146 StartTime: metav1.NewTime(staticTimestamp.Add(-30 * time.Second)),
147 CPU: &statsapi.CPUStats{
148 Time: testTime,
149 UsageCoreNanoSeconds: uint64Ptr(10000000000),
150 },
151 Memory: &statsapi.MemoryStats{
152 Time: testTime,
153 WorkingSetBytes: uint64Ptr(1000),
154 },
155 Swap: &statsapi.SwapStats{
156 Time: testTime,
157 SwapUsageBytes: uint64Ptr(1000),
158 },
159 },
160 {
161 Name: "container_b",
162 StartTime: metav1.NewTime(staticTimestamp.Add(-2 * time.Minute)),
163 CPU: &statsapi.CPUStats{
164 Time: testTime,
165 UsageCoreNanoSeconds: uint64Ptr(10000000000),
166 },
167 Memory: &statsapi.MemoryStats{
168 Time: testTime,
169 WorkingSetBytes: uint64Ptr(1000),
170 },
171 },
172 },
173 },
174 {
175 PodRef: statsapi.PodReference{
176 Name: "pod_b",
177 Namespace: "namespace_b",
178 },
179 Containers: []statsapi.ContainerStats{
180 {
181 Name: "container_a",
182 StartTime: metav1.NewTime(staticTimestamp.Add(-10 * time.Minute)),
183 CPU: &statsapi.CPUStats{
184 Time: testTime,
185 UsageCoreNanoSeconds: uint64Ptr(10000000000),
186 },
187 Memory: &statsapi.MemoryStats{
188 Time: testTime,
189 WorkingSetBytes: uint64Ptr(1000),
190 },
191 },
192 },
193 },
194 },
195 },
196 summaryErr: nil,
197 expectedMetrics: `
198 # HELP scrape_error [ALPHA] 1 if there was an error while getting container metrics, 0 otherwise
199 # TYPE scrape_error gauge
200 scrape_error 0
201 # HELP resource_scrape_error [STABLE] 1 if there was an error while getting container metrics, 0 otherwise
202 # TYPE resource_scrape_error gauge
203 resource_scrape_error 0
204 # HELP container_cpu_usage_seconds_total [STABLE] Cumulative cpu time consumed by the container in core-seconds
205 # TYPE container_cpu_usage_seconds_total counter
206 container_cpu_usage_seconds_total{container="container_a",namespace="namespace_a",pod="pod_a"} 10 1624396278302
207 container_cpu_usage_seconds_total{container="container_a",namespace="namespace_b",pod="pod_b"} 10 1624396278302
208 container_cpu_usage_seconds_total{container="container_b",namespace="namespace_a",pod="pod_a"} 10 1624396278302
209 # HELP container_memory_working_set_bytes [STABLE] Current working set of the container in bytes
210 # TYPE container_memory_working_set_bytes gauge
211 container_memory_working_set_bytes{container="container_a",namespace="namespace_a",pod="pod_a"} 1000 1624396278302
212 container_memory_working_set_bytes{container="container_a",namespace="namespace_b",pod="pod_b"} 1000 1624396278302
213 container_memory_working_set_bytes{container="container_b",namespace="namespace_a",pod="pod_a"} 1000 1624396278302
214 # HELP container_start_time_seconds [STABLE] Start time of the container since unix epoch in seconds
215 # TYPE container_start_time_seconds gauge
216 container_start_time_seconds{container="container_a",namespace="namespace_a",pod="pod_a"} 1.6243962483020916e+09
217 container_start_time_seconds{container="container_a",namespace="namespace_b",pod="pod_b"} 1.6243956783020916e+09
218 container_start_time_seconds{container="container_b",namespace="namespace_a",pod="pod_a"} 1.6243961583020916e+09
219 # HELP container_swap_usage_bytes [ALPHA] Current amount of the container swap usage in bytes. Reported only on non-windows systems
220 # TYPE container_swap_usage_bytes gauge
221 container_swap_usage_bytes{container="container_a",namespace="namespace_a",pod="pod_a"} 1000 1624396278302
222 `,
223 },
224 {
225 name: "arbitrary container metrics for negative StartTime",
226 summary: &statsapi.Summary{
227 Pods: []statsapi.PodStats{
228 {
229 PodRef: statsapi.PodReference{
230 Name: "pod_a",
231 Namespace: "namespace_a",
232 },
233 Containers: []statsapi.ContainerStats{
234 {
235 Name: "container_a",
236 StartTime: metav1.NewTime(time.Unix(0, -1624396278302091597)),
237 CPU: &statsapi.CPUStats{
238 Time: testTime,
239 UsageCoreNanoSeconds: uint64Ptr(10000000000),
240 },
241 Memory: &statsapi.MemoryStats{
242 Time: testTime,
243 WorkingSetBytes: uint64Ptr(1000),
244 },
245 },
246 },
247 },
248 },
249 },
250 summaryErr: nil,
251 expectedMetrics: `
252 # HELP scrape_error [ALPHA] 1 if there was an error while getting container metrics, 0 otherwise
253 # TYPE scrape_error gauge
254 scrape_error 0
255 # HELP resource_scrape_error [STABLE] 1 if there was an error while getting container metrics, 0 otherwise
256 # TYPE resource_scrape_error gauge
257 resource_scrape_error 0
258 # HELP container_cpu_usage_seconds_total [STABLE] Cumulative cpu time consumed by the container in core-seconds
259 # TYPE container_cpu_usage_seconds_total counter
260 container_cpu_usage_seconds_total{container="container_a",namespace="namespace_a",pod="pod_a"} 10 1624396278302
261 # HELP container_memory_working_set_bytes [STABLE] Current working set of the container in bytes
262 # TYPE container_memory_working_set_bytes gauge
263 container_memory_working_set_bytes{container="container_a",namespace="namespace_a",pod="pod_a"} 1000 1624396278302
264 `,
265 },
266 {
267 name: "nil container metrics",
268 summary: &statsapi.Summary{
269 Pods: []statsapi.PodStats{
270 {
271 PodRef: statsapi.PodReference{
272 Name: "pod_a",
273 Namespace: "namespace_a",
274 },
275 Containers: []statsapi.ContainerStats{
276 {
277 Name: "container_a",
278 StartTime: metav1.NewTime(staticTimestamp.Add(-30 * time.Second)),
279 CPU: &statsapi.CPUStats{
280 Time: testTime,
281 UsageCoreNanoSeconds: nil,
282 },
283 Memory: &statsapi.MemoryStats{
284 Time: testTime,
285 WorkingSetBytes: nil,
286 },
287 },
288 },
289 },
290 {
291 PodRef: statsapi.PodReference{
292 Name: "pod_b",
293 Namespace: "namespace_b",
294 },
295 Containers: []statsapi.ContainerStats{
296 {
297 Name: "container_a",
298 StartTime: metav1.NewTime(staticTimestamp.Add(-10 * time.Minute)),
299 CPU: &statsapi.CPUStats{
300 Time: testTime,
301 UsageCoreNanoSeconds: uint64Ptr(10000000000),
302 },
303 Memory: &statsapi.MemoryStats{
304 Time: testTime,
305 WorkingSetBytes: uint64Ptr(1000),
306 },
307 },
308 },
309 },
310 },
311 },
312 summaryErr: nil,
313 expectedMetrics: `
314 # HELP container_cpu_usage_seconds_total [STABLE] Cumulative cpu time consumed by the container in core-seconds
315 # TYPE container_cpu_usage_seconds_total counter
316 container_cpu_usage_seconds_total{container="container_a",namespace="namespace_b",pod="pod_b"} 10 1624396278302
317 # HELP container_memory_working_set_bytes [STABLE] Current working set of the container in bytes
318 # TYPE container_memory_working_set_bytes gauge
319 container_memory_working_set_bytes{container="container_a",namespace="namespace_b",pod="pod_b"} 1000 1624396278302
320 # HELP container_start_time_seconds [STABLE] Start time of the container since unix epoch in seconds
321 # TYPE container_start_time_seconds gauge
322 container_start_time_seconds{container="container_a",namespace="namespace_a",pod="pod_a"} 1.6243962483020916e+09
323 container_start_time_seconds{container="container_a",namespace="namespace_b",pod="pod_b"} 1.6243956783020916e+09
324 # HELP scrape_error [ALPHA] 1 if there was an error while getting container metrics, 0 otherwise
325 # TYPE scrape_error gauge
326 scrape_error 0
327 # HELP resource_scrape_error [STABLE] 1 if there was an error while getting container metrics, 0 otherwise
328 # TYPE resource_scrape_error gauge
329 resource_scrape_error 0
330 `,
331 },
332 {
333 name: "arbitrary pod metrics",
334 summary: &statsapi.Summary{
335 Pods: []statsapi.PodStats{
336 {
337 PodRef: statsapi.PodReference{
338 Name: "pod_a",
339 Namespace: "namespace_a",
340 },
341 CPU: &statsapi.CPUStats{
342 Time: testTime,
343 UsageCoreNanoSeconds: uint64Ptr(10000000000),
344 },
345 Memory: &statsapi.MemoryStats{
346 Time: testTime,
347 WorkingSetBytes: uint64Ptr(1000),
348 },
349 Swap: &statsapi.SwapStats{
350 Time: testTime,
351 SwapUsageBytes: uint64Ptr(5000),
352 },
353 },
354 },
355 },
356 summaryErr: nil,
357 expectedMetrics: `
358 # HELP scrape_error [ALPHA] 1 if there was an error while getting container metrics, 0 otherwise
359 # TYPE scrape_error gauge
360 scrape_error 0
361 # HELP resource_scrape_error [STABLE] 1 if there was an error while getting container metrics, 0 otherwise
362 # TYPE resource_scrape_error gauge
363 resource_scrape_error 0
364 # HELP pod_cpu_usage_seconds_total [STABLE] Cumulative cpu time consumed by the pod in core-seconds
365 # TYPE pod_cpu_usage_seconds_total counter
366 pod_cpu_usage_seconds_total{namespace="namespace_a",pod="pod_a"} 10 1624396278302
367 # HELP pod_memory_working_set_bytes [STABLE] Current working set of the pod in bytes
368 # TYPE pod_memory_working_set_bytes gauge
369 pod_memory_working_set_bytes{namespace="namespace_a",pod="pod_a"} 1000 1624396278302
370 # HELP pod_swap_usage_bytes [ALPHA] Current amount of the pod swap usage in bytes. Reported only on non-windows systems
371 # TYPE pod_swap_usage_bytes gauge
372 pod_swap_usage_bytes{namespace="namespace_a",pod="pod_a"} 5000 1624396278302
373 `,
374 },
375 {
376 name: "nil pod metrics",
377 summary: &statsapi.Summary{
378 Pods: []statsapi.PodStats{
379 {
380 PodRef: statsapi.PodReference{
381 Name: "pod_a",
382 Namespace: "namespace_a",
383 },
384 CPU: &statsapi.CPUStats{
385 Time: testTime,
386 UsageCoreNanoSeconds: nil,
387 },
388 Memory: &statsapi.MemoryStats{
389 Time: testTime,
390 WorkingSetBytes: nil,
391 },
392 },
393 },
394 },
395 summaryErr: nil,
396 expectedMetrics: `
397 # HELP scrape_error [ALPHA] 1 if there was an error while getting container metrics, 0 otherwise
398 # TYPE scrape_error gauge
399 scrape_error 0
400 # HELP resource_scrape_error [STABLE] 1 if there was an error while getting container metrics, 0 otherwise
401 # TYPE resource_scrape_error gauge
402 resource_scrape_error 0
403 `,
404 },
405 }
406
407 for _, test := range tests {
408 tc := test
409 t.Run(tc.name, func(t *testing.T) {
410 ctx := context.Background()
411 provider := summaryprovidertest.NewMockSummaryProvider(mockCtrl)
412 provider.EXPECT().GetCPUAndMemoryStats(ctx).Return(tc.summary, tc.summaryErr).AnyTimes()
413 collector := NewResourceMetricsCollector(provider)
414
415 if err := testutil.CustomCollectAndCompare(collector, strings.NewReader(tc.expectedMetrics), interestedMetrics...); err != nil {
416 t.Fatal(err)
417 }
418 })
419 }
420 }
421
422 func uint64Ptr(u uint64) *uint64 {
423 return &u
424 }
425
View as plain text