@@ -38,6 +38,7 @@ import (
38
38
"k8s.io/autoscaler/cluster-autoscaler/core/scaledown/actuation"
39
39
"k8s.io/autoscaler/cluster-autoscaler/core/scaledown/deletiontracker"
40
40
"k8s.io/autoscaler/cluster-autoscaler/core/scaledown/legacy"
41
+ "k8s.io/autoscaler/cluster-autoscaler/core/scaledown/planner"
41
42
"k8s.io/autoscaler/cluster-autoscaler/core/scaledown/status"
42
43
"k8s.io/autoscaler/cluster-autoscaler/core/scaleup/orchestrator"
43
44
. "k8s.io/autoscaler/cluster-autoscaler/core/test"
@@ -1393,78 +1394,37 @@ func TestStaticAutoscalerRunOnceWithUnselectedNodeGroups(t *testing.T) {
1393
1394
provider .AddNode ("ng1" , n1 )
1394
1395
assert .NotNil (t , provider )
1395
1396
1396
- tests := map [string ]struct {
1397
+ tests := []struct {
1398
+ name string
1397
1399
node * apiv1.Node
1398
1400
pods []* apiv1.Pod
1399
1401
expectedTaints []apiv1.Taint
1400
1402
}{
1401
- "Node from selected node groups can get their deletion candidate taints removed" : {
1403
+ {
1404
+ name : "Node from selected node groups can get their deletion candidate taints removed" ,
1402
1405
node : n1 ,
1403
1406
pods : []* apiv1.Pod {p1 },
1404
1407
expectedTaints : []apiv1.Taint {},
1405
1408
},
1406
- "Node from non-selected node groups should keep their deletion candidate taints" : {
1409
+ {
1410
+ name : "Node from non-selected node groups should keep their deletion candidate taints" ,
1407
1411
node : n2 ,
1408
1412
pods : nil ,
1409
1413
expectedTaints : n2 .Spec .Taints ,
1410
1414
},
1411
1415
}
1412
1416
1413
- for name , test := range tests {
1414
- // prevent issues with scoping, we should be able to get rid of that with Go 1.22
1415
- test := test
1416
- t .Run (name , func (t * testing.T ) {
1417
+ for _ , test := range tests {
1418
+ t .Run (test .name , func (t * testing.T ) {
1417
1419
t .Parallel ()
1418
- // Create fake listers for the generated nodes, nothing returned by the rest (but the ones used in the tested path have to be defined).
1419
- readyNodeLister := kubernetes .NewTestNodeLister ([]* apiv1.Node {test .node })
1420
- allNodeLister := kubernetes .NewTestNodeLister ([]* apiv1.Node {test .node })
1421
- allPodListerMock := kubernetes .NewTestPodLister (test .pods )
1422
- daemonSetLister , err := kubernetes .NewTestDaemonSetLister (nil )
1423
- assert .NoError (t , err )
1424
- listerRegistry := kube_util .NewListerRegistry (allNodeLister , readyNodeLister , allPodListerMock ,
1425
- kubernetes .NewTestPodDisruptionBudgetLister (nil ), daemonSetLister ,
1426
- nil , nil , nil , nil )
1427
-
1428
- // Create context with minimal autoscalingOptions that guarantee we reach the tested logic.
1429
- autoscalingOptions := config.AutoscalingOptions {
1430
- ScaleDownEnabled : true ,
1431
- MaxBulkSoftTaintCount : 10 ,
1432
- MaxBulkSoftTaintTime : 3 * time .Second ,
1433
- }
1434
- processorCallbacks := newStaticAutoscalerProcessorCallbacks ()
1435
- clientset := fake .NewSimpleClientset (test .node )
1436
- context , err := NewScaleTestAutoscalingContext (autoscalingOptions , clientset , listerRegistry , provider , processorCallbacks , nil )
1437
- assert .NoError (t , err )
1438
-
1439
- // Create CSR with unhealthy cluster protection effectively disabled, to guarantee we reach the tested logic.
1440
- clusterStateConfig := clusterstate.ClusterStateRegistryConfig {
1441
- OkTotalUnreadyCount : 1 ,
1442
- }
1443
- processors := NewTestProcessors (& context )
1444
-
1445
- clusterState := clusterstate .NewClusterStateRegistry (provider , clusterStateConfig , context .LogRecorder , NewBackoff (), nodegroupconfig .NewDefaultNodeGroupConfigProcessor (autoscalingOptions .NodeGroupDefaults ), processors .AsyncNodeGroupStateChecker )
1446
-
1447
- // Setting the Actuator is necessary for testing any scale-down logic, it shouldn't have anything to do in this test.
1448
- sdActuator := actuation .NewActuator (& context , clusterState , deletiontracker .NewNodeDeletionTracker (0 * time .Second ), options.NodeDeleteOptions {}, nil , processors .NodeGroupConfigProcessor )
1449
- context .ScaleDownActuator = sdActuator
1450
-
1451
- // Fake planner that keeps track of the scale-down candidates passed to UpdateClusterState.
1452
- sdPlanner := & candidateTrackingFakePlanner {}
1453
-
1454
- autoscaler := & StaticAutoscaler {
1455
- AutoscalingContext : & context ,
1456
- clusterStateRegistry : clusterState ,
1457
- scaleDownPlanner : sdPlanner ,
1458
- scaleDownActuator : sdActuator ,
1459
- processors : processors ,
1460
- loopStartNotifier : loopstart .NewObserversList (nil ),
1461
- processorCallbacks : processorCallbacks ,
1462
- }
1463
-
1464
- err = autoscaler .RunOnce (time .Now ().Add (5 * time .Hour ))
1465
- assert .NoError (t , err )
1466
- newNode , err := clientset .CoreV1 ().Nodes ().Get (stdcontext .TODO (), test .node .Name , metav1.GetOptions {})
1420
+ allNodes := []* apiv1.Node {test .node }
1421
+ fakeClient := buildFakeClient (t , allNodes ... )
1422
+ autoscaler := buildStaticAutoscaler (t , provider , allNodes , allNodes , fakeClient )
1423
+ runningTime := time .Now ()
1424
+ err := autoscaler .RunOnce (runningTime )
1467
1425
assert .NoError (t , err )
1426
+ newNode , clientErr := fakeClient .CoreV1 ().Nodes ().Get (stdcontext .TODO (), test .node .Name , metav1.GetOptions {})
1427
+ assert .NoError (t , clientErr )
1468
1428
assert .Equal (t , test .expectedTaints , newNode .Spec .Taints )
1469
1429
})
1470
1430
}
@@ -2677,3 +2637,155 @@ func newEstimatorBuilder() estimator.EstimatorBuilder {
2677
2637
2678
2638
return estimatorBuilder
2679
2639
}
2640
+
2641
+ func TestCleaningSoftTaintsInScaleDown (t * testing.T ) {
2642
+
2643
+ provider := testprovider .NewTestCloudProvider (nil , nil )
2644
+
2645
+ minSizeNgName := "ng-min-size"
2646
+ nodesToHaveNoTaints := createNodeGroupWithSoftTaintedNodes (provider , minSizeNgName , 2 , 10 , 2 )
2647
+
2648
+ notSizeNgName := "ng"
2649
+ nodesToHaveTaints := createNodeGroupWithSoftTaintedNodes (provider , notSizeNgName , 3 , 10 , 4 )
2650
+
2651
+ tests := []struct {
2652
+ name string
2653
+ testNodes []* apiv1.Node
2654
+ expectedScaleDownCoolDown bool
2655
+ expectedNodesWithSoftTaints []* apiv1.Node
2656
+ expectedNodesWithNoSoftTaints []* apiv1.Node
2657
+ }{
2658
+ {
2659
+ name : "Soft tainted nodes are cleaned in case of scale down is in cool down" ,
2660
+ testNodes : nodesToHaveNoTaints ,
2661
+ expectedScaleDownCoolDown : true ,
2662
+ expectedNodesWithSoftTaints : []* apiv1.Node {},
2663
+ expectedNodesWithNoSoftTaints : nodesToHaveNoTaints ,
2664
+ },
2665
+ {
2666
+ name : "Soft tainted nodes are not cleaned in case of scale down isn't in cool down" ,
2667
+ testNodes : nodesToHaveTaints ,
2668
+ expectedScaleDownCoolDown : false ,
2669
+ expectedNodesWithSoftTaints : nodesToHaveTaints ,
2670
+ expectedNodesWithNoSoftTaints : []* apiv1.Node {},
2671
+ },
2672
+ {
2673
+ name : "Soft tainted nodes are cleaned only from min sized node group in case of scale down isn't in cool down" ,
2674
+ testNodes : append (nodesToHaveNoTaints , nodesToHaveTaints ... ),
2675
+ expectedScaleDownCoolDown : false ,
2676
+ expectedNodesWithSoftTaints : nodesToHaveTaints ,
2677
+ expectedNodesWithNoSoftTaints : nodesToHaveNoTaints ,
2678
+ },
2679
+ }
2680
+ for _ , test := range tests {
2681
+ t .Run (test .name , func (t * testing.T ) {
2682
+ t .Parallel ()
2683
+ fakeClient := buildFakeClient (t , test .testNodes ... )
2684
+
2685
+ autoscaler := buildStaticAutoscaler (t , provider , test .testNodes , test .testNodes , fakeClient )
2686
+
2687
+ err := autoscaler .RunOnce (time .Now ())
2688
+
2689
+ assert .NoError (t , err )
2690
+ candidates , _ := autoscaler .processors .ScaleDownNodeProcessor .GetScaleDownCandidates (autoscaler .AutoscalingContext , test .testNodes )
2691
+ assert .Equal (t , test .expectedScaleDownCoolDown , autoscaler .isScaleDownInCooldown (time .Now (), candidates ))
2692
+
2693
+ assertNodesSoftTaintsStatus (t , fakeClient , test .expectedNodesWithSoftTaints , true )
2694
+ assertNodesSoftTaintsStatus (t , fakeClient , test .expectedNodesWithNoSoftTaints , false )
2695
+ })
2696
+ }
2697
+ }
2698
+
2699
+ func buildStaticAutoscaler (t * testing.T , provider cloudprovider.CloudProvider , allNodes []* apiv1.Node , readyNodes []* apiv1.Node , fakeClient * fake.Clientset ) * StaticAutoscaler {
2700
+ autoscalingOptions := config.AutoscalingOptions {
2701
+ NodeGroupDefaults : config.NodeGroupAutoscalingOptions {
2702
+ ScaleDownUnneededTime : time .Minute ,
2703
+ ScaleDownUnreadyTime : time .Minute ,
2704
+ ScaleDownUtilizationThreshold : 0.5 ,
2705
+ MaxNodeProvisionTime : 10 * time .Second ,
2706
+ },
2707
+ MaxScaleDownParallelism : 10 ,
2708
+ MaxDrainParallelism : 1 ,
2709
+ ScaleDownEnabled : true ,
2710
+ MaxBulkSoftTaintCount : 20 ,
2711
+ MaxBulkSoftTaintTime : 5 * time .Second ,
2712
+ NodeDeleteDelayAfterTaint : 5 * time .Minute ,
2713
+ ScaleDownSimulationTimeout : 10 * time .Second ,
2714
+ }
2715
+
2716
+ allNodeLister := kubernetes .NewTestNodeLister (allNodes )
2717
+ readyNodeLister := kubernetes .NewTestNodeLister (readyNodes )
2718
+
2719
+ daemonSetLister , err := kubernetes .NewTestDaemonSetLister (nil )
2720
+ assert .NoError (t , err )
2721
+ listerRegistry := kube_util .NewListerRegistry (allNodeLister , readyNodeLister ,
2722
+ kubernetes .NewTestPodLister (nil ),
2723
+ kubernetes .NewTestPodDisruptionBudgetLister (nil ), daemonSetLister , nil , nil , nil , nil )
2724
+
2725
+ processorCallbacks := newStaticAutoscalerProcessorCallbacks ()
2726
+
2727
+ ctx , err := NewScaleTestAutoscalingContext (autoscalingOptions , fakeClient , listerRegistry , provider , processorCallbacks , nil )
2728
+ assert .NoError (t , err )
2729
+
2730
+ processors := NewTestProcessors (& ctx )
2731
+ cp := scaledowncandidates .NewCombinedScaleDownCandidatesProcessor ()
2732
+ cp .Register (scaledowncandidates .NewScaleDownCandidatesSortingProcessor ([]scaledowncandidates.CandidatesComparer {}))
2733
+ processors .ScaleDownNodeProcessor = cp
2734
+
2735
+ csr := clusterstate .NewClusterStateRegistry (provider , clusterstate.ClusterStateRegistryConfig {OkTotalUnreadyCount : 1 }, ctx .LogRecorder , NewBackoff (), nodegroupconfig .NewDefaultNodeGroupConfigProcessor (config.NodeGroupAutoscalingOptions {MaxNodeProvisionTime : 15 * time .Minute }), processors .AsyncNodeGroupStateChecker )
2736
+ actuator := actuation .NewActuator (& ctx , csr , deletiontracker .NewNodeDeletionTracker (0 * time .Second ), options.NodeDeleteOptions {}, nil , processors .NodeGroupConfigProcessor )
2737
+ ctx .ScaleDownActuator = actuator
2738
+
2739
+ deleteOptions := options .NewNodeDeleteOptions (ctx .AutoscalingOptions )
2740
+ drainabilityRules := rules .Default (deleteOptions )
2741
+
2742
+ sdPlanner := planner .New (& ctx , processors , deleteOptions , drainabilityRules )
2743
+
2744
+ autoscaler := & StaticAutoscaler {
2745
+ AutoscalingContext : & ctx ,
2746
+ clusterStateRegistry : csr ,
2747
+ scaleDownActuator : actuator ,
2748
+ scaleDownPlanner : sdPlanner ,
2749
+ processors : processors ,
2750
+ loopStartNotifier : loopstart .NewObserversList (nil ),
2751
+ processorCallbacks : processorCallbacks ,
2752
+ }
2753
+ return autoscaler
2754
+ }
2755
+
2756
+ func buildFakeClient (t * testing.T , nodes ... * apiv1.Node ) * fake.Clientset {
2757
+ fakeClient := fake .NewSimpleClientset ()
2758
+ for _ , node := range nodes {
2759
+ _ , err := fakeClient .CoreV1 ().Nodes ().Create (stdcontext .TODO (), node , metav1.CreateOptions {})
2760
+ assert .NoError (t , err )
2761
+ }
2762
+ return fakeClient
2763
+ }
2764
+
2765
+ func createNodeGroupWithSoftTaintedNodes (provider * testprovider.TestCloudProvider , name string , minSize int , maxSize int , size int ) []* apiv1.Node {
2766
+ nodesCreationTime := time.Time {}
2767
+ var ngNodes []* apiv1.Node
2768
+ ng := provider .BuildNodeGroup (name , minSize , maxSize , size , true , false , "" , nil )
2769
+ provider .InsertNodeGroup (ng )
2770
+ for i := range size {
2771
+ node := BuildTestNode (fmt .Sprintf ("%s-node-%d" , name , i ), 2000 , 1000 )
2772
+ node .CreationTimestamp = metav1 .NewTime (nodesCreationTime )
2773
+ node .Spec .Taints = []apiv1.Taint {{
2774
+ Key : taints .DeletionCandidateTaint ,
2775
+ Value : "1" ,
2776
+ Effect : apiv1 .TaintEffectNoSchedule ,
2777
+ }}
2778
+ SetNodeReadyState (node , true , nodesCreationTime )
2779
+ ngNodes = append (ngNodes , node )
2780
+ provider .AddNode (ng .Id (), node )
2781
+ }
2782
+ return ngNodes
2783
+ }
2784
+
2785
+ func assertNodesSoftTaintsStatus (t * testing.T , fakeClient * fake.Clientset , nodes []* apiv1.Node , tainted bool ) {
2786
+ for _ , node := range nodes {
2787
+ newNode , clientErr := fakeClient .CoreV1 ().Nodes ().Get (stdcontext .TODO (), node .Name , metav1.GetOptions {})
2788
+ assert .NoError (t , clientErr )
2789
+ assert .Equal (t , tainted , taints .HasDeletionCandidateTaint (newNode ))
2790
+ }
2791
+ }
0 commit comments