Skip to content

Commit 57ea5b8

Browse files
authored
Merge pull request #4959 from helio/fix-ampm-delete-recreate
AzureMachinePoolMachine remove finalizer and avoid recreation if VMSS is deleting
2 parents 4150054 + f4153d3 commit 57ea5b8

File tree

4 files changed

+177
-2
lines changed

4 files changed

+177
-2
lines changed

azure/scope/machinepool.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,10 @@ func (m *MachinePoolScope) applyAzureMachinePoolMachines(ctx context.Context) er
413413
// determine which machines need to be created to reflect the current state in Azure
414414
azureMachinesByProviderID := m.vmssState.InstancesByProviderID(m.AzureMachinePool.Spec.OrchestrationMode)
415415
for key, val := range azureMachinesByProviderID {
416+
if val.State == infrav1.Deleting || val.State == infrav1.Deleted {
417+
log.V(4).Info("not recreating AzureMachinePoolMachine because VMSS VM is deleting", "providerID", key)
418+
continue
419+
}
416420
if _, ok := existingMachinesByProviderID[key]; !ok {
417421
log.V(4).Info("creating AzureMachinePoolMachine", "providerID", key)
418422
if err := m.createMachine(ctx, val); err != nil {

azure/scope/machinepool_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1449,6 +1449,26 @@ func TestMachinePoolScope_applyAzureMachinePoolMachines(t *testing.T) {
14491449
g.Expect(err).To(HaveOccurred())
14501450
},
14511451
},
1452+
{
1453+
Name: "if existing MachinePool is present but in deleting state, do not recreate AzureMachinePoolMachines",
1454+
Setup: func(mp *expv1.MachinePool, amp *infrav1exp.AzureMachinePool, vmssState *azure.VMSS, cb *fake.ClientBuilder) {
1455+
mp.Spec.Replicas = ptr.To[int32](1)
1456+
1457+
vmssState.Instances = []azure.VMSSVM{
1458+
{
1459+
ID: "/subscriptions/123/resourceGroups/rg/providers/Microsoft.Compute/virtualMachines/vm",
1460+
Name: "vm",
1461+
State: infrav1.Deleting,
1462+
},
1463+
}
1464+
},
1465+
Verify: func(g *WithT, amp *infrav1exp.AzureMachinePool, c client.Client, err error) {
1466+
g.Expect(err).NotTo(HaveOccurred())
1467+
list := infrav1exp.AzureMachinePoolMachineList{}
1468+
g.Expect(c.List(ctx, &list)).NotTo(HaveOccurred())
1469+
g.Expect(list.Items).Should(BeEmpty())
1470+
},
1471+
},
14521472
}
14531473
for _, tt := range tests {
14541474
t.Run(tt.Name, func(t *testing.T) {

exp/controllers/azuremachinepoolmachine_controller.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,9 +207,14 @@ func (ampmr *AzureMachinePoolMachineController) Reconcile(ctx context.Context, r
207207
return reconcile.Result{}, err
208208
}
209209

210-
if machine != nil {
210+
switch {
211+
case machine != nil:
211212
logger = logger.WithValues("machine", machine.Name)
212-
} else {
213+
case !azureMachinePool.ObjectMeta.DeletionTimestamp.IsZero():
214+
logger.Info("AzureMachinePool is being deleted, removing finalizer")
215+
controllerutil.RemoveFinalizer(azureMachine, infrav1exp.AzureMachinePoolMachineFinalizer)
216+
return reconcile.Result{}, ampmr.Client.Update(ctx, azureMachine)
217+
default:
213218
logger.Info("Waiting for Machine Controller to set OwnerRef on AzureMachinePoolMachine")
214219
return reconcile.Result{}, nil
215220
}

exp/controllers/azuremachinepoolmachine_controller_test.go

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,24 @@ func TestAzureMachinePoolMachineReconciler_Reconcile(t *testing.T) {
8989
g.Expect(err.Error()).Should(ContainSubstring("not found"))
9090
},
9191
},
92+
{
93+
Name: "should remove finalizer if Machine is not found and AzureMachinePool has deletionTimestamp set",
94+
Setup: func(cb *fake.ClientBuilder, reconciler *mock_azure.MockReconcilerMockRecorder) {
95+
objects := getDeletingMachinePoolObjects()
96+
cb.WithObjects(objects...)
97+
},
98+
Verify: func(g *WithT, c client.Client, result ctrl.Result, err error) {
99+
g.Expect(err).NotTo(HaveOccurred())
100+
101+
ampm := &infrav1exp.AzureMachinePoolMachine{}
102+
err = c.Get(context.Background(), types.NamespacedName{
103+
Name: "ampm1",
104+
Namespace: "default",
105+
}, ampm)
106+
g.Expect(err).To(HaveOccurred())
107+
g.Expect(err.Error()).Should(ContainSubstring("not found"))
108+
},
109+
},
92110
}
93111

94112
for _, c := range cases {
@@ -282,3 +300,131 @@ func getReadyMachinePoolMachineClusterObjects(ampmIsDeleting bool, ampmProvision
282300

283301
return []client.Object{cluster, azCluster, mp, amp, ma, ampm, fakeIdentity, fakeSecret}
284302
}
303+
304+
func getDeletingMachinePoolObjects() []client.Object {
305+
azCluster := &infrav1.AzureCluster{
306+
TypeMeta: metav1.TypeMeta{
307+
Kind: "AzureCluster",
308+
},
309+
ObjectMeta: metav1.ObjectMeta{
310+
Name: "azCluster1",
311+
Namespace: "default",
312+
},
313+
Spec: infrav1.AzureClusterSpec{
314+
AzureClusterClassSpec: infrav1.AzureClusterClassSpec{
315+
SubscriptionID: "subID",
316+
IdentityRef: &corev1.ObjectReference{
317+
Name: "fake-identity",
318+
Namespace: "default",
319+
Kind: "AzureClusterIdentity",
320+
},
321+
},
322+
},
323+
}
324+
325+
cluster := &clusterv1.Cluster{
326+
TypeMeta: metav1.TypeMeta{
327+
Kind: "Cluster",
328+
},
329+
ObjectMeta: metav1.ObjectMeta{
330+
Name: "cluster1",
331+
Namespace: "default",
332+
},
333+
Spec: clusterv1.ClusterSpec{
334+
InfrastructureRef: &corev1.ObjectReference{
335+
Name: azCluster.Name,
336+
Namespace: "default",
337+
Kind: "AzureCluster",
338+
},
339+
},
340+
Status: clusterv1.ClusterStatus{
341+
InfrastructureReady: true,
342+
},
343+
}
344+
345+
mp := &expv1.MachinePool{
346+
TypeMeta: metav1.TypeMeta{
347+
Kind: "MachinePool",
348+
},
349+
ObjectMeta: metav1.ObjectMeta{
350+
Name: "mp1",
351+
Namespace: "default",
352+
Finalizers: []string{"test"},
353+
DeletionTimestamp: &metav1.Time{
354+
Time: time.Now(),
355+
},
356+
Labels: map[string]string{
357+
"cluster.x-k8s.io/cluster-name": cluster.Name,
358+
},
359+
},
360+
}
361+
362+
amp := &infrav1exp.AzureMachinePool{
363+
TypeMeta: metav1.TypeMeta{
364+
Kind: "AzureMachinePool",
365+
},
366+
ObjectMeta: metav1.ObjectMeta{
367+
Name: "amp1",
368+
Namespace: "default",
369+
Finalizers: []string{"test"},
370+
DeletionTimestamp: &metav1.Time{
371+
Time: time.Now(),
372+
},
373+
OwnerReferences: []metav1.OwnerReference{
374+
{
375+
Name: mp.Name,
376+
Kind: "MachinePool",
377+
APIVersion: expv1.GroupVersion.String(),
378+
},
379+
},
380+
},
381+
}
382+
383+
ampm := &infrav1exp.AzureMachinePoolMachine{
384+
ObjectMeta: metav1.ObjectMeta{
385+
Name: "ampm1",
386+
Namespace: "default",
387+
DeletionTimestamp: &metav1.Time{
388+
Time: time.Now(),
389+
},
390+
Finalizers: []string{infrav1exp.AzureMachinePoolMachineFinalizer},
391+
Labels: map[string]string{
392+
clusterv1.ClusterNameLabel: cluster.Name,
393+
},
394+
OwnerReferences: []metav1.OwnerReference{
395+
{
396+
Name: amp.Name,
397+
Kind: infrav1.AzureMachinePoolKind,
398+
APIVersion: infrav1exp.GroupVersion.String(),
399+
},
400+
},
401+
},
402+
}
403+
404+
fakeIdentity := &infrav1.AzureClusterIdentity{
405+
ObjectMeta: metav1.ObjectMeta{
406+
Name: "fake-identity",
407+
Namespace: "default",
408+
},
409+
Spec: infrav1.AzureClusterIdentitySpec{
410+
Type: infrav1.ServicePrincipal,
411+
ClientSecret: corev1.SecretReference{
412+
Name: "fooSecret",
413+
Namespace: "default",
414+
},
415+
TenantID: "fake-tenantid",
416+
},
417+
}
418+
419+
fakeSecret := &corev1.Secret{
420+
ObjectMeta: metav1.ObjectMeta{
421+
Name: "fooSecret",
422+
Namespace: "default",
423+
},
424+
Data: map[string][]byte{
425+
"clientSecret": []byte("fooSecret"),
426+
},
427+
}
428+
429+
return []client.Object{cluster, azCluster, mp, amp, ampm, fakeIdentity, fakeSecret}
430+
}

0 commit comments

Comments
 (0)