Skip to content

Commit 257fa37

Browse files
committed
Adds code for VMG creation
Removes the extra cases for VMG creation, such that VMG is created for: 1. Multiple zones, multiple MDs with no failureDomain 2. Multiple zones, multiple MDs with failureDomain 3. Single zone, existing cluster with no failureDomain MDs Signed-off-by: Sagar Muchhal <[email protected]>
1 parent 9ec9d58 commit 257fa37

File tree

1 file changed

+140
-156
lines changed

1 file changed

+140
-156
lines changed

controllers/vmware/virtualmachinegroup_reconciler.go

Lines changed: 140 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ package vmware
2020
import (
2121
"context"
2222
"fmt"
23+
"sort"
2324
"strings"
2425
"time"
2526

@@ -37,7 +38,6 @@ import (
3738

3839
vmwarev1 "sigs.k8s.io/cluster-api-provider-vsphere/apis/vmware/v1beta1"
3940
clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2"
40-
ctrlutil "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
4141
)
4242

4343
const (
@@ -63,13 +63,12 @@ func (r *VirtualMachineGroupReconciler) Reconcile(ctx context.Context, req ctrl.
6363
}
6464

6565
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.
6767
if !cluster.DeletionTimestamp.IsZero() {
6868
return reconcile.Result{}, nil
6969
}
7070

7171
vmg := &vmoprv1.VirtualMachineGroup{}
72-
7372
key := &client.ObjectKey{
7473
Namespace: cluster.Namespace,
7574
Name: cluster.Name,
@@ -80,7 +79,6 @@ func (r *VirtualMachineGroupReconciler) Reconcile(ctx context.Context, req ctrl.
8079
log.Error(err, "failed to get VirtualMachineGroup")
8180
return ctrl.Result{}, err
8281
}
83-
// Define the VM Operator VirtualMachine resource to reconcile.
8482
vmg = &vmoprv1.VirtualMachineGroup{
8583
ObjectMeta: metav1.ObjectMeta{
8684
Name: key.Name,
@@ -89,56 +87,41 @@ func (r *VirtualMachineGroupReconciler) Reconcile(ctx context.Context, req ctrl.
8987
}
9088
}
9189

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+
// }
142125

143126
// If ControlPlane haven't initialized, requeue it since VSphereMachines of MachineDeployment will only be created after
144127
// ControlPlane is initialized.
@@ -157,8 +140,8 @@ func (r *VirtualMachineGroupReconciler) createOrUpdateVMG(ctx context.Context, c
157140
log := ctrl.LoggerFrom(ctx)
158141

159142
// Calculate expected Machines of all MachineDeployments.
160-
expectdMachines := getExpectedMachines(cluster)
161-
if expectdMachines == 0 {
143+
expectedMachines := getExpectedMachines(cluster)
144+
if expectedMachines == 0 {
162145
log.Info("none of MachineDeployments specifies replica and node auto replacement doesn't support this scenario")
163146
return reconcile.Result{}, nil
164147
}
@@ -172,44 +155,49 @@ func (r *VirtualMachineGroupReconciler) createOrUpdateVMG(ctx context.Context, c
172155

173156
// Wait until all VSphereMachines are create, this could happen during initial deployment or day-2 like cluster update.
174157
current := int32(len(currentVSphereMachines))
175-
if expectdMachines != current {
158+
if current < expectedMachines {
176159
// 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,
207190
"Current:", current, "ClusterName", cluster.Name, "Namespace", cluster.Namespace)
208191
return reconcile.Result{RequeueAfter: reconciliationDelay}, nil
209192
}
210193

211194
// Generate all the members of the VirtualMachineGroup.
212195
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+
213201
for _, vm := range currentVSphereMachines {
214202
members = append(members, vmoprv1.GroupMember{
215203
Name: vm.Name,
@@ -259,7 +247,7 @@ func (r *VirtualMachineGroupReconciler) createOrUpdateVMG(ctx context.Context, c
259247
}
260248

261249
// 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 {
263251
return errors.Wrapf(err, "failed to mark %s %s/%s as owner of %s %s/%s",
264252
cluster.GroupVersionKind(),
265253
cluster.Namespace,
@@ -332,83 +320,79 @@ func getExpectedMachines(cluster *clusterv1.Cluster) int32 {
332320
func getCurrentVSphereMachines(ctx context.Context, kubeClient client.Client, clusterNamespace, clusterName string) ([]vmwarev1.VSphereMachine, error) {
333321
log := ctrl.LoggerFrom(ctx)
334322

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
396384
var vsMachineList vmwarev1.VSphereMachineList
397385
if err := kubeClient.List(ctx, &vsMachineList,
398386
client.InNamespace(clusterNamespace),
387+
client.MatchingLabels{clusterv1.ClusterNameLabel: clusterName},
399388
); err != nil {
400389
return nil, errors.Wrapf(err, "failed to list VSphereMachines in namespace %s", clusterNamespace)
401390
}
402391

403392
var result []vmwarev1.VSphereMachine
404393
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)
412396
}
413397
}
414398
log.V(4).Info("Final list of VSphereMachines for VMG member generation", "count", len(result))

0 commit comments

Comments
 (0)