github.com/kubewharf/katalyst-core@v0.5.3/pkg/controller/vpa/util/resource_test.go (about) 1 /* 2 Copyright 2022 The Katalyst 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 util 18 19 import ( 20 "fmt" 21 "testing" 22 23 "github.com/stretchr/testify/assert" 24 appsv1 "k8s.io/api/apps/v1" 25 v1 "k8s.io/api/core/v1" 26 "k8s.io/apimachinery/pkg/api/resource" 27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 "k8s.io/apimachinery/pkg/runtime" 29 "k8s.io/apimachinery/pkg/runtime/schema" 30 "k8s.io/apimachinery/pkg/types" 31 "k8s.io/client-go/dynamic/dynamicinformer" 32 dynamicfake "k8s.io/client-go/dynamic/fake" 33 "k8s.io/client-go/informers" 34 "k8s.io/client-go/kubernetes/fake" 35 "k8s.io/client-go/tools/cache" 36 "k8s.io/klog/v2" 37 "k8s.io/utils/pointer" 38 39 apis "github.com/kubewharf/katalyst-api/pkg/apis/autoscaling/v1alpha1" 40 katalystutil "github.com/kubewharf/katalyst-core/pkg/util" 41 "github.com/kubewharf/katalyst-core/pkg/util/native" 42 ) 43 44 var ( 45 stsGVK = schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Statefulset"} 46 stsGVR = schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "statefulsets"} 47 ) 48 49 func TestGetVPAResourceStatusWithCurrent(t *testing.T) { 50 t.Parallel() 51 52 // test with no indexers 53 klog.Infof("================== test with no indexers ==================") 54 testGetVPAResourceStatusWithCurrentAndIndexer(t, nil, []string{}) 55 56 // test with existed indexers 57 indexerKeys := []string{"workload"} 58 podIndexers := cache.Indexers{} 59 for _, key := range indexerKeys { 60 indexer := native.PodLabelIndexer(key) 61 podIndexers[key] = indexer.IndexFunc 62 } 63 klog.Infof("================== test with existed indexers ==================") 64 testGetVPAResourceStatusWithCurrentAndIndexer(t, podIndexers, indexerKeys) 65 66 // test with none-existed indexers 67 indexerKeys = []string{"none-exist"} 68 podIndexers = cache.Indexers{} 69 for _, key := range indexerKeys { 70 indexer := native.PodLabelIndexer(key) 71 podIndexers[key] = indexer.IndexFunc 72 } 73 klog.Infof("================== test with none-existed indexers ==================") 74 testGetVPAResourceStatusWithCurrentAndIndexer(t, podIndexers, indexerKeys) 75 } 76 77 func testGetVPAResourceStatusWithCurrentAndIndexer(t *testing.T, podIndexers cache.Indexers, podIndexerKeys []string) { 78 container1 := &v1.Container{ 79 Name: "c1", 80 Resources: v1.ResourceRequirements{ 81 Limits: map[v1.ResourceName]resource.Quantity{ 82 v1.ResourceMemory: resource.MustParse("4Gi"), 83 v1.ResourceCPU: resource.MustParse("4"), 84 }, 85 Requests: map[v1.ResourceName]resource.Quantity{ 86 v1.ResourceMemory: resource.MustParse("2Gi"), 87 v1.ResourceCPU: resource.MustParse("2"), 88 }, 89 }, 90 } 91 container2 := &v1.Container{ 92 Name: "c1", 93 Resources: v1.ResourceRequirements{ 94 Limits: map[v1.ResourceName]resource.Quantity{ 95 v1.ResourceMemory: resource.MustParse("3Gi"), 96 v1.ResourceCPU: resource.MustParse("3"), 97 }, 98 Requests: map[v1.ResourceName]resource.Quantity{ 99 v1.ResourceMemory: resource.MustParse("2Gi"), 100 v1.ResourceCPU: resource.MustParse("2"), 101 }, 102 }, 103 } 104 pod1 := &v1.Pod{ 105 ObjectMeta: metav1.ObjectMeta{ 106 Namespace: "default", 107 Name: "pod1", 108 Labels: map[string]string{ 109 "workload": "sts", 110 }, 111 OwnerReferences: []metav1.OwnerReference{ 112 { 113 APIVersion: stsGVK.GroupVersion().String(), 114 Kind: stsGVK.Kind, 115 Name: "sts1", 116 }, 117 }, 118 UID: types.UID("pod1-uid"), 119 }, 120 Spec: v1.PodSpec{ 121 Containers: []v1.Container{*container1}, 122 }, 123 } 124 pod2 := &v1.Pod{ 125 ObjectMeta: metav1.ObjectMeta{ 126 Namespace: "default", 127 Name: "pod2", 128 Labels: map[string]string{ 129 "workload": "sts", 130 }, 131 OwnerReferences: []metav1.OwnerReference{ 132 { 133 APIVersion: stsGVK.GroupVersion().String(), 134 Kind: stsGVK.Kind, 135 Name: "sts1", 136 }, 137 }, 138 UID: types.UID("pod2-uid"), 139 }, 140 Spec: v1.PodSpec{ 141 Containers: []v1.Container{*container2}, 142 }, 143 } 144 vpa1 := &apis.KatalystVerticalPodAutoscaler{ 145 ObjectMeta: metav1.ObjectMeta{ 146 Namespace: "default", 147 Name: "vpa1", 148 }, 149 Spec: apis.KatalystVerticalPodAutoscalerSpec{ 150 TargetRef: apis.CrossVersionObjectReference{ 151 Kind: stsGVK.Kind, 152 Name: "sts1", 153 APIVersion: stsGVK.GroupVersion().String(), 154 }, 155 }, 156 Status: apis.KatalystVerticalPodAutoscalerStatus{ 157 ContainerResources: []apis.ContainerResources{ 158 { 159 ContainerName: pointer.String("c1"), 160 Requests: &apis.ContainerResourceList{ 161 Target: map[v1.ResourceName]resource.Quantity{ 162 v1.ResourceMemory: resource.MustParse("1Gi"), 163 v1.ResourceCPU: resource.MustParse("1"), 164 }, 165 UncappedTarget: map[v1.ResourceName]resource.Quantity{ 166 v1.ResourceMemory: resource.MustParse("1Gi"), 167 v1.ResourceCPU: resource.MustParse("1"), 168 }, 169 }, 170 }, 171 }, 172 }, 173 } 174 vpa2 := &apis.KatalystVerticalPodAutoscaler{ 175 ObjectMeta: metav1.ObjectMeta{ 176 Namespace: "default", 177 Name: "vpa1", 178 }, 179 Spec: apis.KatalystVerticalPodAutoscalerSpec{ 180 TargetRef: apis.CrossVersionObjectReference{ 181 Kind: stsGVK.Kind, 182 Name: "sts1", 183 APIVersion: stsGVK.GroupVersion().String(), 184 }, 185 }, 186 Status: apis.KatalystVerticalPodAutoscalerStatus{ 187 ContainerResources: []apis.ContainerResources{ 188 { 189 ContainerName: pointer.String("c1"), 190 Requests: &apis.ContainerResourceList{ 191 Target: map[v1.ResourceName]resource.Quantity{ 192 v1.ResourceMemory: resource.MustParse("3Gi"), 193 v1.ResourceCPU: resource.MustParse("3"), 194 }, 195 UncappedTarget: map[v1.ResourceName]resource.Quantity{ 196 v1.ResourceMemory: resource.MustParse("3Gi"), 197 v1.ResourceCPU: resource.MustParse("3"), 198 }, 199 }, 200 }, 201 }, 202 }, 203 } 204 vpa3 := &apis.KatalystVerticalPodAutoscaler{ 205 ObjectMeta: metav1.ObjectMeta{ 206 Namespace: "default", 207 Name: "vpa1", 208 }, 209 Spec: apis.KatalystVerticalPodAutoscalerSpec{ 210 TargetRef: apis.CrossVersionObjectReference{ 211 Kind: stsGVK.Kind, 212 Name: "sts1", 213 APIVersion: stsGVK.GroupVersion().String(), 214 }, 215 }, 216 Status: apis.KatalystVerticalPodAutoscalerStatus{ 217 PodResources: []apis.PodResources{ 218 { 219 PodName: pointer.String("pod1"), 220 ContainerResources: []apis.ContainerResources{ 221 { 222 ContainerName: pointer.String("c1"), 223 Requests: &apis.ContainerResourceList{ 224 Target: map[v1.ResourceName]resource.Quantity{ 225 v1.ResourceMemory: resource.MustParse("3Gi"), 226 v1.ResourceCPU: resource.MustParse("3"), 227 }, 228 UncappedTarget: map[v1.ResourceName]resource.Quantity{ 229 v1.ResourceMemory: resource.MustParse("3Gi"), 230 v1.ResourceCPU: resource.MustParse("3"), 231 }, 232 }, 233 Limits: &apis.ContainerResourceList{ 234 Target: map[v1.ResourceName]resource.Quantity{ 235 v1.ResourceMemory: resource.MustParse("3Gi"), 236 v1.ResourceCPU: resource.MustParse("3"), 237 }, 238 UncappedTarget: map[v1.ResourceName]resource.Quantity{ 239 v1.ResourceMemory: resource.MustParse("3Gi"), 240 v1.ResourceCPU: resource.MustParse("3"), 241 }, 242 }, 243 }, 244 }, 245 }, 246 }, 247 }, 248 } 249 250 vpa4 := &apis.KatalystVerticalPodAutoscaler{ 251 ObjectMeta: metav1.ObjectMeta{ 252 Namespace: "default", 253 Name: "vpa1", 254 }, 255 Spec: apis.KatalystVerticalPodAutoscalerSpec{ 256 TargetRef: apis.CrossVersionObjectReference{ 257 Kind: stsGVK.Kind, 258 Name: "sts1", 259 APIVersion: stsGVK.GroupVersion().String(), 260 }, 261 }, 262 Status: apis.KatalystVerticalPodAutoscalerStatus{ 263 ContainerResources: []apis.ContainerResources{ 264 { 265 ContainerName: pointer.String("c1"), 266 Limits: &apis.ContainerResourceList{ 267 Target: v1.ResourceList{}, 268 UncappedTarget: v1.ResourceList{}, 269 LowerBound: v1.ResourceList{ 270 v1.ResourceMemory: resource.MustParse("400Mi"), 271 }, 272 UpperBound: v1.ResourceList{ 273 v1.ResourceMemory: resource.MustParse("6000Mi"), 274 }, 275 }, 276 }, 277 }, 278 }, 279 } 280 281 sts1 := &appsv1.StatefulSet{ 282 TypeMeta: metav1.TypeMeta{ 283 Kind: "StatefulSet", 284 APIVersion: "apps/v1", 285 }, 286 ObjectMeta: metav1.ObjectMeta{ 287 Namespace: "default", 288 Name: "sts1", 289 }, 290 Spec: appsv1.StatefulSetSpec{ 291 Replicas: pointer.Int32(1), 292 Template: v1.PodTemplateSpec{ 293 Spec: v1.PodSpec{ 294 Containers: []v1.Container{*container1}, 295 }, 296 }, 297 Selector: &metav1.LabelSelector{ 298 MatchLabels: map[string]string{ 299 "workload": "sts", 300 }, 301 }, 302 }, 303 Status: appsv1.StatefulSetStatus{}, 304 } 305 for _, tc := range []struct { 306 name string 307 vpa *apis.KatalystVerticalPodAutoscaler 308 pods []*v1.Pod 309 object runtime.Object 310 gvk schema.GroupVersionKind 311 vpaPodResources []apis.PodResources 312 vpaContainerResources []apis.ContainerResources 313 }{ 314 { 315 name: "shrink resource: vpa container", 316 vpa: vpa1, 317 pods: []*v1.Pod{pod1}, 318 object: sts1, 319 gvk: stsGVK, 320 vpaPodResources: []apis.PodResources{}, 321 vpaContainerResources: []apis.ContainerResources{ 322 { 323 ContainerName: pointer.String("c1"), 324 Requests: &apis.ContainerResourceList{ 325 Current: v1.ResourceList{ 326 v1.ResourceMemory: resource.MustParse("2Gi"), 327 v1.ResourceCPU: resource.MustParse("2"), 328 }, 329 Target: v1.ResourceList{ 330 v1.ResourceMemory: resource.MustParse("1Gi"), 331 v1.ResourceCPU: resource.MustParse("1"), 332 }, 333 UncappedTarget: v1.ResourceList{ 334 v1.ResourceMemory: resource.MustParse("1Gi"), 335 v1.ResourceCPU: resource.MustParse("1"), 336 }, 337 }, 338 }, 339 }, 340 }, 341 { 342 name: "expand resource: vpa container", 343 vpa: vpa2, 344 pods: []*v1.Pod{pod1}, 345 object: sts1, 346 gvk: stsGVK, 347 vpaPodResources: []apis.PodResources{}, 348 vpaContainerResources: []apis.ContainerResources{ 349 { 350 ContainerName: pointer.String("c1"), 351 Requests: &apis.ContainerResourceList{ 352 Current: v1.ResourceList{ 353 v1.ResourceMemory: resource.MustParse("2Gi"), 354 v1.ResourceCPU: resource.MustParse("2"), 355 }, 356 Target: v1.ResourceList{ 357 v1.ResourceMemory: resource.MustParse("3Gi"), 358 v1.ResourceCPU: resource.MustParse("3"), 359 }, 360 UncappedTarget: v1.ResourceList{ 361 v1.ResourceMemory: resource.MustParse("3Gi"), 362 v1.ResourceCPU: resource.MustParse("3"), 363 }, 364 }, 365 }, 366 }, 367 }, 368 { 369 name: "expand resource: two pod", 370 vpa: vpa3, 371 pods: []*v1.Pod{pod1, pod2}, 372 object: sts1, 373 gvk: stsGVK, 374 vpaPodResources: []apis.PodResources{ 375 { 376 PodName: pointer.String("pod1"), 377 ContainerResources: []apis.ContainerResources{ 378 { 379 ContainerName: pointer.String("c1"), 380 Requests: &apis.ContainerResourceList{ 381 Current: v1.ResourceList{ 382 v1.ResourceMemory: resource.MustParse("2Gi"), 383 v1.ResourceCPU: resource.MustParse("2"), 384 }, 385 Target: v1.ResourceList{ 386 v1.ResourceMemory: resource.MustParse("3Gi"), 387 v1.ResourceCPU: resource.MustParse("3"), 388 }, 389 UncappedTarget: v1.ResourceList{ 390 v1.ResourceMemory: resource.MustParse("3Gi"), 391 v1.ResourceCPU: resource.MustParse("3"), 392 }, 393 }, 394 Limits: &apis.ContainerResourceList{ 395 Current: v1.ResourceList{ 396 v1.ResourceMemory: resource.MustParse("4Gi"), 397 v1.ResourceCPU: resource.MustParse("4"), 398 }, 399 Target: v1.ResourceList{ 400 v1.ResourceMemory: resource.MustParse("3Gi"), 401 v1.ResourceCPU: resource.MustParse("3"), 402 }, 403 UncappedTarget: v1.ResourceList{ 404 v1.ResourceMemory: resource.MustParse("3Gi"), 405 v1.ResourceCPU: resource.MustParse("3"), 406 }, 407 }, 408 }, 409 }, 410 }, 411 }, 412 vpaContainerResources: []apis.ContainerResources{}, 413 }, 414 { 415 name: "expand resource: without limit", 416 vpa: vpa4, 417 pods: []*v1.Pod{pod1}, 418 object: sts1, 419 gvk: stsGVK, 420 vpaPodResources: []apis.PodResources{}, 421 vpaContainerResources: []apis.ContainerResources{ 422 { 423 ContainerName: pointer.String("c1"), 424 Limits: &apis.ContainerResourceList{ 425 Current: v1.ResourceList{}, 426 Target: v1.ResourceList{}, 427 UncappedTarget: v1.ResourceList{}, 428 LowerBound: v1.ResourceList{ 429 v1.ResourceMemory: resource.MustParse("400Mi"), 430 }, 431 UpperBound: v1.ResourceList{ 432 v1.ResourceMemory: resource.MustParse("6000Mi"), 433 }, 434 }, 435 }, 436 }, 437 }, 438 } { 439 t.Run(tc.name, func(t *testing.T) { 440 fakeDynamicClient := dynamicfake.NewSimpleDynamicClient(runtime.NewScheme()) 441 dynamicFactory := dynamicinformer.NewDynamicSharedInformerFactory(fakeDynamicClient, 0) 442 stsInformer := dynamicFactory.ForResource(stsGVR) 443 workloadInformers := map[schema.GroupVersionKind]informers.GenericInformer{ 444 stsGVK: stsInformer, 445 } 446 447 KubeClient := fake.NewSimpleClientset() 448 KubeInformerFactory := informers.NewSharedInformerFactory(KubeClient, 0) 449 450 u, err := native.ToUnstructured(tc.object) 451 assert.NoError(t, err) 452 err = workloadInformers[tc.gvk].Informer().GetStore().Add(u) 453 assert.NoError(t, err) 454 455 podInformer := KubeInformerFactory.Core().V1().Pods() 456 err = podInformer.Informer().AddIndexers(podIndexers) 457 assert.NoError(t, err) 458 459 for _, pod := range tc.pods { 460 err = podInformer.Informer().GetStore().Add(pod) 461 assert.NoError(t, err) 462 } 463 464 pods, err := katalystutil.GetPodListForVPA(tc.vpa, podInformer.Informer().GetIndexer(), podIndexerKeys, workloadInformers[tc.gvk].Lister(), podInformer.Lister()) 465 assert.NoError(t, err) 466 467 vpaPodResources, vpaContainerResources, err := GetVPAResourceStatusWithCurrent(tc.vpa, pods) 468 assert.NoError(t, err) 469 assert.Equal(t, tc.vpaPodResources, vpaPodResources) 470 assert.Equal(t, tc.vpaContainerResources, vpaContainerResources) 471 }) 472 } 473 } 474 475 func TestGetVPAResourceStatusWithRecommendation(t *testing.T) { 476 t.Parallel() 477 478 type args struct { 479 vpa *apis.KatalystVerticalPodAutoscaler 480 recPodResources []apis.RecommendedPodResources 481 recContainerResources []apis.RecommendedContainerResources 482 } 483 tests := []struct { 484 name string 485 args args 486 want []apis.PodResources 487 want1 []apis.ContainerResources 488 wantErr assert.ErrorAssertionFunc 489 }{ 490 { 491 name: "without recommend limit but with memory bound", 492 args: args{ 493 vpa: &apis.KatalystVerticalPodAutoscaler{ 494 ObjectMeta: metav1.ObjectMeta{ 495 Namespace: "default", 496 Name: "vpa1", 497 }, 498 Spec: apis.KatalystVerticalPodAutoscalerSpec{ 499 TargetRef: apis.CrossVersionObjectReference{ 500 Kind: stsGVK.Kind, 501 Name: "sts1", 502 APIVersion: stsGVK.GroupVersion().String(), 503 }, 504 ResourcePolicy: apis.PodResourcePolicy{ 505 ContainerPolicies: []apis.ContainerResourcePolicy{ 506 { 507 ContainerName: pointer.String("nginx"), 508 ControlledResources: []v1.ResourceName{ 509 v1.ResourceCPU, 510 v1.ResourceMemory, 511 }, 512 ControlledValues: apis.ContainerControlledValuesRequestsAndLimits, 513 MaxAllowed: v1.ResourceList{ 514 v1.ResourceMemory: resource.MustParse("6000Mi"), 515 }, 516 MinAllowed: v1.ResourceList{ 517 v1.ResourceMemory: resource.MustParse("400Mi"), 518 }, 519 ResourceResizePolicy: apis.ResourceResizePolicyNone, 520 }, 521 }, 522 }, 523 UpdatePolicy: apis.PodUpdatePolicy{ 524 PodApplyStrategy: apis.PodApplyStrategyStrategyGroup, 525 PodMatchingStrategy: apis.PodMatchingStrategyAll, 526 PodUpdatingStrategy: apis.PodUpdatingStrategyInplace, 527 }, 528 }, 529 Status: apis.KatalystVerticalPodAutoscalerStatus{ 530 ContainerResources: []apis.ContainerResources{ 531 { 532 ContainerName: pointer.String("nginx"), 533 Limits: &apis.ContainerResourceList{ 534 Current: v1.ResourceList{ 535 v1.ResourceCPU: resource.MustParse("4"), 536 v1.ResourceMemory: resource.MustParse("6000Mi"), 537 }, 538 }, 539 }, 540 }, 541 }, 542 }, 543 recContainerResources: []apis.RecommendedContainerResources{ 544 { 545 ContainerName: pointer.String("nginx"), 546 Limits: &apis.RecommendedRequestResources{ 547 Resources: v1.ResourceList{}, 548 }, 549 }, 550 }, 551 }, 552 want: []apis.PodResources{}, 553 want1: []apis.ContainerResources{ 554 { 555 ContainerName: pointer.String("nginx"), 556 Limits: &apis.ContainerResourceList{ 557 Current: v1.ResourceList{ 558 v1.ResourceCPU: resource.MustParse("4"), 559 v1.ResourceMemory: resource.MustParse("6000Mi"), 560 }, 561 Target: v1.ResourceList{}, 562 UncappedTarget: v1.ResourceList{}, 563 LowerBound: v1.ResourceList{ 564 v1.ResourceMemory: resource.MustParse("400Mi"), 565 }, 566 UpperBound: v1.ResourceList{ 567 v1.ResourceMemory: resource.MustParse("6000Mi"), 568 }, 569 }, 570 }, 571 }, 572 wantErr: func(t assert.TestingT, err error, i ...interface{}) bool { 573 return true 574 }, 575 }, 576 { 577 name: "without either recommend limit or bound", 578 args: args{ 579 vpa: &apis.KatalystVerticalPodAutoscaler{ 580 ObjectMeta: metav1.ObjectMeta{ 581 Namespace: "default", 582 Name: "vpa1", 583 }, 584 Spec: apis.KatalystVerticalPodAutoscalerSpec{ 585 TargetRef: apis.CrossVersionObjectReference{ 586 Kind: stsGVK.Kind, 587 Name: "sts1", 588 APIVersion: stsGVK.GroupVersion().String(), 589 }, 590 ResourcePolicy: apis.PodResourcePolicy{ 591 ContainerPolicies: []apis.ContainerResourcePolicy{ 592 { 593 ContainerName: pointer.String("nginx"), 594 ControlledResources: []v1.ResourceName{ 595 v1.ResourceCPU, 596 v1.ResourceMemory, 597 }, 598 ControlledValues: apis.ContainerControlledValuesRequestsAndLimits, 599 ResourceResizePolicy: apis.ResourceResizePolicyNone, 600 }, 601 }, 602 }, 603 UpdatePolicy: apis.PodUpdatePolicy{ 604 PodApplyStrategy: apis.PodApplyStrategyStrategyGroup, 605 PodMatchingStrategy: apis.PodMatchingStrategyAll, 606 PodUpdatingStrategy: apis.PodUpdatingStrategyInplace, 607 }, 608 }, 609 }, 610 recContainerResources: []apis.RecommendedContainerResources{ 611 { 612 ContainerName: pointer.String("nginx"), 613 Limits: &apis.RecommendedRequestResources{ 614 Resources: v1.ResourceList{}, 615 }, 616 }, 617 }, 618 }, 619 want: []apis.PodResources{}, 620 want1: []apis.ContainerResources{ 621 { 622 ContainerName: pointer.String("nginx"), 623 Limits: &apis.ContainerResourceList{ 624 Target: v1.ResourceList{}, 625 UncappedTarget: v1.ResourceList{}, 626 LowerBound: nil, 627 UpperBound: nil, 628 }, 629 }, 630 }, 631 wantErr: func(t assert.TestingT, err error, i ...interface{}) bool { 632 return true 633 }, 634 }, 635 } 636 for _, tt := range tests { 637 tt := tt 638 t.Run(tt.name, func(t *testing.T) { 639 t.Parallel() 640 got, got1, err := GetVPAResourceStatusWithRecommendation(tt.args.vpa, tt.args.recPodResources, tt.args.recContainerResources) 641 if !tt.wantErr(t, err, fmt.Sprintf("GetVPAResourceStatusWithRecommendation(%v, %v, %v)", tt.args.vpa, tt.args.recPodResources, tt.args.recContainerResources)) { 642 return 643 } 644 assert.Equalf(t, tt.want, got, "GetVPAResourceStatusWithRecommendation(%v, %v, %v)", tt.args.vpa, tt.args.recPodResources, tt.args.recContainerResources) 645 assert.Equalf(t, tt.want1, got1, "GetVPAResourceStatusWithRecommendation(%v, %v, %v)", tt.args.vpa, tt.args.recPodResources, tt.args.recContainerResources) 646 }) 647 } 648 }