Skip to content

Commit db88f3c

Browse files
committed
refactor: Replace ActionTestFailover action with DryRun attribute
Replace the separate ActionTestFailover action type with a simpler attribute-based approach using a DryRun boolean field. This cleaner design separates concerns: - Action Failover indicates the operation to perform - DryRun boolean indicates if the operation should be non-destructive/test mode - Progression status (TestingFailover) continues to indicate test mode Changes: - Remove ActionTestFailover from DRAction and VRGAction enums - Remove TestFailover and TestFailedOver DRState constants - Add DryRun field to DRPlacementControlSpec and VolumeReplicationGroupSpec - Update all references to ActionTestFailover to check DryRun flag instead The progression status ProgressionTestingFailover is retained as it provides a unified indicator of test mode across both DRPC and VRG resources.
1 parent 224d401 commit db88f3c

9 files changed

Lines changed: 45 additions & 56 deletions

api/v1alpha1/drplacementcontrol_types.go

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import (
88
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
99
)
1010

11-
// DRAction which will be either a Failover, Relocate or TestFailover action
12-
// +kubebuilder:validation:Enum=Failover;Relocate;TestFailover
11+
// DRAction which will be either a Failover or Relocate action
12+
// +kubebuilder:validation:Enum=Failover;Relocate
1313
type DRAction string
1414

1515
// These are the valid values for DRAction
@@ -20,11 +20,6 @@ const (
2020
// Relocate, restore PVs to the designated TargetCluster. PreferredCluster will change
2121
// to be the TargetCluster.
2222
ActionRelocate = DRAction("Relocate")
23-
24-
// TestFailover is to test the failover to the TargetCluster without impacting the PrimaryCluster.
25-
// This is a non-disruptive action and is used to validate the failover on the TargetCluster
26-
// cluster before performing an actual failover.
27-
ActionTestFailover = DRAction("TestFailover")
2823
)
2924

