@@ -113,6 +113,8 @@ func (d *DRPCInstance) processPlacement() (bool, error) {
113113 return d .RunFailover ()
114114 case rmn .ActionRelocate :
115115 return d .RunRelocate ()
116+ case rmn .ActionTestFailover :
117+ return d .RunTestFailover ()
116118 }
117119
118120 // Not a failover or a relocation. Must be an initial deployment.
@@ -363,7 +365,7 @@ func (d *DRPCInstance) RunFailover() (bool, error) {
363365 return ! done , err
364366 }
365367
366- // IFF VRG exists and it is primary in the failoverCluster, then ensure failover is complete and
368+ // If VRG exists and it is primary in the failoverCluster, then ensure failover is complete and
367369 // clean up and setup VolSync if needed.
368370 if d .vrgExistsAndPrimary (failoverCluster ) {
369371 d .updatePreferredDecision ()
@@ -388,13 +390,18 @@ func (d *DRPCInstance) RunFailover() (bool, error) {
388390 addOrUpdateCondition (& d .instance .Status .Conditions , rmn .ConditionAvailable , d .instance .Generation ,
389391 metav1 .ConditionTrue , string (d .instance .Status .Phase ), "Completed" )
390392
393+ if d .instance .Spec .Action == rmn .ActionTestFailover {
394+ d .setProgression (rmn .ProgressionTestFailover )
395+
396+ return ! done , nil
397+ }
398+
391399 return d .ensureFailoverActionCompleted (failoverCluster )
392400 } else if yes , err := d .mwExistsAndPlacementUpdated (failoverCluster ); yes || err != nil {
393401 // We have to wait for the VRG to appear on the failoverCluster or
394402 // in case of an error, try again later
395403 return ! done , err
396404 }
397-
398405 // Use the DRPC Protected condition to check if it is true and then allow failover
399406 if ! d .isProtected () {
400407 return ! done , nil
@@ -405,6 +412,17 @@ func (d *DRPCInstance) RunFailover() (bool, error) {
405412 return d .switchToFailoverCluster ()
406413}
407414
415+ func (d * DRPCInstance ) RunTestFailover () (bool , error ) {
416+ d .log .Info ("Entering RunTestFailover" , "state" , d .getLastDRState ())
417+
418+ // The logic for test failover is same as actual failover,
419+ // allowing users to understand that we are in a testing failover mode,
420+ // without impacting the real failover, with an option to revert to process whenever needed
421+ // or even to continue with the actual failover if they are satisfied with the test failover results.
422+
423+ return d .RunFailover ()
424+ }
425+
408426func (d * DRPCInstance ) isProtected () bool {
409427 for _ , cond := range d .instance .Status .Conditions {
410428 if cond .Type == rmn .ConditionProtected && cond .Status == metav1 .ConditionTrue {
@@ -2474,12 +2492,31 @@ func (d *DRPCInstance) setDRState(nextState rmn.DRState) {
24742492 d .log .Info (fmt .Sprintf ("Phase: Current '%s'. Next '%s'" ,
24752493 d .instance .Status .Phase , nextState ))
24762494
2477- d .instance .Status .Phase = nextState
2495+ d .instance .Status .Phase = d . adjustPhaseIfTestFailover ( nextState )
24782496 d .instance .Status .ObservedGeneration = d .instance .Generation
24792497 d .reportEvent (nextState )
24802498 }
24812499}
24822500
2501+ func (d * DRPCInstance ) adjustPhaseIfTestFailover (nextState rmn.DRState ) rmn.DRState {
2502+ if d .instance .Spec .Action == rmn .ActionTestFailover {
2503+ return mapPhaseForTestFailover (nextState )
2504+ }
2505+
2506+ return nextState
2507+ }
2508+
2509+ func mapPhaseForTestFailover (nextState rmn.DRState ) rmn.DRState {
2510+ switch nextState {
2511+ case rmn .FailingOver :
2512+ return rmn .TestFailover
2513+ case rmn .FailedOver :
2514+ return rmn .TestFailedOver
2515+ default :
2516+ return nextState
2517+ }
2518+ }
2519+
24832520func updateDRPCProgression (
24842521 drpc * rmn.DRPlacementControl , nextProgression rmn.ProgressionStatus , log logr.Logger ,
24852522) bool {
0 commit comments