@@ -22,6 +22,7 @@ import (
22
22
"fmt"
23
23
"path/filepath"
24
24
"reflect"
25
+ goruntime "runtime"
25
26
"sort"
26
27
"testing"
27
28
"time"
@@ -31,6 +32,7 @@ import (
31
32
32
33
cadvisorapi "github.com/google/cadvisor/info/v1"
33
34
"github.com/stretchr/testify/assert"
35
+ "github.com/stretchr/testify/mock"
34
36
"github.com/stretchr/testify/require"
35
37
noopoteltrace "go.opentelemetry.io/otel/trace/noop"
36
38
@@ -47,6 +49,8 @@ import (
47
49
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
48
50
"k8s.io/kubernetes/pkg/credentialprovider"
49
51
"k8s.io/kubernetes/pkg/features"
52
+ "k8s.io/kubernetes/pkg/kubelet/cm"
53
+ cmtesting "k8s.io/kubernetes/pkg/kubelet/cm/testing"
50
54
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
51
55
containertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
52
56
imagetypes "k8s.io/kubernetes/pkg/kubelet/images"
@@ -2826,3 +2830,197 @@ func TestGetImageVolumes(t *testing.T) {
2826
2830
assert .Equal (t , tc .expectedImageVolumePulls , imageVolumePulls )
2827
2831
}
2828
2832
}
2833
+
2834
+ func TestDoPodResizeAction (t * testing.T ) {
2835
+ if goruntime .GOOS != "linux" {
2836
+ t .Skip ("unsupported OS" )
2837
+ }
2838
+
2839
+ featuregatetesting .SetFeatureGateDuringTest (t , utilfeature .DefaultFeatureGate , features .InPlacePodVerticalScaling , true )
2840
+ _ , _ , m , err := createTestRuntimeManager ()
2841
+ require .NoError (t , err )
2842
+ m .cpuCFSQuota = true // Enforce CPU Limits
2843
+
2844
+ for _ , tc := range []struct {
2845
+ testName string
2846
+ currentResources containerResources
2847
+ desiredResources containerResources
2848
+ updatedResources []v1.ResourceName
2849
+ otherContainersHaveLimits bool
2850
+ expectedError string
2851
+ expectPodCgroupUpdates int
2852
+ }{
2853
+ {
2854
+ testName : "Increase cpu and memory requests and limits, with computed pod limits" ,
2855
+ currentResources : containerResources {
2856
+ cpuRequest : 100 , cpuLimit : 100 ,
2857
+ memoryRequest : 100 , memoryLimit : 100 ,
2858
+ },
2859
+ desiredResources : containerResources {
2860
+ cpuRequest : 200 , cpuLimit : 200 ,
2861
+ memoryRequest : 200 , memoryLimit : 200 ,
2862
+ },
2863
+ otherContainersHaveLimits : true ,
2864
+ updatedResources : []v1.ResourceName {v1 .ResourceCPU , v1 .ResourceMemory },
2865
+ expectPodCgroupUpdates : 3 , // cpu req, cpu lim, mem lim
2866
+ },
2867
+ {
2868
+ testName : "Increase cpu and memory requests and limits, without computed pod limits" ,
2869
+ currentResources : containerResources {
2870
+ cpuRequest : 100 , cpuLimit : 100 ,
2871
+ memoryRequest : 100 , memoryLimit : 100 ,
2872
+ },
2873
+ desiredResources : containerResources {
2874
+ cpuRequest : 200 , cpuLimit : 200 ,
2875
+ memoryRequest : 200 , memoryLimit : 200 ,
2876
+ },
2877
+ // If some containers don't have limits, pod level limits are not applied
2878
+ otherContainersHaveLimits : false ,
2879
+ updatedResources : []v1.ResourceName {v1 .ResourceCPU , v1 .ResourceMemory },
2880
+ expectPodCgroupUpdates : 1 , // cpu req, cpu lim, mem lim
2881
+ },
2882
+ {
2883
+ testName : "Increase cpu and memory requests only" ,
2884
+ currentResources : containerResources {
2885
+ cpuRequest : 100 , cpuLimit : 200 ,
2886
+ memoryRequest : 100 , memoryLimit : 200 ,
2887
+ },
2888
+ desiredResources : containerResources {
2889
+ cpuRequest : 150 , cpuLimit : 200 ,
2890
+ memoryRequest : 150 , memoryLimit : 200 ,
2891
+ },
2892
+ updatedResources : []v1.ResourceName {v1 .ResourceCPU },
2893
+ expectPodCgroupUpdates : 1 , // cpu req
2894
+ },
2895
+ {
2896
+ testName : "Resize memory request no limits" ,
2897
+ currentResources : containerResources {
2898
+ cpuRequest : 100 ,
2899
+ memoryRequest : 100 ,
2900
+ },
2901
+ desiredResources : containerResources {
2902
+ cpuRequest : 100 ,
2903
+ memoryRequest : 200 ,
2904
+ },
2905
+ // Memory request resize doesn't generate an update action.
2906
+ updatedResources : []v1.ResourceName {},
2907
+ },
2908
+ {
2909
+ testName : "Resize cpu request no limits" ,
2910
+ currentResources : containerResources {
2911
+ cpuRequest : 100 ,
2912
+ memoryRequest : 100 ,
2913
+ },
2914
+ desiredResources : containerResources {
2915
+ cpuRequest : 200 ,
2916
+ memoryRequest : 100 ,
2917
+ },
2918
+ updatedResources : []v1.ResourceName {v1 .ResourceCPU },
2919
+ expectPodCgroupUpdates : 1 , // cpu req
2920
+ },
2921
+ {
2922
+ testName : "Add limits" ,
2923
+ currentResources : containerResources {
2924
+ cpuRequest : 100 ,
2925
+ memoryRequest : 100 ,
2926
+ },
2927
+ desiredResources : containerResources {
2928
+ cpuRequest : 100 , cpuLimit : 100 ,
2929
+ memoryRequest : 100 , memoryLimit : 100 ,
2930
+ },
2931
+ updatedResources : []v1.ResourceName {v1 .ResourceCPU , v1 .ResourceMemory },
2932
+ expectPodCgroupUpdates : 0 ,
2933
+ },
2934
+ {
2935
+ testName : "Add limits and pod limits" ,
2936
+ currentResources : containerResources {
2937
+ cpuRequest : 100 ,
2938
+ memoryRequest : 100 ,
2939
+ },
2940
+ desiredResources : containerResources {
2941
+ cpuRequest : 100 , cpuLimit : 100 ,
2942
+ memoryRequest : 100 , memoryLimit : 100 ,
2943
+ },
2944
+ otherContainersHaveLimits : true ,
2945
+ updatedResources : []v1.ResourceName {v1 .ResourceCPU , v1 .ResourceMemory },
2946
+ expectPodCgroupUpdates : 2 , // cpu lim, memory lim
2947
+ },
2948
+ } {
2949
+ t .Run (tc .testName , func (t * testing.T ) {
2950
+ mockCM := cmtesting .NewMockContainerManager (t )
2951
+ m .containerManager = mockCM
2952
+ mockPCM := cmtesting .NewMockPodContainerManager (t )
2953
+ mockCM .EXPECT ().NewPodContainerManager ().Return (mockPCM )
2954
+
2955
+ mockPCM .EXPECT ().GetPodCgroupConfig (mock .Anything , v1 .ResourceMemory ).Return (& cm.ResourceConfig {
2956
+ Memory : ptr .To (tc .currentResources .memoryLimit ),
2957
+ }, nil ).Maybe ()
2958
+ mockPCM .EXPECT ().GetPodCgroupMemoryUsage (mock .Anything ).Return (0 , nil ).Maybe ()
2959
+ // Set up mock pod cgroup config
2960
+ podCPURequest := tc .currentResources .cpuRequest
2961
+ podCPULimit := tc .currentResources .cpuLimit
2962
+ if tc .otherContainersHaveLimits {
2963
+ podCPURequest += 200
2964
+ podCPULimit += 200
2965
+ }
2966
+ mockPCM .EXPECT ().GetPodCgroupConfig (mock .Anything , v1 .ResourceCPU ).Return (& cm.ResourceConfig {
2967
+ CPUShares : ptr .To (cm .MilliCPUToShares (podCPURequest )),
2968
+ CPUQuota : ptr .To (cm .MilliCPUToQuota (podCPULimit , cm .QuotaPeriod )),
2969
+ }, nil ).Maybe ()
2970
+ if tc .expectPodCgroupUpdates > 0 {
2971
+ mockPCM .EXPECT ().SetPodCgroupConfig (mock .Anything , mock .Anything ).Return (nil ).Times (tc .expectPodCgroupUpdates )
2972
+ }
2973
+
2974
+ pod , kps := makeBasePodAndStatus ()
2975
+ // pod spec and allocated resources are already updated as desired when doPodResizeAction() is called.
2976
+ pod .Spec .Containers [0 ].Resources = v1.ResourceRequirements {
2977
+ Requests : v1.ResourceList {
2978
+ v1 .ResourceCPU : * resource .NewMilliQuantity (tc .desiredResources .cpuRequest , resource .DecimalSI ),
2979
+ v1 .ResourceMemory : * resource .NewQuantity (tc .desiredResources .memoryRequest , resource .DecimalSI ),
2980
+ },
2981
+ Limits : v1.ResourceList {
2982
+ v1 .ResourceCPU : * resource .NewMilliQuantity (tc .desiredResources .cpuLimit , resource .DecimalSI ),
2983
+ v1 .ResourceMemory : * resource .NewQuantity (tc .desiredResources .memoryLimit , resource .DecimalSI ),
2984
+ },
2985
+ }
2986
+ if tc .otherContainersHaveLimits {
2987
+ resourceList := v1.ResourceList {
2988
+ v1 .ResourceCPU : resource .MustParse ("100m" ),
2989
+ v1 .ResourceMemory : resource .MustParse ("100M" ),
2990
+ }
2991
+ resources := v1.ResourceRequirements {
2992
+ Requests : resourceList ,
2993
+ Limits : resourceList ,
2994
+ }
2995
+ pod .Spec .Containers [1 ].Resources = resources
2996
+ pod .Spec .Containers [2 ].Resources = resources
2997
+ }
2998
+
2999
+ updateInfo := containerToUpdateInfo {
3000
+ apiContainerIdx : 0 ,
3001
+ kubeContainerID : kps .ContainerStatuses [0 ].ID ,
3002
+ desiredContainerResources : tc .desiredResources ,
3003
+ currentContainerResources : & tc .currentResources ,
3004
+ }
3005
+ containersToUpdate := make (map [v1.ResourceName ][]containerToUpdateInfo )
3006
+ for _ , r := range tc .updatedResources {
3007
+ containersToUpdate [r ] = []containerToUpdateInfo {updateInfo }
3008
+ }
3009
+
3010
+ syncResult := & kubecontainer.PodSyncResult {}
3011
+ actions := podActions {
3012
+ ContainersToUpdate : containersToUpdate ,
3013
+ }
3014
+ m .doPodResizeAction (pod , actions , syncResult )
3015
+
3016
+ if tc .expectedError != "" {
3017
+ require .Error (t , syncResult .Error ())
3018
+ require .EqualError (t , syncResult .Error (), tc .expectedError )
3019
+ } else {
3020
+ require .NoError (t , syncResult .Error ())
3021
+ }
3022
+
3023
+ mock .AssertExpectationsForObjects (t , mockPCM )
3024
+ })
3025
+ }
3026
+ }
0 commit comments