@@ -38,6 +38,8 @@ import (
38
38
frameworktypes "sigs.k8s.io/descheduler/pkg/framework/types"
39
39
"sigs.k8s.io/descheduler/pkg/utils"
40
40
"sigs.k8s.io/descheduler/test"
41
+
42
+ "github.com/prometheus/common/model"
41
43
)
42
44
43
45
func TestLowNodeUtilization (t * testing.T ) {
@@ -1359,3 +1361,162 @@ func TestLowNodeUtilizationWithTaints(t *testing.T) {
1359
1361
})
1360
1362
}
1361
1363
}
1364
+
1365
+ func withLocalStorage (pod * v1.Pod ) {
1366
+ // A pod with local storage.
1367
+ test .SetNormalOwnerRef (pod )
1368
+ pod .Spec .Volumes = []v1.Volume {
1369
+ {
1370
+ Name : "sample" ,
1371
+ VolumeSource : v1.VolumeSource {
1372
+ HostPath : & v1.HostPathVolumeSource {Path : "somePath" },
1373
+ EmptyDir : & v1.EmptyDirVolumeSource {
1374
+ SizeLimit : resource .NewQuantity (int64 (10 ), resource .BinarySI ),
1375
+ },
1376
+ },
1377
+ },
1378
+ }
1379
+ // A Mirror Pod.
1380
+ pod .Annotations = test .GetMirrorPodAnnotation ()
1381
+ }
1382
+
1383
+ func withCriticalPod (pod * v1.Pod ) {
1384
+ // A Critical Pod.
1385
+ test .SetNormalOwnerRef (pod )
1386
+ pod .Namespace = "kube-system"
1387
+ priority := utils .SystemCriticalPriority
1388
+ pod .Spec .Priority = & priority
1389
+ }
1390
+
1391
+ func TestLowNodeUtilizationWithPrometheusMetrics (t * testing.T ) {
1392
+ n1NodeName := "n1"
1393
+ n2NodeName := "n2"
1394
+ n3NodeName := "n3"
1395
+
1396
+ testCases := []struct {
1397
+ name string
1398
+ useDeviationThresholds bool
1399
+ thresholds , targetThresholds api.ResourceThresholds
1400
+ query string
1401
+ samples []model.Sample
1402
+ nodes []* v1.Node
1403
+ pods []* v1.Pod
1404
+ expectedPodsEvicted uint
1405
+ evictedPods []string
1406
+ evictableNamespaces * api.Namespaces
1407
+ }{
1408
+ {
1409
+ name : "with instance:node_cpu:rate:sum query" ,
1410
+ thresholds : api.ResourceThresholds {
1411
+ v1 .ResourceName ("MetricResource" ): 30 ,
1412
+ },
1413
+ targetThresholds : api.ResourceThresholds {
1414
+ v1 .ResourceName ("MetricResource" ): 50 ,
1415
+ },
1416
+ query : "instance:node_cpu:rate:sum" ,
1417
+ samples : []model.Sample {
1418
+ sample ("instance:node_cpu:rate:sum" , n1NodeName , 0.5695757575757561 ),
1419
+ sample ("instance:node_cpu:rate:sum" , n2NodeName , 0.4245454545454522 ),
1420
+ sample ("instance:node_cpu:rate:sum" , n3NodeName , 0.20381818181818104 ),
1421
+ },
1422
+ nodes : []* v1.Node {
1423
+ test .BuildTestNode (n1NodeName , 4000 , 3000 , 9 , nil ),
1424
+ test .BuildTestNode (n2NodeName , 4000 , 3000 , 10 , nil ),
1425
+ test .BuildTestNode (n3NodeName , 4000 , 3000 , 10 , nil ),
1426
+ },
1427
+ pods : []* v1.Pod {
1428
+ test .BuildTestPod ("p1" , 400 , 0 , n1NodeName , test .SetRSOwnerRef ),
1429
+ test .BuildTestPod ("p2" , 400 , 0 , n1NodeName , test .SetRSOwnerRef ),
1430
+ test .BuildTestPod ("p3" , 400 , 0 , n1NodeName , test .SetRSOwnerRef ),
1431
+ test .BuildTestPod ("p4" , 400 , 0 , n1NodeName , test .SetRSOwnerRef ),
1432
+ test .BuildTestPod ("p5" , 400 , 0 , n1NodeName , test .SetRSOwnerRef ),
1433
+ // These won't be evicted.
1434
+ test .BuildTestPod ("p6" , 400 , 0 , n1NodeName , test .SetDSOwnerRef ),
1435
+ test .BuildTestPod ("p7" , 400 , 0 , n1NodeName , withLocalStorage ),
1436
+ test .BuildTestPod ("p8" , 400 , 0 , n1NodeName , withCriticalPod ),
1437
+ test .BuildTestPod ("p9" , 400 , 0 , n2NodeName , test .SetRSOwnerRef ),
1438
+ },
1439
+ expectedPodsEvicted : 1 ,
1440
+ },
1441
+ }
1442
+
1443
+ for _ , tc := range testCases {
1444
+ testFnc := func (metricsEnabled bool , expectedPodsEvicted uint ) func (t * testing.T ) {
1445
+ return func (t * testing.T ) {
1446
+ ctx , cancel := context .WithCancel (context .Background ())
1447
+ defer cancel ()
1448
+
1449
+ var objs []runtime.Object
1450
+ for _ , node := range tc .nodes {
1451
+ objs = append (objs , node )
1452
+ }
1453
+ for _ , pod := range tc .pods {
1454
+ objs = append (objs , pod )
1455
+ }
1456
+
1457
+ fakeClient := fake .NewSimpleClientset (objs ... )
1458
+
1459
+ podsForEviction := make (map [string ]struct {})
1460
+ for _ , pod := range tc .evictedPods {
1461
+ podsForEviction [pod ] = struct {}{}
1462
+ }
1463
+
1464
+ evictionFailed := false
1465
+ if len (tc .evictedPods ) > 0 {
1466
+ fakeClient .Fake .AddReactor ("create" , "pods" , func (action core.Action ) (bool , runtime.Object , error ) {
1467
+ getAction := action .(core.CreateAction )
1468
+ obj := getAction .GetObject ()
1469
+ if eviction , ok := obj .(* policy.Eviction ); ok {
1470
+ if _ , exists := podsForEviction [eviction .Name ]; exists {
1471
+ return true , obj , nil
1472
+ }
1473
+ evictionFailed = true
1474
+ return true , nil , fmt .Errorf ("pod %q was unexpectedly evicted" , eviction .Name )
1475
+ }
1476
+ return true , obj , nil
1477
+ })
1478
+ }
1479
+
1480
+ handle , podEvictor , err := frameworktesting .InitFrameworkHandle (ctx , fakeClient , nil , defaultevictor.DefaultEvictorArgs {NodeFit : true }, nil )
1481
+ if err != nil {
1482
+ t .Fatalf ("Unable to initialize a framework handle: %v" , err )
1483
+ }
1484
+
1485
+ plugin , err := NewLowNodeUtilization (& LowNodeUtilizationArgs {
1486
+ Thresholds : tc .thresholds ,
1487
+ TargetThresholds : tc .targetThresholds ,
1488
+ UseDeviationThresholds : tc .useDeviationThresholds ,
1489
+ EvictableNamespaces : tc .evictableNamespaces ,
1490
+ MetricsUtilization : MetricsUtilization {
1491
+ MetricsServer : true ,
1492
+ PrometheusURL : "http://prometheus.example.orgname" ,
1493
+ PrometheusAuthToken : "XXXXX" ,
1494
+ },
1495
+ },
1496
+ handle )
1497
+ if err != nil {
1498
+ t .Fatalf ("Unable to initialize the plugin: %v" , err )
1499
+ }
1500
+
1501
+ pClient := & fakePromClient {
1502
+ result : tc .samples ,
1503
+ }
1504
+
1505
+ plugin .(* LowNodeUtilization ).usageSnapshot = newPrometheusUsageSnapshot (handle .GetPodsAssignedToNodeFunc (), pClient , tc .query )
1506
+ status := plugin .(frameworktypes.BalancePlugin ).Balance (ctx , tc .nodes )
1507
+ if status != nil {
1508
+ t .Fatalf ("Balance.err: %v" , status .Err )
1509
+ }
1510
+
1511
+ podsEvicted := podEvictor .TotalEvicted ()
1512
+ if expectedPodsEvicted != podsEvicted {
1513
+ t .Errorf ("Expected %v pods to be evicted but %v got evicted" , expectedPodsEvicted , podsEvicted )
1514
+ }
1515
+ if evictionFailed {
1516
+ t .Errorf ("Pod evictions failed unexpectedly" )
1517
+ }
1518
+ }
1519
+ }
1520
+ t .Run (tc .name , testFnc (false , tc .expectedPodsEvicted ))
1521
+ }
1522
+ }
0 commit comments