@@ -1429,78 +1429,39 @@ func TestStaticAutoscalerRunOnceWithUnselectedNodeGroups(t *testing.T) {
1429
1429
provider .AddNode ("ng1" , n1 )
1430
1430
assert .NotNil (t , provider )
1431
1431
1432
- tests := map [string ]struct {
1432
+ tests := []struct {
1433
+ name string
1433
1434
node * apiv1.Node
1434
1435
pods []* apiv1.Pod
1435
1436
expectedTaints []apiv1.Taint
1436
1437
}{
1437
- "Node from selected node groups can get their deletion candidate taints removed" : {
1438
+ {
1439
+ name : "Node from selected node groups can get their deletion candidate taints removed" ,
1438
1440
node : n1 ,
1439
1441
pods : []* apiv1.Pod {p1 },
1440
1442
expectedTaints : []apiv1.Taint {},
1441
1443
},
1442
- "Node from non-selected node groups should keep their deletion candidate taints" : {
1444
+ {
1445
+ name : "Node from non-selected node groups should keep their deletion candidate taints" ,
1443
1446
node : n2 ,
1444
1447
pods : nil ,
1445
1448
expectedTaints : n2 .Spec .Taints ,
1446
1449
},
1447
1450
}
1448
1451
1449
- for name , test := range tests {
1450
- // prevent issues with scoping, we should be able to get rid of that with Go 1.22
1451
- test := test
1452
- t .Run (name , func (t * testing.T ) {
1452
+ for _ , test := range tests {
1453
+ t .Run (test .name , func (t * testing.T ) {
1453
1454
t .Parallel ()
1454
- // Create fake listers for the generated nodes, nothing returned by the rest (but the ones used in the tested path have to be defined).
1455
- readyNodeLister := kubernetes .NewTestNodeLister ([]* apiv1.Node {test .node })
1456
- allNodeLister := kubernetes .NewTestNodeLister ([]* apiv1.Node {test .node })
1457
- allPodListerMock := kubernetes .NewTestPodLister (test .pods )
1458
- daemonSetLister , err := kubernetes .NewTestDaemonSetLister (nil )
1459
- assert .NoError (t , err )
1460
- listerRegistry := kube_util .NewListerRegistry (allNodeLister , readyNodeLister , allPodListerMock ,
1461
- kubernetes .NewTestPodDisruptionBudgetLister (nil ), daemonSetLister ,
1462
- nil , nil , nil , nil )
1455
+ allNodes := []* apiv1.Node {test .node }
1456
+ fakeClient := buildFakeClient (t , allNodes ... )
1457
+ autoscaler := buildStaticAutoscaler (t , provider , allNodes , allNodes , fakeClient )
1463
1458
1464
- // Create context with minimal autoscalingOptions that guarantee we reach the tested logic.
1465
- autoscalingOptions := config.AutoscalingOptions {
1466
- ScaleDownEnabled : true ,
1467
- MaxBulkSoftTaintCount : 10 ,
1468
- MaxBulkSoftTaintTime : 3 * time .Second ,
1469
- }
1470
- processorCallbacks := newStaticAutoscalerProcessorCallbacks ()
1471
- clientset := fake .NewSimpleClientset (test .node )
1472
- context , err := NewScaleTestAutoscalingContext (autoscalingOptions , clientset , listerRegistry , provider , processorCallbacks , nil )
1459
+ runningTime := time .Now ()
1460
+ err := autoscaler .RunOnce (runningTime )
1473
1461
assert .NoError (t , err )
1474
1462
1475
- // Create CSR with unhealthy cluster protection effectively disabled, to guarantee we reach the tested logic.
1476
- clusterStateConfig := clusterstate.ClusterStateRegistryConfig {
1477
- OkTotalUnreadyCount : 1 ,
1478
- }
1479
- processors := processorstest .NewTestProcessors (& context )
1480
-
1481
- clusterState := clusterstate .NewClusterStateRegistry (provider , clusterStateConfig , context .LogRecorder , NewBackoff (), nodegroupconfig .NewDefaultNodeGroupConfigProcessor (autoscalingOptions .NodeGroupDefaults ), processors .AsyncNodeGroupStateChecker )
1482
-
1483
- // Setting the Actuator is necessary for testing any scale-down logic, it shouldn't have anything to do in this test.
1484
- sdActuator := actuation .NewActuator (& context , clusterState , deletiontracker .NewNodeDeletionTracker (0 * time .Second ), options.NodeDeleteOptions {}, nil , processors .NodeGroupConfigProcessor , nil )
1485
- context .ScaleDownActuator = sdActuator
1486
-
1487
- // Fake planner that keeps track of the scale-down candidates passed to UpdateClusterState.
1488
- sdPlanner := & candidateTrackingFakePlanner {}
1489
-
1490
- autoscaler := & StaticAutoscaler {
1491
- AutoscalingContext : & context ,
1492
- clusterStateRegistry : clusterState ,
1493
- scaleDownPlanner : sdPlanner ,
1494
- scaleDownActuator : sdActuator ,
1495
- processors : processors ,
1496
- loopStartNotifier : loopstart .NewObserversList (nil ),
1497
- processorCallbacks : processorCallbacks ,
1498
- }
1499
-
1500
- err = autoscaler .RunOnce (time .Now ().Add (5 * time .Hour ))
1501
- assert .NoError (t , err )
1502
- newNode , err := clientset .CoreV1 ().Nodes ().Get (stdcontext .TODO (), test .node .Name , metav1.GetOptions {})
1503
- assert .NoError (t , err )
1463
+ newNode , clientErr := fakeClient .CoreV1 ().Nodes ().Get (stdcontext .TODO (), test .node .Name , metav1.GetOptions {})
1464
+ assert .NoError (t , clientErr )
1504
1465
assert .Equal (t , test .expectedTaints , newNode .Spec .Taints )
1505
1466
})
1506
1467
}
@@ -2773,3 +2734,155 @@ func newEstimatorBuilder() estimator.EstimatorBuilder {
2773
2734
2774
2735
return estimatorBuilder
2775
2736
}
2737
+
2738
+ func TestCleaningSoftTaintsInScaleDown (t * testing.T ) {
2739
+
2740
+ provider := testprovider .NewTestCloudProvider (nil , nil )
2741
+
2742
+ minSizeNgName := "ng-min-size"
2743
+ nodesToHaveNoTaints := createNodeGroupWithSoftTaintedNodes (provider , minSizeNgName , 2 , 10 , 2 )
2744
+
2745
+ notSizeNgName := "ng"
2746
+ nodesToHaveTaints := createNodeGroupWithSoftTaintedNodes (provider , notSizeNgName , 3 , 10 , 4 )
2747
+
2748
+ tests := []struct {
2749
+ name string
2750
+ testNodes []* apiv1.Node
2751
+ expectedScaleDownCoolDown bool
2752
+ expectedNodesWithSoftTaints []* apiv1.Node
2753
+ expectedNodesWithNoSoftTaints []* apiv1.Node
2754
+ }{
2755
+ {
2756
+ name : "Soft tainted nodes are cleaned in case of scale down is in cool down" ,
2757
+ testNodes : nodesToHaveNoTaints ,
2758
+ expectedScaleDownCoolDown : true ,
2759
+ expectedNodesWithSoftTaints : []* apiv1.Node {},
2760
+ expectedNodesWithNoSoftTaints : nodesToHaveNoTaints ,
2761
+ },
2762
+ {
2763
+ name : "Soft tainted nodes are not cleaned in case of scale down isn't in cool down" ,
2764
+ testNodes : nodesToHaveTaints ,
2765
+ expectedScaleDownCoolDown : false ,
2766
+ expectedNodesWithSoftTaints : nodesToHaveTaints ,
2767
+ expectedNodesWithNoSoftTaints : []* apiv1.Node {},
2768
+ },
2769
+ {
2770
+ name : "Soft tainted nodes are cleaned only from min sized node group in case of scale down isn't in cool down" ,
2771
+ testNodes : append (nodesToHaveNoTaints , nodesToHaveTaints ... ),
2772
+ expectedScaleDownCoolDown : false ,
2773
+ expectedNodesWithSoftTaints : nodesToHaveTaints ,
2774
+ expectedNodesWithNoSoftTaints : nodesToHaveNoTaints ,
2775
+ },
2776
+ }
2777
+ for _ , test := range tests {
2778
+ t .Run (test .name , func (t * testing.T ) {
2779
+ t .Parallel ()
2780
+ fakeClient := buildFakeClient (t , test .testNodes ... )
2781
+
2782
+ autoscaler := buildStaticAutoscaler (t , provider , test .testNodes , test .testNodes , fakeClient )
2783
+
2784
+ err := autoscaler .RunOnce (time .Now ())
2785
+
2786
+ assert .NoError (t , err )
2787
+ candidates , _ := autoscaler .processors .ScaleDownNodeProcessor .GetScaleDownCandidates (autoscaler .AutoscalingContext , test .testNodes )
2788
+ assert .Equal (t , test .expectedScaleDownCoolDown , autoscaler .isScaleDownInCooldown (time .Now (), candidates ))
2789
+
2790
+ assertNodesSoftTaintsStatus (t , fakeClient , test .expectedNodesWithSoftTaints , true )
2791
+ assertNodesSoftTaintsStatus (t , fakeClient , test .expectedNodesWithNoSoftTaints , false )
2792
+ })
2793
+ }
2794
+ }
2795
+
2796
+ func buildStaticAutoscaler (t * testing.T , provider cloudprovider.CloudProvider , allNodes []* apiv1.Node , readyNodes []* apiv1.Node , fakeClient * fake.Clientset ) * StaticAutoscaler {
2797
+ autoscalingOptions := config.AutoscalingOptions {
2798
+ NodeGroupDefaults : config.NodeGroupAutoscalingOptions {
2799
+ ScaleDownUnneededTime : time .Minute ,
2800
+ ScaleDownUnreadyTime : time .Minute ,
2801
+ ScaleDownUtilizationThreshold : 0.5 ,
2802
+ MaxNodeProvisionTime : 10 * time .Second ,
2803
+ },
2804
+ MaxScaleDownParallelism : 10 ,
2805
+ MaxDrainParallelism : 1 ,
2806
+ ScaleDownEnabled : true ,
2807
+ MaxBulkSoftTaintCount : 20 ,
2808
+ MaxBulkSoftTaintTime : 5 * time .Second ,
2809
+ NodeDeleteDelayAfterTaint : 5 * time .Minute ,
2810
+ ScaleDownSimulationTimeout : 10 * time .Second ,
2811
+ }
2812
+
2813
+ allNodeLister := kubernetes .NewTestNodeLister (allNodes )
2814
+ readyNodeLister := kubernetes .NewTestNodeLister (readyNodes )
2815
+
2816
+ daemonSetLister , err := kubernetes .NewTestDaemonSetLister (nil )
2817
+ assert .NoError (t , err )
2818
+ listerRegistry := kube_util .NewListerRegistry (allNodeLister , readyNodeLister ,
2819
+ kubernetes .NewTestPodLister (nil ),
2820
+ kubernetes .NewTestPodDisruptionBudgetLister (nil ), daemonSetLister , nil , nil , nil , nil )
2821
+
2822
+ processorCallbacks := newStaticAutoscalerProcessorCallbacks ()
2823
+
2824
+ ctx , err := NewScaleTestAutoscalingContext (autoscalingOptions , fakeClient , listerRegistry , provider , processorCallbacks , nil )
2825
+ assert .NoError (t , err )
2826
+
2827
+ processors := processorstest .NewTestProcessors (& ctx )
2828
+ cp := scaledowncandidates .NewCombinedScaleDownCandidatesProcessor ()
2829
+ cp .Register (scaledowncandidates .NewScaleDownCandidatesSortingProcessor ([]scaledowncandidates.CandidatesComparer {}))
2830
+ processors .ScaleDownNodeProcessor = cp
2831
+
2832
+ csr := clusterstate .NewClusterStateRegistry (provider , clusterstate.ClusterStateRegistryConfig {OkTotalUnreadyCount : 1 }, ctx .LogRecorder , NewBackoff (), nodegroupconfig .NewDefaultNodeGroupConfigProcessor (config.NodeGroupAutoscalingOptions {MaxNodeProvisionTime : 15 * time .Minute }), processors .AsyncNodeGroupStateChecker )
2833
+ actuator := actuation .NewActuator (& ctx , csr , deletiontracker .NewNodeDeletionTracker (0 * time .Second ), options.NodeDeleteOptions {}, nil , processors .NodeGroupConfigProcessor , nil )
2834
+ ctx .ScaleDownActuator = actuator
2835
+
2836
+ deleteOptions := options .NewNodeDeleteOptions (ctx .AutoscalingOptions )
2837
+ drainabilityRules := rules .Default (deleteOptions )
2838
+
2839
+ sdPlanner := planner .New (& ctx , processors , deleteOptions , drainabilityRules )
2840
+
2841
+ autoscaler := & StaticAutoscaler {
2842
+ AutoscalingContext : & ctx ,
2843
+ clusterStateRegistry : csr ,
2844
+ scaleDownActuator : actuator ,
2845
+ scaleDownPlanner : sdPlanner ,
2846
+ processors : processors ,
2847
+ loopStartNotifier : loopstart .NewObserversList (nil ),
2848
+ processorCallbacks : processorCallbacks ,
2849
+ }
2850
+ return autoscaler
2851
+ }
2852
+
2853
+ func buildFakeClient (t * testing.T , nodes ... * apiv1.Node ) * fake.Clientset {
2854
+ fakeClient := fake .NewSimpleClientset ()
2855
+ for _ , node := range nodes {
2856
+ _ , err := fakeClient .CoreV1 ().Nodes ().Create (stdcontext .TODO (), node , metav1.CreateOptions {})
2857
+ assert .NoError (t , err )
2858
+ }
2859
+ return fakeClient
2860
+ }
2861
+
2862
+ func createNodeGroupWithSoftTaintedNodes (provider * testprovider.TestCloudProvider , name string , minSize int , maxSize int , size int ) []* apiv1.Node {
2863
+ nodesCreationTime := time.Time {}
2864
+ var ngNodes []* apiv1.Node
2865
+ ng := provider .BuildNodeGroup (name , minSize , maxSize , size , true , false , "" , nil )
2866
+ provider .InsertNodeGroup (ng )
2867
+ for i := range size {
2868
+ node := BuildTestNode (fmt .Sprintf ("%s-node-%d" , name , i ), 2000 , 1000 )
2869
+ node .CreationTimestamp = metav1 .NewTime (nodesCreationTime )
2870
+ node .Spec .Taints = []apiv1.Taint {{
2871
+ Key : taints .DeletionCandidateTaint ,
2872
+ Value : "1" ,
2873
+ Effect : apiv1 .TaintEffectNoSchedule ,
2874
+ }}
2875
+ SetNodeReadyState (node , true , nodesCreationTime )
2876
+ ngNodes = append (ngNodes , node )
2877
+ provider .AddNode (ng .Id (), node )
2878
+ }
2879
+ return ngNodes
2880
+ }
2881
+
2882
+ func assertNodesSoftTaintsStatus (t * testing.T , fakeClient * fake.Clientset , nodes []* apiv1.Node , tainted bool ) {
2883
+ for _ , node := range nodes {
2884
+ newNode , clientErr := fakeClient .CoreV1 ().Nodes ().Get (stdcontext .TODO (), node .Name , metav1.GetOptions {})
2885
+ assert .NoError (t , clientErr )
2886
+ assert .Equal (t , tainted , taints .HasDeletionCandidateTaint (newNode ))
2887
+ }
2888
+ }
0 commit comments