@@ -18,6 +18,7 @@ package planner
1818
1919import (
2020 "fmt"
21+ "math"
2122 "testing"
2223 "time"
2324
@@ -35,6 +36,7 @@ import (
3536 "k8s.io/autoscaler/cluster-autoscaler/core/scaledown/status"
3637 "k8s.io/autoscaler/cluster-autoscaler/core/scaledown/unremovable"
3738 . "k8s.io/autoscaler/cluster-autoscaler/core/test"
39+ processorstest "k8s.io/autoscaler/cluster-autoscaler/processors/test"
3840 "k8s.io/autoscaler/cluster-autoscaler/simulator"
3941 "k8s.io/autoscaler/cluster-autoscaler/simulator/clustersnapshot"
4042 "k8s.io/autoscaler/cluster-autoscaler/simulator/options"
@@ -498,7 +500,7 @@ func TestUpdateClusterState(t *testing.T) {
498500 assert .NoError (t , err )
499501 clustersnapshot .InitializeClusterSnapshotOrDie (t , context .ClusterSnapshot , tc .nodes , tc .pods )
500502 deleteOptions := options.NodeDeleteOptions {}
501- p := New (& context , NewTestProcessors (& context ), deleteOptions , nil )
503+ p := New (& context , processorstest . NewTestProcessors (& context ), deleteOptions , nil )
502504 p .eligibilityChecker = & fakeEligibilityChecker {eligible : asMap (tc .eligible )}
503505 if tc .isSimulationTimeout {
504506 context .AutoscalingOptions .ScaleDownSimulationTimeout = 1 * time .Second
@@ -694,7 +696,7 @@ func TestUpdateClusterStatUnneededNodesLimit(t *testing.T) {
694696 assert .NoError (t , err )
695697 clustersnapshot .InitializeClusterSnapshotOrDie (t , context .ClusterSnapshot , nodes , nil )
696698 deleteOptions := options.NodeDeleteOptions {}
697- p := New (& context , NewTestProcessors (& context ), deleteOptions , nil )
699+ p := New (& context , processorstest . NewTestProcessors (& context ), deleteOptions , nil )
698700 p .eligibilityChecker = & fakeEligibilityChecker {eligible : asMap (nodeNames (nodes ))}
699701 p .minUpdateInterval = tc .updateInterval
700702 p .unneededNodes .Update (previouslyUnneeded , time .Now ())
@@ -706,16 +708,18 @@ func TestUpdateClusterStatUnneededNodesLimit(t *testing.T) {
706708
707709func TestNodesToDelete (t * testing.T ) {
708710 testCases := []struct {
709- name string
710- nodes map [cloudprovider.NodeGroup ][]simulator.NodeToBeRemoved
711- wantEmpty []* apiv1.Node
712- wantDrain []* apiv1.Node
711+ name string
712+ nodes map [cloudprovider.NodeGroup ][]simulator.NodeToBeRemoved
713+ wantEmpty []* apiv1.Node
714+ wantDrain []* apiv1.Node
715+ maxNodeCountToBeRemoved int
713716 }{
714717 {
715- name : "empty" ,
716- nodes : map [cloudprovider.NodeGroup ][]simulator.NodeToBeRemoved {},
717- wantEmpty : []* apiv1.Node {},
718- wantDrain : []* apiv1.Node {},
718+ name : "empty" ,
719+ nodes : map [cloudprovider.NodeGroup ][]simulator.NodeToBeRemoved {},
720+ wantEmpty : []* apiv1.Node {},
721+ wantDrain : []* apiv1.Node {},
722+ maxNodeCountToBeRemoved : math .MaxInt ,
719723 },
720724 {
721725 name : "single empty" ,
@@ -727,7 +731,26 @@ func TestNodesToDelete(t *testing.T) {
727731 wantEmpty : []* apiv1.Node {
728732 buildRemovableNode ("test-node" , 0 ).Node ,
729733 },
730- wantDrain : []* apiv1.Node {},
734+ wantDrain : []* apiv1.Node {},
735+ maxNodeCountToBeRemoved : math .MaxInt ,
736+ },
737+ {
738+ name : "multiple empty with limit" ,
739+ nodes : map [cloudprovider.NodeGroup ][]simulator.NodeToBeRemoved {
740+ sizedNodeGroup ("test-ng" , 3 , false ): {
741+ buildRemovableNode ("node-1" , 0 ),
742+ buildRemovableNode ("node-2" , 0 ),
743+ buildRemovableNode ("node-3" , 0 ),
744+ buildRemovableNode ("node-4" , 1 ),
745+ },
746+ },
747+ wantEmpty : []* apiv1.Node {
748+ buildRemovableNode ("node-1" , 0 ).Node ,
749+ buildRemovableNode ("node-2" , 0 ).Node ,
750+ buildRemovableNode ("node-3" , 0 ).Node ,
751+ },
752+ wantDrain : []* apiv1.Node {},
753+ maxNodeCountToBeRemoved : 3 ,
731754 },
732755 {
733756 name : "single drain" ,
@@ -740,6 +763,7 @@ func TestNodesToDelete(t *testing.T) {
740763 wantDrain : []* apiv1.Node {
741764 buildRemovableNode ("test-node" , 1 ).Node ,
742765 },
766+ maxNodeCountToBeRemoved : math .MaxInt ,
743767 },
744768 {
745769 name : "single empty atomic" ,
@@ -748,8 +772,9 @@ func TestNodesToDelete(t *testing.T) {
748772 buildRemovableNode ("node-1" , 0 ),
749773 },
750774 },
751- wantEmpty : []* apiv1.Node {},
752- wantDrain : []* apiv1.Node {},
775+ wantEmpty : []* apiv1.Node {},
776+ wantDrain : []* apiv1.Node {},
777+ maxNodeCountToBeRemoved : math .MaxInt ,
753778 },
754779 {
755780 name : "all empty atomic" ,
@@ -765,7 +790,8 @@ func TestNodesToDelete(t *testing.T) {
765790 buildRemovableNode ("node-2" , 0 ).Node ,
766791 buildRemovableNode ("node-3" , 0 ).Node ,
767792 },
768- wantDrain : []* apiv1.Node {},
793+ wantDrain : []* apiv1.Node {},
794+ maxNodeCountToBeRemoved : math .MaxInt ,
769795 },
770796 {
771797 name : "some drain atomic" ,
@@ -783,6 +809,7 @@ func TestNodesToDelete(t *testing.T) {
783809 wantDrain : []* apiv1.Node {
784810 buildRemovableNode ("node-3" , 1 ).Node ,
785811 },
812+ maxNodeCountToBeRemoved : math .MaxInt ,
786813 },
787814 {
788815 name : "different groups" ,
@@ -836,6 +863,52 @@ func TestNodesToDelete(t *testing.T) {
836863 buildRemovableNode ("node-14" , 0 ).Node ,
837864 buildRemovableNode ("node-15" , 0 ).Node ,
838865 },
866+ maxNodeCountToBeRemoved : math .MaxInt ,
867+ },
868+ {
869+ name : "different groups with max count equal to all empty" ,
870+ nodes : map [cloudprovider.NodeGroup ][]simulator.NodeToBeRemoved {
871+ sizedNodeGroup ("standard-empty-ng" , 3 , false ): {
872+ buildRemovableNode ("node-1" , 0 ),
873+ buildRemovableNode ("node-2" , 0 ),
874+ buildRemovableNode ("node-3" , 0 ),
875+ },
876+ sizedNodeGroup ("standard-drain-ng" , 3 , false ): {
877+ buildRemovableNode ("node-4" , 1 ),
878+ buildRemovableNode ("node-5" , 2 ),
879+ buildRemovableNode ("node-6" , 3 ),
880+ },
881+ sizedNodeGroup ("standard-mixed-ng" , 3 , false ): {
882+ buildRemovableNode ("node-7" , 0 ),
883+ buildRemovableNode ("node-8" , 1 ),
884+ buildRemovableNode ("node-9" , 2 ),
885+ },
886+ sizedNodeGroup ("atomic-empty-ng" , 3 , true ): {
887+ buildRemovableNode ("node-10" , 0 ),
888+ buildRemovableNode ("node-11" , 0 ),
889+ buildRemovableNode ("node-12" , 0 ),
890+ },
891+ sizedNodeGroup ("atomic-mixed-ng" , 3 , true ): {
892+ buildRemovableNode ("node-13" , 0 ),
893+ buildRemovableNode ("node-14" , 1 ),
894+ buildRemovableNode ("node-15" , 2 ),
895+ },
896+ sizedNodeGroup ("atomic-partial-ng" , 3 , true ): {
897+ buildRemovableNode ("node-16" , 0 ),
898+ buildRemovableNode ("node-17" , 1 ),
899+ },
900+ },
901+ wantEmpty : []* apiv1.Node {
902+ buildRemovableNode ("node-1" , 0 ).Node ,
903+ buildRemovableNode ("node-2" , 0 ).Node ,
904+ buildRemovableNode ("node-3" , 0 ).Node ,
905+ buildRemovableNode ("node-7" , 0 ).Node ,
906+ buildRemovableNode ("node-10" , 0 ).Node ,
907+ buildRemovableNode ("node-11" , 0 ).Node ,
908+ buildRemovableNode ("node-12" , 0 ).Node ,
909+ },
910+ wantDrain : []* apiv1.Node {},
911+ maxNodeCountToBeRemoved : 9 ,
839912 },
840913 }
841914 for _ , tc := range testCases {
@@ -862,9 +935,10 @@ func TestNodesToDelete(t *testing.T) {
862935 assert .NoError (t , err )
863936 clustersnapshot .InitializeClusterSnapshotOrDie (t , context .ClusterSnapshot , allNodes , nil )
864937 deleteOptions := options.NodeDeleteOptions {}
865- p := New (& context , NewTestProcessors (& context ), deleteOptions , nil )
938+ p := New (& context , processorstest . NewTestProcessors (& context ), deleteOptions , nil )
866939 p .latestUpdate = time .Now ()
867- p .actuationStatus = deletiontracker .NewNodeDeletionTracker (0 * time .Second )
940+ p .scaleDownContext .ActuationStatus = deletiontracker .NewNodeDeletionTracker (0 * time .Second )
941+ p .scaleDownContext .MaxNodeCountToBeRemoved = tc .maxNodeCountToBeRemoved
868942 p .unneededNodes .Update (allRemovables , time .Now ().Add (- 1 * time .Hour ))
869943 p .eligibilityChecker = & fakeEligibilityChecker {eligible : asMap (nodeNames (allNodes ))}
870944 empty , drain := p .NodesToDelete (time .Now ())
0 commit comments