@@ -20,6 +20,7 @@ package vmware
20
20
import (
21
21
"context"
22
22
"fmt"
23
+ "sort"
23
24
"strings"
24
25
"time"
25
26
@@ -37,7 +38,6 @@ import (
37
38
38
39
vmwarev1 "sigs.k8s.io/cluster-api-provider-vsphere/apis/vmware/v1beta1"
39
40
clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2"
40
- ctrlutil "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
41
41
)
42
42
43
43
const (
@@ -63,13 +63,12 @@ func (r *VirtualMachineGroupReconciler) Reconcile(ctx context.Context, req ctrl.
63
63
}
64
64
65
65
log = log .WithValues ("Cluster" , klog .KObj (cluster ))
66
- // If Cluster is deleted, just return as VirtualMachineGroup will be GCed and no extral process needed.
66
+ // If Cluster is deleted, just return as VirtualMachineGroup will be GCed and no extra processing needed.
67
67
if ! cluster .DeletionTimestamp .IsZero () {
68
68
return reconcile.Result {}, nil
69
69
}
70
70
71
71
vmg := & vmoprv1.VirtualMachineGroup {}
72
-
73
72
key := & client.ObjectKey {
74
73
Namespace : cluster .Namespace ,
75
74
Name : cluster .Name ,
@@ -80,7 +79,6 @@ func (r *VirtualMachineGroupReconciler) Reconcile(ctx context.Context, req ctrl.
80
79
log .Error (err , "failed to get VirtualMachineGroup" )
81
80
return ctrl.Result {}, err
82
81
}
83
- // Define the VM Operator VirtualMachine resource to reconcile.
84
82
vmg = & vmoprv1.VirtualMachineGroup {
85
83
ObjectMeta : metav1.ObjectMeta {
86
84
Name : key .Name ,
@@ -89,56 +87,41 @@ func (r *VirtualMachineGroupReconciler) Reconcile(ctx context.Context, req ctrl.
89
87
}
90
88
}
91
89
92
- // If as least one MachineDeployment of Cluster is specified with failureDomain, then return.
93
- // No need to handle Cluster using explicit placement. For VC 9.1, no mixed mode of explicit and automatic placement
94
- // during initial deployment.
95
- if vmg .CreationTimestamp .IsZero () {
96
- explicitPlacement , err := r .isExplicitPlacement (cluster )
97
- if err != nil {
98
- return reconcile.Result {}, err
99
- }
100
-
101
- if explicitPlacement {
102
- log .Info ("No need to create VirtualMachineGroup for Cluster using explicit placement." )
103
- return reconcile.Result {}, nil
104
- }
105
- }
106
-
107
- // Proceed only if multiple zones are available.
108
- // If there is only one zone(default), node automatic placement is unnecessary
109
- // because all Machine Deployments will be scheduled into that single zone.
110
- // The VSphereCluster resource discovers the underlying zones,
111
- // which we treat as the source of truth.
112
- vsphereClusterList := & vmwarev1.VSphereClusterList {}
113
- labelKey := clusterv1 .ClusterNameLabel
114
- if err := r .Client .List (ctx , vsphereClusterList ,
115
- client .InNamespace (cluster .Namespace ),
116
- client .MatchingLabels (map [string ]string {labelKey : cluster .Name }),
117
- ); err != nil {
118
- return reconcile.Result {}, fmt .Errorf ("failed to list VSphereClusters in namespace %s: %w" , cluster .Namespace , err )
119
- }
120
-
121
- vsphereCluster := & vmwarev1.VSphereCluster {}
122
- switch len (vsphereClusterList .Items ) {
123
- case 0 :
124
- return reconcile.Result {}, fmt .Errorf ("no VSphereCluster found with label %s=%s in namespace %s" , labelKey , cluster .Name , cluster .Namespace )
125
- case 1 :
126
- vsphereCluster = & vsphereClusterList .Items [0 ]
127
- default :
128
- return reconcile.Result {}, fmt .Errorf ("found %d VSphereClusters with label %s=%s in namespace %s; expected exactly 1" , len (vsphereClusterList .Items ), labelKey , cluster .Name , cluster .Namespace )
129
- }
130
-
131
- // Fetch the VSphereCluster instance.
132
- if vsphereCluster .Status .Ready != true {
133
- log .Info ("Waiting for VSphereCluster to be ready with failure domain discovered" )
134
- return reconcile.Result {RequeueAfter : reconciliationDelay }, nil
135
-
136
- }
137
-
138
- if len (vsphereCluster .Status .FailureDomains ) <= 1 {
139
- log .Info ("Single or no zone detected; skipping node automatic placement" )
140
- return reconcile.Result {}, nil
141
- }
90
+ // // Proceed only if multiple zones are available.
91
+ // // If there is only one zone(default), node automatic placement is unnecessary
92
+ // // because all Machine Deployments will be scheduled into that single zone.
93
+ // // The VSphereCluster resource discovers the underlying zones,
94
+ // // which we treat as the source of truth.
95
+ // vsphereClusterList := &vmwarev1.VSphereClusterList{}
96
+ // labelKey := clusterv1.ClusterNameLabel
97
+ // if err := r.Client.List(ctx, vsphereClusterList,
98
+ // client.InNamespace(cluster.Namespace),
99
+ // client.MatchingLabels(map[string]string{labelKey: cluster.Name}),
100
+ // ); err != nil {
101
+ // return reconcile.Result{}, fmt.Errorf("failed to list VSphereClusters in namespace %s: %w", cluster.Namespace, err)
102
+ // }
103
+
104
+ // vsphereCluster := &vmwarev1.VSphereCluster{}
105
+ // switch len(vsphereClusterList.Items) {
106
+ // case 0:
107
+ // return reconcile.Result{}, fmt.Errorf("no VSphereCluster found with label %s=%s in namespace %s", labelKey, cluster.Name, cluster.Namespace)
108
+ // case 1:
109
+ // vsphereCluster = &vsphereClusterList.Items[0]
110
+ // default:
111
+ // return reconcile.Result{}, fmt.Errorf("found %d VSphereClusters with label %s=%s in namespace %s; expected exactly 1", len(vsphereClusterList.Items), labelKey, cluster.Name, cluster.Namespace)
112
+ // }
113
+
114
+ // // Fetch the VSphereCluster instance.
115
+ // if vsphereCluster.Status.Ready != true {
116
+ // log.Info("Waiting for VSphereCluster to be ready with failure domain discovered")
117
+ // return reconcile.Result{RequeueAfter: reconciliationDelay}, nil
118
+
119
+ // }
120
+
121
+ // if len(vsphereCluster.Status.FailureDomains) <= 1 {
122
+ // log.Info("Single or no zone detected; skipping node automatic placement")
123
+ // return reconcile.Result{}, nil
124
+ // }
142
125
143
126
// If ControlPlane haven't initialized, requeue it since VSphereMachines of MachineDeployment will only be created after
144
127
// ControlPlane is initialized.
@@ -157,8 +140,8 @@ func (r *VirtualMachineGroupReconciler) createOrUpdateVMG(ctx context.Context, c
157
140
log := ctrl .LoggerFrom (ctx )
158
141
159
142
// Calculate expected Machines of all MachineDeployments.
160
- expectdMachines := getExpectedMachines (cluster )
161
- if expectdMachines == 0 {
143
+ expectedMachines := getExpectedMachines (cluster )
144
+ if expectedMachines == 0 {
162
145
log .Info ("none of MachineDeployments specifies replica and node auto replacement doesn't support this scenario" )
163
146
return reconcile.Result {}, nil
164
147
}
@@ -172,44 +155,49 @@ func (r *VirtualMachineGroupReconciler) createOrUpdateVMG(ctx context.Context, c
172
155
173
156
// Wait until all VSphereMachines are create, this could happen during initial deployment or day-2 like cluster update.
174
157
current := int32 (len (currentVSphereMachines ))
175
- if expectdMachines != current {
158
+ if current < expectedMachines {
176
159
// Only check timeout if VMG doesn't exist.
177
- if desiredVMG .CreationTimestamp .IsZero () {
178
- if _ , err := r .isMDDefined (ctx , cluster ); err != nil {
179
- log .Error (err , "cluster MachineDeployments are not defined" )
180
- return reconcile.Result {}, nil
181
- }
182
-
183
- mdList := & clusterv1.MachineDeploymentList {}
184
- if err := r .Client .List (ctx , mdList ,
185
- client .InNamespace (cluster .Namespace ),
186
- client.MatchingLabels {clusterv1 .ClusterNameLabel : cluster .Name },
187
- ); err != nil {
188
- return reconcile.Result {}, errors .Errorf ("failed to list MachineDeployments: %w" , err )
189
- }
190
-
191
- // If no deployments exist, report error
192
- if len (mdList .Items ) == 0 {
193
- return reconcile.Result {}, errors .Errorf ("no MachineDeployments found for cluster %s/%s" , cluster .Namespace , cluster .Name )
194
- }
195
-
196
- // Check one MachineDeployment's creation timestamp
197
- firstMD := mdList .Items [0 ]
198
- if time .Since (firstMD .CreationTimestamp .Time ) > 1 * time .Minute {
199
- log .Error (errors .New ("timeout waiting for VSphereMachines" ), "1 minute timeout after MachineDeployment creation" ,
200
- "MachineDeployment" , firstMD .Name , "Cluster" , cluster .Namespace + "/" + cluster .Name )
201
-
202
- return reconcile.Result {}, nil
203
- }
204
- }
205
-
206
- log .Info ("current VSphereMachines do not match expected" , "Expected:" , expectdMachines ,
160
+ // if desiredVMG.CreationTimestamp.IsZero() {
161
+ // if _, err := r.isMDDefined(ctx, cluster); err != nil {
162
+ // log.Error(err, "cluster MachineDeployments are not defined")
163
+ // return reconcile.Result{}, nil
164
+ // }
165
+
166
+ // mdList := &clusterv1.MachineDeploymentList{}
167
+ // if err := r.Client.List(ctx, mdList,
168
+ // client.InNamespace(cluster.Namespace),
169
+ // client.MatchingLabels{clusterv1.ClusterNameLabel: cluster.Name},
170
+ // ); err != nil {
171
+ // return reconcile.Result{}, errors.Errorf("failed to list MachineDeployments: %w", err)
172
+ // }
173
+
174
+ // // If no deployments exist, report error
175
+ // if len(mdList.Items) == 0 {
176
+ // return reconcile.Result{}, errors.Errorf("no MachineDeployments found for cluster %s/%s", cluster.Namespace, cluster.Name)
177
+ // }
178
+
179
+ // // Check one MachineDeployment's creation timestamp
180
+ // firstMD := mdList.Items[0]
181
+ // if time.Since(firstMD.CreationTimestamp.Time) > 1*time.Minute {
182
+ // log.Error(errors.New("timeout waiting for VSphereMachines"), "1 minute timeout after MachineDeployment creation",
183
+ // "MachineDeployment", firstMD.Name, "Cluster", cluster.Namespace+"/"+cluster.Name)
184
+
185
+ // return reconcile.Result{}, nil
186
+ // }
187
+ // }
188
+
189
+ log .Info ("current VSphereMachines do not match expected" , "Expected:" , expectedMachines ,
207
190
"Current:" , current , "ClusterName" , cluster .Name , "Namespace" , cluster .Namespace )
208
191
return reconcile.Result {RequeueAfter : reconciliationDelay }, nil
209
192
}
210
193
211
194
// Generate all the members of the VirtualMachineGroup.
212
195
members := make ([]vmoprv1.GroupMember , 0 , len (currentVSphereMachines ))
196
+ // Sort the VSphereMachines by name for consistent ordering
197
+ sort .Slice (currentVSphereMachines , func (i , j int ) bool {
198
+ return currentVSphereMachines [i ].Name < currentVSphereMachines [j ].Name
199
+ })
200
+
213
201
for _ , vm := range currentVSphereMachines {
214
202
members = append (members , vmoprv1.GroupMember {
215
203
Name : vm .Name ,
@@ -259,7 +247,7 @@ func (r *VirtualMachineGroupReconciler) createOrUpdateVMG(ctx context.Context, c
259
247
}
260
248
261
249
// Make sure the Cluster owns the VM Operator VirtualMachineGroup.
262
- if err = ctrlutil .SetControllerReference (cluster , desiredVMG , r .Client .Scheme ()); err != nil {
250
+ if err = controllerutil .SetControllerReference (cluster , desiredVMG , r .Client .Scheme ()); err != nil {
263
251
return errors .Wrapf (err , "failed to mark %s %s/%s as owner of %s %s/%s" ,
264
252
cluster .GroupVersionKind (),
265
253
cluster .Namespace ,
@@ -332,83 +320,79 @@ func getExpectedMachines(cluster *clusterv1.Cluster) int32 {
332
320
func getCurrentVSphereMachines (ctx context.Context , kubeClient client.Client , clusterNamespace , clusterName string ) ([]vmwarev1.VSphereMachine , error ) {
333
321
log := ctrl .LoggerFrom (ctx )
334
322
335
- // List MachineDeployments for the cluster.
336
- var mdList clusterv1.MachineDeploymentList
337
- if err := kubeClient .List (ctx , & mdList ,
338
- client .InNamespace (clusterNamespace ),
339
- client.MatchingLabels {clusterv1 .ClusterNameLabel : clusterName },
340
- ); err != nil {
341
- return nil , errors .Wrapf (err , "failed to list MachineDeployments for cluster %s/%s" , clusterNamespace , clusterName )
342
- }
343
- validMDs := make (map [string ]struct {})
344
- for _ , md := range mdList .Items {
345
- validMDs [md .Name ] = struct {}{}
346
- }
347
- log .V (6 ).Info ("Identified active MachineDeployments" , "count" , len (validMDs ))
348
-
349
- // List MachineSets and filter those owned by a valid MachineDeployment.
350
- var msList clusterv1.MachineSetList
351
- if err := kubeClient .List (ctx , & msList ,
352
- client .InNamespace (clusterNamespace ),
353
- client.MatchingLabels {clusterv1 .ClusterNameLabel : clusterName },
354
- ); err != nil {
355
- return nil , errors .Wrapf (err , "failed to list MachineSets for cluster %s/%s" , clusterNamespace , clusterName )
356
- }
357
- validMS := make (map [string ]struct {})
358
- for _ , ms := range msList .Items {
359
- for _ , owner := range ms .OwnerReferences {
360
- if owner .Kind == "MachineDeployment" && owner .APIVersion == clusterv1 .GroupVersion .String () {
361
- if _ , ok := validMDs [owner .Name ]; ok {
362
- validMS [ms .Name ] = struct {}{}
363
- break
364
- }
365
- }
366
- }
367
- }
368
- log .V (6 ).Info ("Filtered MachineSets owned by valid MachineDeployments" , "count" , len (validMS ))
369
-
370
- // List Machines and filter those owned by valid MachineSets (skip control plane).
371
- var machineList clusterv1.MachineList
372
- if err := kubeClient .List (ctx , & machineList ,
373
- client .InNamespace (clusterNamespace ),
374
- client.MatchingLabels {clusterv1 .ClusterNameLabel : clusterName },
375
- ); err != nil {
376
- return nil , errors .Wrapf (err , "failed to list Machines for cluster %s/%s" , clusterNamespace , clusterName )
377
- }
378
-
379
- workerMachines := make (map [string ]struct {})
380
- for _ , m := range machineList .Items {
381
- if _ , isControlPlane := m .Labels [clusterv1 .MachineControlPlaneLabel ]; isControlPlane {
382
- continue
383
- }
384
- for _ , owner := range m .OwnerReferences {
385
- if owner .Kind == "MachineSet" && owner .APIVersion == clusterv1 .GroupVersion .String () {
386
- if _ , ok := validMS [owner .Name ]; ok {
387
- workerMachines [m .Name ] = struct {}{}
388
- break
389
- }
390
- }
391
- }
392
- }
393
- log .V (5 ).Info ("Identified worker Machines linked to MachineSets" , "count" , len (workerMachines ))
394
-
395
- // List VSphereMachines and filter those owned by valid worker Machines.
323
+ // // List MachineDeployments for the cluster.
324
+ // var mdList clusterv1.MachineDeploymentList
325
+ // if err := kubeClient.List(ctx, &mdList,
326
+ // client.InNamespace(clusterNamespace),
327
+ // client.MatchingLabels{clusterv1.ClusterNameLabel: clusterName},
328
+ // ); err != nil {
329
+ // return nil, errors.Wrapf(err, "failed to list MachineDeployments for cluster %s/%s", clusterNamespace, clusterName)
330
+ // }
331
+ // validMDs := make(map[string]struct{})
332
+ // for _, md := range mdList.Items {
333
+ // validMDs[md.Name] = struct{}{}
334
+ // }
335
+ // log.V(6).Info("Identified active MachineDeployments", "count", len(validMDs))
336
+
337
+ // // List MachineSets and filter those owned by a valid MachineDeployment.
338
+ // var msList clusterv1.MachineSetList
339
+ // if err := kubeClient.List(ctx, &msList,
340
+ // client.InNamespace(clusterNamespace),
341
+ // client.MatchingLabels{clusterv1.ClusterNameLabel: clusterName},
342
+ // ); err != nil {
343
+ // return nil, errors.Wrapf(err, "failed to list MachineSets for cluster %s/%s", clusterNamespace, clusterName)
344
+ // }
345
+ // validMS := make(map[string]struct{})
346
+ // for _, ms := range msList.Items {
347
+ // for _, owner := range ms.OwnerReferences {
348
+ // if owner.Kind == "MachineDeployment" && owner.APIVersion == clusterv1.GroupVersion.String() {
349
+ // if _, ok := validMDs[owner.Name]; ok {
350
+ // validMS[ms.Name] = struct{}{}
351
+ // break
352
+ // }
353
+ // }
354
+ // }
355
+ // }
356
+ // log.V(6).Info("Filtered MachineSets owned by valid MachineDeployments", "count", len(validMS))
357
+
358
+ // // List Machines and filter those owned by valid MachineSets (skip control plane).
359
+ // var machineList clusterv1.MachineList
360
+ // if err := kubeClient.List(ctx, &machineList,
361
+ // client.InNamespace(clusterNamespace),
362
+ // client.MatchingLabels{clusterv1.ClusterNameLabel: clusterName},
363
+ // ); err != nil {
364
+ // return nil, errors.Wrapf(err, "failed to list Machines for cluster %s/%s", clusterNamespace, clusterName)
365
+ // }
366
+
367
+ // workerMachines := make(map[string]struct{})
368
+ // for _, m := range machineList.Items {
369
+ // if _, isControlPlane := m.Labels[clusterv1.MachineControlPlaneLabel]; isControlPlane {
370
+ // continue
371
+ // }
372
+ // for _, owner := range m.OwnerReferences {
373
+ // if owner.Kind == "MachineSet" && owner.APIVersion == clusterv1.GroupVersion.String() {
374
+ // if _, ok := validMS[owner.Name]; ok {
375
+ // workerMachines[m.Name] = struct{}{}
376
+ // break
377
+ // }
378
+ // }
379
+ // }
380
+ // }
381
+ // log.V(5).Info("Identified worker Machines linked to MachineSets", "count", len(workerMachines))
382
+
383
+ // List VSphereMachine objects
396
384
var vsMachineList vmwarev1.VSphereMachineList
397
385
if err := kubeClient .List (ctx , & vsMachineList ,
398
386
client .InNamespace (clusterNamespace ),
387
+ client.MatchingLabels {clusterv1 .ClusterNameLabel : clusterName },
399
388
); err != nil {
400
389
return nil , errors .Wrapf (err , "failed to list VSphereMachines in namespace %s" , clusterNamespace )
401
390
}
402
391
403
392
var result []vmwarev1.VSphereMachine
404
393
for _ , vs := range vsMachineList .Items {
405
- for _ , owner := range vs .OwnerReferences {
406
- if owner .Kind == "Machine" && owner .APIVersion == clusterv1 .GroupVersion .String () {
407
- if _ , ok := workerMachines [owner .Name ]; ok {
408
- result = append (result , vs )
409
- break
410
- }
411
- }
394
+ if vs .DeletionTimestamp .IsZero () {
395
+ result = append (result , vs )
412
396
}
413
397
}
414
398
log .V (4 ).Info ("Final list of VSphereMachines for VMG member generation" , "count" , len (result ))
0 commit comments