@@ -42,6 +42,7 @@ import (
42
42
"sigs.k8s.io/cluster-api/test/framework/clusterctl"
43
43
"sigs.k8s.io/cluster-api/util"
44
44
"sigs.k8s.io/cluster-api/util/conditions"
45
+ "sigs.k8s.io/cluster-api/util/patch"
45
46
)
46
47
47
48
// The Cluster API test extension uses a ConfigMap named cluster-name + suffix to determine answers to the lifecycle hook calls;
@@ -221,6 +222,10 @@ func clusterUpgradeWithRuntimeSDKSpec(ctx context.Context, inputGetter func() cl
221
222
input .E2EConfig .GetIntervals (specName , "wait-machine-upgrade" ))
222
223
},
223
224
PreWaitForMachineDeploymentToBeUpgraded : func () {
225
+ machineSetPreflightChecksTestHandler (ctx ,
226
+ input .BootstrapClusterProxy .GetClient (),
227
+ clusterRef )
228
+
224
229
afterControlPlaneUpgradeTestHandler (ctx ,
225
230
input .BootstrapClusterProxy .GetClient (),
226
231
clusterRef ,
@@ -281,6 +286,104 @@ func clusterUpgradeWithRuntimeSDKSpec(ctx context.Context, inputGetter func() cl
281
286
})
282
287
}
283
288
289
+ // machineSetPreflightChecksTestHandler verifies the MachineSet preflight checks.
290
+ // At this point in the test the ControlPlane is upgraded to the new version and the upgrade to the MachineDeployments
291
+ // should be blocked by the AfterControlPlaneUpgrade hook.
292
+ // Test the MachineSet preflight checks by scaling up the MachineDeployment. The creation on the new Machine
293
+ // should be blocked because the preflight checks should not pass (kubeadm version skew preflight check should fail).
294
+ func machineSetPreflightChecksTestHandler (ctx context.Context , c client.Client , clusterRef types.NamespacedName ) {
295
+ // Verify that the hook is called and the topology reconciliation is blocked.
296
+ hookName := "AfterControlPlaneUpgrade"
297
+ Eventually (func () error {
298
+ if err := checkLifecycleHooksCalledAtLeastOnce (ctx , c , clusterRef , []string {hookName }); err != nil {
299
+ return err
300
+ }
301
+
302
+ cluster := framework .GetClusterByName (ctx , framework.GetClusterByNameInput {
303
+ Name : clusterRef .Name , Namespace : clusterRef .Namespace , Getter : c })
304
+
305
+ if ! clusterConditionShowsHookBlocking (cluster , hookName ) {
306
+ return errors .Errorf ("Blocking condition for %s not found on Cluster object" , hookName )
307
+ }
308
+
309
+ return nil
310
+ }, 30 * time .Second ).Should (Succeed (), "%s has not been called" , hookName )
311
+
312
+ // Scale up the MachineDeployment
313
+ machineDeployments := framework .GetMachineDeploymentsByCluster (ctx , framework.GetMachineDeploymentsByClusterInput {
314
+ Lister : c ,
315
+ ClusterName : clusterRef .Name ,
316
+ Namespace : clusterRef .Namespace ,
317
+ })
318
+ md := machineDeployments [0 ]
319
+
320
+ // Note: It is fair to assume that the Cluster is ClusterClass based since RuntimeSDK
321
+ // is only supported for ClusterClass based Clusters.
322
+ patchHelper , err := patch .NewHelper (md , c )
323
+ Expect (err ).To (BeNil ())
324
+
325
+ // Scale up the MachineDeployment.
326
+ // IMPORTANT: Since the MachineDeployment is pending an upgrade at this point the topology controller will not push any changes
327
+ // to the MachineDeployment. Therefore, the changes made to the MachineDeployment here will not be replaced
328
+ // until the AfterControlPlaneUpgrade hook unblocks the upgrade.
329
+ * md .Spec .Replicas ++
330
+ Eventually (func () error {
331
+ return patchHelper .Patch (ctx , md )
332
+ }).Should (Succeed (), "Failed to scale up the MachineDeployment %s" , klog .KObj (md ))
333
+ // Verify the MachineDeployment updated replicas are not overridden by the topology controller.
334
+ // Note: This verifies that the topology controller in fact holds any reconciliation of this MachineDeployment.
335
+ Consistently (func (g Gomega ) {
336
+ // Get the updated MachineDeployment.
337
+ targetMD := & clusterv1.MachineDeployment {}
338
+ // Wrap in an Eventually block for additional safety. Since all of this is in a Consistently block it
339
+ // will fail if we hit a transient error like a network flake.
340
+ g .Eventually (func () error {
341
+ return c .Get (ctx , client .ObjectKeyFromObject (md ), targetMD )
342
+ }).Should (Succeed (), "Failed to get MachineDeployment %s" , klog .KObj (md ))
343
+ // Verify replicas are not overridden.
344
+ g .Expect (targetMD .Spec .Replicas ).To (Equal (md .Spec .Replicas ))
345
+ }, 10 * time .Second , 1 * time .Second )
346
+
347
+ // Since the MachineDeployment is scaled up (overriding the topology controller) at this point the MachineSet would
348
+ // also scale up. However, a new Machine creation would be blocked by one of the MachineSet preflight checks (KubeadmVersionSkew).
349
+ // Verify the MachineSet is blocking new Machine creation.
350
+ Eventually (func (g Gomega ) {
351
+ machineSets := framework .GetMachineSetsByDeployment (ctx , framework.GetMachineSetsByDeploymentInput {
352
+ Lister : c ,
353
+ MDName : md .Name ,
354
+ Namespace : md .Namespace ,
355
+ })
356
+ g .Expect (conditions .IsFalse (machineSets [0 ], clusterv1 .MachinesCreatedCondition )).To (BeTrue ())
357
+ machinesCreatedCondition := conditions .Get (machineSets [0 ], clusterv1 .MachinesCreatedCondition )
358
+ g .Expect (machinesCreatedCondition ).NotTo (BeNil ())
359
+ g .Expect (machinesCreatedCondition .Reason ).To (Equal (clusterv1 .PreflightCheckFailedReason ))
360
+ g .Expect (machineSets [0 ].Spec .Replicas ).To (Equal (md .Spec .Replicas ))
361
+ }).Should (Succeed (), "New Machine creation not blocked by MachineSet preflight checks" )
362
+
363
+ // Verify that the MachineSet is not creating the new Machine.
364
+ // No new machines should be created for this MachineDeployment even though it is scaled up.
365
+ // Creation of new Machines will be blocked by MachineSet preflight checks (KubeadmVersionSkew).
366
+ Consistently (func (g Gomega ) {
367
+ originalReplicas := int (* md .Spec .Replicas - 1 )
368
+ machines := framework .GetMachinesByMachineDeployments (ctx , framework.GetMachinesByMachineDeploymentsInput {
369
+ Lister : c ,
370
+ ClusterName : clusterRef .Name ,
371
+ Namespace : clusterRef .Namespace ,
372
+ MachineDeployment : * md ,
373
+ })
374
+ g .Expect (machines ).To (HaveLen (originalReplicas ), "New Machines should not be created" )
375
+ }, 10 * time .Second , time .Second )
376
+
377
+ // Scale down the MachineDeployment to the original replicas to restore to the state of the MachineDeployment
378
+ // it existed in before this test block.
379
+ patchHelper , err = patch .NewHelper (md , c )
380
+ Expect (err ).To (BeNil ())
381
+ * md .Spec .Replicas --
382
+ Eventually (func () error {
383
+ return patchHelper .Patch (ctx , md )
384
+ }).Should (Succeed (), "Failed to scale down the MachineDeployment %s" , klog .KObj (md ))
385
+ }
386
+
284
387
// extensionConfig generates an ExtensionConfig.
285
388
// We make sure this cluster-wide object does not conflict with others by using a random generated
286
389
// name and a NamespaceSelector selecting on the namespace of the current test.
0 commit comments