Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Block RDR policy creation for non-DR optimized cluster #1116

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions locales/en/plugin__odf-console.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
"We could not retrieve any information about the managed cluster {{names}}. Check the documentation for potential causes and follow the steps mentioned and try again.": "We could not retrieve any information about the managed cluster {{names}}. Check the documentation for potential causes and follow the steps mentioned and try again.",
"{{ names }} has either an unsupported ODF version or the ODF operator is missing, install or update to ODF {{ version }} or the latest version to enable DR protection.": "{{ names }} has either an unsupported ODF version or the ODF operator is missing, install or update to ODF {{ version }} or the latest version to enable DR protection.",
"{{ names }} is not connected to RHCS": "{{ names }} is not connected to RHCS",
"Cluster not pre-configured for Regional-DR": "Cluster not pre-configured for Regional-DR",
"The selected cluster(s)[{{clusters}}] is not pre-configured for a Regional-DR setup. Migrate the cluster's OSD to optimise it for Disaster recovery services. To learn more about OSDs migration best practices and its consequences refer to the documentation.": "The selected cluster(s)[{{clusters}}] is not pre-configured for a Regional-DR setup. Migrate the cluster's OSD to optimise it for Disaster recovery services. To learn more about OSDs migration best practices and its consequences refer to the documentation.",
"Sync schedule": "Sync schedule",
"Replication policy": "Replication policy",
"Information unavailable": "Information unavailable",
Expand Down
142 changes: 141 additions & 1 deletion packages/mco/components/create-dr-policy/create-dr-policy.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ const managedClusters: ACMManagedClusterKind[] = [
name: 'count.storagecluster.odf.openshift.io',
value: '1',
},
{
name: 'droptimized.odf.openshift.io',
value: 'true',
},
],
conditions: [
{
Expand Down Expand Up @@ -95,6 +99,10 @@ const managedClusters: ACMManagedClusterKind[] = [
name: 'count.storagecluster.odf.openshift.io',
value: '1',
},
{
name: 'droptimized.odf.openshift.io',
value: 'true',
},
],
conditions: [
{
Expand Down Expand Up @@ -201,6 +209,10 @@ const managedClusters: ACMManagedClusterKind[] = [
name: 'count.storagecluster.odf.openshift.io',
value: '2',
},
{
name: 'droptimized.odf.openshift.io',
value: 'true',
},
],
conditions: [
{
Expand Down Expand Up @@ -283,6 +295,10 @@ const managedClusters: ACMManagedClusterKind[] = [
name: 'count.storagecluster.odf.openshift.io',
value: '1',
},
{
name: 'droptimized.odf.openshift.io',
value: 'true',
},
],
conditions: [
{
Expand Down Expand Up @@ -334,6 +350,65 @@ const managedClusters: ACMManagedClusterKind[] = [
name: 'count.storagecluster.odf.openshift.io',
value: '1',
},
{
name: 'droptimized.odf.openshift.io',
value: 'true',
},
],
conditions: [
{
type: 'ManagedClusterJoined',
lastTransitionTime: '2023-11-29T04:30:13Z',
message: 'Managed cluster joined',
reason: 'ManagedClusterJoined',
status: 'True',
},
{
lastTransitionTime: '2023-11-29T04:30:13Z',
message: 'Managed cluster is available',
reason: 'ManagedClusterAvailable',
status: 'True',
type: 'ManagedClusterConditionAvailable',
},
],
},
},
{
apiVersion: 'cluster.open-cluster-management.io/v1',
kind: 'ManagedCluster',
metadata: {
name: 'east-5',
},
status: {
clusterClaims: [
{
name: 'region.open-cluster-management.io',
value: 'us-east-5',
},
{
name: 'cephfsid.odf.openshift.io',
value: 'c1ea826f-1dc2-4faa-87b2-f0fc4665b11a',
},
{
name: 'storageclustername.odf.openshift.io',
value: 'ocs-storagecluster/openshift-storage',
},
{
name: 'storagesystemname.odf.openshift.io',
value: 'ocs-storagecluster-storagesystem/openshift-storage',
},
{
name: 'version.odf.openshift.io',
value: '4.14.0-rhodf',
},
{
name: 'count.storagecluster.odf.openshift.io',
value: '1',
},
{
name: 'droptimized.odf.openshift.io',
value: 'false',
},
],
conditions: [
{
Expand Down Expand Up @@ -466,7 +541,7 @@ describe('Test drpolicy list page', () => {
expect(screen.getByText('Sync schedule')).toBeInTheDocument();
expect(screen.getByText('minutes')).toBeInTheDocument();

// Create button should be disabled
// Create button should be enabled
expect(screen.getByTestId('create-button')).toBeEnabled();
await waitFor(() => fireEvent.click(screen.getByTestId('create-button')));
expect(
Expand All @@ -481,16 +556,28 @@ describe('Test drpolicy list page', () => {

test('Partially imported cluster test', async () => {
let nonExist = false;
// Enter policy name
expect(screen.getByText('Policy name')).toBeInTheDocument();
fireEvent.change(screen.getByTestId('policy-name'), {
target: { value: 'policy-1' },
});
try {
// east-2 is partially imported cluster
screen.getByText('east-2');
} catch (error) {
nonExist = true;
}
expect(nonExist).toBe(true);
// Create button should be disabled
expect(screen.getByTestId('create-button')).toBeDisabled();
});

test('Down cluser selection test', async () => {
// Enter policy name
expect(screen.getByText('Policy name')).toBeInTheDocument();
fireEvent.change(screen.getByTestId('policy-name'), {
target: { value: 'policy-1' },
});
// Select west-2 down cluster
await waitFor(() => fireEvent.click(screen.getByTestId('west-2')));
// Error message for down cluster
Expand All @@ -502,14 +589,28 @@ describe('Test drpolicy list page', () => {
'The status for both the managed clusters must be available for creating a DR policy. To restore a cluster to an available state, refer to the instructions in the ACM documentation.'
)
).toBeInTheDocument();
// Create button should be disabled
expect(screen.getByTestId('create-button')).toBeDisabled();
});

test('Multiple ODF cluster selection test', async () => {
// Enter policy name
expect(screen.getByText('Policy name')).toBeInTheDocument();
fireEvent.change(screen.getByTestId('policy-name'), {
target: { value: 'policy-1' },
});
// East-3 multiple ODF cluster
expect(screen.getByTestId('east-3')).toBeDisabled();
// Create button should be disabled
expect(screen.getByTestId('create-button')).toBeDisabled();
});

test('Non ODF cluster selection test', async () => {
// Enter policy name
expect(screen.getByText('Policy name')).toBeInTheDocument();
fireEvent.change(screen.getByTestId('policy-name'), {
target: { value: 'policy-1' },
});
// Select west-3 non ODF cluster
await waitFor(() => fireEvent.click(screen.getByTestId('west-3')));
// Error message for non ODF detection
Expand All @@ -521,18 +622,32 @@ describe('Test drpolicy list page', () => {
'We could not retrieve any information about the managed cluster {{names}}. Check the documentation for potential causes and follow the steps mentioned and try again.'
)
).toBeInTheDocument();
// Create button should be disabled
expect(screen.getByTestId('create-button')).toBeDisabled();
});

test('Select partially deployed ODF cluster test', async () => {
// Enter policy name
expect(screen.getByText('Policy name')).toBeInTheDocument();
fireEvent.change(screen.getByTestId('policy-name'), {
target: { value: 'policy-1' },
});
// Select west-4 ceph cluster creation inprogress
await waitFor(() => fireEvent.click(screen.getByTestId('west-4')));
// Error message for cephFSID not found
expect(
screen.getByText('{{ names }} is not connected to RHCS')
).toBeInTheDocument();
// Create button should be disabled
expect(screen.getByTestId('create-button')).toBeDisabled();
});

test('Select unsupported ODF version cluster test', async () => {
// Enter policy name
expect(screen.getByText('Policy name')).toBeInTheDocument();
fireEvent.change(screen.getByTestId('policy-name'), {
target: { value: 'policy-1' },
});
// Select east-4 unsupported ODF operator version
await waitFor(() => fireEvent.click(screen.getByTestId('east-4')));
// Error message for unsupported ODF version
Expand All @@ -541,5 +656,30 @@ describe('Test drpolicy list page', () => {
'{{ names }} has either an unsupported ODF version or the ODF operator is missing, install or update to ODF {{ version }} or the latest version to enable DR protection.'
)
).toBeInTheDocument();
// Create button should be disabled
expect(screen.getByTestId('create-button')).toBeDisabled();
});

test('Select non-DR optimized cluster test', async () => {
// Enter policy name
expect(screen.getByText('Policy name')).toBeInTheDocument();
fireEvent.change(screen.getByTestId('policy-name'), {
target: { value: 'policy-1' },
});
// Select east-1 DR optimized ODF
await waitFor(() => fireEvent.click(screen.getByTestId('east-1')));
// Select east-5 none DR optimized ODF
await waitFor(() => fireEvent.click(screen.getByTestId('east-5')));
// Error message for not DR optimized
expect(
screen.getByText('Cluster not pre-configured for Regional-DR')
).toBeInTheDocument();
expect(
screen.getByText(
"The selected cluster(s)[{{clusters}}] is not pre-configured for a Regional-DR setup. Migrate the cluster's OSD to optimise it for Disaster recovery services. To learn more about OSDs migration best practices and its consequences refer to the documentation."
)
).toBeInTheDocument();
// Create button should be disabled
expect(screen.getByTestId('create-button')).toBeDisabled();
});
});
12 changes: 9 additions & 3 deletions packages/mco/components/create-dr-policy/create-dr-policy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ import {
} from './reducer';
import { SelectClusterList } from './select-cluster-list';
import { DRReplicationType } from './select-replication-type';
import { SelectedCluster } from './selected-cluster-view';
import { SelectedCluster, checkForErrors } from './selected-cluster-view';
import './create-dr-policy.scss';
import '../../style.scss';

Expand Down Expand Up @@ -179,7 +179,8 @@ export const CreateDRPolicy: React.FC<{}> = () => {
const areDRPolicyInputsValid = () =>
!!state.policyName &&
!!state.replicationType &&
state.selectedClusters.length === MAX_ALLOWED_CLUSTERS;
state.selectedClusters.length === MAX_ALLOWED_CLUSTERS &&
!checkForErrors(state.selectedClusters, state.replicationType);

return (
<div>
Expand Down Expand Up @@ -239,7 +240,12 @@ export const CreateDRPolicy: React.FC<{}> = () => {
{!!state.selectedClusters.length && (
<FormGroup fieldId="selected-clusters" label={t('Selected clusters')}>
{state.selectedClusters.map((c, i) => (
<SelectedCluster key={c.name} id={i + 1} cluster={c} />
<SelectedCluster
key={c.name}
id={i + 1}
cluster={c}
replicationType={state.replicationType}
/>
))}
</FormGroup>
)}
Expand Down
2 changes: 2 additions & 0 deletions packages/mco/components/create-dr-policy/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export type StorageClusterInfoType = {
storageSystemNamespacedName: string;
// Ceph FSID to determine RDR/MDR.
cephFSID: string;
// OSDs are migrated for the RDR or not.
isDROptimized: boolean;
};

export type ODFConfigInfoType = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ const getODFInfo = (
clusterClaims,
ClusterClaimTypes.STORAGE_CLUSTER_COUNT
);
const isDROptimized = getValueFromClusterClaim(
clusterClaims,
ClusterClaimTypes.DR_OPTIMIZED
);
return {
odfVersion: odfVersion,
isValidODFVersion: isMinimumSupportedODFVersion(
Expand All @@ -95,6 +99,7 @@ const getODFInfo = (
storageClusterNamespacedName: storageClusterNamespacedName,
storageSystemNamespacedName: storageSystemNamespacedName,
cephFSID: cephFsid,
isDROptimized: isDROptimized === 'true',
},
};
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { SingleSelectDropdown } from '@odf/shared/dropdown';
import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook';
import { RequestSizeInput } from '@odf/shared/utils/RequestSizeInput';
import { SelectOption } from '@patternfly/react-core/next';
import { TFunction } from 'i18next';
import { FormGroup, Alert, AlertVariant } from '@patternfly/react-core';
import {
REPLICATION_TYPE,
Expand Down Expand Up @@ -42,20 +43,25 @@ const getClusterErrorInfo = (
if (!storageClusterInfo?.cephFSID) {
acc.clustersWithUnsuccessfulODF.push(cluster.name);
}
if (!storageClusterInfo?.isDROptimized) {
acc.clustersWithoutDROptimizedODF.push(cluster.name);
}
return acc;
},
{
unavailableClusters: [],
clustersWithUnsupportedODF: [],
clustersWithoutODF: [],
clustersWithUnsuccessfulODF: [],
clustersWithoutDROptimizedODF: [],
}
);

const getErrorMessage = (
selectedClusters: ManagedClusterInfoType[],
requiredODFVersion: string,
t
replicationType: REPLICATION_TYPE,
t: TFunction
): ErrorMessageType => {
const clusterErrorInfo = getClusterErrorInfo(selectedClusters);
if (!!clusterErrorInfo.unavailableClusters.length) {
Expand Down Expand Up @@ -89,6 +95,15 @@ const getErrorMessage = (
names: clusterErrorInfo.clustersWithUnsuccessfulODF.join(' & '),
}),
};
} else if (!!clusterErrorInfo.clustersWithoutDROptimizedODF.length) {
return {
message: t('Cluster not pre-configured for Regional-DR'),
description: t(
"The selected cluster(s)[{{clusters}}] is not pre-configured for a Regional-DR setup. Migrate the cluster's OSD to optimise it for Disaster recovery services. To learn more about OSDs migration best practices and its consequences refer to the documentation.",
{ clusters: clusterErrorInfo.clustersWithoutDROptimizedODF.join(', ') }
),
isHidden: replicationType !== REPLICATION_TYPE.ASYNC,
};
}
return null;
};
Expand Down Expand Up @@ -130,7 +145,12 @@ export const DRReplicationType: React.FC<DRReplicationTypeProps> = ({
dispatch,
}) => {
const { t } = useCustomTranslation();
const errorMessage = getErrorMessage(selectedClusters, requiredODFVersion, t);
const errorMessage = getErrorMessage(
selectedClusters,
requiredODFVersion,
replicationType,
t
);

React.useEffect(() => {
if (selectedClusters.length === 2) {
Expand Down Expand Up @@ -174,7 +194,7 @@ export const DRReplicationType: React.FC<DRReplicationTypeProps> = ({

return (
<>
{!!errorMessage ? (
{!!errorMessage && !errorMessage.isHidden ? (
<Alert
data-test="odf-not-found-alert"
className="odf-alert mco-create-data-policy__alert"
Expand Down Expand Up @@ -233,9 +253,11 @@ type ClusterErrorType = {
clustersWithUnsupportedODF: string[];
clustersWithoutODF: string[];
clustersWithUnsuccessfulODF: string[];
clustersWithoutDROptimizedODF: string[];
};

type ErrorMessageType = {
message?: string;
description?: string;
isHidden?: boolean;
};
Loading