@@ -26,13 +26,15 @@ import (
26
26
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27
27
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
28
28
"k8s.io/client-go/tools/record"
29
+ utilfeature "k8s.io/component-base/featuregate/testing"
29
30
"k8s.io/utils/pointer"
30
31
"sigs.k8s.io/controller-runtime/pkg/client"
31
32
"sigs.k8s.io/controller-runtime/pkg/client/fake"
32
33
"sigs.k8s.io/controller-runtime/pkg/reconcile"
33
34
34
35
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
35
36
"sigs.k8s.io/cluster-api/controllers/external"
37
+ "sigs.k8s.io/cluster-api/feature"
36
38
"sigs.k8s.io/cluster-api/internal/contract"
37
39
"sigs.k8s.io/cluster-api/internal/test/builder"
38
40
"sigs.k8s.io/cluster-api/internal/util/ssa"
@@ -1326,6 +1328,190 @@ func TestMachineSetReconciler_syncMachines(t *testing.T) {
1326
1328
}, 5 * time .Second ).Should (Succeed ())
1327
1329
}
1328
1330
1331
+ func TestMachineSetReconciler_reconcileUnhealthyMachines (t * testing.T ) {
1332
+ t .Run ("should delete unhealthy machines if preflight checks pass" , func (t * testing.T ) {
1333
+ defer utilfeature .SetFeatureGateDuringTest (t , feature .Gates , feature .MachineSetPreflightChecks , true )()
1334
+
1335
+ g := NewWithT (t )
1336
+
1337
+ controlPlaneStable := builder .ControlPlane ("default" , "cp1" ).
1338
+ WithVersion ("v1.26.2" ).
1339
+ WithStatusFields (map [string ]interface {}{
1340
+ "status.version" : "v1.26.2" ,
1341
+ }).
1342
+ Build ()
1343
+ cluster := & clusterv1.Cluster {
1344
+ ObjectMeta : metav1.ObjectMeta {
1345
+ Name : "test-cluster" ,
1346
+ Namespace : "default" ,
1347
+ },
1348
+ Spec : clusterv1.ClusterSpec {
1349
+ ControlPlaneRef : contract .ObjToRef (controlPlaneStable ),
1350
+ },
1351
+ }
1352
+ machineSet := & clusterv1.MachineSet {}
1353
+
1354
+ unhealthyMachine := & clusterv1.Machine {
1355
+ ObjectMeta : metav1.ObjectMeta {
1356
+ Name : "unhealthy-machine" ,
1357
+ Namespace : "default" ,
1358
+ },
1359
+ Status : clusterv1.MachineStatus {
1360
+ Conditions : []clusterv1.Condition {
1361
+ {
1362
+ Type : clusterv1 .MachineOwnerRemediatedCondition ,
1363
+ Status : corev1 .ConditionFalse ,
1364
+ },
1365
+ },
1366
+ },
1367
+ }
1368
+ healthyMachine := & clusterv1.Machine {
1369
+ ObjectMeta : metav1.ObjectMeta {
1370
+ Name : "healthy-machine" ,
1371
+ Namespace : "default" ,
1372
+ },
1373
+ }
1374
+
1375
+ machines := []* clusterv1.Machine {unhealthyMachine , healthyMachine }
1376
+
1377
+ fakeClient := fake .NewClientBuilder ().WithObjects (controlPlaneStable , unhealthyMachine , healthyMachine ).Build ()
1378
+ r := & Reconciler {Client : fakeClient }
1379
+ _ , err := r .reconcileUnhealthyMachines (ctx , cluster , machineSet , machines )
1380
+ g .Expect (err ).To (BeNil ())
1381
+ // Verify the unhealthy machine is deleted.
1382
+ m := & clusterv1.Machine {}
1383
+ err = r .Client .Get (ctx , client .ObjectKeyFromObject (unhealthyMachine ), m )
1384
+ g .Expect (apierrors .IsNotFound (err )).To (BeTrue ())
1385
+ // Verify the healthy machine is not deleted.
1386
+ m = & clusterv1.Machine {}
1387
+ g .Expect (r .Client .Get (ctx , client .ObjectKeyFromObject (healthyMachine ), m )).Should (Succeed ())
1388
+ })
1389
+
1390
+ t .Run ("should update the unhealthy machine MachineOwnerRemediated condition if preflight checks did not pass" , func (t * testing.T ) {
1391
+ defer utilfeature .SetFeatureGateDuringTest (t , feature .Gates , feature .MachineSetPreflightChecks , true )()
1392
+
1393
+ g := NewWithT (t )
1394
+
1395
+ // An upgrading control plane should cause the preflight checks to not pass.
1396
+ controlPlaneUpgrading := builder .ControlPlane ("default" , "cp1" ).
1397
+ WithVersion ("v1.26.2" ).
1398
+ WithStatusFields (map [string ]interface {}{
1399
+ "status.version" : "v1.25.2" ,
1400
+ }).
1401
+ Build ()
1402
+ cluster := & clusterv1.Cluster {
1403
+ ObjectMeta : metav1.ObjectMeta {
1404
+ Name : "test-cluster" ,
1405
+ Namespace : "default" ,
1406
+ },
1407
+ Spec : clusterv1.ClusterSpec {
1408
+ ControlPlaneRef : contract .ObjToRef (controlPlaneUpgrading ),
1409
+ },
1410
+ }
1411
+ machineSet := & clusterv1.MachineSet {}
1412
+
1413
+ unhealthyMachine := & clusterv1.Machine {
1414
+ ObjectMeta : metav1.ObjectMeta {
1415
+ Name : "unhealthy-machine" ,
1416
+ Namespace : "default" ,
1417
+ },
1418
+ Status : clusterv1.MachineStatus {
1419
+ Conditions : []clusterv1.Condition {
1420
+ {
1421
+ Type : clusterv1 .MachineOwnerRemediatedCondition ,
1422
+ Status : corev1 .ConditionFalse ,
1423
+ },
1424
+ },
1425
+ },
1426
+ }
1427
+ healthyMachine := & clusterv1.Machine {
1428
+ ObjectMeta : metav1.ObjectMeta {
1429
+ Name : "healthy-machine" ,
1430
+ Namespace : "default" ,
1431
+ },
1432
+ }
1433
+
1434
+ machines := []* clusterv1.Machine {unhealthyMachine , healthyMachine }
1435
+ fakeClient := fake .NewClientBuilder ().WithObjects (controlPlaneUpgrading , unhealthyMachine , healthyMachine ).WithStatusSubresource (& clusterv1.Machine {}).Build ()
1436
+ r := & Reconciler {Client : fakeClient }
1437
+ _ , err := r .reconcileUnhealthyMachines (ctx , cluster , machineSet , machines )
1438
+ g .Expect (err ).To (BeNil ())
1439
+
1440
+ // Verify the unhealthy machine has the updated condition.
1441
+ condition := clusterv1 .MachineOwnerRemediatedCondition
1442
+ m := & clusterv1.Machine {}
1443
+ g .Expect (r .Client .Get (ctx , client .ObjectKeyFromObject (unhealthyMachine ), m )).To (Succeed ())
1444
+ g .Expect (conditions .Has (m , condition )).
1445
+ To (BeTrue (), "Machine should have the %s condition set" , condition )
1446
+ machineOwnerRemediatedCondition := conditions .Get (m , condition )
1447
+ g .Expect (machineOwnerRemediatedCondition .Status ).
1448
+ To (Equal (corev1 .ConditionFalse ), "%s condition status should be false" , condition )
1449
+ g .Expect (machineOwnerRemediatedCondition .Reason ).
1450
+ To (Equal (clusterv1 .WaitingForRemediationReason ), "%s condition should have reason %s" , condition , clusterv1 .WaitingForRemediationReason )
1451
+
1452
+ // Verify the healthy machine continues to not have the MachineOwnerRemediated condition.
1453
+ m = & clusterv1.Machine {}
1454
+ g .Expect (r .Client .Get (ctx , client .ObjectKeyFromObject (healthyMachine ), m )).To (Succeed ())
1455
+ g .Expect (conditions .Has (m , condition )).
1456
+ To (BeFalse (), "Machine should not have the %s condition set" , condition )
1457
+ })
1458
+ }
1459
+
1460
+ func TestMachineSetReconciler_syncReplicas (t * testing.T ) {
1461
+ t .Run ("should hold off on creating new machines when preflight checks do not pass" , func (t * testing.T ) {
1462
+ defer utilfeature .SetFeatureGateDuringTest (t , feature .Gates , feature .MachineSetPreflightChecks , true )()
1463
+
1464
+ g := NewWithT (t )
1465
+
1466
+ // An upgrading control plane should cause the preflight checks to not pass.
1467
+ controlPlaneUpgrading := builder .ControlPlane ("default" , "test-cp" ).
1468
+ WithVersion ("v1.26.2" ).
1469
+ WithStatusFields (map [string ]interface {}{
1470
+ "status.version" : "v1.25.2" ,
1471
+ }).
1472
+ Build ()
1473
+ cluster := & clusterv1.Cluster {
1474
+ ObjectMeta : metav1.ObjectMeta {
1475
+ Name : "test-cluster" ,
1476
+ Namespace : "default" ,
1477
+ },
1478
+ Spec : clusterv1.ClusterSpec {
1479
+ ControlPlaneRef : contract .ObjToRef (controlPlaneUpgrading ),
1480
+ },
1481
+ }
1482
+ machineSet := & clusterv1.MachineSet {
1483
+ ObjectMeta : metav1.ObjectMeta {
1484
+ Name : "test-machineset" ,
1485
+ Namespace : "default" ,
1486
+ },
1487
+ Spec : clusterv1.MachineSetSpec {
1488
+ Replicas : pointer .Int32 (1 ),
1489
+ },
1490
+ }
1491
+
1492
+ fakeClient := fake .NewClientBuilder ().WithObjects (controlPlaneUpgrading , machineSet ).WithStatusSubresource (& clusterv1.MachineSet {}).Build ()
1493
+ r := & Reconciler {Client : fakeClient }
1494
+ result , err := r .syncReplicas (ctx , cluster , machineSet , nil )
1495
+ g .Expect (err ).To (BeNil ())
1496
+ g .Expect (result .IsZero ()).To (BeFalse (), "syncReplicas should not return a 'zero' result" )
1497
+
1498
+ // Verify the proper condition is set on the MachineSet.
1499
+ condition := clusterv1 .MachinesCreatedCondition
1500
+ g .Expect (conditions .Has (machineSet , condition )).
1501
+ To (BeTrue (), "MachineSet should have the %s condition set" , condition )
1502
+ machinesCreatedCondition := conditions .Get (machineSet , condition )
1503
+ g .Expect (machinesCreatedCondition .Status ).
1504
+ To (Equal (corev1 .ConditionFalse ), "%s condition status should be %s" , condition , corev1 .ConditionFalse )
1505
+ g .Expect (machinesCreatedCondition .Reason ).
1506
+ To (Equal (clusterv1 .PreflightCheckFailedReason ), "%s condition reason should be %s" , condition , clusterv1 .PreflightCheckFailedReason )
1507
+
1508
+ // Verify no new Machines are created.
1509
+ machineList := & clusterv1.MachineList {}
1510
+ g .Expect (r .Client .List (ctx , machineList )).To (Succeed ())
1511
+ g .Expect (machineList .Items ).To (BeEmpty (), "There should not be any machines" )
1512
+ })
1513
+ }
1514
+
1329
1515
func TestComputeDesiredMachine (t * testing.T ) {
1330
1516
duration5s := & metav1.Duration {Duration : 5 * time .Second }
1331
1517
duration10s := & metav1.Duration {Duration : 10 * time .Second }
0 commit comments