Skip to content

Commit

Permalink
Merge pull request #1646 from TimothyAsirJeyasing/sync-delay-dr
Browse files Browse the repository at this point in the history
Display non-blocking warnings for synchronization delays
  • Loading branch information
openshift-merge-bot[bot] authored Oct 30, 2024
2 parents 3125cfb + 3465b48 commit 950d0eb
Show file tree
Hide file tree
Showing 14 changed files with 205 additions and 73 deletions.
2 changes: 2 additions & 0 deletions locales/en/plugin__odf-console.json
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,8 @@
"Other applications may be affected.": "Other applications may be affected.",
"<0>This application uses placement that are also used by other applications. Failing over will automatically trigger a failover for other applications sharing the same placement.</0>": "<0>This application uses placement that are also used by other applications. Failing over will automatically trigger a failover for other applications sharing the same placement.</0>",
"<0>This application uses placement that are also used by other applications. Relocating will automatically trigger a relocate for other applications sharing the same placement.</0>": "<0>This application uses placement that are also used by other applications. Relocating will automatically trigger a relocate for other applications sharing the same placement.</0>",
"Inconsistent data on target cluster": "Inconsistent data on target cluster",
"The target cluster's volumes contain data inconsistencies caused by synchronization delays. Performing the failover could lead to data loss. Refer to the corresponding VolumeSynchronizationDelay OpenShift alert(s) for more information.": "The target cluster's volumes contain data inconsistencies caused by synchronization delays. Performing the failover could lead to data loss. Refer to the corresponding VolumeSynchronizationDelay OpenShift alert(s) for more information.",
"Attention": "Attention",
"A failover will occur for all namespaces currently under this DRPC.": "A failover will occur for all namespaces currently under this DRPC.",
"You need to clean up manually to begin replication after a successful failover.": "You need to clean up manually to begin replication after a successful failover.",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as React from 'react';
import { getReplicationHealth } from '@odf/mco/utils';
import { ACM_DEFAULT_DOC_VERSION } from '@odf/shared/constants';
import { utcDateTimeFormatter } from '@odf/shared/details-page/datetime';
import { useDocVersion, DOC_VERSION as mcoDocVersion } from '@odf/shared/hooks';
Expand All @@ -25,6 +26,7 @@ import {
DRActionType,
REPLICATION_TYPE,
ACM_OPERATOR_SPEC_NAME,
VOLUME_REPLICATION_HEALTH,
} from '../../../constants';
import {
ErrorMessageType,
Expand Down Expand Up @@ -79,30 +81,56 @@ const validatePlacement = (
action: DRActionType
): ErrorMessageType => {
const isFailoverAction = action === DRActionType.FAILOVER;

// Check if DR is disabled
if (!placementControl?.drPlacementControlName) {
// DR is disabled
return isFailoverAction
? ErrorMessageType.DR_IS_NOT_ENABLED_FAILOVER
: ErrorMessageType.DR_IS_NOT_ENABLED_RELOCATE;
} else if (!placementControl?.isDRActionReady) {
// Either Peer is not ready for DR failover
// Or, Peer is not ready/available for DR relocate
}

// Check if DR action is ready
// Either Peer is not ready for DR failover
// Or, Peer is not ready/available for DR relocate
if (!placementControl?.isDRActionReady) {
return isFailoverAction
? ErrorMessageType.FAILOVER_READINESS_CHECK_FAILED
: ErrorMessageType.RELOCATE_READINESS_CHECK_FAILED;
} else {
const errorMessage = isFailoverAction
? failoverPreCheck(placementControl)
: relocatePreCheck(placementControl);
if (!errorMessage) {
if (placementControl?.areSiblingApplicationsFound) {
return isFailoverAction
? ErrorMessageType.SIBLING_APPLICATIONS_FOUND_FAILOVER
: ErrorMessageType.SIBLING_APPLICATIONS_FOUND_RELOCATE;
}
}
return errorMessage;
}

// Perform failover or relocate pre-check
const preCheckError = isFailoverAction
? failoverPreCheck(placementControl)
: relocatePreCheck(placementControl);

if (preCheckError) {
return preCheckError;
}

// Check volume replication health
if (
[
VOLUME_REPLICATION_HEALTH.CRITICAL,
VOLUME_REPLICATION_HEALTH.WARNING,
].includes(
getReplicationHealth(
placementControl?.snapshotTakenTime,
placementControl?.schedulingInterval,
placementControl?.replicationType
)
)
) {
return ErrorMessageType.VOLUME_SYNC_DELAY;
}

// Check if sibling applications are found
if (placementControl?.areSiblingApplicationsFound) {
return isFailoverAction
? ErrorMessageType.SIBLING_APPLICATIONS_FOUND_FAILOVER
: ErrorMessageType.SIBLING_APPLICATIONS_FOUND_RELOCATE;
}

return null;
};

const MessageStatus = ({ message }: { message: MessageKind }) => (
Expand Down Expand Up @@ -331,6 +359,7 @@ export type PlacementControlProps = Partial<{
isPrimaryClusterFenced: boolean;
areSiblingApplicationsFound: boolean;
kubeObjectLastSyncTime: string;
schedulingInterval: string;
}>;

export type ApplicationProps = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export const FailoverRelocateModal: React.FC<FailoverRelocateModalProps> = (

const onClick = () => {
setFooterStatus(ModalFooterStatus.INPROGRESS);
// Prefered cluster and failover cluster should not be same for failover and relocate.
// Preferred cluster and failover cluster should not be the same for failover and relocate.
const patch = [
{
op: 'replace',
Expand Down Expand Up @@ -120,6 +120,7 @@ export const FailoverRelocateModal: React.FC<FailoverRelocateModalProps> = (
setCanInitiate={setCanInitiate}
setPlacement={setPlacementControl}
/>

{(!!errorMessage || !!loadError) && (
<Alert
title={errorMessage || getErrorMessage(loadError)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export enum ErrorMessageType {
// Warning message priority start from 20
SIBLING_APPLICATIONS_FOUND_FAILOVER = 20,
SIBLING_APPLICATIONS_FOUND_RELOCATE,
VOLUME_SYNC_DELAY,
}

export type MessageKind = Partial<{
Expand Down Expand Up @@ -269,6 +270,17 @@ export const ErrorMessages = (
),
variant: AlertVariant.warning,
},
[ErrorMessageType.VOLUME_SYNC_DELAY]: {
title: t('Inconsistent data on target cluster'),
message: (
<p>
{t(
"The target cluster's volumes contain data inconsistencies caused by synchronization delays. Performing the failover could lead to data loss. Refer to the corresponding VolumeSynchronizationDelay OpenShift alert(s) for more information."
)}
</p>
),
variant: AlertVariant.warning,
},
});

export const evaluateErrorMessage = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,11 @@ export const ArogoApplicationSetParser = (
props: ArogoApplicationSetParserProps
) => {
const { application, action, isOpen, close } = props;

const [drResources, drLoaded, drLoadError] = useDisasterRecoveryResourceWatch(
getDRResources(getNamespace(application))
);

const [aroAppSetResources, loaded, loadError] =
useArgoApplicationSetResourceWatch(
getApplicationSetResources(
Expand All @@ -99,15 +101,22 @@ export const ArogoApplicationSetParser = (
drLoadError
)
);

const aroAppSetResource = aroAppSetResources?.formattedResources?.[0];

const placementControls: PlacementControlProps[] = React.useMemo(() => {
const {
managedClusters,
siblingApplications,
placements: resourcePlacements,
} = aroAppSetResource || {};
const { drClusters, drPlacementControl, placementDecision, placement } =
resourcePlacements?.[0] || {};
const {
drClusters,
drPolicy,
drPlacementControl,
placementDecision,
placement,
} = resourcePlacements?.[0] || {};
const deploymentClusters = findDeploymentClusters(
placementDecision,
drPlacementControl
Expand All @@ -133,6 +142,7 @@ export const ArogoApplicationSetParser = (
targetCluster,
MANAGED_CLUSTER_CONDITION_AVAILABLE
);

return loaded && !loadError
? [
{
Expand All @@ -150,6 +160,7 @@ export const ArogoApplicationSetParser = (
isTargetClusterFenced: isDRClusterFenced(targetDRCluster),
isPrimaryClusterFenced: isDRClusterFenced(primaryDRCluster),
areSiblingApplicationsFound: !!siblingApplications?.length,
schedulingInterval: drPolicy?.spec?.schedulingInterval,
},
]
: [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export const DRPolicySelector: React.FC<DRPolicySelectorProps> = ({
setSelected({
policyName: getName(drPolicy),
drClusters: drPolicy?.spec?.drClusters,
schedulingInterval: drPolicy?.spec?.schedulingInterval,
});
setOpen(false);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ export enum ErrorMessageType {
PRIMARY_CLUSTER_IS_NOT_FENCED,
TARGET_CLUSTER_IS_FENCED,
NO_SUBSCRIPTION_GROUP_FOUND,
// Warning Messages
VOLUME_SYNC_DELAY,
}

export type MessageKind = Partial<{
Expand Down Expand Up @@ -243,4 +245,15 @@ export const ErrorMessages = (
title: t('No subscription groups are found.'),
variant: AlertVariant.danger,
},
[ErrorMessageType.VOLUME_SYNC_DELAY]: {
title: t('Inconsistent data on target cluster'),
message: (
<p>
{t(
"The target cluster's volumes contain data inconsistencies caused by synchronization delays. Performing the failover could lead to data loss. Refer to the corresponding VolumeSynchronizationDelay OpenShift alert(s) for more information."
)}
</p>
),
variant: AlertVariant.warning,
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,17 @@ import {
import { SubscriptionGroupSelector } from './subscription-group-selector';
import { TargetClusterSelector } from './target-cluster-selector';

export const findErrorMessage = (errorMessage: ErrorMessage) =>
export const findErrorMessage = (
errorMessage: ErrorMessage,
includeWarning: Boolean
) =>
[
errorMessage.drPolicyControlStateErrorMessage,
errorMessage.managedClustersErrorMessage,
errorMessage.targetClusterErrorMessage,
errorMessage.subscriptionGroupErrorMessage,
errorMessage.peerStatusErrorMessage,
includeWarning && errorMessage.syncDelayWarningMessage,
]
.filter(Boolean)
.find((errorMessageItem) => errorMessageItem);
Expand Down Expand Up @@ -250,11 +254,11 @@ export const FailoverRelocateModalBody: React.FC<FailoverRelocateModalBodyProps>
})}
/>
)) ||
((!!findErrorMessage(state.errorMessage) ||
((!!findErrorMessage(state.errorMessage, true) ||
!_.isEmpty(state.actionErrorMessage)) && (
<MessageStatus
{...(ErrorMessages(t, mcoDocVersion, acmDocVersion)[
findErrorMessage(state.errorMessage)
findErrorMessage(state.errorMessage, true)
] || state.actionErrorMessage)}
/>
))}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ export const SubscriptionFailoverRelocateModal: React.FC<FailoverRelocateModalPr
});

const canInitiate = () =>
!findErrorMessage(state.errorMessage) &&
!findErrorMessage(state.errorMessage, false) &&
!!state.selectedSubsGroups.length;

const onClick = () => {
Expand Down
Loading

0 comments on commit 950d0eb

Please sign in to comment.