@@ -20,6 +20,7 @@ import (
2020 "context"
2121 "errors"
2222 "fmt"
23+ "maps"
2324 "strings"
2425
2526 apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
@@ -61,6 +62,8 @@ const (
6162 resourcePromiseVersionStatus = "promiseVersion"
6263 resourceBindingVersionStatus = "resourceBindingVersion"
6364 promiseRevisionLookupFailedReason = "FailedPromiseRevisionLookup"
65+ UnversionedPromiseVersion = "not-set"
66+ LatestVersion = "latest"
6467)
6568
6669type DynamicResourceRequestController struct {
@@ -79,7 +82,7 @@ type DynamicResourceRequestController struct {
7982 NumberOfJobsToKeep int
8083 ReconciliationInterval time.Duration
8184 EventRecorder record.EventRecorder
82- PromiseUpgrade bool
85+ PromiseUpgradeFeatFlag bool
8386}
8487
8588//+kubebuilder:rbac:groups="batch",resources=jobs,verbs=get;list;watch;create;update;patch;delete
@@ -169,7 +172,7 @@ func (r *DynamicResourceRequestController) Reconcile(ctx context.Context, req ct
169172 promiseRevisionUsed * v1alpha1.PromiseRevision
170173 bindingVersion string
171174 )
172- if r .PromiseUpgrade {
175+ if r .PromiseUpgradeFeatFlag {
173176 logging .Trace (baseLogger ,
174177 "PromiseUpgrade feature flag set to true; will reconcile with a PromiseRevision." )
175178
@@ -205,18 +208,17 @@ func (r *DynamicResourceRequestController) Reconcile(ctx context.Context, req ct
205208 if resourceutil .FinalizersAreMissing (
206209 rr , []string {workFinalizer , removeAllWorkflowJobsFinalizer , runDeleteWorkflowsFinalizer },
207210 ) {
208- if err := addFinalizers (opts , rr , r .getRRFinalizers ()); err != nil {
209- if apierrors .IsConflict (err ) {
210- return fastRequeue , nil
211- }
212- return ctrl.Result {}, err
211+ err := addFinalizers (opts , rr , r .getRRFinalizers ())
212+ if apierrors .IsConflict (err ) {
213+ return fastRequeue , nil
213214 }
214- return ctrl.Result {}, nil
215+ return ctrl.Result {}, err
215216 }
216217
217- if r .PromiseUpgrade {
218+ if r .PromiseUpgradeFeatFlag {
218219 err := r .updateResourceBinding (ctx , logger , rr , promise )
219220 if err != nil {
221+ logging .Error (logger , err , "failed to update resource binding for resource request" )
220222 return ctrl.Result {}, err
221223 }
222224 }
@@ -290,19 +292,19 @@ func (r *DynamicResourceRequestController) Reconcile(ctx context.Context, req ct
290292 return r .nextReconciliation (logger ), r .cleanupWorkflowCountersAndExecution (ctx , logger , rr )
291293 }
292294
293- rrNamespace := ""
294- if promise .WorkflowPipelineNamespaceSet () {
295- rrNamespace = rr .GetNamespace ()
296- }
297- workLabels := resourceutil .GetWorkLabels (r .PromiseIdentifier , rr .GetName (), rrNamespace , "" , v1alpha1 .WorkTypeResource )
298-
299- statusUpdate , err := r .generateResourceStatus (ctx , logger , rr , int64 (len (pipelineResources )), workLabels , bindingVersion , promiseRevisionUsed )
295+ statusUpdated , err := r .ensureResourceStatus (ctx , logger , rr , promise , pipelineResources , bindingVersion , promiseRevisionUsed )
300296 if err != nil {
301297 return ctrl.Result {}, err
302298 }
299+ if statusUpdated {
300+ return ctrl.Result {}, nil
301+ }
303302
304- if statusUpdate {
305- return ctrl.Result {}, r .Client .Status ().Update (ctx , rr )
303+ if r .PromiseUpgradeFeatFlag {
304+ if err := r .updateResourceBindingVersionStatus (ctx , logger , promise .GetName (), rr ); err != nil {
305+ logging .Error (logger , err , "failed to update resource binding version status" )
306+ return ctrl.Result {}, err
307+ }
306308 }
307309
308310 workflowCompletedCondition := resourceutil .GetCondition (rr , resourceutil .ConfigureWorkflowCompletedCondition )
@@ -316,7 +318,7 @@ func (r *DynamicResourceRequestController) Reconcile(ctx context.Context, req ct
316318 return r .nextReconciliation (logger ), nil
317319 }
318320
319- if r .PromiseUpgrade {
321+ if r .PromiseUpgradeFeatFlag {
320322 if r .updatePromiseVersionStatus (logger , rr , bindingVersion , promiseRevisionUsed ) {
321323 return ctrl.Result {}, r .Client .Status ().Update (ctx , rr )
322324 }
@@ -325,6 +327,53 @@ func (r *DynamicResourceRequestController) Reconcile(ctx context.Context, req ct
325327 return ctrl.Result {}, nil
326328}
327329
330+ func (r * DynamicResourceRequestController ) ensureResourceStatus (
331+ ctx context.Context ,
332+ logger logr.Logger ,
333+ rr * unstructured.Unstructured ,
334+ promise * v1alpha1.Promise ,
335+ pipelineResources []v1alpha1.PipelineJobResources ,
336+ bindingVersion string ,
337+ promiseRevisionUsed * v1alpha1.PromiseRevision ,
338+ ) (bool , error ) {
339+ rrNamespace := ""
340+ if promise .WorkflowPipelineNamespaceSet () {
341+ rrNamespace = rr .GetNamespace ()
342+ }
343+ workLabels := resourceutil .GetWorkLabels (r .PromiseIdentifier , rr .GetName (), rrNamespace , "" , v1alpha1 .WorkTypeResource )
344+
345+ statusUpdate , err := r .generateResourceStatus (ctx , logger , rr , int64 (len (pipelineResources )), workLabels , bindingVersion , promiseRevisionUsed )
346+ if err != nil {
347+ return false , err
348+ }
349+
350+ if statusUpdate {
351+ return true , r .Client .Status ().Update (ctx , rr )
352+ }
353+ return false , nil
354+ }
355+
356+ func (r * DynamicResourceRequestController ) updateResourceBindingVersionStatus (ctx context.Context , logger logr.Logger , promiseName string , rr * unstructured.Unstructured ) error {
357+ resourceBindingName := objectutil .GenerateDeterministicObjectName (fmt .Sprintf ("%s-%s" , rr .GetName (), promiseName ))
358+ resourceBinding := & v1alpha1.ResourceBinding {}
359+ err := r .Client .Get (ctx , types.NamespacedName {Name : resourceBindingName , Namespace : rr .GetNamespace ()}, resourceBinding )
360+ if err != nil {
361+ return fmt .Errorf ("failed to get resourceBinding: %w" , err )
362+ }
363+
364+ desiredVersion := resourceutil .GetStatus (rr , resourcePromiseVersionStatus )
365+ if resourceBinding .Status .LastAppliedVersion == desiredVersion {
366+ return nil
367+ }
368+
369+ logging .Info (logger , "updating resource binding Status.LastAppliedVersion" , "oldVersion" , resourceBinding .Status .LastAppliedVersion , "newVersion" , desiredVersion )
370+ resourceBinding .Status .LastAppliedVersion = desiredVersion
371+ if err := r .Client .Status ().Update (ctx , resourceBinding ); err != nil {
372+ return fmt .Errorf ("failed to update resource binding status: %w" , err )
373+ }
374+ return nil
375+ }
376+
328377func (r * DynamicResourceRequestController ) updateResourceBinding (ctx context.Context , logger logr.Logger , rr * unstructured.Unstructured , promise * v1alpha1.Promise ) error {
329378 resourceBinding := & v1alpha1.ResourceBinding {
330379 ObjectMeta : metav1.ObjectMeta {
@@ -333,9 +382,20 @@ func (r *DynamicResourceRequestController) updateResourceBinding(ctx context.Con
333382 },
334383 }
335384 op , err := controllerutil .CreateOrUpdate (ctx , r .Client , resourceBinding , func () error {
336- resourceBinding .SetLabels (resourceBindingLabels (rr , promise ))
385+ // Exclude one-shot trigger labels — propagating them would cause the
386+ // ResourceBinding controller to re-trigger reconciliation in an infinite loop.
387+ mergedLabels := rr .GetLabels ()
388+ delete (mergedLabels , resourceutil .ManualReconciliationLabel )
389+ delete (mergedLabels , resourceutil .WorkflowRunFromStartLabel )
390+ if mergedLabels == nil {
391+ mergedLabels = make (map [string ]string )
392+ }
393+ maps .Copy (mergedLabels , resourceBindingLabels (rr , promise ))
394+ resourceBinding .SetLabels (mergedLabels )
337395 if resourceBinding .Spec .Version == "" {
338- resourceBinding .Spec .Version = "latest"
396+ resourceBinding .Spec .Version = LatestVersion
397+ // if the resource binding got deleted, when we recreate the resource binding we infer what the resource binding
398+ // version used to be from the resource request `status.resourceBindingVersion`
339399 existingPromiseVersion := resourceutil .GetStatus (rr , resourceBindingVersionStatus )
340400 if existingPromiseVersion != "" {
341401 resourceBinding .Spec .Version = existingPromiseVersion
@@ -355,6 +415,7 @@ func (r *DynamicResourceRequestController) updateResourceBinding(ctx context.Con
355415 }
356416
357417 logging .Debug (logger , "ResourceBinding reconciled for Resource" ,
418+ "resourceBindingName" , resourceBinding .GetName (),
358419 "operation" , op ,
359420 "promiseName" , resourceBinding .Spec .PromiseRef .Name ,
360421 "promiseVersion" , resourceBinding .Spec .Version ,
@@ -559,7 +620,7 @@ func (r *DynamicResourceRequestController) updateReconciledCondition(rr *unstruc
559620
560621func (r * DynamicResourceRequestController ) updatePromiseVersionStatus (logger logr.Logger , rr * unstructured.Unstructured , bindingVersion string , promiseRevision * v1alpha1.PromiseRevision ) bool {
561622 logging .Trace (logger , "Checking if we need to update the promise version in the status" )
562- if ! r .PromiseUpgrade || promiseRevision == nil {
623+ if ! r .PromiseUpgradeFeatFlag || promiseRevision == nil {
563624 logging .Trace (logger , "Feature flag disabled or no PromiseRevision: no update promise version required" )
564625 return false
565626 }
@@ -761,7 +822,7 @@ func (r *DynamicResourceRequestController) deleteResources(o opts, promise *v1al
761822 return fastRequeue , nil
762823 }
763824
764- if r .PromiseUpgrade {
825+ if r .PromiseUpgradeFeatFlag {
765826 if controllerutil .ContainsFinalizer (resourceRequest , resourceBindingFinalizer ) {
766827 err := r .deleteResourceBinding (o , resourceRequest , promise , resourceBindingFinalizer )
767828 if err != nil {
@@ -1041,6 +1102,11 @@ func latestRevision(ctx context.Context, c client.Client, promise *v1alpha1.Prom
10411102func fetchRevision (ctx context.Context , c client.Client , promise * v1alpha1.Promise ,
10421103 binding * v1alpha1.ResourceBinding , promiseVersionFromRRStatus string ) (* v1alpha1.PromiseRevision , error ) {
10431104
1105+ // there is a scenario where the PromiseVersion from resource request status
1106+ // is set, but no resource binding exists, which means the resource binding was
1107+ // deleted at some point.
1108+ //
1109+ // We infer what the resource binding version used to be from the resource request `status.resourceBindingVersion`
10441110 desiredVersion := promiseVersionFromRRStatus
10451111
10461112 if binding != nil {
@@ -1098,7 +1164,7 @@ func (r *DynamicResourceRequestController) setPromiseLabels(ctx context.Context,
10981164
10991165func (r * DynamicResourceRequestController ) getRRFinalizers () []string {
11001166 rrFinalizers := []string {workFinalizer , removeAllWorkflowJobsFinalizer , runDeleteWorkflowsFinalizer }
1101- if r .PromiseUpgrade {
1167+ if r .PromiseUpgradeFeatFlag {
11021168 rrFinalizers = append (rrFinalizers , resourceBindingFinalizer )
11031169 }
11041170 return rrFinalizers
0 commit comments