@@ -20,6 +20,7 @@ package vmware
2020import (
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
4343const (
@@ -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,80 @@ func getExpectedMachines(cluster *clusterv1.Cluster) int32 {
332320func 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 },
388+ client.HasLabels {clusterv1 .MachineDeploymentNameLabel },
399389 ); err != nil {
400390 return nil , errors .Wrapf (err , "failed to list VSphereMachines in namespace %s" , clusterNamespace )
401391 }
402392
403393 var result []vmwarev1.VSphereMachine
404394 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- }
395+ if vs .DeletionTimestamp .IsZero () {
396+ result = append (result , vs )
412397 }
413398 }
414399 log .V (4 ).Info ("Final list of VSphereMachines for VMG member generation" , "count" , len (result ))
@@ -456,7 +441,7 @@ func GenerateVMGPlacementLabels(ctx context.Context, vmg *vmoprv1.VirtualMachine
456441 }
457442
458443 log .Info (fmt .Sprintf ("VM %s in VMG %s/%s has been placed in zone %s" , member .Name , vmg .Namespace , vmg .Name , zone ))
459- labels [md ] = zone
444+ labels [fmt . Sprintf ( "zone.cluster.x-k8s.io/%s" , md ) ] = zone
460445 }
461446 }
462447 }
0 commit comments