Skip to content

Commit 659025e

Browse files
committed
fix: (azure)machinepools: stop continuous reconciliation of resources by sorting ProviderIDs
1 parent 01f24d8 commit 659025e

File tree

4 files changed

+55
-12
lines changed

4 files changed

+55
-12
lines changed

azure/scope/machinepool.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"encoding/base64"
2222
"encoding/json"
2323
"fmt"
24+
"slices"
2425
"strings"
2526

2627
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v2"
@@ -357,6 +358,9 @@ func (m *MachinePoolScope) updateReplicasAndProviderIDs(ctx context.Context) err
357358
providerIDs[i] = machine.Spec.ProviderID
358359
}
359360

361+
// Sort providerIDs to ensure deterministic ordering to prevent continuous reconciliation.
362+
slices.Sort(providerIDs)
363+
360364
m.AzureMachinePool.Status.Replicas = readyReplicas
361365
m.AzureMachinePool.Spec.ProviderIDList = providerIDs
362366
return nil

azure/scope/machinepool_test.go

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -647,7 +647,7 @@ func TestMachinePoolScope_updateReplicasAndProviderIDs(t *testing.T) {
647647
Verify: func(g *WithT, amp *infrav1exp.AzureMachinePool, err error) {
648648
g.Expect(err).NotTo(HaveOccurred())
649649
g.Expect(amp.Status.Replicas).To(BeEquivalentTo(3))
650-
g.Expect(amp.Spec.ProviderIDList).To(ConsistOf("azure://foo/ampm0", "azure://foo/ampm1", "azure://foo/ampm2"))
650+
g.Expect(amp.Spec.ProviderIDList).To(Equal([]string{"azure://foo/ampm0", "azure://foo/ampm1", "azure://foo/ampm2"}))
651651
},
652652
},
653653
{
@@ -680,6 +680,32 @@ func TestMachinePoolScope_updateReplicasAndProviderIDs(t *testing.T) {
680680
g.Expect(amp.Status.Replicas).To(BeEquivalentTo(2))
681681
},
682682
},
683+
{
684+
Name: "providerIDList should be sorted for deterministic ordering",
685+
Setup: func(cb *fake.ClientBuilder) {
686+
// Create machines with provider IDs that would be unsorted if not explicitly sorted
687+
machines := getReadyAzureMachinePoolMachines(3)
688+
// Swap provider IDs to create non-alphabetical order: ampm2, ampm0, ampm1
689+
machines[0].Spec.ProviderID = "azure://foo/ampm2"
690+
machines[1].Spec.ProviderID = "azure://foo/ampm0"
691+
machines[2].Spec.ProviderID = "azure://foo/ampm1"
692+
for _, machine := range machines {
693+
obj := machine
694+
cb.WithObjects(&obj)
695+
}
696+
},
697+
Verify: func(g *WithT, amp *infrav1exp.AzureMachinePool, err error) {
698+
g.Expect(err).NotTo(HaveOccurred())
699+
g.Expect(amp.Status.Replicas).To(BeEquivalentTo(3))
700+
// Verify provider IDs are sorted alphabetically to ensure deterministic ordering
701+
// and prevent continuous reconciliation due to order changes
702+
g.Expect(amp.Spec.ProviderIDList).To(Equal([]string{
703+
"azure://foo/ampm0",
704+
"azure://foo/ampm1",
705+
"azure://foo/ampm2",
706+
}))
707+
},
708+
},
683709
}
684710

