@@ -478,6 +478,24 @@ func (r *WatcherReconciler) Reconcile(ctx context.Context, req ctrl.Request) (re
478478 return ctrl.Result {}, err
479479 }
480480
481+ // Add consumer finalizer to the new AC secret early, before deployment.
482+ // The old secret's finalizer is removed later (after all services deploy)
483+ // so that rapid rotations don't revoke a credential still in use by pods.
484+ if instance .Spec .Auth .ApplicationCredentialSecret != "" {
485+ if err := keystonev1 .ManageACSecretFinalizer (ctx , helper , instance .Namespace ,
486+ instance .Spec .Auth .ApplicationCredentialSecret ,
487+ "" ,
488+ watcher .ACConsumerFinalizer ); err != nil {
489+ instance .Status .Conditions .Set (condition .FalseCondition (
490+ condition .ServiceConfigReadyCondition ,
491+ condition .ErrorReason ,
492+ condition .SeverityWarning ,
493+ condition .ServiceConfigReadyErrorMessage ,
494+ err .Error ()))
495+ return ctrl.Result {}, err
496+ }
497+ }
498+
481499 instance .Status .Conditions .MarkTrue (condition .ServiceConfigReadyCondition , condition .ServiceConfigReadyMessage )
482500 // End of config generation for dbsync
483501
@@ -523,6 +541,27 @@ func (r *WatcherReconciler) Reconcile(ctx context.Context, req ctrl.Request) (re
523541 }
524542 // End of Watcher Applier deploy
525543
544+ // Manage the old AC secret's finalizer and status tracking.
545+ // On rotation (old != new), only remove the old secret's finalizer after
546+ // all sub-services are ready with the new credentials. This prevents
547+ // premature revocation during rapid rotations.
548+ isRotation := instance .Status .ApplicationCredentialSecret != "" && instance .Status .ApplicationCredentialSecret != instance .Spec .Auth .ApplicationCredentialSecret
549+
550+ if isRotation {
551+ allServicesReady := instance .Status .Conditions .IsTrue (watcherv1beta1 .WatcherAPIReadyCondition ) &&
552+ instance .Status .Conditions .IsTrue (watcherv1beta1 .WatcherApplierReadyCondition ) &&
553+ instance .Status .Conditions .IsTrue (watcherv1beta1 .WatcherDecisionEngineReadyCondition )
554+ if allServicesReady {
555+ if err := keystonev1 .RemoveACSecretConsumerFinalizer (ctx , helper , instance .Namespace ,
556+ instance .Status .ApplicationCredentialSecret , watcher .ACConsumerFinalizer ); err != nil {
557+ return ctrl.Result {}, err
558+ }
559+ instance .Status .ApplicationCredentialSecret = instance .Spec .Auth .ApplicationCredentialSecret
560+ }
561+ } else {
562+ instance .Status .ApplicationCredentialSecret = instance .Spec .Auth .ApplicationCredentialSecret
563+ }
564+
526565 //
527566 // remove finalizers from unused MariaDBAccount records
528567 // this assumes all database-depedendent deployments are up and
@@ -1347,6 +1386,17 @@ func (r *WatcherReconciler) reconcileDelete(ctx context.Context, instance *watch
13471386 }
13481387 //
13491388
1389+ // Remove consumer finalizer from AC secrets watcher was consuming.
1390+ for _ , secretName := range []string {
1391+ instance .Status .ApplicationCredentialSecret ,
1392+ instance .Spec .Auth .ApplicationCredentialSecret ,
1393+ } {
1394+ if err := keystonev1 .RemoveACSecretConsumerFinalizer (ctx , helper , instance .Namespace ,
1395+ secretName , watcher .ACConsumerFinalizer ); err != nil {
1396+ return ctrl.Result {}, err
1397+ }
1398+ }
1399+
13501400 controllerutil .RemoveFinalizer (instance , helper .GetFinalizer ())
13511401 Log .Info (fmt .Sprintf ("Reconciled Service '%s' delete successfully" , instance .Name ))
13521402 return ctrl.Result {}, nil
0 commit comments