k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/kubelet/apis/podresources/server_v1_test.go (about) 1 /* 2 Copyright 2018 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 podresources 18 19 import ( 20 "context" 21 "fmt" 22 "reflect" 23 "sort" 24 "testing" 25 26 "go.uber.org/mock/gomock" 27 v1 "k8s.io/api/core/v1" 28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 29 "k8s.io/apimachinery/pkg/types" 30 utilfeature "k8s.io/apiserver/pkg/util/feature" 31 featuregatetesting "k8s.io/component-base/featuregate/testing" 32 podresourcesapi "k8s.io/kubelet/pkg/apis/podresources/v1" 33 pkgfeatures "k8s.io/kubernetes/pkg/features" 34 podresourcetest "k8s.io/kubernetes/pkg/kubelet/apis/podresources/testing" 35 ) 36 37 func TestListPodResourcesV1(t *testing.T) { 38 featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, pkgfeatures.KubeletPodResourcesDynamicResources, true) 39 40 podName := "pod-name" 41 podNamespace := "pod-namespace" 42 podUID := types.UID("pod-uid") 43 containerName := "container-name" 44 numaID := int64(1) 45 46 mockCtrl := gomock.NewController(t) 47 defer mockCtrl.Finish() 48 49 devs := []*podresourcesapi.ContainerDevices{ 50 { 51 ResourceName: "resource", 52 DeviceIds: []string{"dev0", "dev1"}, 53 Topology: &podresourcesapi.TopologyInfo{Nodes: []*podresourcesapi.NUMANode{{ID: numaID}}}, 54 }, 55 } 56 57 cpus := []int64{12, 23, 30} 58 59 memory := []*podresourcesapi.ContainerMemory{ 60 { 61 MemoryType: "memory", 62 Size_: 1073741824, 63 Topology: &podresourcesapi.TopologyInfo{Nodes: []*podresourcesapi.NUMANode{{ID: numaID}}}, 64 }, 65 { 66 MemoryType: "hugepages-1Gi", 67 Size_: 1073741824, 68 Topology: &podresourcesapi.TopologyInfo{Nodes: []*podresourcesapi.NUMANode{{ID: numaID}}}, 69 }, 70 } 71 72 containers := []v1.Container{ 73 { 74 Name: containerName, 75 }, 76 } 77 pods := []*v1.Pod{ 78 { 79 ObjectMeta: metav1.ObjectMeta{ 80 Name: podName, 81 Namespace: podNamespace, 82 UID: podUID, 83 }, 84 Spec: v1.PodSpec{ 85 Containers: containers, 86 }, 87 }, 88 } 89 90 pluginCDIDevices := []*podresourcesapi.CDIDevice{{Name: "dra-dev0"}, {Name: "dra-dev1"}} 91 draDevs := []*podresourcesapi.DynamicResource{ 92 { 93 ClassName: "resource-class", 94 ClaimName: "claim-name", 95 ClaimNamespace: "default", 96 ClaimResources: []*podresourcesapi.ClaimResource{{CDIDevices: pluginCDIDevices}}, 97 }, 98 } 99 100 for _, tc := range []struct { 101 desc string 102 pods []*v1.Pod 103 devices []*podresourcesapi.ContainerDevices 104 cpus []int64 105 memory []*podresourcesapi.ContainerMemory 106 dynamicResources []*podresourcesapi.DynamicResource 107 expectedResponse *podresourcesapi.ListPodResourcesResponse 108 }{ 109 { 110 desc: "no pods", 111 pods: []*v1.Pod{}, 112 devices: []*podresourcesapi.ContainerDevices{}, 113 cpus: []int64{}, 114 memory: []*podresourcesapi.ContainerMemory{}, 115 dynamicResources: []*podresourcesapi.DynamicResource{}, 116 expectedResponse: &podresourcesapi.ListPodResourcesResponse{}, 117 }, 118 { 119 desc: "pod without devices", 120 pods: pods, 121 devices: []*podresourcesapi.ContainerDevices{}, 122 cpus: []int64{}, 123 memory: []*podresourcesapi.ContainerMemory{}, 124 dynamicResources: []*podresourcesapi.DynamicResource{}, 125 expectedResponse: &podresourcesapi.ListPodResourcesResponse{ 126 PodResources: []*podresourcesapi.PodResources{ 127 { 128 Name: podName, 129 Namespace: podNamespace, 130 Containers: []*podresourcesapi.ContainerResources{ 131 { 132 Name: containerName, 133 Devices: []*podresourcesapi.ContainerDevices{}, 134 DynamicResources: []*podresourcesapi.DynamicResource{}, 135 }, 136 }, 137 }, 138 }, 139 }, 140 }, 141 { 142 desc: "pod with devices", 143 pods: pods, 144 devices: devs, 145 cpus: cpus, 146 memory: memory, 147 dynamicResources: []*podresourcesapi.DynamicResource{}, 148 expectedResponse: &podresourcesapi.ListPodResourcesResponse{ 149 PodResources: []*podresourcesapi.PodResources{ 150 { 151 Name: podName, 152 Namespace: podNamespace, 153 Containers: []*podresourcesapi.ContainerResources{ 154 { 155 Name: containerName, 156 Devices: devs, 157 CpuIds: cpus, 158 Memory: memory, 159 DynamicResources: []*podresourcesapi.DynamicResource{}, 160 }, 161 }, 162 }, 163 }, 164 }, 165 }, 166 { 167 desc: "pod with dynamic resources", 168 pods: pods, 169 devices: []*podresourcesapi.ContainerDevices{}, 170 cpus: cpus, 171 memory: memory, 172 dynamicResources: draDevs, 173 expectedResponse: &podresourcesapi.ListPodResourcesResponse{ 174 PodResources: []*podresourcesapi.PodResources{ 175 { 176 Name: podName, 177 Namespace: podNamespace, 178 Containers: []*podresourcesapi.ContainerResources{ 179 { 180 Name: containerName, 181 Devices: []*podresourcesapi.ContainerDevices{}, 182 CpuIds: cpus, 183 Memory: memory, 184 DynamicResources: draDevs, 185 }, 186 }, 187 }, 188 }, 189 }, 190 }, 191 { 192 desc: "pod with dynamic resources and devices", 193 pods: pods, 194 devices: devs, 195 cpus: cpus, 196 memory: memory, 197 dynamicResources: draDevs, 198 expectedResponse: &podresourcesapi.ListPodResourcesResponse{ 199 PodResources: []*podresourcesapi.PodResources{ 200 { 201 Name: podName, 202 Namespace: podNamespace, 203 Containers: []*podresourcesapi.ContainerResources{ 204 { 205 Name: containerName, 206 Devices: devs, 207 CpuIds: cpus, 208 Memory: memory, 209 DynamicResources: draDevs, 210 }, 211 }, 212 }, 213 }, 214 }, 215 }, 216 } { 217 t.Run(tc.desc, func(t *testing.T) { 218 mockDevicesProvider := podresourcetest.NewMockDevicesProvider(mockCtrl) 219 mockPodsProvider := podresourcetest.NewMockPodsProvider(mockCtrl) 220 mockCPUsProvider := podresourcetest.NewMockCPUsProvider(mockCtrl) 221 mockMemoryProvider := podresourcetest.NewMockMemoryProvider(mockCtrl) 222 mockDynamicResourcesProvider := podresourcetest.NewMockDynamicResourcesProvider(mockCtrl) 223 224 mockPodsProvider.EXPECT().GetPods().Return(tc.pods).AnyTimes() 225 mockDevicesProvider.EXPECT().GetDevices(string(podUID), containerName).Return(tc.devices).AnyTimes() 226 mockCPUsProvider.EXPECT().GetCPUs(string(podUID), containerName).Return(tc.cpus).AnyTimes() 227 mockMemoryProvider.EXPECT().GetMemory(string(podUID), containerName).Return(tc.memory).AnyTimes() 228 mockDynamicResourcesProvider.EXPECT().GetDynamicResources(pods[0], &containers[0]).Return(tc.dynamicResources).AnyTimes() 229 mockDevicesProvider.EXPECT().UpdateAllocatedDevices().Return().AnyTimes() 230 mockCPUsProvider.EXPECT().GetAllocatableCPUs().Return([]int64{}).AnyTimes() 231 mockDevicesProvider.EXPECT().GetAllocatableDevices().Return([]*podresourcesapi.ContainerDevices{}).AnyTimes() 232 mockMemoryProvider.EXPECT().GetAllocatableMemory().Return([]*podresourcesapi.ContainerMemory{}).AnyTimes() 233 234 providers := PodResourcesProviders{ 235 Pods: mockPodsProvider, 236 Devices: mockDevicesProvider, 237 Cpus: mockCPUsProvider, 238 Memory: mockMemoryProvider, 239 DynamicResources: mockDynamicResourcesProvider, 240 } 241 server := NewV1PodResourcesServer(providers) 242 resp, err := server.List(context.TODO(), &podresourcesapi.ListPodResourcesRequest{}) 243 if err != nil { 244 t.Errorf("want err = %v, got %q", nil, err) 245 } 246 if !equalListResponse(tc.expectedResponse, resp) { 247 t.Errorf("want resp = %s, got %s", tc.expectedResponse.String(), resp.String()) 248 } 249 }) 250 } 251 } 252 253 func TestListPodResourcesWithInitContainersV1(t *testing.T) { 254 featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, pkgfeatures.KubeletPodResourcesDynamicResources, true) 255 256 podName := "pod-name" 257 podNamespace := "pod-namespace" 258 podUID := types.UID("pod-uid") 259 initContainerName := "init-container-name" 260 containerName := "container-name" 261 numaID := int64(1) 262 containerRestartPolicyAlways := v1.ContainerRestartPolicyAlways 263 264 devs := []*podresourcesapi.ContainerDevices{ 265 { 266 ResourceName: "resource", 267 DeviceIds: []string{"dev0", "dev1"}, 268 Topology: &podresourcesapi.TopologyInfo{Nodes: []*podresourcesapi.NUMANode{{ID: numaID}}}, 269 }, 270 } 271 272 cpus := []int64{12, 23, 30} 273 274 memory := []*podresourcesapi.ContainerMemory{ 275 { 276 MemoryType: "memory", 277 Size_: 1073741824, 278 Topology: &podresourcesapi.TopologyInfo{Nodes: []*podresourcesapi.NUMANode{{ID: numaID}}}, 279 }, 280 { 281 MemoryType: "hugepages-1Gi", 282 Size_: 1073741824, 283 Topology: &podresourcesapi.TopologyInfo{Nodes: []*podresourcesapi.NUMANode{{ID: numaID}}}, 284 }, 285 } 286 287 containers := []v1.Container{ 288 { 289 Name: containerName, 290 }, 291 } 292 293 for _, tc := range []struct { 294 desc string 295 pods []*v1.Pod 296 mockFunc func( 297 []*v1.Pod, 298 *podresourcetest.MockDevicesProvider, 299 *podresourcetest.MockCPUsProvider, 300 *podresourcetest.MockMemoryProvider, 301 *podresourcetest.MockDynamicResourcesProvider) 302 sidecarContainersEnabled bool 303 expectedResponse *podresourcesapi.ListPodResourcesResponse 304 }{ 305 { 306 desc: "pod having an init container", 307 pods: []*v1.Pod{ 308 { 309 ObjectMeta: metav1.ObjectMeta{ 310 Name: podName, 311 Namespace: podNamespace, 312 UID: podUID, 313 }, 314 Spec: v1.PodSpec{ 315 InitContainers: []v1.Container{ 316 { 317 Name: initContainerName, 318 }, 319 }, 320 Containers: containers, 321 }, 322 }, 323 }, 324 mockFunc: func( 325 pods []*v1.Pod, 326 devicesProvider *podresourcetest.MockDevicesProvider, 327 cpusProvider *podresourcetest.MockCPUsProvider, 328 memoryProvider *podresourcetest.MockMemoryProvider, 329 dynamicResourcesProvider *podresourcetest.MockDynamicResourcesProvider) { 330 devicesProvider.EXPECT().UpdateAllocatedDevices().Return().AnyTimes() 331 devicesProvider.EXPECT().GetDevices(string(podUID), containerName).Return(devs).AnyTimes() 332 cpusProvider.EXPECT().GetCPUs(string(podUID), containerName).Return(cpus).AnyTimes() 333 memoryProvider.EXPECT().GetMemory(string(podUID), containerName).Return(memory).AnyTimes() 334 dynamicResourcesProvider.EXPECT().GetDynamicResources(pods[0], &pods[0].Spec.Containers[0]).Return([]*podresourcesapi.DynamicResource{}).AnyTimes() 335 336 }, 337 expectedResponse: &podresourcesapi.ListPodResourcesResponse{ 338 PodResources: []*podresourcesapi.PodResources{ 339 { 340 Name: podName, 341 Namespace: podNamespace, 342 Containers: []*podresourcesapi.ContainerResources{ 343 { 344 Name: containerName, 345 Devices: devs, 346 CpuIds: cpus, 347 Memory: memory, 348 DynamicResources: []*podresourcesapi.DynamicResource{}, 349 }, 350 }, 351 }, 352 }, 353 }, 354 }, 355 { 356 desc: "pod having an init container with SidecarContainers enabled", 357 pods: []*v1.Pod{ 358 { 359 ObjectMeta: metav1.ObjectMeta{ 360 Name: podName, 361 Namespace: podNamespace, 362 UID: podUID, 363 }, 364 Spec: v1.PodSpec{ 365 InitContainers: []v1.Container{ 366 { 367 Name: initContainerName, 368 }, 369 }, 370 Containers: containers, 371 }, 372 }, 373 }, 374 mockFunc: func( 375 pods []*v1.Pod, 376 devicesProvider *podresourcetest.MockDevicesProvider, 377 cpusProvider *podresourcetest.MockCPUsProvider, 378 memoryProvider *podresourcetest.MockMemoryProvider, 379 dynamicResourcesProvider *podresourcetest.MockDynamicResourcesProvider) { 380 devicesProvider.EXPECT().UpdateAllocatedDevices().Return().AnyTimes() 381 devicesProvider.EXPECT().GetDevices(string(podUID), containerName).Return(devs).AnyTimes() 382 cpusProvider.EXPECT().GetCPUs(string(podUID), containerName).Return(cpus).AnyTimes() 383 memoryProvider.EXPECT().GetMemory(string(podUID), containerName).Return(memory).AnyTimes() 384 dynamicResourcesProvider.EXPECT().GetDynamicResources(pods[0], &pods[0].Spec.Containers[0]).Return([]*podresourcesapi.DynamicResource{}).AnyTimes() 385 386 }, 387 sidecarContainersEnabled: true, 388 expectedResponse: &podresourcesapi.ListPodResourcesResponse{ 389 PodResources: []*podresourcesapi.PodResources{ 390 { 391 Name: podName, 392 Namespace: podNamespace, 393 Containers: []*podresourcesapi.ContainerResources{ 394 { 395 Name: containerName, 396 Devices: devs, 397 CpuIds: cpus, 398 Memory: memory, 399 DynamicResources: []*podresourcesapi.DynamicResource{}, 400 }, 401 }, 402 }, 403 }, 404 }, 405 }, 406 { 407 desc: "pod having a restartable init container with SidecarContainers disabled", 408 pods: []*v1.Pod{ 409 { 410 ObjectMeta: metav1.ObjectMeta{ 411 Name: podName, 412 Namespace: podNamespace, 413 UID: podUID, 414 }, 415 Spec: v1.PodSpec{ 416 InitContainers: []v1.Container{ 417 { 418 Name: initContainerName, 419 RestartPolicy: &containerRestartPolicyAlways, 420 }, 421 }, 422 Containers: containers, 423 }, 424 }, 425 }, 426 mockFunc: func( 427 pods []*v1.Pod, 428 devicesProvider *podresourcetest.MockDevicesProvider, 429 cpusProvider *podresourcetest.MockCPUsProvider, 430 memoryProvider *podresourcetest.MockMemoryProvider, 431 dynamicResourcesProvider *podresourcetest.MockDynamicResourcesProvider) { 432 devicesProvider.EXPECT().UpdateAllocatedDevices().Return().AnyTimes() 433 434 devicesProvider.EXPECT().GetDevices(string(podUID), containerName).Return(devs).AnyTimes() 435 cpusProvider.EXPECT().GetCPUs(string(podUID), containerName).Return(cpus).AnyTimes() 436 memoryProvider.EXPECT().GetMemory(string(podUID), containerName).Return(memory).AnyTimes() 437 dynamicResourcesProvider.EXPECT().GetDynamicResources(pods[0], &pods[0].Spec.Containers[0]).Return([]*podresourcesapi.DynamicResource{}).AnyTimes() 438 439 }, 440 expectedResponse: &podresourcesapi.ListPodResourcesResponse{ 441 PodResources: []*podresourcesapi.PodResources{ 442 { 443 Name: podName, 444 Namespace: podNamespace, 445 Containers: []*podresourcesapi.ContainerResources{ 446 { 447 Name: containerName, 448 Devices: devs, 449 CpuIds: cpus, 450 Memory: memory, 451 DynamicResources: []*podresourcesapi.DynamicResource{}, 452 }, 453 }, 454 }, 455 }, 456 }, 457 }, 458 { 459 desc: "pod having an init container with SidecarContainers enabled", 460 pods: []*v1.Pod{ 461 { 462 ObjectMeta: metav1.ObjectMeta{ 463 Name: podName, 464 Namespace: podNamespace, 465 UID: podUID, 466 }, 467 Spec: v1.PodSpec{ 468 InitContainers: []v1.Container{ 469 { 470 Name: initContainerName, 471 RestartPolicy: &containerRestartPolicyAlways, 472 }, 473 }, 474 Containers: containers, 475 }, 476 }, 477 }, 478 mockFunc: func( 479 pods []*v1.Pod, 480 devicesProvider *podresourcetest.MockDevicesProvider, 481 cpusProvider *podresourcetest.MockCPUsProvider, 482 memoryProvider *podresourcetest.MockMemoryProvider, 483 dynamicResourcesProvider *podresourcetest.MockDynamicResourcesProvider) { 484 devicesProvider.EXPECT().UpdateAllocatedDevices().Return().AnyTimes() 485 486 devicesProvider.EXPECT().GetDevices(string(podUID), initContainerName).Return(devs).AnyTimes() 487 cpusProvider.EXPECT().GetCPUs(string(podUID), initContainerName).Return(cpus).AnyTimes() 488 memoryProvider.EXPECT().GetMemory(string(podUID), initContainerName).Return(memory).AnyTimes() 489 dynamicResourcesProvider.EXPECT().GetDynamicResources(pods[0], &pods[0].Spec.InitContainers[0]).Return([]*podresourcesapi.DynamicResource{}).AnyTimes() 490 491 devicesProvider.EXPECT().GetDevices(string(podUID), containerName).Return(devs).AnyTimes() 492 cpusProvider.EXPECT().GetCPUs(string(podUID), containerName).Return(cpus).AnyTimes() 493 memoryProvider.EXPECT().GetMemory(string(podUID), containerName).Return(memory).AnyTimes() 494 dynamicResourcesProvider.EXPECT().GetDynamicResources(pods[0], &pods[0].Spec.Containers[0]).Return([]*podresourcesapi.DynamicResource{}).AnyTimes() 495 496 }, 497 sidecarContainersEnabled: true, 498 expectedResponse: &podresourcesapi.ListPodResourcesResponse{ 499 PodResources: []*podresourcesapi.PodResources{ 500 { 501 Name: podName, 502 Namespace: podNamespace, 503 Containers: []*podresourcesapi.ContainerResources{ 504 { 505 Name: initContainerName, 506 Devices: devs, 507 CpuIds: cpus, 508 Memory: memory, 509 DynamicResources: []*podresourcesapi.DynamicResource{}, 510 }, 511 { 512 Name: containerName, 513 Devices: devs, 514 CpuIds: cpus, 515 Memory: memory, 516 DynamicResources: []*podresourcesapi.DynamicResource{}, 517 }, 518 }, 519 }, 520 }, 521 }, 522 }, 523 } { 524 t.Run(tc.desc, func(t *testing.T) { 525 featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, pkgfeatures.SidecarContainers, tc.sidecarContainersEnabled) 526 527 mockCtrl := gomock.NewController(t) 528 defer mockCtrl.Finish() 529 530 mockDevicesProvider := podresourcetest.NewMockDevicesProvider(mockCtrl) 531 mockPodsProvider := podresourcetest.NewMockPodsProvider(mockCtrl) 532 mockCPUsProvider := podresourcetest.NewMockCPUsProvider(mockCtrl) 533 mockMemoryProvider := podresourcetest.NewMockMemoryProvider(mockCtrl) 534 mockDynamicResourcesProvider := podresourcetest.NewMockDynamicResourcesProvider(mockCtrl) 535 536 mockPodsProvider.EXPECT().GetPods().Return(tc.pods).AnyTimes() 537 tc.mockFunc(tc.pods, mockDevicesProvider, mockCPUsProvider, mockMemoryProvider, mockDynamicResourcesProvider) 538 539 providers := PodResourcesProviders{ 540 Pods: mockPodsProvider, 541 Devices: mockDevicesProvider, 542 Cpus: mockCPUsProvider, 543 Memory: mockMemoryProvider, 544 DynamicResources: mockDynamicResourcesProvider, 545 } 546 server := NewV1PodResourcesServer(providers) 547 resp, err := server.List(context.TODO(), &podresourcesapi.ListPodResourcesRequest{}) 548 if err != nil { 549 t.Errorf("want err = %v, got %q", nil, err) 550 } 551 if !equalListResponse(tc.expectedResponse, resp) { 552 t.Errorf("want resp = %s, got %s", tc.expectedResponse.String(), resp.String()) 553 } 554 }) 555 } 556 } 557 558 func TestAllocatableResources(t *testing.T) { 559 mockCtrl := gomock.NewController(t) 560 defer mockCtrl.Finish() 561 562 allDevs := []*podresourcesapi.ContainerDevices{ 563 { 564 ResourceName: "resource", 565 DeviceIds: []string{"dev0"}, 566 Topology: &podresourcesapi.TopologyInfo{ 567 Nodes: []*podresourcesapi.NUMANode{ 568 { 569 ID: 0, 570 }, 571 }, 572 }, 573 }, 574 { 575 ResourceName: "resource", 576 DeviceIds: []string{"dev1"}, 577 Topology: &podresourcesapi.TopologyInfo{ 578 Nodes: []*podresourcesapi.NUMANode{ 579 { 580 ID: 1, 581 }, 582 }, 583 }, 584 }, 585 { 586 ResourceName: "resource-nt", 587 DeviceIds: []string{"devA"}, 588 }, 589 { 590 ResourceName: "resource-mm", 591 DeviceIds: []string{"devM0"}, 592 Topology: &podresourcesapi.TopologyInfo{ 593 Nodes: []*podresourcesapi.NUMANode{ 594 { 595 ID: 0, 596 }, 597 }, 598 }, 599 }, 600 { 601 ResourceName: "resource-mm", 602 DeviceIds: []string{"devMM"}, 603 Topology: &podresourcesapi.TopologyInfo{ 604 Nodes: []*podresourcesapi.NUMANode{ 605 { 606 ID: 0, 607 }, 608 { 609 ID: 1, 610 }, 611 }, 612 }, 613 }, 614 } 615 616 allCPUs := []int64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} 617 618 allMemory := []*podresourcesapi.ContainerMemory{ 619 { 620 MemoryType: "memory", 621 Size_: 5368709120, 622 Topology: &podresourcesapi.TopologyInfo{ 623 Nodes: []*podresourcesapi.NUMANode{ 624 { 625 ID: 0, 626 }, 627 }, 628 }, 629 }, 630 { 631 MemoryType: "hugepages-2Mi", 632 Size_: 1073741824, 633 Topology: &podresourcesapi.TopologyInfo{ 634 Nodes: []*podresourcesapi.NUMANode{ 635 { 636 ID: 0, 637 }, 638 }, 639 }, 640 }, 641 { 642 MemoryType: "memory", 643 Size_: 5368709120, 644 Topology: &podresourcesapi.TopologyInfo{ 645 Nodes: []*podresourcesapi.NUMANode{ 646 { 647 ID: 1, 648 }, 649 }, 650 }, 651 }, 652 { 653 MemoryType: "hugepages-2Mi", 654 Size_: 1073741824, 655 Topology: &podresourcesapi.TopologyInfo{ 656 Nodes: []*podresourcesapi.NUMANode{ 657 { 658 ID: 1, 659 }, 660 }, 661 }, 662 }, 663 } 664 665 for _, tc := range []struct { 666 desc string 667 allCPUs []int64 668 allDevices []*podresourcesapi.ContainerDevices 669 allMemory []*podresourcesapi.ContainerMemory 670 expectedAllocatableResourcesResponse *podresourcesapi.AllocatableResourcesResponse 671 }{ 672 { 673 desc: "no devices, no CPUs", 674 allCPUs: []int64{}, 675 allDevices: []*podresourcesapi.ContainerDevices{}, 676 allMemory: []*podresourcesapi.ContainerMemory{}, 677 expectedAllocatableResourcesResponse: &podresourcesapi.AllocatableResourcesResponse{}, 678 }, 679 { 680 desc: "no devices, all CPUs", 681 allCPUs: allCPUs, 682 allDevices: []*podresourcesapi.ContainerDevices{}, 683 expectedAllocatableResourcesResponse: &podresourcesapi.AllocatableResourcesResponse{ 684 CpuIds: allCPUs, 685 }, 686 }, 687 { 688 desc: "no devices, no CPUs, all memory", 689 allCPUs: []int64{}, 690 allDevices: []*podresourcesapi.ContainerDevices{}, 691 expectedAllocatableResourcesResponse: &podresourcesapi.AllocatableResourcesResponse{ 692 Memory: allMemory, 693 }, 694 }, 695 { 696 desc: "with devices, all CPUs", 697 allCPUs: allCPUs, 698 allDevices: allDevs, 699 expectedAllocatableResourcesResponse: &podresourcesapi.AllocatableResourcesResponse{ 700 CpuIds: allCPUs, 701 Devices: []*podresourcesapi.ContainerDevices{ 702 { 703 ResourceName: "resource", 704 DeviceIds: []string{"dev0"}, 705 Topology: &podresourcesapi.TopologyInfo{ 706 Nodes: []*podresourcesapi.NUMANode{ 707 { 708 ID: 0, 709 }, 710 }, 711 }, 712 }, 713 { 714 ResourceName: "resource", 715 DeviceIds: []string{"dev1"}, 716 Topology: &podresourcesapi.TopologyInfo{ 717 Nodes: []*podresourcesapi.NUMANode{ 718 { 719 ID: 1, 720 }, 721 }, 722 }, 723 }, 724 { 725 ResourceName: "resource-nt", 726 DeviceIds: []string{"devA"}, 727 }, 728 { 729 ResourceName: "resource-mm", 730 DeviceIds: []string{"devM0"}, 731 Topology: &podresourcesapi.TopologyInfo{ 732 Nodes: []*podresourcesapi.NUMANode{ 733 { 734 ID: 0, 735 }, 736 }, 737 }, 738 }, 739 { 740 ResourceName: "resource-mm", 741 DeviceIds: []string{"devMM"}, 742 Topology: &podresourcesapi.TopologyInfo{ 743 Nodes: []*podresourcesapi.NUMANode{ 744 { 745 ID: 0, 746 }, 747 { 748 ID: 1, 749 }, 750 }, 751 }, 752 }, 753 }, 754 }, 755 }, 756 { 757 desc: "with devices, no CPUs", 758 allCPUs: []int64{}, 759 allDevices: allDevs, 760 expectedAllocatableResourcesResponse: &podresourcesapi.AllocatableResourcesResponse{ 761 Devices: []*podresourcesapi.ContainerDevices{ 762 { 763 ResourceName: "resource", 764 DeviceIds: []string{"dev0"}, 765 Topology: &podresourcesapi.TopologyInfo{ 766 Nodes: []*podresourcesapi.NUMANode{ 767 { 768 ID: 0, 769 }, 770 }, 771 }, 772 }, 773 { 774 ResourceName: "resource", 775 DeviceIds: []string{"dev1"}, 776 Topology: &podresourcesapi.TopologyInfo{ 777 Nodes: []*podresourcesapi.NUMANode{ 778 { 779 ID: 1, 780 }, 781 }, 782 }, 783 }, 784 { 785 ResourceName: "resource-nt", 786 DeviceIds: []string{"devA"}, 787 }, 788 { 789 ResourceName: "resource-mm", 790 DeviceIds: []string{"devM0"}, 791 Topology: &podresourcesapi.TopologyInfo{ 792 Nodes: []*podresourcesapi.NUMANode{ 793 { 794 ID: 0, 795 }, 796 }, 797 }, 798 }, 799 { 800 ResourceName: "resource-mm", 801 DeviceIds: []string{"devMM"}, 802 Topology: &podresourcesapi.TopologyInfo{ 803 Nodes: []*podresourcesapi.NUMANode{ 804 { 805 ID: 0, 806 }, 807 { 808 ID: 1, 809 }, 810 }, 811 }, 812 }, 813 }, 814 }, 815 }, 816 } { 817 t.Run(tc.desc, func(t *testing.T) { 818 mockDevicesProvider := podresourcetest.NewMockDevicesProvider(mockCtrl) 819 mockPodsProvider := podresourcetest.NewMockPodsProvider(mockCtrl) 820 mockCPUsProvider := podresourcetest.NewMockCPUsProvider(mockCtrl) 821 mockMemoryProvider := podresourcetest.NewMockMemoryProvider(mockCtrl) 822 823 mockDevicesProvider.EXPECT().GetDevices("", "").Return([]*podresourcesapi.ContainerDevices{}).AnyTimes() 824 mockCPUsProvider.EXPECT().GetCPUs("", "").Return([]int64{}).AnyTimes() 825 mockMemoryProvider.EXPECT().GetMemory("", "").Return([]*podresourcesapi.ContainerMemory{}).AnyTimes() 826 mockDevicesProvider.EXPECT().UpdateAllocatedDevices().Return().AnyTimes() 827 mockDevicesProvider.EXPECT().GetAllocatableDevices().Return(tc.allDevices).AnyTimes() 828 mockCPUsProvider.EXPECT().GetAllocatableCPUs().Return(tc.allCPUs).AnyTimes() 829 mockMemoryProvider.EXPECT().GetAllocatableMemory().Return(tc.allMemory).AnyTimes() 830 831 providers := PodResourcesProviders{ 832 Pods: mockPodsProvider, 833 Devices: mockDevicesProvider, 834 Cpus: mockCPUsProvider, 835 Memory: mockMemoryProvider, 836 } 837 server := NewV1PodResourcesServer(providers) 838 839 resp, err := server.GetAllocatableResources(context.TODO(), &podresourcesapi.AllocatableResourcesRequest{}) 840 if err != nil { 841 t.Errorf("want err = %v, got %q", nil, err) 842 } 843 844 if !equalAllocatableResourcesResponse(tc.expectedAllocatableResourcesResponse, resp) { 845 t.Errorf("want resp = %s, got %s", tc.expectedAllocatableResourcesResponse.String(), resp.String()) 846 } 847 }) 848 } 849 } 850 851 func TestGetPodResourcesV1(t *testing.T) { 852 featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, pkgfeatures.KubeletPodResourcesGet, true) 853 featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, pkgfeatures.KubeletPodResourcesDynamicResources, true) 854 855 podName := "pod-name" 856 podNamespace := "pod-namespace" 857 podUID := types.UID("pod-uid") 858 containerName := "container-name" 859 numaID := int64(1) 860 861 mockCtrl := gomock.NewController(t) 862 defer mockCtrl.Finish() 863 864 devs := []*podresourcesapi.ContainerDevices{ 865 { 866 ResourceName: "resource", 867 DeviceIds: []string{"dev0", "dev1"}, 868 Topology: &podresourcesapi.TopologyInfo{Nodes: []*podresourcesapi.NUMANode{{ID: numaID}}}, 869 }, 870 } 871 872 cpus := []int64{12, 23, 30} 873 874 memory := []*podresourcesapi.ContainerMemory{ 875 { 876 MemoryType: "memory", 877 Size_: 1073741824, 878 Topology: &podresourcesapi.TopologyInfo{Nodes: []*podresourcesapi.NUMANode{{ID: numaID}}}, 879 }, 880 { 881 MemoryType: "hugepages-1Gi", 882 Size_: 1073741824, 883 Topology: &podresourcesapi.TopologyInfo{Nodes: []*podresourcesapi.NUMANode{{ID: numaID}}}, 884 }, 885 } 886 887 containers := []v1.Container{ 888 { 889 Name: containerName, 890 }, 891 } 892 893 pod := &v1.Pod{ 894 ObjectMeta: metav1.ObjectMeta{ 895 Name: podName, 896 Namespace: podNamespace, 897 UID: podUID, 898 }, 899 Spec: v1.PodSpec{ 900 Containers: containers, 901 }, 902 } 903 904 pluginCDIDevices := []*podresourcesapi.CDIDevice{{Name: "dra-dev0"}, {Name: "dra-dev1"}} 905 draDevs := []*podresourcesapi.DynamicResource{ 906 { 907 ClassName: "resource-class", 908 ClaimName: "claim-name", 909 ClaimNamespace: "default", 910 ClaimResources: []*podresourcesapi.ClaimResource{{CDIDevices: pluginCDIDevices}}, 911 }, 912 } 913 914 for _, tc := range []struct { 915 desc string 916 err error 917 exist bool 918 pod *v1.Pod 919 devices []*podresourcesapi.ContainerDevices 920 cpus []int64 921 memory []*podresourcesapi.ContainerMemory 922 dynamicResources []*podresourcesapi.DynamicResource 923 expectedResponse *podresourcesapi.GetPodResourcesResponse 924 }{ 925 { 926 desc: "pod not exist", 927 err: fmt.Errorf("pod %s in namespace %s not found", podName, podNamespace), 928 exist: false, 929 pod: nil, 930 devices: []*podresourcesapi.ContainerDevices{}, 931 cpus: []int64{}, 932 memory: []*podresourcesapi.ContainerMemory{}, 933 dynamicResources: []*podresourcesapi.DynamicResource{}, 934 935 expectedResponse: &podresourcesapi.GetPodResourcesResponse{}, 936 }, 937 { 938 desc: "pod without devices", 939 err: nil, 940 exist: true, 941 pod: pod, 942 devices: []*podresourcesapi.ContainerDevices{}, 943 cpus: []int64{}, 944 memory: []*podresourcesapi.ContainerMemory{}, 945 dynamicResources: []*podresourcesapi.DynamicResource{}, 946 expectedResponse: &podresourcesapi.GetPodResourcesResponse{ 947 PodResources: &podresourcesapi.PodResources{ 948 Name: podName, 949 Namespace: podNamespace, 950 Containers: []*podresourcesapi.ContainerResources{ 951 { 952 Name: containerName, 953 Devices: []*podresourcesapi.ContainerDevices{}, 954 DynamicResources: []*podresourcesapi.DynamicResource{}, 955 }, 956 }, 957 }, 958 }, 959 }, 960 { 961 desc: "pod with devices", 962 err: nil, 963 exist: true, 964 pod: pod, 965 devices: devs, 966 cpus: cpus, 967 memory: memory, 968 dynamicResources: draDevs, 969 expectedResponse: &podresourcesapi.GetPodResourcesResponse{ 970 PodResources: &podresourcesapi.PodResources{ 971 Name: podName, 972 Namespace: podNamespace, 973 Containers: []*podresourcesapi.ContainerResources{ 974 { 975 Name: containerName, 976 Devices: devs, 977 CpuIds: cpus, 978 Memory: memory, 979 DynamicResources: draDevs, 980 }, 981 }, 982 }, 983 }, 984 }, 985 } { 986 t.Run(tc.desc, func(t *testing.T) { 987 mockDevicesProvider := podresourcetest.NewMockDevicesProvider(mockCtrl) 988 mockPodsProvider := podresourcetest.NewMockPodsProvider(mockCtrl) 989 mockCPUsProvider := podresourcetest.NewMockCPUsProvider(mockCtrl) 990 mockMemoryProvider := podresourcetest.NewMockMemoryProvider(mockCtrl) 991 mockDynamicResourcesProvider := podresourcetest.NewMockDynamicResourcesProvider(mockCtrl) 992 993 mockPodsProvider.EXPECT().GetPodByName(podNamespace, podName).Return(tc.pod, tc.exist).AnyTimes() 994 mockDevicesProvider.EXPECT().GetDevices(string(podUID), containerName).Return(tc.devices).AnyTimes() 995 mockCPUsProvider.EXPECT().GetCPUs(string(podUID), containerName).Return(tc.cpus).AnyTimes() 996 mockMemoryProvider.EXPECT().GetMemory(string(podUID), containerName).Return(tc.memory).AnyTimes() 997 mockDynamicResourcesProvider.EXPECT().GetDynamicResources(pod, &containers[0]).Return(tc.dynamicResources).AnyTimes() 998 mockDevicesProvider.EXPECT().UpdateAllocatedDevices().Return().AnyTimes() 999 mockCPUsProvider.EXPECT().GetAllocatableCPUs().Return([]int64{}).AnyTimes() 1000 mockDevicesProvider.EXPECT().GetAllocatableDevices().Return([]*podresourcesapi.ContainerDevices{}).AnyTimes() 1001 mockMemoryProvider.EXPECT().GetAllocatableMemory().Return([]*podresourcesapi.ContainerMemory{}).AnyTimes() 1002 1003 providers := PodResourcesProviders{ 1004 Pods: mockPodsProvider, 1005 Devices: mockDevicesProvider, 1006 Cpus: mockCPUsProvider, 1007 Memory: mockMemoryProvider, 1008 DynamicResources: mockDynamicResourcesProvider, 1009 } 1010 server := NewV1PodResourcesServer(providers) 1011 podReq := &podresourcesapi.GetPodResourcesRequest{PodName: podName, PodNamespace: podNamespace} 1012 resp, err := server.Get(context.TODO(), podReq) 1013 1014 if err != nil { 1015 if err.Error() != tc.err.Error() { 1016 t.Errorf("want exit = %v, got %v", tc.err, err) 1017 } 1018 } else { 1019 if err != tc.err { 1020 t.Errorf("want exit = %v, got %v", tc.err, err) 1021 } else { 1022 if !equalGetResponse(tc.expectedResponse, resp) { 1023 t.Errorf("want resp = %s, got %s", tc.expectedResponse.String(), resp.String()) 1024 } 1025 } 1026 } 1027 }) 1028 } 1029 1030 } 1031 1032 func TestGetPodResourcesWithInitContainersV1(t *testing.T) { 1033 featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, pkgfeatures.KubeletPodResourcesGet, true) 1034 featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, pkgfeatures.KubeletPodResourcesDynamicResources, true) 1035 1036 podName := "pod-name" 1037 podNamespace := "pod-namespace" 1038 podUID := types.UID("pod-uid") 1039 initContainerName := "init-container-name" 1040 containerName := "container-name" 1041 numaID := int64(1) 1042 containerRestartPolicyAlways := v1.ContainerRestartPolicyAlways 1043 1044 devs := []*podresourcesapi.ContainerDevices{ 1045 { 1046 ResourceName: "resource", 1047 DeviceIds: []string{"dev0", "dev1"}, 1048 Topology: &podresourcesapi.TopologyInfo{Nodes: []*podresourcesapi.NUMANode{{ID: numaID}}}, 1049 }, 1050 } 1051 1052 cpus := []int64{12, 23, 30} 1053 1054 memory := []*podresourcesapi.ContainerMemory{ 1055 { 1056 MemoryType: "memory", 1057 Size_: 1073741824, 1058 Topology: &podresourcesapi.TopologyInfo{Nodes: []*podresourcesapi.NUMANode{{ID: numaID}}}, 1059 }, 1060 { 1061 MemoryType: "hugepages-1Gi", 1062 Size_: 1073741824, 1063 Topology: &podresourcesapi.TopologyInfo{Nodes: []*podresourcesapi.NUMANode{{ID: numaID}}}, 1064 }, 1065 } 1066 1067 containers := []v1.Container{ 1068 { 1069 Name: containerName, 1070 }, 1071 } 1072 1073 for _, tc := range []struct { 1074 desc string 1075 pod *v1.Pod 1076 mockFunc func( 1077 *v1.Pod, 1078 *podresourcetest.MockDevicesProvider, 1079 *podresourcetest.MockCPUsProvider, 1080 *podresourcetest.MockMemoryProvider, 1081 *podresourcetest.MockDynamicResourcesProvider) 1082 sidecarContainersEnabled bool 1083 expectedResponse *podresourcesapi.GetPodResourcesResponse 1084 }{ 1085 { 1086 desc: "pod having an init container", 1087 pod: &v1.Pod{ 1088 ObjectMeta: metav1.ObjectMeta{ 1089 Name: podName, 1090 Namespace: podNamespace, 1091 UID: podUID, 1092 }, 1093 Spec: v1.PodSpec{ 1094 InitContainers: []v1.Container{ 1095 { 1096 Name: initContainerName, 1097 }, 1098 }, 1099 Containers: containers, 1100 }, 1101 }, 1102 mockFunc: func( 1103 pod *v1.Pod, 1104 devicesProvider *podresourcetest.MockDevicesProvider, 1105 cpusProvider *podresourcetest.MockCPUsProvider, 1106 memoryProvider *podresourcetest.MockMemoryProvider, 1107 dynamicResourcesProvider *podresourcetest.MockDynamicResourcesProvider) { 1108 devicesProvider.EXPECT().UpdateAllocatedDevices().Return().AnyTimes() 1109 devicesProvider.EXPECT().GetDevices(string(podUID), containerName).Return(devs).AnyTimes() 1110 cpusProvider.EXPECT().GetCPUs(string(podUID), containerName).Return(cpus).AnyTimes() 1111 memoryProvider.EXPECT().GetMemory(string(podUID), containerName).Return(memory).AnyTimes() 1112 dynamicResourcesProvider.EXPECT().GetDynamicResources(pod, &pod.Spec.Containers[0]).Return([]*podresourcesapi.DynamicResource{}).AnyTimes() 1113 1114 }, 1115 expectedResponse: &podresourcesapi.GetPodResourcesResponse{ 1116 PodResources: &podresourcesapi.PodResources{ 1117 Name: podName, 1118 Namespace: podNamespace, 1119 Containers: []*podresourcesapi.ContainerResources{ 1120 { 1121 Name: containerName, 1122 Devices: devs, 1123 CpuIds: cpus, 1124 Memory: memory, 1125 DynamicResources: []*podresourcesapi.DynamicResource{}, 1126 }, 1127 }, 1128 }, 1129 }, 1130 }, 1131 { 1132 desc: "pod having an init container with SidecarContainers enabled", 1133 pod: &v1.Pod{ 1134 ObjectMeta: metav1.ObjectMeta{ 1135 Name: podName, 1136 Namespace: podNamespace, 1137 UID: podUID, 1138 }, 1139 Spec: v1.PodSpec{ 1140 InitContainers: []v1.Container{ 1141 { 1142 Name: initContainerName, 1143 }, 1144 }, 1145 Containers: containers, 1146 }, 1147 }, 1148 mockFunc: func( 1149 pod *v1.Pod, 1150 devicesProvider *podresourcetest.MockDevicesProvider, 1151 cpusProvider *podresourcetest.MockCPUsProvider, 1152 memoryProvider *podresourcetest.MockMemoryProvider, 1153 dynamicResourcesProvider *podresourcetest.MockDynamicResourcesProvider) { 1154 devicesProvider.EXPECT().UpdateAllocatedDevices().Return().AnyTimes() 1155 devicesProvider.EXPECT().GetDevices(string(podUID), containerName).Return(devs).AnyTimes() 1156 cpusProvider.EXPECT().GetCPUs(string(podUID), containerName).Return(cpus).AnyTimes() 1157 memoryProvider.EXPECT().GetMemory(string(podUID), containerName).Return(memory).AnyTimes() 1158 dynamicResourcesProvider.EXPECT().GetDynamicResources(pod, &pod.Spec.Containers[0]).Return([]*podresourcesapi.DynamicResource{}).AnyTimes() 1159 1160 }, 1161 sidecarContainersEnabled: true, 1162 expectedResponse: &podresourcesapi.GetPodResourcesResponse{ 1163 PodResources: &podresourcesapi.PodResources{ 1164 Name: podName, 1165 Namespace: podNamespace, 1166 Containers: []*podresourcesapi.ContainerResources{ 1167 { 1168 Name: containerName, 1169 Devices: devs, 1170 CpuIds: cpus, 1171 Memory: memory, 1172 DynamicResources: []*podresourcesapi.DynamicResource{}, 1173 }, 1174 }, 1175 }, 1176 }, 1177 }, 1178 { 1179 desc: "pod having a restartable init container with SidecarContainers disabled", 1180 pod: &v1.Pod{ 1181 ObjectMeta: metav1.ObjectMeta{ 1182 Name: podName, 1183 Namespace: podNamespace, 1184 UID: podUID, 1185 }, 1186 Spec: v1.PodSpec{ 1187 InitContainers: []v1.Container{ 1188 { 1189 Name: initContainerName, 1190 RestartPolicy: &containerRestartPolicyAlways, 1191 }, 1192 }, 1193 Containers: containers, 1194 }, 1195 }, 1196 mockFunc: func( 1197 pod *v1.Pod, 1198 devicesProvider *podresourcetest.MockDevicesProvider, 1199 cpusProvider *podresourcetest.MockCPUsProvider, 1200 memoryProvider *podresourcetest.MockMemoryProvider, 1201 dynamicResourcesProvider *podresourcetest.MockDynamicResourcesProvider) { 1202 devicesProvider.EXPECT().UpdateAllocatedDevices().Return().AnyTimes() 1203 1204 devicesProvider.EXPECT().GetDevices(string(podUID), containerName).Return(devs).AnyTimes() 1205 cpusProvider.EXPECT().GetCPUs(string(podUID), containerName).Return(cpus).AnyTimes() 1206 memoryProvider.EXPECT().GetMemory(string(podUID), containerName).Return(memory).AnyTimes() 1207 dynamicResourcesProvider.EXPECT().GetDynamicResources(pod, &pod.Spec.Containers[0]).Return([]*podresourcesapi.DynamicResource{}).AnyTimes() 1208 1209 }, 1210 expectedResponse: &podresourcesapi.GetPodResourcesResponse{ 1211 PodResources: &podresourcesapi.PodResources{ 1212 Name: podName, 1213 Namespace: podNamespace, 1214 Containers: []*podresourcesapi.ContainerResources{ 1215 { 1216 Name: containerName, 1217 Devices: devs, 1218 CpuIds: cpus, 1219 Memory: memory, 1220 DynamicResources: []*podresourcesapi.DynamicResource{}, 1221 }, 1222 }, 1223 }, 1224 }, 1225 }, 1226 { 1227 desc: "pod having an init container with SidecarContainers enabled", 1228 pod: &v1.Pod{ 1229 ObjectMeta: metav1.ObjectMeta{ 1230 Name: podName, 1231 Namespace: podNamespace, 1232 UID: podUID, 1233 }, 1234 Spec: v1.PodSpec{ 1235 InitContainers: []v1.Container{ 1236 { 1237 Name: initContainerName, 1238 RestartPolicy: &containerRestartPolicyAlways, 1239 }, 1240 }, 1241 Containers: containers, 1242 }, 1243 }, 1244 mockFunc: func( 1245 pod *v1.Pod, 1246 devicesProvider *podresourcetest.MockDevicesProvider, 1247 cpusProvider *podresourcetest.MockCPUsProvider, 1248 memoryProvider *podresourcetest.MockMemoryProvider, 1249 dynamicResourcesProvider *podresourcetest.MockDynamicResourcesProvider) { 1250 devicesProvider.EXPECT().UpdateAllocatedDevices().Return().AnyTimes() 1251 1252 devicesProvider.EXPECT().GetDevices(string(podUID), initContainerName).Return(devs).AnyTimes() 1253 cpusProvider.EXPECT().GetCPUs(string(podUID), initContainerName).Return(cpus).AnyTimes() 1254 memoryProvider.EXPECT().GetMemory(string(podUID), initContainerName).Return(memory).AnyTimes() 1255 dynamicResourcesProvider.EXPECT().GetDynamicResources(pod, &pod.Spec.InitContainers[0]).Return([]*podresourcesapi.DynamicResource{}).AnyTimes() 1256 1257 devicesProvider.EXPECT().GetDevices(string(podUID), containerName).Return(devs).AnyTimes() 1258 cpusProvider.EXPECT().GetCPUs(string(podUID), containerName).Return(cpus).AnyTimes() 1259 memoryProvider.EXPECT().GetMemory(string(podUID), containerName).Return(memory).AnyTimes() 1260 dynamicResourcesProvider.EXPECT().GetDynamicResources(pod, &pod.Spec.Containers[0]).Return([]*podresourcesapi.DynamicResource{}).AnyTimes() 1261 1262 }, 1263 sidecarContainersEnabled: true, 1264 expectedResponse: &podresourcesapi.GetPodResourcesResponse{ 1265 PodResources: &podresourcesapi.PodResources{ 1266 Name: podName, 1267 Namespace: podNamespace, 1268 Containers: []*podresourcesapi.ContainerResources{ 1269 { 1270 Name: initContainerName, 1271 Devices: devs, 1272 CpuIds: cpus, 1273 Memory: memory, 1274 DynamicResources: []*podresourcesapi.DynamicResource{}, 1275 }, 1276 { 1277 Name: containerName, 1278 Devices: devs, 1279 CpuIds: cpus, 1280 Memory: memory, 1281 DynamicResources: []*podresourcesapi.DynamicResource{}, 1282 }, 1283 }, 1284 }, 1285 }, 1286 }, 1287 } { 1288 t.Run(tc.desc, func(t *testing.T) { 1289 featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, pkgfeatures.SidecarContainers, tc.sidecarContainersEnabled) 1290 1291 mockCtrl := gomock.NewController(t) 1292 defer mockCtrl.Finish() 1293 1294 mockDevicesProvider := podresourcetest.NewMockDevicesProvider(mockCtrl) 1295 mockPodsProvider := podresourcetest.NewMockPodsProvider(mockCtrl) 1296 mockCPUsProvider := podresourcetest.NewMockCPUsProvider(mockCtrl) 1297 mockMemoryProvider := podresourcetest.NewMockMemoryProvider(mockCtrl) 1298 mockDynamicResourcesProvider := podresourcetest.NewMockDynamicResourcesProvider(mockCtrl) 1299 1300 mockPodsProvider.EXPECT().GetPodByName(podNamespace, podName).Return(tc.pod, true).AnyTimes() 1301 tc.mockFunc(tc.pod, mockDevicesProvider, mockCPUsProvider, mockMemoryProvider, mockDynamicResourcesProvider) 1302 1303 providers := PodResourcesProviders{ 1304 Pods: mockPodsProvider, 1305 Devices: mockDevicesProvider, 1306 Cpus: mockCPUsProvider, 1307 Memory: mockMemoryProvider, 1308 DynamicResources: mockDynamicResourcesProvider, 1309 } 1310 server := NewV1PodResourcesServer(providers) 1311 podReq := &podresourcesapi.GetPodResourcesRequest{PodName: podName, PodNamespace: podNamespace} 1312 resp, err := server.Get(context.TODO(), podReq) 1313 if err != nil { 1314 t.Errorf("want err = %v, got %q", nil, err) 1315 } 1316 if !equalGetResponse(tc.expectedResponse, resp) { 1317 t.Errorf("want resp = %s, got %s", tc.expectedResponse.String(), resp.String()) 1318 } 1319 }) 1320 } 1321 } 1322 1323 func equalListResponse(respA, respB *podresourcesapi.ListPodResourcesResponse) bool { 1324 if len(respA.PodResources) != len(respB.PodResources) { 1325 return false 1326 } 1327 for idx := 0; idx < len(respA.PodResources); idx++ { 1328 podResA := respA.PodResources[idx] 1329 podResB := respB.PodResources[idx] 1330 if podResA.Name != podResB.Name { 1331 return false 1332 } 1333 if podResA.Namespace != podResB.Namespace { 1334 return false 1335 } 1336 if len(podResA.Containers) != len(podResB.Containers) { 1337 return false 1338 } 1339 for jdx := 0; jdx < len(podResA.Containers); jdx++ { 1340 cntA := podResA.Containers[jdx] 1341 cntB := podResB.Containers[jdx] 1342 1343 if cntA.Name != cntB.Name { 1344 return false 1345 } 1346 if !equalInt64s(cntA.CpuIds, cntB.CpuIds) { 1347 return false 1348 } 1349 1350 if !equalContainerDevices(cntA.Devices, cntB.Devices) { 1351 return false 1352 } 1353 1354 if !equalDynamicResources(cntA.DynamicResources, cntB.DynamicResources) { 1355 return false 1356 } 1357 } 1358 } 1359 return true 1360 } 1361 1362 func equalDynamicResources(draResA, draResB []*podresourcesapi.DynamicResource) bool { 1363 if len(draResA) != len(draResB) { 1364 return false 1365 } 1366 1367 for idx := 0; idx < len(draResA); idx++ { 1368 cntDraResA := draResA[idx] 1369 cntDraResB := draResB[idx] 1370 1371 if cntDraResA.ClassName != cntDraResB.ClassName { 1372 return false 1373 } 1374 if cntDraResA.ClaimName != cntDraResB.ClaimName { 1375 return false 1376 } 1377 if cntDraResA.ClaimNamespace != cntDraResB.ClaimNamespace { 1378 return false 1379 } 1380 if len(cntDraResA.ClaimResources) != len(cntDraResB.ClaimResources) { 1381 return false 1382 } 1383 for i := 0; i < len(cntDraResA.ClaimResources); i++ { 1384 claimResA := cntDraResA.ClaimResources[i] 1385 claimResB := cntDraResB.ClaimResources[i] 1386 if len(claimResA.CDIDevices) != len(claimResB.CDIDevices) { 1387 return false 1388 } 1389 for y := 0; y < len(claimResA.CDIDevices); y++ { 1390 cdiDeviceA := claimResA.CDIDevices[y] 1391 cdiDeviceB := claimResB.CDIDevices[y] 1392 if cdiDeviceA.Name != cdiDeviceB.Name { 1393 return false 1394 } 1395 } 1396 } 1397 } 1398 1399 return true 1400 } 1401 1402 func equalContainerDevices(devA, devB []*podresourcesapi.ContainerDevices) bool { 1403 if len(devA) != len(devB) { 1404 return false 1405 } 1406 1407 for idx := 0; idx < len(devA); idx++ { 1408 cntDevA := devA[idx] 1409 cntDevB := devB[idx] 1410 1411 if cntDevA.ResourceName != cntDevB.ResourceName { 1412 return false 1413 } 1414 if !equalTopology(cntDevA.Topology, cntDevB.Topology) { 1415 return false 1416 } 1417 if !equalStrings(cntDevA.DeviceIds, cntDevB.DeviceIds) { 1418 return false 1419 } 1420 } 1421 1422 return true 1423 } 1424 1425 func equalInt64s(a, b []int64) bool { 1426 if len(a) != len(b) { 1427 return false 1428 } 1429 aCopy := append([]int64{}, a...) 1430 sort.Slice(aCopy, func(i, j int) bool { return aCopy[i] < aCopy[j] }) 1431 bCopy := append([]int64{}, b...) 1432 sort.Slice(bCopy, func(i, j int) bool { return bCopy[i] < bCopy[j] }) 1433 return reflect.DeepEqual(aCopy, bCopy) 1434 } 1435 1436 func equalStrings(a, b []string) bool { 1437 if len(a) != len(b) { 1438 return false 1439 } 1440 aCopy := append([]string{}, a...) 1441 sort.Strings(aCopy) 1442 bCopy := append([]string{}, b...) 1443 sort.Strings(bCopy) 1444 return reflect.DeepEqual(aCopy, bCopy) 1445 } 1446 1447 func equalTopology(a, b *podresourcesapi.TopologyInfo) bool { 1448 if a == nil && b != nil { 1449 return false 1450 } 1451 if a != nil && b == nil { 1452 return false 1453 } 1454 return reflect.DeepEqual(a, b) 1455 } 1456 1457 func equalAllocatableResourcesResponse(respA, respB *podresourcesapi.AllocatableResourcesResponse) bool { 1458 if !equalInt64s(respA.CpuIds, respB.CpuIds) { 1459 return false 1460 } 1461 return equalContainerDevices(respA.Devices, respB.Devices) 1462 } 1463 1464 func equalGetResponse(ResA, ResB *podresourcesapi.GetPodResourcesResponse) bool { 1465 podResA := ResA.PodResources 1466 podResB := ResB.PodResources 1467 if podResA.Name != podResB.Name { 1468 return false 1469 } 1470 if podResA.Namespace != podResB.Namespace { 1471 return false 1472 } 1473 if len(podResA.Containers) != len(podResB.Containers) { 1474 return false 1475 } 1476 for jdx := 0; jdx < len(podResA.Containers); jdx++ { 1477 cntA := podResA.Containers[jdx] 1478 cntB := podResB.Containers[jdx] 1479 1480 if cntA.Name != cntB.Name { 1481 return false 1482 } 1483 if !equalInt64s(cntA.CpuIds, cntB.CpuIds) { 1484 return false 1485 } 1486 1487 if !equalContainerDevices(cntA.Devices, cntB.Devices) { 1488 return false 1489 } 1490 1491 if !equalDynamicResources(cntA.DynamicResources, cntB.DynamicResources) { 1492 return false 1493 } 1494 1495 } 1496 return true 1497 }