@@ -14,6 +14,7 @@ import (
1414 "github.com/google/go-cmp/cmp"
1515 "github.com/stretchr/testify/assert"
1616 corev1 "k8s.io/api/core/v1"
17+ "k8s.io/apimachinery/pkg/api/resource"
1718 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1819
1920 "github.com/newrelic/nri-kubernetes/v3/internal/config"
@@ -573,7 +574,8 @@ func TestFillContainerStatuses_IsReadyForAllStates(t *testing.T) {
573574 {Name : testContainerName , Ready : true , State : corev1.ContainerState {Running : & corev1.ContainerStateRunning {}}},
574575 }
575576 dest := make (map [string ]definition.RawMetrics )
576- fillContainerStatuses (pod , dest )
577+ containerStatusByName := getContainerStatusesByName (pod )
578+ fillContainerStatuses (pod , containerStatusByName , dest )
577579 id := fmt .Sprintf ("%s_%s_%s" , namespace , podName , testContainerName )
578580 assert .Equal (t , true , dest [id ]["isReady" ])
579581 })
@@ -585,7 +587,8 @@ func TestFillContainerStatuses_IsReadyForAllStates(t *testing.T) {
585587 {Name : testContainerName , Ready : false , State : corev1.ContainerState {Waiting : & corev1.ContainerStateWaiting {Reason : "CrashLoopBackOff" }}},
586588 }
587589 dest := make (map [string ]definition.RawMetrics )
588- fillContainerStatuses (pod , dest )
590+ containerStatusByName := getContainerStatusesByName (pod )
591+ fillContainerStatuses (pod , containerStatusByName , dest )
589592 id := fmt .Sprintf ("%s_%s_%s" , namespace , podName , testContainerName )
590593 assert .Equal (t , false , dest [id ]["isReady" ])
591594 })
@@ -597,7 +600,8 @@ func TestFillContainerStatuses_IsReadyForAllStates(t *testing.T) {
597600 {Name : testContainerName , Ready : false , State : corev1.ContainerState {Terminated : & corev1.ContainerStateTerminated {ExitCode : 0 }}},
598601 }
599602 dest := make (map [string ]definition.RawMetrics )
600- fillContainerStatuses (pod , dest )
603+ containerStatusByName := getContainerStatusesByName (pod )
604+ fillContainerStatuses (pod , containerStatusByName , dest )
601605 id := fmt .Sprintf ("%s_%s_%s" , namespace , podName , testContainerName )
602606 assert .Equal (t , false , dest [id ]["isReady" ])
603607 })
@@ -609,7 +613,8 @@ func TestFillContainerStatuses_IsReadyForAllStates(t *testing.T) {
609613 {Name : testContainerName , Ready : false , State : corev1.ContainerState {}}, // all nil → Unknown
610614 }
611615 dest := make (map [string ]definition.RawMetrics )
612- fillContainerStatuses (pod , dest )
616+ containerStatusByName := getContainerStatusesByName (pod )
617+ fillContainerStatuses (pod , containerStatusByName , dest )
613618 id := fmt .Sprintf ("%s_%s_%s" , namespace , podName , testContainerName )
614619 assert .Equal (t , false , dest [id ]["isReady" ])
615620 })
@@ -706,3 +711,92 @@ func TestFetchPodData_WithPriorityClassNameOnly(t *testing.T) {
706711 assert .False (t , hasPriority , "priority should not be present when nil" )
707712 assert .Equal (t , "system-cluster-critical" , result ["priorityClassName" ])
708713}
714+
715+ // TestFetchContainersData_InPlaceVerticalScaling verifies that when
716+ // ContainerStatus.Resources is populated (K8s 1.33+ in-place pod vertical scaling),
717+ // the actual applied resources are used instead of Spec resources (desired state).
718+ func TestFetchContainersData_InPlaceVerticalScaling (t * testing.T ) {
719+ t .Parallel ()
720+
721+ specCPU := resource .MustParse ("100m" )
722+ specMemory := resource .MustParse ("128Mi" )
723+ statusCPU := resource .MustParse ("200m" ) // resized up
724+ statusMemory := resource .MustParse ("256Mi" )
725+
726+ pod := & corev1.Pod {
727+ ObjectMeta : metav1.ObjectMeta {
728+ Name : "test-pod" ,
729+ Namespace : "default" ,
730+ },
731+ Spec : corev1.PodSpec {
732+ NodeName : "test-node" ,
733+ Containers : []corev1.Container {
734+ {
735+ Name : "app" ,
736+ Image : "app:latest" ,
737+ Resources : corev1.ResourceRequirements {
738+ Requests : corev1.ResourceList {
739+ corev1 .ResourceCPU : specCPU ,
740+ corev1 .ResourceMemory : specMemory ,
741+ },
742+ Limits : corev1.ResourceList {
743+ corev1 .ResourceCPU : specCPU ,
744+ corev1 .ResourceMemory : specMemory ,
745+ },
746+ },
747+ },
748+ {
749+ Name : "sidecar" ,
750+ Image : "sidecar:latest" ,
751+ Resources : corev1.ResourceRequirements {
752+ Requests : corev1.ResourceList {
753+ corev1 .ResourceCPU : specCPU ,
754+ corev1 .ResourceMemory : specMemory ,
755+ },
756+ },
757+ },
758+ },
759+ },
760+ Status : corev1.PodStatus {
761+ HostIP : "192.168.0.1" ,
762+ ContainerStatuses : []corev1.ContainerStatus {
763+ {
764+ Name : "app" ,
765+ // Resources populated: actual applied state after resize
766+ Resources : & corev1.ResourceRequirements {
767+ Requests : corev1.ResourceList {
768+ corev1 .ResourceCPU : statusCPU ,
769+ corev1 .ResourceMemory : statusMemory ,
770+ },
771+ Limits : corev1.ResourceList {
772+ corev1 .ResourceCPU : statusCPU ,
773+ corev1 .ResourceMemory : statusMemory ,
774+ },
775+ },
776+ },
777+ {
778+ Name : "sidecar" ,
779+ Resources : nil , // no status resources; fall back to spec
780+ },
781+ },
782+ },
783+ }
784+
785+ podFetcher := & PodsFetcher {}
786+ result := podFetcher .fetchContainersData (pod )
787+
788+ appID := "default_test-pod_app"
789+ sidecarID := "default_test-pod_sidecar"
790+
791+ // "app" container: status resources should take precedence
792+ assert .Equal (t , statusCPU .MilliValue (), result [appID ]["cpuRequestedCores" ], "app: should use status CPU request" )
793+ assert .Equal (t , statusCPU .MilliValue (), result [appID ]["cpuLimitCores" ], "app: should use status CPU limit" )
794+ assert .Equal (t , statusMemory .Value (), result [appID ]["memoryRequestedBytes" ], "app: should use status memory request" )
795+ assert .Equal (t , statusMemory .Value (), result [appID ]["memoryLimitBytes" ], "app: should use status memory limit" )
796+
797+ // "sidecar" container: status resources nil, should fall back to spec
798+ assert .Equal (t , specCPU .MilliValue (), result [sidecarID ]["cpuRequestedCores" ], "sidecar: should fall back to spec CPU request" )
799+ assert .Equal (t , specMemory .Value (), result [sidecarID ]["memoryRequestedBytes" ], "sidecar: should fall back to spec memory request" )
800+ _ , hasLimit := result [sidecarID ]["cpuLimitCores" ]
801+ assert .False (t , hasLimit , "sidecar: no CPU limit in spec, should not be set" )
802+ }
0 commit comments