685711
for _, c := range cases {

controllers/azuremanagedmachinepool_controller_test.go

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,14 @@ func TestAzureManagedMachinePoolReconcile(t *testing.T) {
6161
Setup: func(cb *fake.ClientBuilder, reconciler pausingReconciler, agentpools *mock_agentpools.MockAgentPoolScopeMockRecorder, nodelister *MockNodeListerMockRecorder) {
6262
cluster, azManagedCluster, azManagedControlPlane, ammp, mp := newReadyAzureManagedMachinePoolCluster()
6363
fakeAgentPoolSpec := fakeAgentPool()
64-
providerIDs := []string{"azure:///subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroupname/providers/Microsoft.Compute/virtualMachineScaleSets/myScaleSetName/virtualMachines/156"}
64+
// Provider IDs should be sorted alphabetically for deterministic ordering (100 < 12 < 156)
65+
providerIDs := []string{
66+
"azure:///subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroupname/providers/Microsoft.Compute/virtualMachineScaleSets/myScaleSetName/virtualMachines/100",
67+
"azure:///subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroupname/providers/Microsoft.Compute/virtualMachineScaleSets/myScaleSetName/virtualMachines/12",
68+
"azure:///subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroupname/providers/Microsoft.Compute/virtualMachineScaleSets/myScaleSetName/virtualMachines/156",
69+
}
6570
fakeVirtualMachineScaleSet := fakeVirtualMachineScaleSet()
71+
// fakeVirtualMachineScaleSetVM returns VMs in unsorted order (156, 12, 100)
6672
fakeVirtualMachineScaleSetVM := fakeVirtualMachineScaleSetVM()
6773

6874
reconciler.MockReconciler.EXPECT().Reconcile(gomock2.AContext()).Return(nil)
@@ -346,22 +352,26 @@ func fakeAgentPool(changes ...func(*agentpools.AgentPoolSpec)) agentpools.AgentP
346352
return pool
347353
}
348354

355+
// fakeVirtualMachineScaleSetVM returns VMs in non-sorted order (156, 12, 100) to verify
356+
// that provider IDs are sorted for deterministic ordering.
349357
func fakeVirtualMachineScaleSetVM() []armcompute.VirtualMachineScaleSetVM {
350-
virtualMachineScaleSetVM := []armcompute.VirtualMachineScaleSetVM{
358+
return []armcompute.VirtualMachineScaleSetVM{
351359
{
352-
InstanceID: ptr.To("0"),
360+
InstanceID: ptr.To("2"),
353361
ID: ptr.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroupName/providers/Microsoft.Compute/virtualMachineScaleSets/myScaleSetName/virtualMachines/156"),
362+
Name: ptr.To("vm2"),
363+
},
364+
{
365+
InstanceID: ptr.To("0"),
366+
ID: ptr.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroupName/providers/Microsoft.Compute/virtualMachineScaleSets/myScaleSetName/virtualMachines/12"),
354367
Name: ptr.To("vm0"),
355-
Zones: []*string{ptr.To("zone0")},
356-
Properties: &armcompute.VirtualMachineScaleSetVMProperties{
357-
ProvisioningState: ptr.To("Succeeded"),
358-
OSProfile: &armcompute.OSProfile{
359-
ComputerName: ptr.To("instance-000000"),
360-
},
361-
},
368+
},
369+
{
370+
InstanceID: ptr.To("1"),
371+
ID: ptr.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroupName/providers/Microsoft.Compute/virtualMachineScaleSets/myScaleSetName/virtualMachines/100"),
372+
Name: ptr.To("vm1"),
362373
},
363374
}
364-
return virtualMachineScaleSetVM
365375
}
366376

367377
func fakeVirtualMachineScaleSet() []armcompute.VirtualMachineScaleSet {

controllers/azuremanagedmachinepool_reconciler.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package controllers
1919
import (
2020
"context"
2121
"fmt"
22+
"slices"
2223
"time"
2324

2425
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5"
@@ -164,6 +165,8 @@ func (s *azureManagedMachinePoolService) Reconcile(ctx context.Context) error {
164165
providerIDs[i] = providerID
165166
}
166167

168+
// Sort providerIDs to ensure deterministic ordering to prevent continuous reconciliation.
169+
slices.Sort(providerIDs)
167170
s.scope.SetAgentPoolProviderIDList(providerIDs)
168171
s.scope.SetAgentPoolReplicas(int32(len(providerIDs)))
169172
s.scope.SetAgentPoolReady(true)

0 commit comments

Comments
 (0)