@@ -77,6 +77,10 @@ func (r *Reconciler) Reconcile(ctx context.Context, req runtime.Request) (runtim
7777 klog .ErrorS (err , "Failed to get updateRun object" , "updateRun" , req .NamespacedName )
7878 return runtime.Result {}, client .IgnoreNotFound (err )
7979 }
80+
81+ // Update all existing conditions' ObservedGeneration to the current generation.
82+ updateAllStatusConditionsGeneration (updateRun .GetUpdateRunStatus (), updateRun .GetGeneration ())
83+
8084 runObjRef := klog .KObj (updateRun )
8185
8286 // Remove waitTime from the updateRun status for BeforeStageTask and AfterStageTask for type Approval.
@@ -110,12 +114,9 @@ func (r *Reconciler) Reconcile(ctx context.Context, req runtime.Request) (runtim
110114 var toBeUpdatedBindings , toBeDeletedBindings []placementv1beta1.BindingObj
111115 updateRunStatus := updateRun .GetUpdateRunStatus ()
112116 initCond := meta .FindStatusCondition (updateRunStatus .Conditions , string (placementv1beta1 .StagedUpdateRunConditionInitialized ))
113- // Check if initialized regardless of generation.
114- // The updateRun spec fields are immutable except for the state field. When the state changes,
115- // the update run generation increments, but we don't need to reinitialize since initialization is a one-time setup.
116- if ! (initCond != nil && initCond .Status == metav1 .ConditionTrue ) {
117+ if ! condition .IsConditionStatusTrue (initCond , updateRun .GetGeneration ()) {
117118 // Check if initialization failed for the current generation.
118- if initCond != nil && initCond . Status == metav1 . ConditionFalse {
119+ if condition . IsConditionStatusFalse ( initCond , updateRun . GetGeneration ()) {
119120 klog .V (2 ).InfoS ("The updateRun has failed to initialize" , "errorMsg" , initCond .Message , "updateRun" , runObjRef )
120121 return runtime.Result {}, nil
121122 }
@@ -158,9 +159,12 @@ func (r *Reconciler) Reconcile(ctx context.Context, req runtime.Request) (runtim
158159 return runtime.Result {}, r .recordUpdateRunSucceeded (ctx , updateRun )
159160 }
160161
161- // Execute the updateRun.
162- if state == placementv1beta1 .StateRun {
163- klog .V (2 ).InfoS ("Continue to execute the updateRun" , "state" , state , "updatingStageIndex" , updatingStageIndex , "updateRun" , runObjRef )
162+ switch state {
163+ case placementv1beta1 .StateInitialize :
164+ klog .V (2 ).InfoS ("The updateRun is initialized but not executed, waiting to execute" , "state" , state , "updateRun" , runObjRef )
165+ case placementv1beta1 .StateRun :
166+ // Execute the updateRun.
167+ klog .InfoS ("Continue to execute the updateRun" , "updatingStageIndex" , updatingStageIndex , "updateRun" , runObjRef )
164168 finished , waitTime , execErr := r .execute (ctx , updateRun , updatingStageIndex , toBeUpdatedBindings , toBeDeletedBindings )
165169 if errors .Is (execErr , errStagedUpdatedAborted ) {
166170 // errStagedUpdatedAborted cannot be retried.
@@ -182,8 +186,19 @@ func (r *Reconciler) Reconcile(ctx context.Context, req runtime.Request) (runtim
182186 return runtime.Result {}, execErr
183187 }
184188 return runtime.Result {Requeue : true , RequeueAfter : waitTime }, nil
189+ case placementv1beta1 .StateStop :
190+ // Stop the updateRun.
191+ klog .InfoS ("Stopping the updateRun" , "state" , state , "updatingStageIndex" , updatingStageIndex , "updateRun" , runObjRef )
192+ // TODO(britaniar): Implement the stopping logic for in-progress stages.
193+
194+ klog .V (2 ).InfoS ("The updateRun is stopped" , "updateRun" , runObjRef )
195+ return runtime.Result {}, r .recordUpdateRunStopped (ctx , updateRun )
196+ default :
197+ // Initialize, Run, or Stop are the only supported states.
198+ unexpectedErr := controller .NewUnexpectedBehaviorError (fmt .Errorf ("found unsupported updateRun state: %s" , state ))
199+ klog .ErrorS (unexpectedErr , "Invalid updateRun state" , "state" , state , "updateRun" , runObjRef )
200+ return runtime.Result {}, r .recordUpdateRunFailed (ctx , updateRun , unexpectedErr .Error ())
185201 }
186- klog .V (2 ).InfoS ("The updateRun is initialized but not executed, waiting to execute" , "state" , state , "updateRun" , runObjRef )
187202 return runtime.Result {}, nil
188203}
189204
@@ -277,6 +292,25 @@ func (r *Reconciler) recordUpdateRunFailed(ctx context.Context, updateRun placem
277292 return nil
278293}
279294
295+ // recordUpdateRunStopped records the progressing condition as stopped in the updateRun status.
296+ func (r * Reconciler ) recordUpdateRunStopped (ctx context.Context , updateRun placementv1beta1.UpdateRunObj ) error {
297+ updateRunStatus := updateRun .GetUpdateRunStatus ()
298+ meta .SetStatusCondition (& updateRunStatus .Conditions , metav1.Condition {
299+ Type : string (placementv1beta1 .StagedUpdateRunConditionProgressing ),
300+ Status : metav1 .ConditionFalse ,
301+ ObservedGeneration : updateRun .GetGeneration (),
302+ Reason : condition .UpdateRunStoppedReason ,
303+ Message : "The update run has been stopped" ,
304+ })
305+
306+ if updateErr := r .Client .Status ().Update (ctx , updateRun ); updateErr != nil {
307+ klog .ErrorS (updateErr , "Failed to update the updateRun status as stopped" , "updateRun" , klog .KObj (updateRun ))
308+ // updateErr can be retried.
309+ return controller .NewUpdateIgnoreConflictError (updateErr )
310+ }
311+ return nil
312+ }
313+
280314// recordUpdateRunStatus records the updateRun status.
281315func (r * Reconciler ) recordUpdateRunStatus (ctx context.Context , updateRun placementv1beta1.UpdateRunObj ) error {
282316 if updateErr := r .Client .Status ().Update (ctx , updateRun ); updateErr != nil {
@@ -484,3 +518,57 @@ func removeWaitTimeFromUpdateRunStatus(updateRun placementv1beta1.UpdateRunObj)
484518 }
485519 }
486520}
521+
522+ // updateAllStatusConditionsGeneration iterates through all existing conditions in the UpdateRun status
523+ // and updates their ObservedGeneration field to the current UpdateRun generation.
524+ func updateAllStatusConditionsGeneration (updateRunStatus * placementv1beta1.UpdateRunStatus , generation int64 ) {
525+ // Update main UpdateRun conditions.
526+ for i := range updateRunStatus .Conditions {
527+ updateRunStatus .Conditions [i ].ObservedGeneration = generation
528+ }
529+
530+ // Update stage-level conditions and nested task conditions if it exists.
531+ for i := range updateRunStatus .StagesStatus {
532+ stageStatus := & updateRunStatus .StagesStatus [i ]
533+
534+ // Update stage conditions.
535+ updateAllStageStatusConditionsGeneration (stageStatus , generation )
536+ }
537+
538+ // Update deletion stage conditions and nested tasks if it exists.
539+ if updateRunStatus .DeletionStageStatus != nil {
540+ deletionStageStatus := updateRunStatus .DeletionStageStatus
541+
542+ // Update deletion stage conditions.
543+ updateAllStageStatusConditionsGeneration (deletionStageStatus , generation )
544+ }
545+ }
546+
547+ // updateAllStageStatusConditionsGeneration updates all conditions' ObservedGeneration in the given stage status.
548+ func updateAllStageStatusConditionsGeneration (stageStatus * placementv1beta1.StageUpdatingStatus , generation int64 ) {
549+ // Update stage conditions.
550+ for j := range stageStatus .Conditions {
551+ stageStatus .Conditions [j ].ObservedGeneration = generation
552+ }
553+
554+ // Update before stage task conditions.
555+ for j := range stageStatus .BeforeStageTaskStatus {
556+ for k := range stageStatus .BeforeStageTaskStatus [j ].Conditions {
557+ stageStatus .BeforeStageTaskStatus [j ].Conditions [k ].ObservedGeneration = generation
558+ }
559+ }
560+
561+ // Update after stage task conditions.
562+ for j := range stageStatus .AfterStageTaskStatus {
563+ for k := range stageStatus .AfterStageTaskStatus [j ].Conditions {
564+ stageStatus .AfterStageTaskStatus [j ].Conditions [k ].ObservedGeneration = generation
565+ }
566+ }
567+
568+ // Update cluster-level conditions.
569+ for j := range stageStatus .Clusters {
570+ for k := range stageStatus .Clusters [j ].Conditions {
571+ stageStatus .Clusters [j ].Conditions [k ].ObservedGeneration = generation
572+ }
573+ }
574+ }
0 commit comments