@@ -395,6 +395,13 @@ func (r *CommitmentReservationController) reconcileAllocations(ctx context.Conte
395395 res .Status .CommittedResourceReservation = & v1alpha1.CommittedResourceReservationStatus {}
396396 }
397397
398+ // Snapshot existing Status.Allocations before we overwrite it so we can detect
399+ // which VM UUIDs are newly confirmed after the patch.
400+ existingStatusAllocations := make (map [string ]string , len (res .Status .CommittedResourceReservation .Allocations ))
401+ for k , v := range res .Status .CommittedResourceReservation .Allocations {
402+ existingStatusAllocations [k ] = v
403+ }
404+
398405 // Build new Status.Allocations map based on HV CRD state.
399406 newStatusAllocations := make (map [string ]string )
400407 // Track allocations to remove from Spec (stale/leaving VMs).
@@ -469,6 +476,19 @@ func (r *CommitmentReservationController) reconcileAllocations(ctx context.Conte
469476 res .Status .CommittedResourceReservation .Allocations = newStatusAllocations
470477 }
471478
479+ // Proactively remove this VM UUID from all other candidate reservations that still
480+ // carry it in their Spec.Allocations. Only do this for VMs that are newly confirmed
481+ // in this reconcile cycle (present in newStatusAllocations but absent in the snapshot
482+ // taken before any patch) to avoid redundant work on subsequent reconciles.
483+ for vmUUID := range newStatusAllocations {
484+ if _ , wasAlreadyConfirmed := existingStatusAllocations [vmUUID ]; wasAlreadyConfirmed {
485+ continue
486+ }
487+ if err := r .cleanupCandidateReservations (ctx , res .Name , vmUUID ); err != nil {
488+ return nil , fmt .Errorf ("failed to cleanup candidate reservations for vm %s: %w" , vmUUID , err )
489+ }
490+ }
491+
472492 // Patch Status
473493 patch := client .MergeFrom (old )
474494 if err := r .Status ().Patch (ctx , res , patch ); err != nil {
@@ -487,6 +507,41 @@ func (r *CommitmentReservationController) reconcileAllocations(ctx context.Conte
487507 return result , nil
488508}
489509
510+ // cleanupCandidateReservations removes vmUUID from Spec.Allocations of all Reservation CRDs
511+ // other than the one that just confirmed the VM. This is called once per newly confirmed VM
512+ // so that candidate slots on non-selected hosts are freed immediately rather than waiting
513+ // for those reservations' own grace period or periodic requeue.
514+ func (r * CommitmentReservationController ) cleanupCandidateReservations (ctx context.Context , confirmedReservationName , vmUUID string ) error {
515+ logger := LoggerFromContext (ctx ).WithValues ("component" , "controller" , "vm" , vmUUID )
516+
517+ var candidates v1alpha1.ReservationList
518+ if err := r .List (ctx , & candidates , client.MatchingFields {idxReservationByAllocationVMUUID : vmUUID }); err != nil {
519+ return fmt .Errorf ("failed to list candidate reservations: %w" , err )
520+ }
521+
522+ for i := range candidates .Items {
523+ candidate := & candidates .Items [i ]
524+ if candidate .Name == confirmedReservationName {
525+ continue
526+ }
527+ if candidate .Spec .CommittedResourceReservation == nil {
528+ continue
529+ }
530+ if _ , ok := candidate .Spec .CommittedResourceReservation .Allocations [vmUUID ]; ! ok {
531+ continue
532+ }
533+ old := candidate .DeepCopy ()
534+ delete (candidate .Spec .CommittedResourceReservation .Allocations , vmUUID )
535+ if err := r .Patch (ctx , candidate , client .MergeFrom (old )); err != nil {
536+ if client .IgnoreNotFound (err ) != nil {
537+ return fmt .Errorf ("failed to patch candidate reservation %s: %w" , candidate .Name , err )
538+ }
539+ }
540+ logger .Info ("removed vm from candidate reservation" , "reservation" , candidate .Name , "host" , candidate .Status .Host )
541+ }
542+ return nil
543+ }
544+
490545// getPipelineForFlavorGroup returns the pipeline name for a given flavor group.
491546func (r * CommitmentReservationController ) getPipelineForFlavorGroup (flavorGroupName string , logger logr.Logger ) string {
492547 // Try exact match first (e.g., "2152" -> "kvm-cr-hana")
@@ -581,6 +636,10 @@ func (r *CommitmentReservationController) SetupWithManager(mgr ctrl.Manager, mcl
581636 return err
582637 }
583638
639+ if err := indexReservationByAllocationVMUUID (context .Background (), mcl ); err != nil {
640+ return fmt .Errorf ("failed to set up reservation allocation VM UUID index: %w" , err )
641+ }
642+
584643 // Use WatchesMulticluster to watch Reservations across all configured clusters
585644 // (home + remotes). This is required because Reservation CRDs may be stored
586645 // in remote clusters, not just the home cluster. Without this, the controller
0 commit comments