@@ -45,7 +45,7 @@ const (
4545 // reconciliationDelay is the delay time for requeueAfter.
4646 reconciliationDelay = 10 * time .Second
4747 // ZoneAnnotationPrefix is the prefix used for placement decision annotations which will be set on VirtualMachineGroup.
48- ZoneAnnotationPrefix = "zone.cluster.x-k8s.io"
48+ ZoneAnnotationPrefix = "zone.vmware.infrastructure. cluster.x-k8s.io"
4949)
5050
5151// VirtualMachineGroupReconciler reconciles VirtualMachineGroup.
@@ -66,13 +66,13 @@ type VirtualMachineGroupReconciler struct {
6666// the VirtualMachineGroup (VMG) object with respect to the worker VSphereMachines in the Cluster.
6767//
6868// - Batch Coordination: Gating the initial creation of the VMG until all expected worker
69- // VSphereMachines are present. This ensures the complete VM member list is sent to the VM
70- // Service in a single batch operation due to a limitation of underlying service.
69+ // VSphereMachines are present. It will compare the desired count of VirtualMachines
70+ // derived from the sum of all desired MachineDeployment Replicas) against the count
71+ // of existing, matching VSphereMachine objects.
7172//
7273// - Placement Persistence: Persisting the MachineDeployment-to-Zone mapping (placement decision) as a
73- // metadata annotation on the VMG object. This decision is crucial for guiding newer VMs created
74- // during Day-2 operations such as scaling, upgrades, and remediations, ensuring consistency. This is also due to
75- // a known limitation of underlying services.
74+ // metadata annotation on the VMG object. The same decision must be respected also for placement
75+ // of machines created after initial placement.
7676//
7777// - Membership Maintenance: Dynamically updating the VMG's member list to reflect the current
7878// state of VMs belonging to MachineDeployments (handling scale-up/down events).
@@ -111,49 +111,49 @@ func (r *VirtualMachineGroupReconciler) Reconcile(ctx context.Context, req ctrl.
111111func (r * VirtualMachineGroupReconciler ) createOrUpdateVirtualMachineGroup (ctx context.Context , cluster * clusterv1.Cluster ) (reconcile.Result , error ) {
112112 log := ctrl .LoggerFrom (ctx )
113113
114- // Calculate current Machines of all MachineDeployments.
115- current , err := getCurrentVSphereMachines (ctx , r .Client , cluster .Namespace , cluster .Name )
114+ // Get current VSphereMachines of all MachineDeployments.
115+ currentVSphereMachines , err := getCurrentVSphereMachines (ctx , r .Client , cluster .Namespace , cluster .Name )
116116 if err != nil {
117117 return reconcile.Result {}, errors .Wrapf (err , "failed to get current VSphereMachine of cluster %s/%s" ,
118118 cluster .Name , cluster .Namespace )
119119 }
120120
121- desiredVMG := & vmoprv1.VirtualMachineGroup {}
121+ vmg := & vmoprv1.VirtualMachineGroup {}
122122 key := & client.ObjectKey {
123123 Namespace : cluster .Namespace ,
124124 Name : cluster .Name ,
125125 }
126126
127- if err := r .Client .Get (ctx , * key , desiredVMG ); err != nil {
127+ if err := r .Client .Get (ctx , * key , vmg ); err != nil {
128128 if ! apierrors .IsNotFound (err ) {
129- log .Error (err , "failed to get VirtualMachineGroup" )
129+ log .Error (err , "failed to get VirtualMachineGroup" , "namespacedName" , * key )
130130 return reconcile.Result {}, err
131131 }
132132
133- // Calculate expected Machines of all MachineDeployments.
134- // CAPV retrieves placement decisions from the VirtualMachineGroup to guide
135- // day-2 VM placement. At least one VM is required for each MachineDeployment.
136- expected , err := getExpectedVSphereMachineCount (ctx , r .Client , cluster )
133+ // Calculate expected VSphereMachine count of all MachineDeployments.
134+ expectedVSphereMachineCount , err := getExpectedVSphereMachineCount (ctx , r .Client , cluster )
137135 if err != nil {
138- log .Error (err , "failed to get expected Machines of all MachineDeployment" )
136+ log .Error (err , "failed to get expected Machines of all MachineDeployment" , "Cluster" , klog . KObj ( cluster ) )
139137 return reconcile.Result {}, err
140138 }
141139
142- if expected == 0 {
140+ // Since CAPV retrieves placement decisions from the VirtualMachineGroup to guide
141+ // day-2 VM placement. At least one VM is required for each MachineDeployment during initial Cluster creation.
142+ if expectedVSphereMachineCount == 0 {
143143 errMsg := fmt .Sprintf ("Found 0 desired VSphereMachine for Cluster %s/%s" , cluster .Name , cluster .Namespace )
144144 log .Error (nil , errMsg )
145145 return reconcile.Result {}, errors .New (errMsg )
146146 }
147147
148148 // Wait for all intended VSphereMachines corresponding to MachineDeployment to exist only during initial Cluster creation.
149- current := int32 (len (current ))
150- if current < expected {
151- log .Info ("current VSphereMachines do not match expected " , "Expected:" , expected ,
152- "Current:" , current , "ClusterName" , cluster .Name , "Namespace" , cluster .Namespace )
149+ currentVSphereMachineCount := int32 (len (currentVSphereMachines ))
150+ if currentVSphereMachineCount < expectedVSphereMachineCount {
151+ log .Info ("Waiting for expected VSphereMachines to be created " , "Expected:" , expectedVSphereMachineCount ,
152+ "Current:" , currentVSphereMachineCount , "ClusterName" , cluster .Name , "Namespace" , cluster .Namespace )
153153 return reconcile.Result {RequeueAfter : reconciliationDelay }, nil
154154 }
155155
156- desiredVMG = & vmoprv1.VirtualMachineGroup {
156+ vmg = & vmoprv1.VirtualMachineGroup {
157157 ObjectMeta : metav1.ObjectMeta {
158158 Name : key .Name ,
159159 Namespace : key .Namespace ,
@@ -162,8 +162,8 @@ func (r *VirtualMachineGroupReconciler) createOrUpdateVirtualMachineGroup(ctx co
162162 }
163163
164164 // Generate VM names according to the naming strategy set on the VSphereMachine.
165- vmNames := make ([]string , 0 , len (current ))
166- for _ , machine := range current {
165+ vmNames := make ([]string , 0 , len (currentVSphereMachines ))
166+ for _ , machine := range currentVSphereMachines {
167167 name , err := GenerateVirtualMachineName (machine .Name , machine .Spec .NamingStrategy )
168168 if err != nil {
169169 return reconcile.Result {}, err
@@ -175,7 +175,7 @@ func (r *VirtualMachineGroupReconciler) createOrUpdateVirtualMachineGroup(ctx co
175175 return vmNames [i ] < vmNames [j ]
176176 })
177177
178- members := make ([]vmoprv1.GroupMember , 0 , len (current ))
178+ members := make ([]vmoprv1.GroupMember , 0 , len (currentVSphereMachines ))
179179 for _ , name := range vmNames {
180180 members = append (members , vmoprv1.GroupMember {
181181 Name : name ,
@@ -196,50 +196,51 @@ func (r *VirtualMachineGroupReconciler) createOrUpdateVirtualMachineGroup(ctx co
196196 }
197197
198198 // Use CreateOrPatch to create or update the VirtualMachineGroup.
199- _ , err = controllerutil .CreateOrPatch (ctx , r .Client , desiredVMG , func () error {
200- return r .reconcileVirtualMachineState (ctx , desiredVMG , cluster , members , mdNames )
199+ _ , err = controllerutil .CreateOrPatch (ctx , r .Client , vmg , func () error {
200+ return r .reconcileVirtualMachineState (ctx , vmg , cluster , members , mdNames )
201201 })
202202
203203 return reconcile.Result {}, err
204204}
205205
206- // reconcileVirtualMachineState mutates the desiredVMG object to reflect the necessary spec and metadata changes.
207- func (r * VirtualMachineGroupReconciler ) reconcileVirtualMachineState (ctx context.Context , desiredVMG * vmoprv1.VirtualMachineGroup , cluster * clusterv1.Cluster , members []vmoprv1.GroupMember , mdNames []string ) error {
206+ // reconcileVirtualMachineState mutates the VirtualMachineGroup object to reflect the necessary spec and metadata changes.
207+ func (r * VirtualMachineGroupReconciler ) reconcileVirtualMachineState (ctx context.Context , vmg * vmoprv1.VirtualMachineGroup , cluster * clusterv1.Cluster , members []vmoprv1.GroupMember , mdNames []string ) error {
208208 // Set the desired labels
209- if desiredVMG .Labels == nil {
210- desiredVMG .Labels = make (map [string ]string )
211- desiredVMG .Labels [clusterv1 .ClusterNameLabel ] = cluster .Name
209+ if vmg .Labels == nil {
210+ vmg .Labels = make (map [string ]string )
212211 }
212+ // Always ensure cluster name label is set
213+ vmg .Labels [clusterv1 .ClusterNameLabel ] = cluster .Name
213214
214- if desiredVMG .Annotations == nil {
215- desiredVMG .Annotations = make (map [string ]string )
215+ if vmg .Annotations == nil {
216+ vmg .Annotations = make (map [string ]string )
216217 }
217218
218219 // Add per-md-zone label for day-2 operations once placement of a VM belongs to MachineDeployment is done.
219220 // Do not update per-md-zone label once set, as placement decision should not change without user explicitly
220221 // set failureDomain.
221- placementAnnotations , err := GenerateVirtualMachineGroupAnnotations (ctx , r .Client , desiredVMG , mdNames )
222+ placementAnnotations , err := GenerateVirtualMachineGroupAnnotations (ctx , r .Client , vmg , mdNames )
222223 if err != nil {
223224 return err
224225 }
225226 if len (placementAnnotations ) > 0 {
226227 for k , v := range placementAnnotations {
227- if _ , exists := desiredVMG .Annotations [k ]; ! exists {
228- desiredVMG .Annotations [k ] = v
228+ if _ , exists := vmg .Annotations [k ]; ! exists {
229+ vmg .Annotations [k ] = v
229230 }
230231 }
231232 }
232233
233234 // Set the BootOrder spec as the
234- desiredVMG .Spec .BootOrder = []vmoprv1.VirtualMachineGroupBootOrderGroup {
235+ vmg .Spec .BootOrder = []vmoprv1.VirtualMachineGroupBootOrderGroup {
235236 {
236237 Members : members ,
237238 },
238239 }
239240
240241 // Set the owner reference
241- if err := controllerutil .SetControllerReference (cluster , desiredVMG , r .Client .Scheme ()); err != nil {
242- return errors .Wrapf (err , "failed to mark %s as owner of %s" , klog .KObj (cluster ), klog .KObj (desiredVMG ))
242+ if err := controllerutil .SetControllerReference (cluster , vmg , r .Client .Scheme ()); err != nil {
243+ return errors .Wrapf (err , "failed to mark %s as owner of %s" , klog .KObj (cluster ), klog .KObj (vmg ))
243244 }
244245
245246 return nil
@@ -279,7 +280,7 @@ func getCurrentVSphereMachines(ctx context.Context, kubeClient client.Client, cl
279280 client.MatchingLabels {clusterv1 .ClusterNameLabel : clusterName },
280281 client.HasLabels {clusterv1 .MachineDeploymentNameLabel },
281282 ); err != nil {
282- return nil , errors .Wrapf (err , "failed to list VSphereMachines in namespace %s" , clusterNamespace )
283+ return nil , errors .Wrapf (err , "failed to list VSphereMachines" )
283284 }
284285
285286 var result []vmwarev1.VSphereMachine
@@ -352,7 +353,7 @@ func GenerateVirtualMachineGroupAnnotations(ctx context.Context, kubeClient clie
352353 }
353354
354355 // Check if this VM belongs to any of our target MachineDeployments.
355- // Annotation format is "zone.cluster.x-k8s.io/{machine-deployment-name}".
356+ // Annotation format is "zone.vmware.infrastructure. cluster.x-k8s.io/{machine-deployment-name}".
356357 for _ , md := range machineDeployments {
357358 if mdNameFromLabel != md {
358359 continue
0 commit comments