@@ -5,6 +5,7 @@ package controller
55import (
66 "context"
77 "fmt"
8+ "maps"
89 "strings"
910
1011 corev1 "k8s.io/api/core/v1"
@@ -221,15 +222,19 @@ func (r *InstanceReconciler) Reconcile(ctx context.Context, req mcreconcile.Requ
221222 if err := cl .GetClient ().Status ().Update (ctx , & instance ); err != nil {
222223 return ctrl.Result {}, err
223224 }
224- if err := r .writeBackToUpstream (ctx , req .ClusterName , & instance ); err != nil {
225- return ctrl.Result {}, err
225+ // Return with the quota error (nil or transient) so controller-runtime
226+ // requeues with backoff on failures. On the success path (quotaErr==nil)
227+ // we fall through to removeQuotaSchedulingGate below instead of returning
228+ // early, so the gate is cleared in the same reconcile pass rather than
229+ // waiting for a requeue that may never come (ResourceClaim is immutable
230+ // and local Instances are not watched).
231+ if quotaErr != nil {
232+ if err := r .writeBackToUpstream (ctx , req .ClusterName , & instance ); err != nil {
233+ return ctrl.Result {}, err
234+ }
235+ return ctrl.Result {}, quotaErr
226236 }
227- // Return after the status update. If there was a quota error, return it
228- // so controller-runtime requeues with backoff for transient failures.
229- return ctrl.Result {}, quotaErr
230- }
231-
232- if quotaErr != nil {
237+ } else if quotaErr != nil {
233238 // No status change but quota evaluation failed — return error to requeue.
234239 return ctrl.Result {}, quotaErr
235240 }
@@ -470,13 +475,37 @@ func (r *InstanceReconciler) writeBackToUpstream(ctx context.Context, clusterNam
470475 }
471476 }
472477
478+ logger := log .FromContext (ctx )
479+ missingLabels := []string {}
480+ for _ , key := range []string {
481+ computev1alpha .WorkloadUIDLabel ,
482+ computev1alpha .WorkloadDeploymentUIDLabel ,
483+ computev1alpha .InstanceIndexLabel ,
484+ } {
485+ if instance .Labels [key ] == "" {
486+ missingLabels = append (missingLabels , key )
487+ }
488+ }
489+ if len (missingLabels ) > 0 {
490+ logger .Info ("instance is missing linking labels for write-back; projection owner-ref will not be set" ,
491+ "instance" , instance .Name , "namespace" , instance .Namespace ,
492+ "missingLabels" , missingLabels )
493+ }
494+
473495 writeBack := & computev1alpha.Instance {
474496 ObjectMeta : metav1.ObjectMeta {
475497 Name : instance .Name ,
476498 Namespace : instance .Namespace ,
477499 Labels : map [string ]string {
478500 downstreamclient .UpstreamOwnerClusterNameLabel : encodedClusterName ,
479501 downstreamclient .UpstreamOwnerNamespaceLabel : upstreamNamespace ,
502+ computev1alpha .WorkloadUIDLabel : instance .Labels [computev1alpha .WorkloadUIDLabel ],
503+ computev1alpha .WorkloadDeploymentUIDLabel : instance .Labels [computev1alpha .WorkloadDeploymentUIDLabel ],
504+ computev1alpha .InstanceIndexLabel : instance .Labels [computev1alpha .InstanceIndexLabel ],
505+ computev1alpha .WorkloadDeploymentNameLabel : instance .Labels [computev1alpha .WorkloadDeploymentNameLabel ],
506+ computev1alpha .CityCodeLabel : instance .Labels [computev1alpha .CityCodeLabel ],
507+ computev1alpha .WorkloadNameLabel : instance .Labels [computev1alpha .WorkloadNameLabel ],
508+ computev1alpha .PlacementNameLabel : instance .Labels [computev1alpha .PlacementNameLabel ],
480509 },
481510 },
482511 Spec : instance .Spec ,
@@ -503,11 +532,24 @@ func (r *InstanceReconciler) writeBackToUpstream(ctx context.Context, clusterNam
503532 return fmt .Errorf ("failed getting downstream instance: %w" , err )
504533 }
505534
506- // Update spec + labels only if they differ.
535+ // Build a comparable map containing only the keys this function owns so that
536+ // Karmada-managed labels on the existing object do not cause spurious updates.
537+ ownedLabels := make (map [string ]string , len (writeBack .Labels ))
538+ for k := range writeBack .Labels {
539+ ownedLabels [k ] = existing .Labels [k ]
540+ }
541+
542+ // Update spec + labels only if owned keys differ.
507543 if ! apiequality .Semantic .DeepEqual (existing .Spec , instance .Spec ) ||
508- ! apiequality .Semantic .DeepEqual (existing . Labels , writeBack .Labels ) {
544+ ! apiequality .Semantic .DeepEqual (ownedLabels , writeBack .Labels ) {
509545 existing .Spec = instance .Spec
510- existing .Labels = writeBack .Labels
546+ // Merge writeBack.Labels into existing.Labels. Only keys owned by
547+ // writeBackToUpstream are written; any labels Karmada or other actors
548+ // have placed on the downstream object are preserved.
549+ if existing .Labels == nil {
550+ existing .Labels = make (map [string ]string )
551+ }
552+ maps .Copy (existing .Labels , writeBack .Labels )
511553 if err := r .FederationClient .Update (ctx , existing ); err != nil {
512554 return fmt .Errorf ("failed updating downstream write-back instance: %w" , err )
513555 }
0 commit comments