k8s.io/kubernetes@v1.29.3/pkg/kubelet/metrics/collectors/resource_metrics_test.go (about) 1 /* 2 Copyright 2019 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 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 // a static timestamp: 2021-06-23 05:11:18.302091597 +0800 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 }