@@ -800,3 +800,209 @@ func TestMinimumModelIndex(t *testing.T) {
800800 })
801801 }
802802}
803+
804+ func TestGetMaxAvailableComponentSetsGeneral (t * testing.T ) {
805+ tests := []struct {
806+ name string
807+ cluster * clusterv1alpha1.Cluster
808+ components []* workv1alpha2.Component
809+ expected int32
810+ }{
811+ {
812+ name : "nil resource summary" ,
813+ cluster : & clusterv1alpha1.Cluster {
814+ Status : clusterv1alpha1.ClusterStatus {},
815+ },
816+ expected : 0 ,
817+ },
818+ {
819+ name : "no allowed pods" ,
820+ cluster : & clusterv1alpha1.Cluster {
821+ Status : clusterv1alpha1.ClusterStatus {
822+ ResourceSummary : & clusterv1alpha1.ResourceSummary {
823+ Allocatable : corev1.ResourceList {
824+ corev1 .ResourcePods : resource .MustParse ("10" ),
825+ },
826+ Allocated : corev1.ResourceList {
827+ corev1 .ResourcePods : resource .MustParse ("10" ),
828+ },
829+ },
830+ },
831+ },
832+ expected : 0 ,
833+ },
834+ {
835+ name : "empty component list should return max pod allowance" ,
836+ cluster : & clusterv1alpha1.Cluster {
837+ Status : clusterv1alpha1.ClusterStatus {
838+ ResourceSummary : & clusterv1alpha1.ResourceSummary {
839+ Allocatable : corev1.ResourceList {
840+ corev1 .ResourcePods : resource .MustParse ("10" ),
841+ },
842+ },
843+ },
844+ },
845+ expected : 10 ,
846+ },
847+ {
848+ name : "basic resource estimation" ,
849+ cluster : & clusterv1alpha1.Cluster {
850+ Status : clusterv1alpha1.ClusterStatus {
851+ ResourceSummary : & clusterv1alpha1.ResourceSummary {
852+ Allocatable : corev1.ResourceList {
853+ corev1 .ResourcePods : resource .MustParse ("100" ),
854+ corev1 .ResourceCPU : resource .MustParse ("10" ),
855+ corev1 .ResourceMemory : resource .MustParse ("8Gi" ),
856+ },
857+ Allocated : corev1.ResourceList {
858+ corev1 .ResourcePods : resource .MustParse ("20" ),
859+ corev1 .ResourceCPU : resource .MustParse ("0" ),
860+ corev1 .ResourceMemory : resource .MustParse ("2Gi" ),
861+ },
862+ },
863+ },
864+ },
865+ components : []* workv1alpha2.Component {
866+ {
867+ Name : "jobmanager" ,
868+ Replicas : 1 ,
869+ ReplicaRequirements : & workv1alpha2.ComponentReplicaRequirements {
870+ ResourceRequest : corev1.ResourceList {
871+ corev1 .ResourceCPU : resource .MustParse ("1" ),
872+ },
873+ },
874+ },
875+ {
876+ Name : "taskmanager" ,
877+ Replicas : 2 ,
878+ ReplicaRequirements : & workv1alpha2.ComponentReplicaRequirements {
879+ ResourceRequest : corev1.ResourceList {
880+ corev1 .ResourceCPU : resource .MustParse ("1.5" ),
881+ },
882+ },
883+ },
884+ },
885+ expected : 2 ,
886+ },
887+ {
888+ name : "resource estimation with mixed components" ,
889+ cluster : & clusterv1alpha1.Cluster {
890+ Status : clusterv1alpha1.ClusterStatus {
891+ ResourceSummary : & clusterv1alpha1.ResourceSummary {
892+ Allocatable : corev1.ResourceList {
893+ corev1 .ResourcePods : resource .MustParse ("100" ),
894+ corev1 .ResourceCPU : resource .MustParse ("10" ),
895+ corev1 .ResourceMemory : resource .MustParse ("8Gi" ),
896+ },
897+ Allocated : corev1.ResourceList {
898+ corev1 .ResourcePods : resource .MustParse ("20" ),
899+ corev1 .ResourceCPU : resource .MustParse ("0" ),
900+ corev1 .ResourceMemory : resource .MustParse ("2Gi" ),
901+ },
902+ },
903+ },
904+ },
905+ components : []* workv1alpha2.Component {
906+ {
907+ Name : "jobmanager" ,
908+ Replicas : 1 ,
909+ ReplicaRequirements : & workv1alpha2.ComponentReplicaRequirements {
910+ ResourceRequest : corev1.ResourceList {
911+ corev1 .ResourceCPU : resource .MustParse ("1" ),
912+ corev1 .ResourceMemory : resource .MustParse ("2Gi" ),
913+ },
914+ },
915+ },
916+ {
917+ Name : "taskmanager" ,
918+ Replicas : 2 ,
919+ ReplicaRequirements : & workv1alpha2.ComponentReplicaRequirements {
920+ ResourceRequest : corev1.ResourceList {
921+ corev1 .ResourceCPU : resource .MustParse ("2000m" ),
922+ corev1 .ResourceMemory : resource .MustParse ("2Gi" ),
923+ },
924+ },
925+ },
926+ },
927+ // Per-set demand: 2 replicas × (2 CPU, 2Gi) + 1 replica x (1 CPU, 2Gi) = (5 CPU, 6Gi)
928+ // Cluster allocatable: 10 CPU, 6Gi
929+ // 10/5 = 2 sets (CPU), 6Gi/6 = 1 set (Mem)
930+ // min(2,1) = 1 set total
931+ expected : 1 ,
932+ },
933+ {
934+ name : "estimation limited by pod count" ,
935+ cluster : & clusterv1alpha1.Cluster {
936+ Status : clusterv1alpha1.ClusterStatus {
937+ ResourceSummary : & clusterv1alpha1.ResourceSummary {
938+ Allocatable : corev1.ResourceList {
939+ corev1 .ResourcePods : resource .MustParse ("3" ),
940+ corev1 .ResourceCPU : resource .MustParse ("100" ),
941+ corev1 .ResourceMemory : resource .MustParse ("1Ti" ),
942+ },
943+ },
944+ },
945+ },
946+ components : []* workv1alpha2.Component {
947+ {
948+ Name : "small-component" ,
949+ Replicas : 3 ,
950+ ReplicaRequirements : & workv1alpha2.ComponentReplicaRequirements {
951+ ResourceRequest : corev1.ResourceList {
952+ corev1 .ResourceCPU : resource .MustParse ("10m" ),
953+ corev1 .ResourceMemory : resource .MustParse ("1Mi" ),
954+ },
955+ },
956+ },
957+ },
958+ expected : 1 , // limited by pods
959+ },
960+ {
961+ name : "custom resource estimation with GPUs" ,
962+ cluster : & clusterv1alpha1.Cluster {
963+ Status : clusterv1alpha1.ClusterStatus {
964+ ResourceSummary : & clusterv1alpha1.ResourceSummary {
965+ Allocatable : corev1.ResourceList {
966+ corev1 .ResourcePods : resource .MustParse ("20" ),
967+ corev1 .ResourceCPU : resource .MustParse ("40" ),
968+ corev1 .ResourceMemory : resource .MustParse ("64Gi" ),
969+ "nvidia.com/gpu" : resource .MustParse ("8" ),
970+ },
971+ Allocated : corev1.ResourceList {
972+ corev1 .ResourcePods : resource .MustParse ("0" ),
973+ corev1 .ResourceCPU : resource .MustParse ("0" ),
974+ corev1 .ResourceMemory : resource .MustParse ("0Gi" ),
975+ "nvidia.com/gpu" : resource .MustParse ("0" ),
976+ },
977+ },
978+ },
979+ },
980+ components : []* workv1alpha2.Component {
981+ {
982+ Name : "gpu-worker" ,
983+ Replicas : 2 ,
984+ ReplicaRequirements : & workv1alpha2.ComponentReplicaRequirements {
985+ ResourceRequest : corev1.ResourceList {
986+ corev1 .ResourceCPU : resource .MustParse ("4" ),
987+ corev1 .ResourceMemory : resource .MustParse ("8Gi" ),
988+ "nvidia.com/gpu" : resource .MustParse ("1" ),
989+ },
990+ },
991+ },
992+ },
993+ // Per-set demand: 2 replicas × (4 CPU, 8Gi, 1 GPU) = (8 CPU, 16Gi, 2 GPUs)
994+ // Cluster allocatable: 40 CPU, 64Gi, 8 GPUs
995+ // 40/8 = 5 sets (CPU), 64/16 = 4 sets (Mem), 8/2 = 4 sets (GPU)
996+ // min(5, 4, 4) = 4 sets total
997+ expected : 4 ,
998+ },
999+ }
1000+
1001+ estimator := NewGeneralEstimator ()
1002+ for _ , tt := range tests {
1003+ t .Run (tt .name , func (t * testing.T ) {
1004+ result := estimator .maxAvailableComponentSets (tt .cluster , tt .components )
1005+ assert .Equal (t , tt .expected , result )
1006+ })
1007+ }
1008+ }
0 commit comments