3025
// DRState for keeping track of the DR placement
@@ -59,14 +54,6 @@ const (
5954
// process has completed
6055
FailedOver = DRState("FailedOver")
6156

62-
// TestFailover, state recorded in the DRPC status when the test failover
63-
// is initiated
64-
TestFailover = DRState("TestFailover")
65-
66-
// TestFailedOver, state recorded in the DRPC status when the test failover process
67-
// is in progress but has not been completed yet
68-
TestFailedOver = DRState("TestFailedOver")
69-
7057
// Relocating, state recorded in the DRPC status to indicate that the
7158
// relocation is in progress
7259
Relocating = DRState("Relocating")
@@ -172,6 +159,12 @@ type DRPlacementControlSpec struct {
172159
// Action is either Failover or Relocate operation
173160
Action DRAction `json:"action,omitempty"`
174161

162+
// DryRun when set to true, makes the action Failover non-destructive.
163+
// The secondary is temporarily promoted to primary to verify readiness and data consistency
164+
// without committing to the actual failover. Can be aborted to return to the original state.
165+
// +kubebuilder:validation:Optional
166+
DryRun bool `json:"dryRun,omitempty"`
167+
175168
// +optional
176169
KubeObjectProtection *KubeObjectProtectionSpec `json:"kubeObjectProtection,omitempty"`
177170

api/v1alpha1/volumereplicationgroup_types.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ type MoverConfig struct {
160160
}
161161

162162
// VRGAction which will be either a Failover or Relocate
163-
// +kubebuilder:validation:Enum=Failover;Relocate;TestFailover
163+
// +kubebuilder:validation:Enum=Failover;Relocate
164164
type VRGAction string
165165

166166
// These are the valid values for VRGAction
@@ -172,11 +172,6 @@ const (
172172
// Relocate, VRG was relocated to/from this cluster,
173173
// the to/from is determined by VRG spec.ReplicationState values of Primary/Secondary respectively
174174
VRGActionRelocate = VRGAction("Relocate")
175-
176-
// TestFailover, VRG is in a test failover state where the secondary is temporarily promoted
177-
// to primary to verify readiness and data consistency without committing to the actual failover.
178-
// Test failover is non-destructive and can be aborted to return to the original state.
179-
VRGActionTestFailover = VRGAction("TestFailover")
180175
)
181176

182177
type KubeObjectProtectionSpec struct {
@@ -257,6 +252,10 @@ type VolumeReplicationGroupSpec struct {
257252
// Action is either Failover or Relocate
258253
//+optional
259254
Action VRGAction `json:"action,omitempty"`
255+
// DryRun indicates whether the action should be executed in test/non-destructive mode.
256+
// When true, no permanent changes are made on the failover cluster.
257+
//+optional
258+
DryRun bool `json:"dryRun,omitempty"`
260259
//+optional
261260
KubeObjectProtection *KubeObjectProtectionSpec `json:"kubeObjectProtection,omitempty"`
262261

config/crd/bases/ramendr.openshift.io_drplacementcontrols.yaml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@ spec:
7979
enum:
8080
- Failover
8181
- Relocate
82-
- TestFailover
8382
type: string
8483
drPolicyRef:
8584
description: DRPolicyRef is the reference to the DRPolicy participating
@@ -128,6 +127,12 @@ spec:
128127
x-kubernetes-validations:
129128
- message: drPolicyRef is immutable
130129
rule: self == oldSelf
130+
dryRun:
131+
description: |-
132+
DryRun when set to true, makes the action (Failover or Relocate) non-destructive.
133+
The secondary is temporarily promoted to primary to verify readiness and data consistency
134+
without committing to the actual failover. Can be aborted to return to the original state.
135+
type: boolean
131136
failoverCluster:
132137
description: |-
133138
FailoverCluster is the cluster name that the user wants to failover the application to.

config/crd/bases/ramendr.openshift.io_protectedvolumereplicationgrouplists.yaml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,6 @@ spec:
115115
enum:
116116
- Failover
117117
- Relocate
118-
- TestFailover
119118
type: string
120119
async:
121120
description: VRGAsyncSpec has the parameters associated
@@ -331,6 +330,11 @@ spec:
331330
required:
332331
- schedulingInterval
333332
type: object
333+
dryRun:
334+
description: |-
335+
DryRun indicates whether the action should be executed in test/non-destructive mode.
336+
When true, no permanent changes are made to the replication group or data.
337+
type: boolean
334338
kubeObjectProtection:
335339
properties:
336340
captureInterval:

config/crd/bases/ramendr.openshift.io_volumereplicationgroups.yaml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ spec:
6666
enum:
6767
- Failover
6868
- Relocate
69-
- TestFailover
7069
type: string
7170
async:
7271
description: VRGAsyncSpec has the parameters associated with RegionalDR
@@ -281,6 +280,11 @@ spec:
281280
required:
282281
- schedulingInterval
283282
type: object
283+
dryRun:
284+
description: |-
285+
DryRun indicates whether the action should be executed in test/non-destructive mode.
286+
When true, no permanent changes are made to the replication group or data.
287+
type: boolean
284288
kubeObjectProtection:
285289
properties:
286290
captureInterval:

internal/controller/drplacementcontrol.go

Lines changed: 11 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -110,15 +110,15 @@ func (d *DRPCInstance) processPlacement() (bool, error) {
110110
d.log.Info("Process DRPC Placement", "DRAction", d.instance.Spec.Action)
111111

112112
// Handle test failover cleanup when switching away from test failover to another action
113-
if d.instance.Spec.Action != rmn.ActionTestFailover {
113+
if !d.instance.Spec.DryRun {
114114
rmnutil.AddAnnotation(d.instance, "ramendr.openshift.io/last-action", string(d.instance.Spec.Action))
115115

116116
// If we're exiting a test failover (were in TestingFailover progression), clean up placement
117117
// decisions that were retained during the test, then requeue for the new action.
118118
if d.instance.Status.Progression == rmn.ProgressionTestingFailover {
119119
for clusterName, vrg := range d.vrgs {
120120
// Find the primary VRG that was used for testing and clean up after it
121-
if isVRGPrimary(vrg) && vrg.Spec.Action == rmn.VRGActionTestFailover {
121+
if isVRGPrimary(vrg) && vrg.Spec.DryRun {
122122
// Remove cluster decision that was retained during test failover
123123
if err := d.reconciler.removeClusterDecisionAfterTestFailover(d.ctx, d.userPlacement, clusterName); err != nil {
124124
return false, err
@@ -143,8 +143,6 @@ func (d *DRPCInstance) executeAction() (bool, error) {
143143
return d.RunFailover()
144144
case rmn.ActionRelocate:
145145
return d.RunRelocate()
146-
case rmn.ActionTestFailover:
147-
return d.RunTestFailover()
148146
}
149147

150148
// Not a failover or a relocation. Must be an initial deployment.
@@ -366,7 +364,7 @@ func (d *DRPCInstance) startDeploying(homeCluster, homeClusterNamespace string)
366364
// on the failover cluster
367365
// 3. Else, initiate failover to the desired failoverCluster (switchToFailoverCluster)
368366
//
369-
//nolint:cyclop,funlen,nestif
367+
//nolint:cyclop,funlen,nestif,gocognit
370368
func (d *DRPCInstance) RunFailover() (bool, error) {
371369
d.log.Info("Entering RunFailover", "state", d.getLastDRState())
372370

@@ -417,7 +415,7 @@ func (d *DRPCInstance) RunFailover() (bool, error) {
417415
metav1.ConditionTrue, string(d.instance.Status.Phase), "Completed")
418416

419417
// Opltimize by adding a reconciler so that we reconcile at 1 minute at most.
420-
if d.instance.Spec.Action == rmn.ActionTestFailover {
418+
if d.instance.Spec.DryRun && d.instance.Spec.Action == rmn.ActionFailover {
421419
d.setProgression(rmn.ProgressionTestingFailover)
422420

423421
if err := d.ensurePlacement(failoverCluster); err != nil {
@@ -1184,7 +1182,7 @@ func (d *DRPCInstance) areMultipleVRGsPrimary() bool {
11841182
numOfPrimaries := 0
11851183

11861184
for _, vrg := range d.vrgs {
1187-
if isVRGPrimary(vrg) && vrg.Spec.Action != rmn.VRGActionTestFailover {
1185+
if isVRGPrimary(vrg) && !vrg.Spec.DryRun {
11881186
numOfPrimaries++
11891187
}
11901188
}
@@ -1534,7 +1532,7 @@ func (d *DRPCInstance) updateUserPlacementRule(homeCluster, reason string) error
15341532
d.userPlacement.GetName(), homeCluster))
15351533

15361534
added := false
1537-
if d.instance.Spec.Action != rmn.ActionTestFailover {
1535+
if !d.instance.Spec.DryRun {
15381536
added = rmnutil.AddAnnotation(d.instance, LastAppDeploymentCluster, homeCluster)
15391537
}
15401538

@@ -1958,8 +1956,6 @@ func vrgAction(drpcAction rmn.DRAction) rmn.VRGAction {
19581956
return rmn.VRGActionFailover
19591957
case rmn.ActionRelocate:
19601958
return rmn.VRGActionRelocate
1961-
case rmn.ActionTestFailover:
1962-
return rmn.VRGActionTestFailover
19631959
default:
19641960
return ""
19651961
}
@@ -1972,6 +1968,9 @@ func (d *DRPCInstance) setVRGAction(vrg *rmn.VolumeReplicationGroup) {
19721968
}
19731969

19741970
vrg.Spec.Action = action
1971+
if action == rmn.VRGActionFailover {
1972+
vrg.Spec.DryRun = d.instance.Spec.DryRun
1973+
}
19751974
}
19761975

19771976
func (d *DRPCInstance) newVRG(
@@ -2546,25 +2545,11 @@ func (d *DRPCInstance) setDRState(nextState rmn.DRState) {
25462545
}
25472546

25482547
func (d *DRPCInstance) adjustPhaseIfTestFailover(nextState rmn.DRState) rmn.DRState {
2549-
if d.instance.Spec.Action == rmn.ActionTestFailover {
2550-
return mapPhaseForTestFailover(nextState)
2551-
}
2552-
2548+
// With the DryRun approach, we don't need to adjust phases for test failover
2549+
// The progression status (ProgressionTestingFailover) indicates test mode
25532550
return nextState
25542551
}
25552552

2556-
//nolint:exhaustive
2557-
func mapPhaseForTestFailover(nextState rmn.DRState) rmn.DRState {
2558-
switch nextState {
2559-
case rmn.FailingOver:
2560-
return rmn.TestFailover
2561-
case rmn.FailedOver:
2562-
return rmn.TestFailedOver
2563-
default:
2564-
return nextState
2565-
}
2566-
}
2567-
25682553
func updateDRPCProgression(
25692554
drpc *rmn.DRPlacementControl, nextProgression rmn.ProgressionStatus, log logr.Logger,
25702555
) bool {

internal/controller/drplacementcontrol_controller.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1577,10 +1577,11 @@ func (r *DRPlacementControlReconciler) clusterForVRGStatus(
15771577

15781578
switch drpc.Spec.Action {
15791579
case rmn.ActionFailover:
1580-
case rmn.ActionTestFailover:
15811580
// Failover can rely on inspecting VRG from clusterDecision as it is never made nil, hence till
15821581
// placementDecision is changed to failoverCluster, we can inspect VRG from the existing cluster
1583-
return clusterName
1582+
if drpc.Spec.DryRun {
1583+
return clusterName
1584+
}
15841585
case rmn.ActionRelocate:
15851586
if drpc.Status.ObservedGeneration != drpc.Generation {
15861587
log.Info("DPRC observedGeneration mismatches current generation, using ClusterDecision instead",

internal/controller/vrg_volrep.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1263,8 +1263,7 @@ func (v *VRGInstance) autoResync(state volrep.ReplicationState) bool {
12631263
return false
12641264
}
12651265

1266-
if v.instance.Spec.Action != ramendrv1alpha1.VRGActionFailover &&
1267-
v.instance.Spec.Action != ramendrv1alpha1.VRGActionTestFailover {
1266+
if v.instance.Spec.Action != ramendrv1alpha1.VRGActionFailover {
12681267
return false
12691268
}
12701269

internal/controller/vrg_volsync.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,7 @@ func (v *VRGInstance) restorePVsAndPVCsForVolSync() (int, error) {
3333
numPVsRestored := 0
3434

3535
for _, rdSpec := range v.instance.Spec.VolSync.RDSpec {
36-
failoverAction := (v.instance.Spec.Action == ramendrv1alpha1.VRGActionFailover) ||
37-
(v.instance.Spec.Action == ramendrv1alpha1.VRGActionTestFailover)
36+
failoverAction := v.instance.Spec.Action == ramendrv1alpha1.VRGActionFailover
3837

3938
var err error
4039
// Source conditions are not needed and should not be added to vrg.status.ProtectedPVCs,

0 commit comments

Comments
 (0)