From 5ba28f422356a641cc63833e1dd33a8a8c2488c0 Mon Sep 17 00:00:00 2001 From: Gowtham Shanmugasundaram Date: Thu, 26 Oct 2023 14:35:15 +0530 Subject: [PATCH] Removing dead code and minor refactoring Signed-off-by: Gowtham Shanmugasundaram --- locales/en/plugin__odf-console.json | 27 +- .../create-dr-policy/create-dr-policy.scss | 0 .../create-dr-policy/create-dr-policy.tsx | 8 +- .../create-dr-policy/reducer.ts | 0 .../create-dr-policy/select-cluster-list.scss | 0 .../create-dr-policy/select-cluster-list.tsx | 6 +- .../select-replication-type.tsx | 4 +- .../selected-cluster-view.tsx | 0 .../data-policies/data-policies-page.tsx | 2 +- .../drpolicy-list-page.scss | 0 .../drpolicy-list-page.spec.tsx | 2 +- .../drpolicy-list-page/drpolicy-list-page.tsx | 14 +- .../drpolicy-list-page/helper.ts | 2 +- .../applicationset-handler.tsx | 225 ------- .../apply-policy-dual-list-selector.scss | 31 - .../apply-policy-dual-list-selector.tsx | 412 ------------ .../apply-policy-modal.scss | 7 - .../apply-policy-modal.tsx | 319 --------- .../modals/drpolicy-apps-apply/reducer.ts | 140 ---- .../subscriptions/application-selector.scss | 22 - .../subscriptions/application-selector.tsx | 256 ------- .../subscriptions/apply-policy-modal.scss | 25 - .../subscriptions/apply-policy-modal.tsx | 636 ------------------ .../modals/drpolicy-apps-apply/utils.tsx | 170 ----- packages/mco/utils/acm.ts | 96 --- packages/mco/utils/common.ts | 5 - packages/mco/utils/disaster-recovery.tsx | 94 ++- packages/mco/utils/index.ts | 1 - plugins/mco/console-plugin.json | 2 +- 29 files changed, 105 insertions(+), 2401 deletions(-) rename packages/mco/components/{disaster-recovery => }/create-dr-policy/create-dr-policy.scss (100%) rename packages/mco/components/{disaster-recovery => }/create-dr-policy/create-dr-policy.tsx (98%) rename packages/mco/components/{disaster-recovery => }/create-dr-policy/reducer.ts (100%) rename packages/mco/components/{disaster-recovery => }/create-dr-policy/select-cluster-list.scss (100%) rename packages/mco/components/{disaster-recovery => }/create-dr-policy/select-cluster-list.tsx (98%) rename packages/mco/components/{disaster-recovery => }/create-dr-policy/select-replication-type.tsx (99%) rename packages/mco/components/{disaster-recovery => }/create-dr-policy/selected-cluster-view.tsx (100%) rename packages/mco/components/{disaster-recovery => }/drpolicy-list-page/drpolicy-list-page.scss (100%) rename packages/mco/components/{disaster-recovery => }/drpolicy-list-page/drpolicy-list-page.spec.tsx (97%) rename packages/mco/components/{disaster-recovery => }/drpolicy-list-page/drpolicy-list-page.tsx (95%) rename packages/mco/components/{disaster-recovery => }/drpolicy-list-page/helper.ts (97%) delete mode 100644 packages/mco/components/modals/drpolicy-apps-apply/applicationset-handler.tsx delete mode 100644 packages/mco/components/modals/drpolicy-apps-apply/apply-policy-dual-list-selector.scss delete mode 100644 packages/mco/components/modals/drpolicy-apps-apply/apply-policy-dual-list-selector.tsx delete mode 100644 packages/mco/components/modals/drpolicy-apps-apply/apply-policy-modal.scss delete mode 100644 packages/mco/components/modals/drpolicy-apps-apply/apply-policy-modal.tsx delete mode 100644 packages/mco/components/modals/drpolicy-apps-apply/reducer.ts delete mode 100644 packages/mco/components/modals/drpolicy-apps-apply/subscriptions/application-selector.scss delete mode 100644 packages/mco/components/modals/drpolicy-apps-apply/subscriptions/application-selector.tsx delete mode 100644 packages/mco/components/modals/drpolicy-apps-apply/subscriptions/apply-policy-modal.scss delete mode 100644 packages/mco/components/modals/drpolicy-apps-apply/subscriptions/apply-policy-modal.tsx delete mode 100644 packages/mco/components/modals/drpolicy-apps-apply/utils.tsx delete mode 100644 packages/mco/utils/acm.ts diff --git a/locales/en/plugin__odf-console.json b/locales/en/plugin__odf-console.json index b27bd3a81..2800d05f9 100644 --- a/locales/en/plugin__odf-console.json +++ b/locales/en/plugin__odf-console.json @@ -19,8 +19,6 @@ "Reveal password": "Reveal password", "Pool name": "Pool name", "Volume mode": "Volume mode", - "Data policies": "Data policies", - "Disaster recovery": "Disaster recovery", "Create DRPolicy": "Create DRPolicy", "Get a quick recovery in a remote or secondary cluster with a disaster recovery (DR) policy": "Get a quick recovery in a remote or secondary cluster with a disaster recovery (DR) policy", "Policy name": "Policy name", @@ -47,6 +45,8 @@ "Replication policy": "Replication policy", "Sync schedule": "Sync schedule", "Information unavailable": "Information unavailable", + "Data policies": "Data policies", + "Disaster recovery": "Disaster recovery", "Validated": "Validated", "Not Validated": "Not Validated", "Application": "Application", @@ -266,27 +266,6 @@ "No matching application found": "No matching application found", "Namespace": "Namespace", "Type": "Type", - "There are no applications under Available list.": "There are no applications under Available list.", - "There are no more applications under Available list to protect.": "There are no more applications under Available list to protect.", - "No protected applications": "No protected applications", - "You can create applications from the <2> Create application set page.": "You can create applications from the <2> Create application set page.", - "There are no applications under the Protected list. Move applications from the available list to assign DR policy.": "There are no applications under the Protected list. Move applications from the available list to assign DR policy.", - "Available": "Available", - "{{ selected }} of {{ total }} applications selected": "{{ selected }} of {{ total }} applications selected", - "Protected": "Protected", - "Add label": "Add label", - "Cannot unassign the policy": "Cannot unassign the policy", - "You cannot unassign the policy as this action is not yet supported.": "You cannot unassign the policy as this action is not yet supported.", - "Assign policy to protect critical applications and ensure quick recovery. Unassign policy from an application when they no longer require to be managed.": "Assign policy to protect critical applications and ensure quick recovery. Unassign policy from an application when they no longer require to be managed.", - "Manage policy for ApplicationSets": "Manage policy for ApplicationSets", - "Save": "Save", - "Application search": "Application search", - "Select the applications for assigning the DRPolicy": "Select the applications for assigning the DRPolicy", - "Assign policy to Subscriptions": "Assign policy to Subscriptions", - "PVC label": "PVC label", - "A selector label to DR protect only specific PVCs within an application.": "A selector label to DR protect only specific PVCs within an application.", - "When multiple applications are selected, DR protection will be applied for all the PVCs under the application's namespace.": "When multiple applications are selected, DR protection will be applied for all the PVCs under the application's namespace.", - "Protect all PVCs within the application's namespace": "Protect all PVCs within the application's namespace", "Asynchronous": "Asynchronous", "Synchronous": "Synchronous", "minutes": "minutes", @@ -374,6 +353,7 @@ "Throughput": "Throughput", "Raw capacity": "Raw capacity", "Used": "Used", + "Available": "Available", "Available versus Used Capacity": "Available versus Used Capacity", "Used of {{capacity}}": "Used of {{capacity}}", "Not Available": "Not Available", @@ -478,6 +458,7 @@ "Try Again": "Try Again", "Finish": "Finish", "Go To PVC List": "Go To PVC List", + "Save": "Save", "BlockPool Update Form": "BlockPool Update Form", "Filesystem name": "Filesystem name", "Enter filesystem name": "Enter filesystem name", diff --git a/packages/mco/components/disaster-recovery/create-dr-policy/create-dr-policy.scss b/packages/mco/components/create-dr-policy/create-dr-policy.scss similarity index 100% rename from packages/mco/components/disaster-recovery/create-dr-policy/create-dr-policy.scss rename to packages/mco/components/create-dr-policy/create-dr-policy.scss diff --git a/packages/mco/components/disaster-recovery/create-dr-policy/create-dr-policy.tsx b/packages/mco/components/create-dr-policy/create-dr-policy.tsx similarity index 98% rename from packages/mco/components/disaster-recovery/create-dr-policy/create-dr-policy.tsx rename to packages/mco/components/create-dr-policy/create-dr-policy.tsx index b27dcb6c8..d515bf36c 100644 --- a/packages/mco/components/disaster-recovery/create-dr-policy/create-dr-policy.tsx +++ b/packages/mco/components/create-dr-policy/create-dr-policy.tsx @@ -30,9 +30,9 @@ import { REPLICATION_TYPE, ODFMCO_OPERATOR, HUB_CLUSTER_NAME, -} from '../../../constants'; -import { DRPolicyModel, MirrorPeerModel } from '../../../models'; -import { DRPolicyKind, MirrorPeerKind } from '../../../types'; +} from '../../constants'; +import { DRPolicyModel, MirrorPeerModel } from '../../models'; +import { DRPolicyKind, MirrorPeerKind } from '../../types'; import { drPolicyReducer, drPolicyInitialState, @@ -42,7 +42,7 @@ import { SelectClusterList } from './select-cluster-list'; import { DRReplicationType } from './select-replication-type'; import { SelectedCluster } from './selected-cluster-view'; import './create-dr-policy.scss'; -import '../../../style.scss'; +import '../../style.scss'; const fetchMirrorPeer = ( mirrorPeers: MirrorPeerKind[], diff --git a/packages/mco/components/disaster-recovery/create-dr-policy/reducer.ts b/packages/mco/components/create-dr-policy/reducer.ts similarity index 100% rename from packages/mco/components/disaster-recovery/create-dr-policy/reducer.ts rename to packages/mco/components/create-dr-policy/reducer.ts diff --git a/packages/mco/components/disaster-recovery/create-dr-policy/select-cluster-list.scss b/packages/mco/components/create-dr-policy/select-cluster-list.scss similarity index 100% rename from packages/mco/components/disaster-recovery/create-dr-policy/select-cluster-list.scss rename to packages/mco/components/create-dr-policy/select-cluster-list.scss diff --git a/packages/mco/components/disaster-recovery/create-dr-policy/select-cluster-list.tsx b/packages/mco/components/create-dr-policy/select-cluster-list.tsx similarity index 98% rename from packages/mco/components/disaster-recovery/create-dr-policy/select-cluster-list.tsx rename to packages/mco/components/create-dr-policy/select-cluster-list.tsx index 99c9b59c0..cacdf597e 100644 --- a/packages/mco/components/disaster-recovery/create-dr-policy/select-cluster-list.tsx +++ b/packages/mco/components/create-dr-policy/select-cluster-list.tsx @@ -33,9 +33,9 @@ import { ClusterClaimTypes, MANAGED_CLUSTER_JOINED, MANAGED_CLUSTER_CONDITION_AVAILABLE, -} from '../../../constants'; -import { ACMManagedClusterModel } from '../../../models'; -import { ACMManagedClusterKind } from '../../../types'; +} from '../../constants'; +import { ACMManagedClusterModel } from '../../models'; +import { ACMManagedClusterKind } from '../../types'; import { DRPolicyState, DRPolicyAction, diff --git a/packages/mco/components/disaster-recovery/create-dr-policy/select-replication-type.tsx b/packages/mco/components/create-dr-policy/select-replication-type.tsx similarity index 99% rename from packages/mco/components/disaster-recovery/create-dr-policy/select-replication-type.tsx rename to packages/mco/components/create-dr-policy/select-replication-type.tsx index 2d23ae31a..701cc0cf4 100644 --- a/packages/mco/components/disaster-recovery/create-dr-policy/select-replication-type.tsx +++ b/packages/mco/components/create-dr-policy/select-replication-type.tsx @@ -16,14 +16,14 @@ import { REPLICATION_DISPLAY_TEXT, TIME_UNITS, SYNC_SCHEDULE_DISPLAY_TEXT, -} from '../../../constants'; +} from '../../constants'; import { DRPolicyState, DRPolicyAction, DRPolicyActionType, Cluster, } from './reducer'; -import '../../../style.scss'; +import '../../style.scss'; type SyncScheduleProps = { state: DRPolicyState; diff --git a/packages/mco/components/disaster-recovery/create-dr-policy/selected-cluster-view.tsx b/packages/mco/components/create-dr-policy/selected-cluster-view.tsx similarity index 100% rename from packages/mco/components/disaster-recovery/create-dr-policy/selected-cluster-view.tsx rename to packages/mco/components/create-dr-policy/selected-cluster-view.tsx diff --git a/packages/mco/components/data-policies/data-policies-page.tsx b/packages/mco/components/data-policies/data-policies-page.tsx index 5bf470bad..5e11ce87a 100644 --- a/packages/mco/components/data-policies/data-policies-page.tsx +++ b/packages/mco/components/data-policies/data-policies-page.tsx @@ -5,7 +5,7 @@ import { HorizontalNav, useFlag } from '@openshift-console/dynamic-plugin-sdk'; import { Helmet } from 'react-helmet'; import { RouteComponentProps } from 'react-router'; import { ACM_OBSERVABILITY_FLAG, ADMIN_FLAG } from '../../constants'; -import { DRPolicyListPage } from '../disaster-recovery/drpolicy-list-page/drpolicy-list-page'; +import { DRPolicyListPage } from '../drpolicy-list-page/drpolicy-list-page'; import DRDashboard from '../mco-dashboard/data-policy/dr-dashboard'; type DataPoliciesPageProps = { diff --git a/packages/mco/components/disaster-recovery/drpolicy-list-page/drpolicy-list-page.scss b/packages/mco/components/drpolicy-list-page/drpolicy-list-page.scss similarity index 100% rename from packages/mco/components/disaster-recovery/drpolicy-list-page/drpolicy-list-page.scss rename to packages/mco/components/drpolicy-list-page/drpolicy-list-page.scss diff --git a/packages/mco/components/disaster-recovery/drpolicy-list-page/drpolicy-list-page.spec.tsx b/packages/mco/components/drpolicy-list-page/drpolicy-list-page.spec.tsx similarity index 97% rename from packages/mco/components/disaster-recovery/drpolicy-list-page/drpolicy-list-page.spec.tsx rename to packages/mco/components/drpolicy-list-page/drpolicy-list-page.spec.tsx index 74c0760df..63b9c6a12 100644 --- a/packages/mco/components/disaster-recovery/drpolicy-list-page/drpolicy-list-page.spec.tsx +++ b/packages/mco/components/drpolicy-list-page/drpolicy-list-page.spec.tsx @@ -52,7 +52,7 @@ jest.mock('react-router', () => ({ useHistory: jest.fn(() => []), })); -jest.doMock('../../../assets/EmptyPageIcon.png', () => ''); +jest.doMock('../../assets/EmptyPageIcon.png', () => ''); jest.mock('react-i18next', () => ({ useTranslation: (_ns: string) => ({ t: (children: any) => children }), diff --git a/packages/mco/components/disaster-recovery/drpolicy-list-page/drpolicy-list-page.tsx b/packages/mco/components/drpolicy-list-page/drpolicy-list-page.tsx similarity index 95% rename from packages/mco/components/disaster-recovery/drpolicy-list-page/drpolicy-list-page.tsx rename to packages/mco/components/drpolicy-list-page/drpolicy-list-page.tsx index f0ed3bb16..312770619 100644 --- a/packages/mco/components/disaster-recovery/drpolicy-list-page/drpolicy-list-page.tsx +++ b/packages/mco/components/drpolicy-list-page/drpolicy-list-page.tsx @@ -18,21 +18,21 @@ import { } from '@openshift-console/dynamic-plugin-sdk'; import { Trans } from 'react-i18next'; import { useHistory, useLocation } from 'react-router'; -import { HUB_CLUSTER_NAME } from '../../../constants'; +import { HUB_CLUSTER_NAME } from '../../constants'; import { ApplicationRefKind, getDRPolicyResourceObj, useApplicationsWatch, -} from '../../../hooks'; -import { DRPolicyModel } from '../../../models'; -import { DRPolicyKind } from '../../../types'; +} from '../../hooks'; +import { DRPolicyModel } from '../../models'; +import { DRPolicyKind } from '../../types'; import { getReplicationType, findAppsUsingDRPolicy, isDRPolicyValidated, -} from '../../../utils'; -import EmptyPage from '../../empty-state-page/empty-page'; -import { ConnectedApplicationsModal } from '../../modals/connected-apps-modal/connected-apps-modal'; +} from '../../utils'; +import EmptyPage from '../empty-state-page/empty-page'; +import { ConnectedApplicationsModal } from '../modals/connected-apps-modal/connected-apps-modal'; import { Header, kebabActionItems, tableColumnInfo } from './helper'; import './drpolicy-list-page.scss'; diff --git a/packages/mco/components/disaster-recovery/drpolicy-list-page/helper.ts b/packages/mco/components/drpolicy-list-page/helper.ts similarity index 97% rename from packages/mco/components/disaster-recovery/drpolicy-list-page/helper.ts rename to packages/mco/components/drpolicy-list-page/helper.ts index b6b1732af..a706caea8 100644 --- a/packages/mco/components/disaster-recovery/drpolicy-list-page/helper.ts +++ b/packages/mco/components/drpolicy-list-page/helper.ts @@ -2,7 +2,7 @@ import { ModalKeys } from '@odf/shared/modals/types'; import classNames from 'classnames'; import { TFunction } from 'i18next'; import { sortable, wrappable } from '@patternfly/react-table'; -import { Actions } from '../../../constants'; +import { Actions } from '../../constants'; export const Header = (t: TFunction) => [ { diff --git a/packages/mco/components/modals/drpolicy-apps-apply/applicationset-handler.tsx b/packages/mco/components/modals/drpolicy-apps-apply/applicationset-handler.tsx deleted file mode 100644 index 9eaa9a12b..000000000 --- a/packages/mco/components/modals/drpolicy-apps-apply/applicationset-handler.tsx +++ /dev/null @@ -1,225 +0,0 @@ -import * as React from 'react'; -import { useK8sGet } from '@odf/shared/hooks/k8s-get-hook'; -import { arrayify } from '@odf/shared/modals/EditLabelModal'; -import { getName, getNamespace, getAnnotations } from '@odf/shared/selectors'; -import { ListKind } from '@odf/shared/types'; -import { referenceForModel } from '@odf/shared/utils'; -import * as _ from 'lodash-es'; -import { HUB_CLUSTER_NAME } from '../../../constants'; -import { - ArgoApplicationSetModel, - ACMPlacementModel, - ACMPlacementDecisionModel, -} from '../../../models'; -import { - ArgoApplicationSetKind, - ACMPlacementKind, - ACMPlacementDecisionKind, - DRPlacementControlKind, - PlacementToAppSets, - PlacementToDrpcMap, -} from '../../../types'; -import { - matchClusters, - findPlacementNameFromAppSet, - findPlacementDecisionUsingPlacement, - getGVKFromObjectRef, -} from '../../../utils'; -import { ApplyPolicyAction, ApplyPolicyType } from './reducer'; - -const getAppSets = ( - pl: ACMPlacementKind, - appSets: ArgoApplicationSetKind[] -): string[] => - appSets - .filter( - (appSet: ArgoApplicationSetKind) => - getNamespace(appSet) === getNamespace(pl) && - findPlacementNameFromAppSet(appSet) === getName(pl) - ) - .map((appSet: ArgoApplicationSetKind) => getName(appSet)); - -const getPlsDecisionAndClusters = ( - pl: ACMPlacementKind, - plsDecisions: ACMPlacementDecisionKind[] -): [string, string[]] => { - const plsDecision = findPlacementDecisionUsingPlacement(pl, plsDecisions); - return [ - getName(plsDecision), - plsDecision?.status?.decisions.map((decision) => decision?.clusterName), - ]; -}; - -const getDRPlacementControl = ( - pl: ACMPlacementKind, - drplcontrols: DRPlacementControlKind[] -): [string, string, string[]] => { - const plDrpc = drplcontrols.find( - (drplcontrol) => - getNamespace(drplcontrol) === getNamespace(pl) && - drplcontrol?.spec?.placementRef?.name === getName(pl) && - referenceForModel(ACMPlacementModel) === - getGVKFromObjectRef(drplcontrol?.spec?.placementRef) - ); - return [ - getName(plDrpc), - plDrpc?.spec?.drPolicyRef?.name, - arrayify(plDrpc?.spec?.pvcSelector?.matchLabels), - ]; -}; - -const setDrpcPvcLabels = ( - pls: ACMPlacementKind[], - drplcontrols: DRPlacementControlKind[], - drpcPvcLabels: PlacementToDrpcMap -): void => - pls.forEach((pl: ACMPlacementKind) => { - const [drpcName, drPolicyName, existingLabels] = getDRPlacementControl( - pl, - drplcontrols - ); - if (!!drpcName) { - !drpcPvcLabels.hasOwnProperty(getNamespace(pl)) && - (drpcPvcLabels[pl?.metadata.namespace] = {}); - drpcPvcLabels[getNamespace(pl)][getName(pl)] = { - drpcName, - drPolicyName, - existingLabels, - updateLabels: existingLabels, - }; - } - }); - -const getProtectedAndAvailableResources = ( - appSets: ArgoApplicationSetKind[], - pls: ACMPlacementKind[], - plsDecisions: ACMPlacementDecisionKind[], - drClusterNames: string[], - drPolicyName: string, - drpcPvcLabels: PlacementToDrpcMap -) => { - const protectedResources: PlacementToAppSets[] = []; - const availableResources: PlacementToAppSets[] = []; - - pls.forEach((pl: ACMPlacementKind) => { - const [placementDecision, decisionClusters] = getPlsDecisionAndClusters( - pl, - plsDecisions - ); - - const isAlreadyProtected: boolean = - !!drpcPvcLabels[getNamespace(pl)]?.[getName(pl)]?.drpcName; - const drPolicyRefName = - drpcPvcLabels[getNamespace(pl)]?.[getName(pl)]?.drPolicyName; - const isDRPolicyMatching = isAlreadyProtected - ? drPolicyRefName === drPolicyName - : true; - if ( - !!matchClusters(drClusterNames, decisionClusters) && - isDRPolicyMatching - ) { - const resourcesMapRef: PlacementToAppSets[] = isAlreadyProtected - ? protectedResources - : availableResources; - const appSetNames: string[] = getAppSets(pl, appSets); - appSetNames.forEach((appSetName) => - resourcesMapRef.push({ - namespace: getNamespace(pl), - placement: getName(pl), - havePlacementAnnotations: !_.isEmpty(getAnnotations(pl)), - isAlreadyProtected, - appSetName, - placementDecision, - decisionClusters, - selected: false, - isVisible: true, - }) - ); - } - }); - - return { protectedResources, availableResources }; -}; - -export const useAppSetTypeResources = ( - drplcontrols: DRPlacementControlKind[], - drClusterNames: string[], - drPolicyName: string, - dispatch: React.Dispatch -) => { - const [applicationSets, applicationSetsLoaded, applicationSetsError] = - useK8sGet>( - ArgoApplicationSetModel, - null, - null, - HUB_CLUSTER_NAME - ); - const [placements, placementsLoaded, placementsError] = useK8sGet< - ListKind - >(ACMPlacementModel, null, null, HUB_CLUSTER_NAME); - const [ - placementDecisions, - placementDecisionsLoaded, - placementDecisionsError, - ] = useK8sGet>( - ACMPlacementDecisionModel, - null, - null, - HUB_CLUSTER_NAME - ); - - /** - * setting up the initial state (should be set only once) -- - * 1. with one on one mapping of Placement to DRPlacementControl per Namespace. - * 2. with one on one mapping of Placement to ApplicationSets/PlacementDisicions per Namespace. - * */ - React.useEffect(() => { - const pls: ACMPlacementKind[] = placements?.items; - - // 1. Placement to DRPlacementControl mapping - const drpcPvcLabels: PlacementToDrpcMap = {}; - if (!!drplcontrols?.length && !!pls?.length) { - setDrpcPvcLabels(pls, drplcontrols, drpcPvcLabels); - dispatch({ - type: ApplyPolicyType.SET_DRPC_PVC_LABELS, - payload: drpcPvcLabels, - }); - } - - // 2. Placement to ApplicationSets/PlacementDisicions mapping - const appSets: ArgoApplicationSetKind[] = applicationSets?.items; - const plsDecisions: ACMPlacementDecisionKind[] = placementDecisions?.items; - if (!!appSets?.length && !!pls?.length && !!plsDecisions?.length) { - const { protectedResources, availableResources } = - getProtectedAndAvailableResources( - appSets, - pls, - plsDecisions, - drClusterNames, - drPolicyName, - drpcPvcLabels - ); - dispatch({ - type: ApplyPolicyType.SET_PROTECTED_RESOURCES, - payload: protectedResources, - }); - dispatch({ - type: ApplyPolicyType.SET_AVAILABLE_RESOURCES, - payload: availableResources, - }); - } - }, [ - drplcontrols, - applicationSets, - placements, - placementDecisions, - drClusterNames, - drPolicyName, - dispatch, - ]); - - return [ - applicationSetsLoaded && placementsLoaded && placementDecisionsLoaded, - applicationSetsError || placementsError || placementDecisionsError, - ]; -}; diff --git a/packages/mco/components/modals/drpolicy-apps-apply/apply-policy-dual-list-selector.scss b/packages/mco/components/modals/drpolicy-apps-apply/apply-policy-dual-list-selector.scss deleted file mode 100644 index e89a0f0b2..000000000 --- a/packages/mco/components/modals/drpolicy-apps-apply/apply-policy-dual-list-selector.scss +++ /dev/null @@ -1,31 +0,0 @@ -.mco-apply-policy-dual-selector { - &__listRow{ - display: flex; - flex-direction: column; - text-align: start; - } - &__listItem{ - display: flex; - padding: 0; - } - - &__ItemDescription--color { - color: var(--pf-global--Color--200); - } - &__button { - padding: 0; - display: flex; - background-color: transparent; - border: none; - } - &__listItem--text { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - flex-grow: 1; - color: var(--pf-c-dual-list-selector__item-text--Color); - } - &__ExpandItem--indent { - padding-left: var(--pf-global--spacer--lg); - } - } diff --git a/packages/mco/components/modals/drpolicy-apps-apply/apply-policy-dual-list-selector.tsx b/packages/mco/components/modals/drpolicy-apps-apply/apply-policy-dual-list-selector.tsx deleted file mode 100644 index db61b5212..000000000 --- a/packages/mco/components/modals/drpolicy-apps-apply/apply-policy-dual-list-selector.tsx +++ /dev/null @@ -1,412 +0,0 @@ -import * as React from 'react'; -import { CustomDualListSelector } from '@odf/shared/generic/custom-dual-list-selector'; -import { SelectorInput } from '@odf/shared/modals/Selector'; -import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook'; -import AngleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-right-icon'; -import classNames from 'classnames'; -import { Trans } from 'react-i18next'; -import { - DualListSelectorPane, - DualListSelectorList, - DualListSelectorListItem, - EmptyState, - EmptyStateIcon, - EmptyStateBody, - Title, - Label, - FormGroup, - Form, -} from '@patternfly/react-core'; -import { - ExclamationTriangleIcon, - LockIcon, - CheckCircleIcon, -} from '@patternfly/react-icons'; -import { PlacementToAppSets, PlacementToDrpcMap } from '../../../types'; -import { - ApplyPolicyInitialState, - ApplyPolicyAction, - ApplyPolicyType, -} from './reducer'; -import './apply-policy-dual-list-selector.scss'; - -type ApplyPolicyDualListSelectorProps = { - state: ApplyPolicyInitialState; - dispatch: React.Dispatch; -}; - -type DualListSelectorAvailablePaneProps = { - availableOptions: PlacementToAppSets[]; - chosenOptions: PlacementToAppSets[]; - buildSearchInput: (isAvailable: boolean) => JSX.Element; - onOptionSelect: ( - _event: React.MouseEvent | React.ChangeEvent | React.KeyboardEvent, - index: number, - isChosen: boolean - ) => void; -}; - -type DualListSelectorChosenPaneProps = Omit< - DualListSelectorAvailablePaneProps, - 'availableOptions' -> & { - chosenOptions: PlacementToAppSets[]; - state: ApplyPolicyInitialState; - dispatch: React.Dispatch; -}; - -type DualListSelectorChosenListItemProps = { - option: PlacementToAppSets; - optionIndex: number; - drpcPvcLabels: PlacementToDrpcMap; - setDrpcPvcLabels: (options: PlacementToDrpcMap) => void; -} & Pick; - -type DualListSelectorExpandListItemProps = { - id: string; - appName: string; - appNamespace: string; - isAlreadyProtected: boolean; - children: React.ReactNode; -}; - -const defaultLabelsState = { - drpcName: undefined, - existingLabels: [], - updateLabels: [], -}; - -type EmptyPaneProp = { - title: string; - children?: React.ReactNode; - icon: React.ComponentClass; -}; - -type PaneMessageProps = { - totalChosenOptions: number; - leftPane?: boolean; -}; - -const PaneMessage: React.FC = ({ - leftPane, - totalChosenOptions, -}) => { - const { t } = useCustomTranslation(); - const title = leftPane - ? !totalChosenOptions - ? t('There are no applications under Available list.') - : t('There are no more applications under Available list to protect.') - : t('No protected applications'); - const icon = leftPane - ? !totalChosenOptions - ? ExclamationTriangleIcon - : CheckCircleIcon - : LockIcon; - const message = leftPane ? ( - - You can create applications from the{' '} - Create application set page. - - ) : ( - t( - 'There are no applications under the Protected list. Move applications from the available list to assign DR policy.' - ) - ); - - return ( - - {message} - - ); -}; - -const EmptyPane: React.FC = ({ title, children, icon }) => { - return ( - - - {title} - {children} - - ); -}; - -const DualListSelectorAvailablePane: React.FC = - ({ availableOptions, chosenOptions, buildSearchInput, onOptionSelect }) => { - const { t } = useCustomTranslation(); - - const totalAvailableOptions: number = availableOptions.filter( - (option) => option.isVisible - ).length; - const totalChosenOptions: number = chosenOptions.filter( - (option) => option.isVisible - ).length; - - return ( - option.selected && option.isVisible - ).length, - total: availableOptions.filter((option) => option.isVisible).length, - })} - searchInput={buildSearchInput(true)} - > - - {!totalAvailableOptions ? ( - - ) : ( - availableOptions.map((option, optionIndex) => - option.isVisible ? ( - onOptionSelect(e, optionIndex, false)} - translate={false} - > -
-
- - {option.appSetName} - - {option.isAlreadyProtected && ( - - - - )} -
-
- {option.namespace} -
-
-
- ) : null - ) - )} -
-
- ); - }; - -const DualListSelectorExpandListItem: React.FunctionComponent = - ({ - children, - id, - appName, - appNamespace, - isAlreadyProtected, - ...props - }: DualListSelectorExpandListItemProps) => { - const { t } = useCustomTranslation(); - const [isExpanded, setIsExpanded] = React.useState(false); - - return ( -
  • -
    - - - {appName} - - - {isAlreadyProtected && ( - - )} - -
    -
    - {appNamespace} -
    - -
  • - ); - }; - -const DualListSelectorChosenListItem: React.FC = - ({ - option, - optionIndex, - onOptionSelect, - drpcPvcLabels, - setDrpcPvcLabels, - }) => { - const { t } = useCustomTranslation(); - const labels: string[] = - drpcPvcLabels?.[option.namespace]?.[option.placement]?.updateLabels || []; - - const setLabels = (l: string[]) => { - const options: PlacementToDrpcMap = { ...drpcPvcLabels }; - - !options.hasOwnProperty(option.namespace) && - (options[option.namespace] = { - [option.placement]: { ...defaultLabelsState }, - }); - !options[option.namespace].hasOwnProperty(option.placement) && - (options[option.namespace][option.placement] = { - ...defaultLabelsState, - }); - - options[option.namespace][option.placement].updateLabels = [...l]; - setDrpcPvcLabels({ ...options }); - }; - - return ( - onOptionSelect(e, optionIndex, true)} - translate={false} - > - -
    - e.stopPropagation()} - onClick={(e) => e.stopPropagation()} - > - setLabels(l)} - tags={labels} - disabled={option.isAlreadyProtected} - /> - -
    -
    -
    - ); - }; - -const DualListSelectorChosenPane: React.FC = ({ - chosenOptions, - buildSearchInput, - onOptionSelect, - state, - dispatch, -}) => { - const { t } = useCustomTranslation(); - - const drpcPvcLabels: PlacementToDrpcMap = state.drpcPvcLabels[state.appType]; - const setDrpcPvcLabels = (options: PlacementToDrpcMap) => - dispatch({ - type: ApplyPolicyType.SET_DRPC_PVC_LABELS, - payload: options, - }); - - const totalChosenOptions: number = chosenOptions.filter( - (option) => option.isVisible - ).length; - - return ( - option.selected && option.isVisible - ).length, - total: totalChosenOptions, - })} - searchInput={buildSearchInput(false)} - isChosen - > - - {!totalChosenOptions ? ( - - ) : ( - chosenOptions.map((option, optionIndex) => - option.isVisible ? ( - - ) : null - ) - )} - - - ); -}; - -export const ApplyPolicyDualListSelector: React.FC = - ({ state, dispatch }) => { - const availableOptions: PlacementToAppSets[] = - state.availableResources[state.appType]; - const chosenOptions: PlacementToAppSets[] = - state.protectedResources[state.appType]; - const setAvailableOptions = (options: PlacementToAppSets[]) => - dispatch({ - type: ApplyPolicyType.SET_AVAILABLE_RESOURCES, - payload: options, - }); - const setChosenOptions = (options: PlacementToAppSets[]) => - dispatch({ - type: ApplyPolicyType.SET_PROTECTED_RESOURCES, - payload: options, - }); - - return ( - - option.appSetName.toLowerCase().includes(value.toLowerCase()) - } - state={state} - dispatch={dispatch} - /> - ); - }; diff --git a/packages/mco/components/modals/drpolicy-apps-apply/apply-policy-modal.scss b/packages/mco/components/modals/drpolicy-apps-apply/apply-policy-modal.scss deleted file mode 100644 index 8e1b82996..000000000 --- a/packages/mco/components/modals/drpolicy-apps-apply/apply-policy-modal.scss +++ /dev/null @@ -1,7 +0,0 @@ -.mco-apply-policy-modal { - &__modal { - max-width: 1100px; - padding: var(--pf-global--spacer--sm) var(--pf-global--spacer--sm) - var(--pf-global--spacer--xs) var(--pf-global--spacer--sm); - } -} diff --git a/packages/mco/components/modals/drpolicy-apps-apply/apply-policy-modal.tsx b/packages/mco/components/modals/drpolicy-apps-apply/apply-policy-modal.tsx deleted file mode 100644 index 9554323b0..000000000 --- a/packages/mco/components/modals/drpolicy-apps-apply/apply-policy-modal.tsx +++ /dev/null @@ -1,319 +0,0 @@ -import * as React from 'react'; -import { LoadingInline } from '@odf/shared/generic/Loading'; -import { StatusBox } from '@odf/shared/generic/status-box'; -import { useDeepCompareMemoize } from '@odf/shared/hooks/deep-compare-memoize'; -import { useK8sGet } from '@odf/shared/hooks/k8s-get-hook'; -import { - ModalBody, - ModalFooter, - CommonModalProps, -} from '@odf/shared/modals/Modal'; -import { getName } from '@odf/shared/selectors'; -import { ListKind, K8sResourceKind } from '@odf/shared/types'; -import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook'; -import { K8sModel } from '@openshift-console/dynamic-plugin-sdk/lib/api/common-types'; -import * as _ from 'lodash-es'; -import { - Alert, - Text, - Modal, - Button, - ButtonVariant, - TextVariants, - AlertVariant, - TextContent, -} from '@patternfly/react-core'; -import { HUB_CLUSTER_NAME, APPLICATION_TYPE } from '../../../constants'; -import { DRPlacementControlModel } from '../../../models'; -import { - DRPlacementControlKind, - PlacementToDrpcMap, - PlacementToAppSets, -} from '../../../types'; -import { DRPolicyKind } from '../../../types'; -import { useAppSetTypeResources } from './applicationset-handler'; -import { ApplyPolicyDualListSelector } from './apply-policy-dual-list-selector'; -import { - ApplyPolicyReducer, - applyPolicyInitialState, - ApplyPolicyType, - MessageType, -} from './reducer'; -import { getAvailablePanelPromises, getProtectedPanelPromises } from './utils'; -import './apply-policy-modal.scss'; -import '../../../style.scss'; - -type ApplyModalExtraProps = { - resource: DRPolicyKind; - resourceModel: K8sModel; -}; - -const ApplyDRPolicyModal: React.FC> = ( - props -) => { - const { t } = useCustomTranslation(); - const { closeModal, isOpen, extraProps } = props; - const { resource } = extraProps; - - const drPolicyName = getName(resource); - const drClusterNames = useDeepCompareMemoize( - resource?.spec?.drClusters ?? [] - ); - - const [state, dispatch] = React.useReducer( - ApplyPolicyReducer, - applyPolicyInitialState - ); - - const [drpcs, drpcsLoaded, drpcsError] = useK8sGet< - ListKind - >(DRPlacementControlModel, null, null, HUB_CLUSTER_NAME); - - const [appSetResourcesLoaded, appSetResourcesError] = useAppSetTypeResources( - drpcs?.items, - drClusterNames, - drPolicyName, - dispatch - ); - - const isSubmitDisabled: boolean = React.useMemo(() => { - const availableResources: PlacementToAppSets[] = - state.availableResources[state.appType]; - const protectedResources: PlacementToAppSets[] = - state.protectedResources[state.appType]; - const drpcPvcLabels: PlacementToDrpcMap = - state.drpcPvcLabels[state.appType]; - - // if any protected resource present on available panel => we need to delete DRPC, enable "Save changes" in this case - const applicationsToDisable = availableResources.some( - (availableResource: PlacementToAppSets) => - availableResource.isAlreadyProtected - ); - - // if any non-protected resource present on protected panel => we need to create DRPC, enable "Save" in this - const [applicationsToEnable, isLablesAreNotEmpty] = - protectedResources.reduce( - (acc, protectedResource: PlacementToAppSets) => { - const [applications, isLabelFound] = acc; - const updateLabels = - drpcPvcLabels?.[protectedResource.namespace]?.[ - protectedResource.placement - ]?.updateLabels || []; - acc = !protectedResource.isAlreadyProtected - ? [ - [...applications, protectedResource], - isLabelFound && !!updateLabels.length, - ] - : acc; - return acc; - }, - [[], true] - ); - - const changesFoundOnRightSide = !!applicationsToEnable.length; - const noIssuesFoundOnRightSide = isLablesAreNotEmpty; - const changesFoundOnLeftSide = !!applicationsToDisable; - // we dont have any left side check, By default keeping it as true - const noIssuesFoundOnLeftSide = true; - - if (!state.message.text && !!changesFoundOnLeftSide) - dispatch({ - type: ApplyPolicyType.SET_MESSAGE, - payload: { - text: t('Cannot unassign the policy'), - description: t( - 'You cannot unassign the policy as this action is not yet supported.' - ), - variant: AlertVariant.info, - type: MessageType.UNSUPPORTED_OPERATION, - }, - }); - else if ( - state.message.type === MessageType.UNSUPPORTED_OPERATION && - !changesFoundOnLeftSide - ) - dispatch({ - type: ApplyPolicyType.SET_MESSAGE, - payload: { - text: '', - variant: AlertVariant.info, - }, - }); - - return !( - changesFoundOnRightSide && - !changesFoundOnLeftSide && - noIssuesFoundOnRightSide && - noIssuesFoundOnLeftSide - ); - }, [state, dispatch, t]); - - const allLoaded = drpcsLoaded && appSetResourcesLoaded; - const anyError = drpcsError || appSetResourcesError; - - const setLoading = (isLoading: boolean) => - dispatch({ - type: ApplyPolicyType.SET_LOADING, - payload: isLoading, - }); - const setError = (errorMsg: string) => - dispatch({ - type: ApplyPolicyType.SET_MESSAGE, - payload: { - text: errorMsg, - variant: AlertVariant.danger, - }, - }); - - const submit = (_event: React.FormEvent) => { - setLoading(true); - const promises: Promise[] = []; - - _.forEach(APPLICATION_TYPE, (type) => { - const placementsSet: Set = new Set(); - const availableResources: PlacementToAppSets[] = - state.availableResources[type]; - const protectedResources: PlacementToAppSets[] = - state.protectedResources[type]; - const drpcPvcLabels: PlacementToDrpcMap = state.drpcPvcLabels[type]; - - /** available panel -- 1. remove annotation from Placement and delete existing DRPC */ - availableResources.forEach((availableResource: PlacementToAppSets) => { - // 1. patching Placement and deleting DRPC - if ( - availableResource.isAlreadyProtected && - !placementsSet.has( - availableResource.namespace + '%#%' + availableResource.placement - ) - ) { - placementsSet.add( - availableResource.namespace + '%#%' + availableResource.placement - ); - promises.push( - ...getAvailablePanelPromises(availableResource, drpcPvcLabels) - ); - } - }); - - /** protected panel -- 1. add annotation to Placement and create new DRPC - -- 2. patch existing DRPC with edited/updated PVC labels */ - protectedResources.forEach((protectedResource: PlacementToAppSets) => { - // 1. patching Placement and creating DRPC - if ( - !protectedResource.isAlreadyProtected && - !placementsSet.has( - protectedResource.namespace + '%#%' + protectedResource.placement - ) - ) { - placementsSet.add( - protectedResource.namespace + '%#%' + protectedResource.placement - ); - promises.push( - ...getProtectedPanelPromises( - drPolicyName, - drClusterNames, - protectedResource, - drpcPvcLabels - ) - ); - } - - // TODO: Allow PVC selector update when the ramen is supporting - /* // 2. updating PVC labels only if: DRPC is neither deleted nor created in previous steps - if ( - protectedResource.isAlreadyProtected && - !placementsSet.has( - protectedResource.namespace + '%#%' + protectedResource.placement - ) - ) { - const existingLabels = - drpcPvcLabels?.[protectedResource.namespace]?.[ - protectedResource.placement - ]?.existingLabels || []; - const updateLabels = - drpcPvcLabels?.[protectedResource.namespace]?.[ - protectedResource.placement - ]?.updateLabels || []; - // update only if original and updated labels are different - areLabelsDifferent(existingLabels, updateLabels) && - promises.push( - ...getUpdatedDRPCPromise(protectedResource, drpcPvcLabels) - ); - } */ - }); - }); - - Promise.all(promises) - .then(() => { - closeModal(); - }) - .catch((error) => { - setError(error?.message); - setLoading(false); - }); - }; - - return ( - - - {t( - 'Assign policy to protect critical applications and ensure quick recovery. Unassign policy from an application when they no longer require to be managed.' - )} - - {drPolicyName} - - } - title={t('Manage policy for ApplicationSets')} - isOpen={isOpen} - onClose={closeModal} - className="mco-apply-policy-modal__modal" - > - {!allLoaded && !anyError ? ( - - ) : ( - <> - - - {!!state.message.text && ( - - {state.message?.description} - - )} - - - - {!state.loading ? ( - - ) : ( - - )} - - - )} - - ); -}; - -export default ApplyDRPolicyModal; diff --git a/packages/mco/components/modals/drpolicy-apps-apply/reducer.ts b/packages/mco/components/modals/drpolicy-apps-apply/reducer.ts deleted file mode 100644 index 6e3bce2e0..000000000 --- a/packages/mco/components/modals/drpolicy-apps-apply/reducer.ts +++ /dev/null @@ -1,140 +0,0 @@ -import { AlertVariant } from '@patternfly/react-core'; -import { APPLICATION_TYPE } from '../../../constants'; -import { PlacementToAppSets, PlacementToDrpcMap } from '../../../types'; - -export enum MessageType { - UNSUPPORTED_OPERATION, -} - -type Message = { - text: string; - description?: string; - type?: MessageType; - variant: AlertVariant; -}; - -export enum ApplyPolicyType { - SET_APP_TYPE = 'SET_APP_TYPE', - SET_LOADING = 'SET_LOADING', - SET_MESSAGE = 'SET_MESSAGE', - SET_PROTECTED_RESOURCES = 'SET_PROTECTED_RESOURCES', - SET_AVAILABLE_RESOURCES = 'SET_AVAILABLE_RESOURCES', - SET_DRPC_PVC_LABELS = 'SET_DRPC_PVC_LABELS', -} - -export type ApplyPolicyInitialState = { - appType: APPLICATION_TYPE; - protectedResources: { - [APPLICATION_TYPE.APPSET]: PlacementToAppSets[]; - }; - availableResources: { - [APPLICATION_TYPE.APPSET]: PlacementToAppSets[]; - }; - drpcPvcLabels: { - [APPLICATION_TYPE.APPSET]: PlacementToDrpcMap; - }; - loading: boolean; - message: Message; -}; - -export const applyPolicyInitialState: ApplyPolicyInitialState = { - appType: APPLICATION_TYPE.APPSET, - protectedResources: { - [APPLICATION_TYPE.APPSET]: [], - }, - availableResources: { - [APPLICATION_TYPE.APPSET]: [], - }, - drpcPvcLabels: { - [APPLICATION_TYPE.APPSET]: {}, - }, - loading: false, - message: { - text: '', - variant: AlertVariant.info, - }, -}; - -export type ApplyPolicyAction = - | { - type: ApplyPolicyType.SET_APP_TYPE; - payload: APPLICATION_TYPE; - } - | { - type: ApplyPolicyType.SET_LOADING; - payload: boolean; - } - | { - type: ApplyPolicyType.SET_MESSAGE; - payload: Message; - } - | { - type: ApplyPolicyType.SET_PROTECTED_RESOURCES; - payload: PlacementToAppSets[]; - } - | { - type: ApplyPolicyType.SET_AVAILABLE_RESOURCES; - payload: PlacementToAppSets[]; - } - | { - type: ApplyPolicyType.SET_DRPC_PVC_LABELS; - payload: PlacementToDrpcMap; - }; - -export const ApplyPolicyReducer = ( - state: ApplyPolicyInitialState, - action: ApplyPolicyAction -) => { - switch (action.type) { - case ApplyPolicyType.SET_APP_TYPE: { - return { - ...state, - appType: action.payload, - }; - } - case ApplyPolicyType.SET_LOADING: { - return { - ...state, - loading: action.payload, - }; - } - case ApplyPolicyType.SET_MESSAGE: { - return { - ...state, - message: action.payload, - }; - } - case ApplyPolicyType.SET_PROTECTED_RESOURCES: { - const protectedResources = { - ...state.protectedResources, - [state.appType]: action.payload, - }; - return { - ...state, - protectedResources, - }; - } - case ApplyPolicyType.SET_AVAILABLE_RESOURCES: { - const availableResources = { - ...state.availableResources, - [state.appType]: action.payload, - }; - return { - ...state, - availableResources, - }; - } - case ApplyPolicyType.SET_DRPC_PVC_LABELS: { - const drpcPvcLabels = { - ...state.drpcPvcLabels, - [state.appType]: action.payload, - }; - return { - ...state, - drpcPvcLabels, - }; - } - default: - return state; - } -}; diff --git a/packages/mco/components/modals/drpolicy-apps-apply/subscriptions/application-selector.scss b/packages/mco/components/modals/drpolicy-apps-apply/subscriptions/application-selector.scss deleted file mode 100644 index ac967f1de..000000000 --- a/packages/mco/components/modals/drpolicy-apps-apply/subscriptions/application-selector.scss +++ /dev/null @@ -1,22 +0,0 @@ -.mco-subs-application-selector { - &__box { - border: var(--pf-global--BorderWidth--sm) solid - var(--pf-global--BorderColor--100); - } - - &__checkboxtree { - border-top: var(--pf-global--BorderWidth--sm) solid - var(--pf-global--BorderColor--100); - max-height: 12.5rem; - min-height: 12.5rem; - overflow-y: auto; - } - - &__bullseye { - min-height: 12.5rem; - } - - &__button { - padding: 0; - } -} diff --git a/packages/mco/components/modals/drpolicy-apps-apply/subscriptions/application-selector.tsx b/packages/mco/components/modals/drpolicy-apps-apply/subscriptions/application-selector.tsx deleted file mode 100644 index 3d275c14e..000000000 --- a/packages/mco/components/modals/drpolicy-apps-apply/subscriptions/application-selector.tsx +++ /dev/null @@ -1,256 +0,0 @@ -import * as React from 'react'; -import { getName } from '@odf/shared/selectors'; -import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook'; -import { - TreeView, - TreeViewDataItem, - Toolbar, - ToolbarContent, - ToolbarItem, - Text, - TextVariants, - TextContent, - SearchInput, - Bullseye, -} from '@patternfly/react-core'; -import { AppToPlacementType } from '../../../../types'; -import './application-selector.scss'; - -type TreeViewDataItemMap = { - checkedItems: TreeViewDataItem[]; -}; - -type BadgeProps = { - subNames: string[]; - plsRule: string; -}; - -type ApplicationSelectorProps = { - applicationToPlacementMap: AppToPlacementType; - selectedNames: TreeViewDataItemMap; - setSelectedNames: React.Dispatch>; -}; - -const filterItems = (item: TreeViewDataItem, checkedItem: TreeViewDataItem) => { - if (item.id === checkedItem.id) { - return true; - } - - if (item.children) { - return ( - (item.children = item.children - .map((opt) => Object.assign({}, opt)) - .filter((child) => filterItems(child, checkedItem))).length > 0 - ); - } -}; - -const flattenTree = (tree: TreeViewDataItem[]) => { - let result: TreeViewDataItem[] = []; - tree.forEach((item) => { - result.push(item); - if (item.children) { - result = result.concat(flattenTree(item.children)); - } - }); - return result; -}; - -// Helper functions -const isChecked = ( - dataItem: TreeViewDataItem, - selectedNames: TreeViewDataItemMap -) => selectedNames.checkedItems.some((item) => item.id === dataItem.id); - -const areAllDescendantsChecked = ( - dataItem: TreeViewDataItem, - selectedNames: TreeViewDataItemMap -) => - dataItem.children - ? dataItem.children.every((child) => - areAllDescendantsChecked(child, selectedNames) - ) - : isChecked(dataItem, selectedNames); - -const areSomeDescendantsChecked = ( - dataItem: TreeViewDataItem, - selectedNames: TreeViewDataItemMap -) => - dataItem.children - ? dataItem.children.some((child) => - areSomeDescendantsChecked(child, selectedNames) - ) - : isChecked(dataItem, selectedNames); - -const mapTree = ( - item: TreeViewDataItem, - selectedNames: TreeViewDataItemMap -) => { - // Reset checked properties to be updated - item.checkProps.checked = false; - const hasCheck = areAllDescendantsChecked(item, selectedNames); - const hasPartialCheck = areSomeDescendantsChecked(item, selectedNames); - if (hasCheck) { - item.checkProps.checked = true; - } else if (hasPartialCheck) { - item.checkProps.checked = null; - } - if (item.children) { - return { - ...item, - children: item.children.map((child) => mapTree(child, selectedNames)), - }; - } - return item; -}; - -const Badge: React.FC = (props) => { - const { subNames, plsRule } = props; - - return ( - <> - {subNames.slice(0, subNames.length).map((subName) => ( -

    {subName}

    - ))} - - {plsRule} - - - ); -}; - -const filterOptions = (option: TreeViewDataItem, searchValue: string) => - option?.name?.toString()?.toLowerCase()?.includes(searchValue.toLowerCase()); - -export const ApplicationSelector: React.FC = ( - props -) => { - const { applicationToPlacementMap, selectedNames, setSelectedNames } = props; - - const { t } = useCustomTranslation(); - const [options, setOptions] = React.useState([]); - const [filteredOptions, setFilteredOptions] = React.useState< - TreeViewDataItem[] - >([]); - const [searchAppName, setSearchAppName] = React.useState(''); - - React.useEffect(() => { - const tempItem: TreeViewDataItem[] = []; - Object.keys(applicationToPlacementMap).forEach((appUniqueName) => { - const childerns: TreeViewDataItem[] = []; - Object.keys(applicationToPlacementMap[appUniqueName]?.placements).forEach( - (placementUniqueName) => { - const placementName = - applicationToPlacementMap[appUniqueName].placements[ - placementUniqueName - ]; - const { subscriptions, placement } = placementName || {}; - const subNames = subscriptions?.map(getName); - childerns.push({ - name: ( - - ), - id: `${appUniqueName}:${placementUniqueName}`, - checkProps: { checked: false }, - }); - } - ); - tempItem.push({ - name: getName(applicationToPlacementMap?.[appUniqueName]?.application), - id: appUniqueName, - checkProps: { checked: false }, - children: childerns, - customBadgeContent: ' ', - }); - setOptions(tempItem); - }); - }, [applicationToPlacementMap]); - - React.useEffect(() => { - searchAppName === '' && setFilteredOptions(options); - }, [options, searchAppName]); - - const getCheckedItems = React.useCallback( - (treeViewItem) => - filteredOptions - .map((opt) => Object.assign({}, opt)) - .filter((item) => filterItems(item, treeViewItem)), - [filteredOptions] - ); - - const onCheck = (evt, treeViewItem) => { - const checked = evt.target.checked; - const checkedItemTree = getCheckedItems(treeViewItem); - const flatCheckedItems = flattenTree(checkedItemTree); - setSelectedNames((prevState) => ({ - checkedItems: checked - ? prevState.checkedItems.concat( - flatCheckedItems.filter( - (item) => !prevState.checkedItems.some((i) => i.id === item.id) - ) - ) - : prevState.checkedItems.filter( - (item) => !flatCheckedItems.some((i) => i.id === item.id) - ), - })); - }; - - // **Note: PatternFly change the fn signature - // From: (value: string, event: React.FormEvent) => void - // To: (_event: React.FormEvent, value: string) => void - // both cases need to be handled for backwards compatibility - const onSearch = (input: any) => { - const searchValue = - typeof input === 'string' - ? input - : (input.target as HTMLInputElement)?.value; - setSearchAppName(searchValue); - if (searchValue !== '') { - setFilteredOptions( - options?.filter((option) => filterOptions(option, searchValue)) ?? [] - ); - } - }; - - const mapped = filteredOptions?.map((item) => mapTree(item, selectedNames)); - - return ( -
    - - - - setSearchAppName('')} - /> - - - - {Object.keys(filteredOptions)?.length === 0 ? ( - - {t('No matching application found')} - - ) : ( - - )} -
    - ); -}; diff --git a/packages/mco/components/modals/drpolicy-apps-apply/subscriptions/apply-policy-modal.scss b/packages/mco/components/modals/drpolicy-apps-apply/subscriptions/apply-policy-modal.scss deleted file mode 100644 index 3c13b4353..000000000 --- a/packages/mco/components/modals/drpolicy-apps-apply/subscriptions/apply-policy-modal.scss +++ /dev/null @@ -1,25 +0,0 @@ -.mco-subs-apply-policy-modal { - &__form { - padding: var(--pf-global--spacer--sm) var(--pf-global--spacer--sm) - var(--pf-global--spacer--xs) var(--pf-global--spacer--sm); - } - - &__description { - margin-bottom: var(--pf-global--spacer--lg); - } - - &__body { - margin-left: var(--pf-global--spacer--xs); - margin-right: var(--pf-global--spacer--lg); - } - - &__alert { - margin-top: var(--pf-global--spacer--md); - margin-bottom: var(--pf-global--spacer--xs); - --pf-global--FontSize--md: 13px; - } - - &__pvcselector { - margin-top: var(--pf-global--spacer--md); - } -} diff --git a/packages/mco/components/modals/drpolicy-apps-apply/subscriptions/apply-policy-modal.tsx b/packages/mco/components/modals/drpolicy-apps-apply/subscriptions/apply-policy-modal.tsx deleted file mode 100644 index ea989dbae..000000000 --- a/packages/mco/components/modals/drpolicy-apps-apply/subscriptions/apply-policy-modal.tsx +++ /dev/null @@ -1,636 +0,0 @@ -import * as React from 'react'; -import { LoadingInline } from '@odf/shared/generic/Loading'; -import { objectify } from '@odf/shared/modals/EditLabelModal'; -import { - ModalBody, - ModalFooter, - CommonModalProps, -} from '@odf/shared/modals/Modal'; -import { SelectorInput } from '@odf/shared/modals/Selector'; -import { - getName, - getNamespace, - getLabels, - getAnnotations, -} from '@odf/shared/selectors'; -import { K8sResourceKind } from '@odf/shared/types'; -import { ApplicationKind } from '@odf/shared/types/k8s'; -import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook'; -import { getAPIVersionForModel } from '@odf/shared/utils'; -import { - k8sPatch, - k8sCreate, - useK8sWatchResources, -} from '@openshift-console/dynamic-plugin-sdk'; -import { K8sModel } from '@openshift-console/dynamic-plugin-sdk/lib/api/common-types'; -import * as _ from 'lodash-es'; -import { - Alert, - Text, - Modal, - Button, - ButtonVariant, - TextVariants, - TextContent, - TreeViewDataItem, - Form, - FormGroup, - Checkbox, -} from '@patternfly/react-core'; -import { - DR_SECHEDULER_NAME, - HUB_CLUSTER_NAME, - PROTECTED_APP_ANNOTATION_WO_SLASH, - PROTECTED_APP_ANNOTATION, -} from '../../../../constants'; -import { - getApplicationResourceObj, - getPlacementDecisionsResourceObj, - getPlacementResourceObj, - getPlacementRuleResourceObj, - getSubscriptionResourceObj, -} from '../../../../hooks'; -import { - ACMPlacementModel, - ACMPlacementRuleModel, - ACMSubscriptionModel, - DRPlacementControlModel, - DRPolicyModel, -} from '../../../../models'; -import { - DRPolicyKind, - ACMPlacementRuleKind, - ACMSubscriptionKind, - DRPlacementControlKind, - AppToPlacementType, - ACMPlacementKind, - ACMPlacementDecisionKind, - PlacementInfoType, - ACMPlacementType, -} from '../../../../types'; -import { - matchApplicationToSubscription, - getPlacementUniqueId, - findPlacementDecisionUsingPlacement, - isPlacementModel, -} from '../../../../utils'; -import { ApplicationSelector } from './application-selector'; -import './apply-policy-modal.scss'; -import '../../../../style.scss'; - -const resources = { - applications: getApplicationResourceObj(), - subscriptions: getSubscriptionResourceObj(), - placementRules: getPlacementRuleResourceObj(), - placements: getPlacementResourceObj(), - placementDecisions: getPlacementDecisionsResourceObj(), -}; - -const getDRPlacementControlKindObj = ( - pls: ACMPlacementType, - resource: DRPolicyKind, - deploymentClusterName: string, - pvcSelectors: string[] -): DRPlacementControlKind => ({ - apiVersion: getAPIVersionForModel(DRPlacementControlModel), - kind: DRPlacementControlModel.kind, - metadata: { - name: `${getName(pls)}-drpc`, - namespace: getNamespace(pls), - labels: getLabels(pls), - }, - spec: { - drPolicyRef: { - name: getName(resource), - kind: DRPolicyModel.kind, - apiVersion: getAPIVersionForModel(DRPolicyModel), - }, - placementRef: { - name: getName(pls), - kind: pls?.kind, - namespace: getNamespace(pls), - apiVersion: getAPIVersionForModel(ACMPlacementRuleModel), - }, - preferredCluster: deploymentClusterName, - pvcSelector: { - matchLabels: objectify(pvcSelectors), - }, - }, -}); - -const clusterMatch = ( - decisions: { clusterName?: string }[], - managedClusterNames: string[] -) => - decisions?.find((decision) => - managedClusterNames?.includes(decision?.clusterName) - ) || {}; - -const appFilter = (application: ApplicationKind) => - application?.spec?.componentKinds?.some( - (componentKind) => - componentKind?.group === ACMSubscriptionModel?.apiGroup && - componentKind?.kind === ACMSubscriptionModel?.kind - ); - -const getSelectedPlacementRules = ( - selectedApps: TreeViewDataItem[] -): TreeViewDataItem[] => selectedApps.filter((app) => !app.children); - -const filterSelectedPlacements = ( - checkedItems: TreeViewDataItem[], - appToPlacementRuleMap: AppToPlacementType -): PlacementInfoType => { - // Remove duplicate placement rules when it is used under more than one apps - const placements: PlacementInfoType = {}; - checkedItems?.forEach((app) => { - const appId = app.id.split(':')[0]; - if (appToPlacementRuleMap?.hasOwnProperty(appId) && !app?.children) { - const placementId = app.id.split(':')[1]; - if (!placements?.hasOwnProperty(placementId)) { - const appToPlsMap = appToPlacementRuleMap?.[appId]; - const pls = appToPlsMap?.placements?.[placementId]; - placements[placementId] = pls; - } - } - }); - return placements; -}; - -const generateApplicationToPlacementMap = ( - app: ApplicationKind, - subsMap: SubscriptionMap, - plsRuleMap: PlacementRuleMap, - plsMap: PlacementMap, - plsDecisionMap: PlacementDecisionMap, - managedClusterNames: string[] -): AppToPlacementType => { - let appToPlsMap: AppToPlacementType = {}; - const appName = getName(app); - const appNamespace = getNamespace(app); - const namespcedSubscriptions = subsMap?.[appNamespace] ?? {}; - - Object.values(namespcedSubscriptions).forEach((subs) => { - // applying subscription filter from application - const valid = matchApplicationToSubscription(subs, app); - if (valid) { - const placementRefKind = subs?.spec?.placement?.placementRef?.kind; - const placementRefName = subs?.spec?.placement?.placementRef?.name; - const placement: PlacementInfoType = {}; - let placementUniqueId = ''; - if (placementRefKind === ACMPlacementRuleModel?.kind) { - // fetch placement rule using placement ref - const plsRule = plsRuleMap?.[appNamespace]?.[placementRefName]; - const matchedCluster = clusterMatch( - plsRule?.status?.decisions, - managedClusterNames - ); - if ( - !_.isEmpty(matchedCluster) && - plsRule?.spec?.schedulerName !== DR_SECHEDULER_NAME - ) { - // placementUniqueId is used to group the multiple subscription set under the same app. - placementUniqueId = getPlacementUniqueId( - getName(plsRule), - getNamespace(plsRule), - plsRule?.kind - ); - // generating application to placement rule map - placement[placementUniqueId] = { - placement: plsRule, - subscriptions: [subs], - deploymentClusterName: matchedCluster?.clusterName, - }; - } - } else if (placementRefKind === ACMPlacementModel?.kind) { - // fetch placement using placement ref - const pls = plsMap?.[appNamespace]?.[placementRefName]; - const plsDecisions = plsDecisionMap?.[appNamespace]; - const plsDecision = findPlacementDecisionUsingPlacement( - pls, - plsDecisions - ); - const matchedCluster = clusterMatch( - plsDecision?.status?.decisions, - managedClusterNames - ); - if ( - !_.isEmpty(matchedCluster) && - !getAnnotations(pls)?.[PROTECTED_APP_ANNOTATION] - ) { - // placementUniqueId is used to group the multiple subscription set under the same app. - placementUniqueId = getPlacementUniqueId( - getName(pls), - getNamespace(pls), - pls?.kind - ); - // generating application to placement map - placement[placementUniqueId] = { - placement: pls, - subscriptions: [subs], - deploymentClusterName: matchedCluster?.clusterName, - }; - } - } - - if (!_.isEmpty(placement)) { - // appUniqueName will handle the corner case of same app name used across namespaces. - const appUniqueName = `${appName}%${appNamespace}`; - - if (appUniqueName in appToPlsMap) { - let placements = appToPlsMap[appUniqueName]?.placements || {}; - if (placementUniqueId in placements) { - // same placement rule is present for more than on subscription - placements?.[placementUniqueId]?.subscriptions.push(subs); - } else { - placements[placementUniqueId] = placement[placementUniqueId]; - } - } else { - // app to placement rule map - appToPlsMap[appUniqueName] = { - application: app, - placements: placement, - }; - } - } - } - }); - - return appToPlsMap; -}; - -const ApplyDRPolicyModal: React.FC> = ( - props -) => { - const { t } = useCustomTranslation(); - const { closeModal, isOpen, extraProps } = props; - const { resource } = extraProps; - const { drClusters: managedClusterNames } = resource?.spec || {}; - const [labels, setLabels] = React.useState([]); - const [isProtectAllPVCChecked, setProtectAllPVC] = React.useState(false); - const [error, setError] = React.useState(''); - const [loading, setLoading] = React.useState(false); - const [selectedApps, setSelectedApps] = React.useState<{ - checkedItems: TreeViewDataItem[]; - }>({ checkedItems: [] }); - const [appToPlacementMap, setAppToPlacementMap] = - React.useState({}); - const response = - useK8sWatchResources(resources); - const { - data: apps, - loaded: appsLoaded, - loadError: appsLoadError, - } = response?.applications; - const { - data: subs, - loaded: subsLoaded, - loadError: subsLoadError, - } = response?.subscriptions; - const { - data: plsRules, - loaded: plsRulesLoaded, - loadError: plsRulesLoadError, - } = response?.placementRules; - const { - data: pls, - loaded: plsLoaded, - loadError: plsLoadError, - } = response?.placements; - const { - data: plsDecisions, - loaded: plsDecisionsLoaded, - loadError: plsDecisionsLoadError, - } = response?.placementDecisions; - - const loaded = - appsLoaded && - subsLoaded && - plsRulesLoaded && - plsLoaded && - plsDecisionsLoaded; - const loadError = - appsLoadError || - subsLoadError || - plsRulesLoadError || - plsLoadError || - plsDecisionsLoadError; - - React.useEffect(() => { - if (loaded && !loadError) { - // namespace wise application object - const appsMap: ApplicationMap = apps?.reduce( - (arr, application) => - appFilter(application) - ? { - ...arr, - [getNamespace(application)]: { - ...arr[getNamespace(application)], - [getName(application)]: application, - }, - } - : arr, - {} - ); - - // namespace wise subscription object - const subsMap: SubscriptionMap = subs?.reduce( - (arr, subscription) => - isPlacementModel(subscription) - ? { - ...arr, - [getNamespace(subscription)]: { - ...arr[getNamespace(subscription)], - [getName(subscription)]: subscription, - }, - } - : arr, - {} - ); - - // namespace wise placementrule object - const plsRuleMap: PlacementRuleMap = plsRules?.reduce( - (arr, placementRule) => ({ - ...arr, - [getNamespace(placementRule)]: { - ...arr[getNamespace(placementRule)], - [getName(placementRule)]: placementRule, - }, - }), - {} - ); - - // namespace wise Placement object - const plsMap: PlacementMap = pls?.reduce( - (arr, placement) => ({ - ...arr, - [getNamespace(placement)]: { - ...arr[getNamespace(placement)], - [getName(placement)]: placement, - }, - }), - {} - ); - - // namespace wise PlacementDecision object - const plsDecisionMap: PlacementDecisionMap = plsDecisions?.reduce( - (arr, plDecision) => ({ - ...arr, - [getNamespace(plDecision)]: [ - ...(arr[getNamespace(plDecision)] || []), - plDecision, - ], - }), - {} as PlacementDecisionMap - ); - - if ( - !_.isEmpty(appsMap) && - !_.isEmpty(subsMap) && - (!_.isEmpty(plsRuleMap) || !_.isEmpty(plsMap)) - ) { - let appToPlsMap: AppToPlacementType = {}; - Object.keys(appsMap).forEach((namespace) => { - Object.keys(appsMap[namespace]).forEach((name) => { - appToPlsMap = { - ...appToPlsMap, - ...generateApplicationToPlacementMap( - appsMap[namespace][name], - subsMap, - plsRuleMap, - plsMap, - plsDecisionMap, - managedClusterNames - ), - }; - }); - }); - setAppToPlacementMap(appToPlsMap); - } - } else { - setError(!!loadError ? loadError?.message : ''); - } - }, [ - apps, - subs, - plsRules, - pls, - plsDecisions, - loaded, - loadError, - managedClusterNames, - setError, - ]); - - const selectedPlacementRules = React.useMemo( - () => getSelectedPlacementRules(selectedApps.checkedItems), - [selectedApps] - ); - - const submit = (event: React.FormEvent) => { - event.preventDefault(); - setLoading(true); - const selectedPlacements: PlacementInfoType = filterSelectedPlacements( - selectedApps?.checkedItems, - appToPlacementMap - ); - const promises: Promise[] = []; - Object.values(selectedPlacements)?.forEach((placementInfo) => { - const { placement, deploymentClusterName } = placementInfo; - const patch = []; - if (placement?.kind === ACMPlacementRuleModel.kind) { - patch.push({ - op: 'replace', - path: '/spec/schedulerName', - value: DR_SECHEDULER_NAME, - }); - } else { - _.isEmpty(getAnnotations(placement)) && - // will give error otherwise, case when Placement does not have any annotations - patch.push({ op: 'add', path: '/metadata/annotations', value: {} }); - patch.push({ - op: 'add', - path: `/metadata/annotations/${PROTECTED_APP_ANNOTATION_WO_SLASH}`, - value: 'true', - }); - } - promises.push( - k8sPatch({ - model: - placement?.kind === ACMPlacementRuleModel.kind - ? ACMPlacementRuleModel - : ACMPlacementModel, - resource: placement, - data: patch, - cluster: HUB_CLUSTER_NAME, - }) - ); - promises.push( - k8sCreate({ - model: DRPlacementControlModel, - data: getDRPlacementControlKindObj( - placement, - resource, - deploymentClusterName, - _.size(selectedPlacements) === 1 ? labels : [] - ), - cluster: HUB_CLUSTER_NAME, - }) - ); - }); - Promise.all(promises) - .then(() => { - closeModal(); - }) - .catch((resourceError) => { - setError(resourceError?.message); - setLoading(false); - }); - }; - - return ( - - - {t('Select the applications for assigning the DRPolicy')} - - {getName(resource)} - - } - title={t('Assign policy to Subscriptions')} - isOpen={isOpen} - onClose={closeModal} - className="mco-subs-apply-policy-modal__form" - > - - -
    - {selectedPlacementRules?.length <= 1 ? ( - - setLabels(l)} tags={labels} /> - - ) : ( - <> - - - - {t("Protect all PVCs within the application's namespace")} - * - - } - isChecked={isProtectAllPVCChecked} - onChange={() => setProtectAllPVC(!isProtectAllPVCChecked)} - /> - - - )} - - {error && ( - - {error} - - )} -
    - - - {!loading ? ( - - ) : ( - - )} - -
    - ); -}; - -type ApplyModalExtraProps = { - resource: DRPolicyKind; - resourceModel: K8sModel; -}; - -type ApplicationMap = { - [namespace in string]: { - [app in string]: ApplicationKind; - }; -}; - -type SubscriptionMap = { - [namespace in string]: { - [sub in string]: ACMSubscriptionKind; - }; -}; - -type PlacementRuleMap = { - [namespace in string]: { - [plsRule in string]: ACMPlacementRuleKind; - }; -}; - -type PlacementMap = { - [namespace in string]: { - [pls in string]: ACMPlacementKind; - }; -}; - -type PlacementDecisionMap = { - [namespace in string]: ACMPlacementDecisionKind[]; -}; - -type ApplyDRPolicyWatchResourceType = { - applications: ApplicationKind[]; - subscriptions: ACMSubscriptionKind[]; - placementRules: ACMPlacementRuleKind[]; - placements: ACMPlacementKind[]; - placementDecisions: ACMPlacementDecisionKind[]; -}; - -export default ApplyDRPolicyModal; diff --git a/packages/mco/components/modals/drpolicy-apps-apply/utils.tsx b/packages/mco/components/modals/drpolicy-apps-apply/utils.tsx deleted file mode 100644 index 981f9bb5e..000000000 --- a/packages/mco/components/modals/drpolicy-apps-apply/utils.tsx +++ /dev/null @@ -1,170 +0,0 @@ -import { getDRPCKindObj } from '@odf/mco/utils'; -import { objectify } from '@odf/shared/modals/EditLabelModal'; -import { K8sResourceKind } from '@odf/shared/types'; -import { getAPIVersionForModel } from '@odf/shared/utils'; -import { - k8sPatch, - k8sCreate, - k8sDelete, -} from '@openshift-console/dynamic-plugin-sdk'; -import { - HUB_CLUSTER_NAME, - PROTECTED_APP_ANNOTATION_WO_SLASH, -} from '../../../constants'; -import { DRPlacementControlModel, ACMPlacementModel } from '../../../models'; -import { PlacementToDrpcMap, PlacementToAppSets } from '../../../types'; - -export const areLabelsDifferent = ( - existingLabels: string[], - updateLabels: string[] -) => { - if (existingLabels.length !== updateLabels.length) return true; - const existingLabelsSet: Set = new Set(); - existingLabels.forEach((label) => existingLabelsSet.add(label)); - return updateLabels.some((label) => !existingLabelsSet.has(label)); -}; - -export const getAvailablePanelPromises = ( - availableResource: PlacementToAppSets, - drpcPvcLabels: PlacementToDrpcMap -) => { - const availablePanelPromises: Promise[] = []; - - const patch = [ - { - op: 'remove', - path: `/metadata/annotations/${PROTECTED_APP_ANNOTATION_WO_SLASH}`, - }, - ]; - - availableResource.havePlacementAnnotations && - availablePanelPromises.push( - k8sPatch({ - model: ACMPlacementModel, - resource: { - metadata: { - name: availableResource.placement, - namespace: availableResource.namespace, - }, - }, - data: patch, - cluster: HUB_CLUSTER_NAME, - }) - ); - const drpcName = - drpcPvcLabels?.[availableResource.namespace]?.[availableResource.placement] - ?.drpcName; - !!drpcName && - availablePanelPromises.push( - k8sDelete({ - model: DRPlacementControlModel, - resource: { - metadata: { - name: drpcName, - namespace: availableResource.namespace, - }, - }, - requestInit: null, - json: null, - cluster: HUB_CLUSTER_NAME, - }) - ); - - return availablePanelPromises; -}; - -export const getProtectedPanelPromises = ( - drPolicyName: string, - drClusterNames: string[], - protectedResource: PlacementToAppSets, - drpcPvcLabels: PlacementToDrpcMap -) => { - const protectedPanelPromises: Promise[] = []; - - const patch = []; - !protectedResource.havePlacementAnnotations && - // will give error otherwise, case when Placement does not have any annotations - patch.push({ op: 'add', path: '/metadata/annotations', value: {} }); - patch.push({ - op: 'add', - path: `/metadata/annotations/${PROTECTED_APP_ANNOTATION_WO_SLASH}`, - value: 'true', - }); - - protectedPanelPromises.push( - k8sPatch({ - model: ACMPlacementModel, - resource: { - metadata: { - name: protectedResource.placement, - namespace: protectedResource.namespace, - }, - }, - data: patch, - cluster: HUB_CLUSTER_NAME, - }) - ); - const drpcName = - drpcPvcLabels?.[protectedResource.namespace]?.[protectedResource.placement] - ?.drpcName; - const pvcSelectors = - drpcPvcLabels?.[protectedResource.namespace]?.[protectedResource.placement] - ?.updateLabels; - !drpcName && - protectedPanelPromises.push( - k8sCreate({ - model: DRPlacementControlModel, - data: getDRPCKindObj( - protectedResource.placement, - protectedResource.namespace, - ACMPlacementModel.kind, - getAPIVersionForModel(ACMPlacementModel), - drPolicyName, - drClusterNames, - protectedResource.decisionClusters, - pvcSelectors - ), - cluster: HUB_CLUSTER_NAME, - }) - ); - - return protectedPanelPromises; -}; - -export const getUpdatedDRPCPromise = ( - protectedResource: PlacementToAppSets, - drpcPvcLabels: PlacementToDrpcMap -) => { - const updatedDRPCPromise: Promise[] = []; - - const drpcName = - drpcPvcLabels?.[protectedResource.namespace]?.[protectedResource.placement] - ?.drpcName; - const updateLabels = - drpcPvcLabels?.[protectedResource.namespace]?.[protectedResource.placement] - ?.updateLabels; - const patch = [ - { - op: 'replace', - path: '/spec/pvcSelector/matchLabels', - value: objectify(updateLabels), - }, - ]; - - !!drpcName && - updatedDRPCPromise.push( - k8sPatch({ - model: DRPlacementControlModel, - resource: { - metadata: { - name: drpcName, - namespace: protectedResource.namespace, - }, - }, - data: patch, - cluster: HUB_CLUSTER_NAME, - }) - ); - - return updatedDRPCPromise; -}; diff --git a/packages/mco/utils/acm.ts b/packages/mco/utils/acm.ts deleted file mode 100644 index 85526b10a..000000000 --- a/packages/mco/utils/acm.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { - getAnnotations, - getLabel, - getName, - getNamespace, -} from '@odf/shared/selectors'; -import { - LAST_APP_DEPLOYMENT_CLUSTER_ANNOTATION, - PLACEMENT_REF_LABEL, -} from '../constants'; -import { - ArgoApplicationSetKind, - ACMManagedClusterKind, - ACMPlacementDecisionKind, - ACMPlacementKind, - ACMPlacementRuleKind, - DRPlacementControlKind, -} from '../types'; - -// Finding placement from application generators -export const findPlacementNameFromAppSet = ( - application: ArgoApplicationSetKind -): string => - application?.spec?.generators?.[0]?.clusterDecisionResource?.labelSelector - ?.matchLabels?.[PLACEMENT_REF_LABEL] || ''; - -export const findPlacementDecisionUsingPlacement = ( - placement: ACMPlacementKind, - placementDecisions: ACMPlacementDecisionKind[] -) => - placementDecisions?.find( - (placementDecision) => - getLabel(placementDecision, PLACEMENT_REF_LABEL, '') === - getName(placement) && - getNamespace(placementDecision) === getNamespace(placement) - ); - -export const getManagedClusterCondition = ( - managedCluster: ACMManagedClusterKind, - conditionType: string -) => - managedCluster?.status?.conditions?.find( - (condition) => - condition?.type === conditionType && condition.status === 'True' - ); - -export const findSiblingArgoAppSetsFromPlacement = ( - appName: String, - placement: ACMPlacementKind, - applications: ArgoApplicationSetKind[] -): ArgoApplicationSetKind[] => - applications?.filter( - (application) => - appName !== getName(application) && - getNamespace(application) === getNamespace(placement) && - findPlacementNameFromAppSet(application) === getName(placement) - ); - -export const findPlacementFromArgoAppSet = ( - placements: ACMPlacementKind[], - application: ArgoApplicationSetKind -): ACMPlacementKind => - placements?.find( - (placement) => - getNamespace(placement) === getNamespace(application) && - findPlacementNameFromAppSet(application) === getName(placement) - ); - -export const getClustersFromDecisions = ( - placement: ACMPlacementDecisionKind | ACMPlacementRuleKind -): string[] => - placement?.status?.decisions.map((decision) => decision?.clusterName) || []; - -export const getRemoteNamespaceFromAppSet = ( - application: ArgoApplicationSetKind -): string => application?.spec?.template?.spec?.destination?.namespace; - -export const getLastAppDeploymentClusterName = ( - drPlacementControl: DRPlacementControlKind -) => - getAnnotations(drPlacementControl)?.[ - LAST_APP_DEPLOYMENT_CLUSTER_ANNOTATION - ] || ''; - -export const findDeploymentClusters = ( - placementDecision: ACMPlacementDecisionKind, - drPlacementControl: DRPlacementControlKind -): string[] => { - if ((placementDecision ?? {}).status?.decisions?.length > 0) { - return getClustersFromDecisions(placementDecision); - } else { - const lastDeploymentClusterName = - getLastAppDeploymentClusterName(drPlacementControl); - return !!lastDeploymentClusterName ? [lastDeploymentClusterName] : []; - } -}; diff --git a/packages/mco/utils/common.ts b/packages/mco/utils/common.ts index ce3d8d177..db1889a37 100644 --- a/packages/mco/utils/common.ts +++ b/packages/mco/utils/common.ts @@ -10,11 +10,6 @@ import { K8sResourceCommon, } from '@openshift-console/dynamic-plugin-sdk'; -export const getGVKFromObjectRef = (objectRef: ObjectReference) => { - const { group, version } = groupVersionFor(objectRef?.apiVersion || ''); - return referenceFor(group)(version)(objectRef?.kind); -}; - export const getGVKFromK8Resource = (resource: K8sResourceCommon) => { const { group, version } = groupVersionFor(resource?.apiVersion || ''); return referenceFor(group)(version)(resource?.kind); diff --git a/packages/mco/utils/disaster-recovery.tsx b/packages/mco/utils/disaster-recovery.tsx index 5908f429a..b23ffc8ff 100644 --- a/packages/mco/utils/disaster-recovery.tsx +++ b/packages/mco/utils/disaster-recovery.tsx @@ -39,6 +39,8 @@ import { REPLICATION_TYPE, TIME_UNITS, PROTECTED_APP_ANNOTATION, + PLACEMENT_REF_LABEL, + LAST_APP_DEPLOYMENT_CLUSTER_ANNOTATION, } from '../constants'; import { DisasterRecoveryFormatted, ApplicationRefKind } from '../hooks'; import { @@ -62,11 +64,8 @@ import { ACMPlacementDecisionKind, ACMPlacementKind, MirrorPeerKind, + ArgoApplicationSetKind, } from '../types'; -import { - findPlacementDecisionUsingPlacement, - getLastAppDeploymentClusterName, -} from './acm'; export type PlacementMap = { [placementUniqueId: string]: string; @@ -349,15 +348,6 @@ export const filerDRClustersUsingDRPolicy = ( drPolicy?.spec?.drClusters?.includes(getName(drCluster)) ); -export const findDRPolicyUsingDRPC = ( - drpc: DRPlacementControlKind, - drPolicies: DRPolicyKind[] -): DRPolicyKind => { - return drPolicies?.find( - (drPolicy) => drpc?.spec?.drPolicyRef?.name === getName(drPolicy) - ); -}; - export const findDRPCUsingDRPolicy = ( drpcs: DRPlacementControlKind[], drPolicyName: string @@ -568,3 +558,81 @@ export const getDRPCKindObj = ( }, }, }); + +// Finding placement from application generators +export const findPlacementNameFromAppSet = ( + application: ArgoApplicationSetKind +): string => + application?.spec?.generators?.[0]?.clusterDecisionResource?.labelSelector + ?.matchLabels?.[PLACEMENT_REF_LABEL] || ''; + +export const findPlacementDecisionUsingPlacement = ( + placement: ACMPlacementKind, + placementDecisions: ACMPlacementDecisionKind[] +) => + placementDecisions?.find( + (placementDecision) => + getLabel(placementDecision, PLACEMENT_REF_LABEL, '') === + getName(placement) && + getNamespace(placementDecision) === getNamespace(placement) + ); + +export const getManagedClusterCondition = ( + managedCluster: ACMManagedClusterKind, + conditionType: string +) => + managedCluster?.status?.conditions?.find( + (condition) => + condition?.type === conditionType && condition.status === 'True' + ); + +export const findSiblingArgoAppSetsFromPlacement = ( + appName: string, + placement: ACMPlacementKind, + applications: ArgoApplicationSetKind[] +): ArgoApplicationSetKind[] => + applications?.filter( + (application) => + appName !== getName(application) && + getNamespace(application) === getNamespace(placement) && + findPlacementNameFromAppSet(application) === getName(placement) + ); + +export const findPlacementFromArgoAppSet = ( + placements: ACMPlacementKind[], + application: ArgoApplicationSetKind +): ACMPlacementKind => + placements?.find( + (placement) => + getNamespace(placement) === getNamespace(application) && + findPlacementNameFromAppSet(application) === getName(placement) + ); + +export const getClustersFromDecisions = ( + placement: ACMPlacementDecisionKind | ACMPlacementRuleKind +): string[] => + placement?.status?.decisions.map((decision) => decision?.clusterName) || []; + +export const getRemoteNamespaceFromAppSet = ( + application: ArgoApplicationSetKind +): string => application?.spec?.template?.spec?.destination?.namespace; + +export const getLastAppDeploymentClusterName = ( + drPlacementControl: DRPlacementControlKind +) => + getAnnotations(drPlacementControl)?.[ + LAST_APP_DEPLOYMENT_CLUSTER_ANNOTATION + ] || ''; + +export const findDeploymentClusters = ( + placementDecision: ACMPlacementDecisionKind, + drPlacementControl: DRPlacementControlKind +): string[] => { + if ((placementDecision ?? {}).status?.decisions?.length > 0) { + return getClustersFromDecisions(placementDecision); + } else { + const lastDeploymentClusterName = + getLastAppDeploymentClusterName(drPlacementControl); + return !!lastDeploymentClusterName ? [lastDeploymentClusterName] : []; + } +}; diff --git a/packages/mco/utils/index.ts b/packages/mco/utils/index.ts index e4bd5f0e8..e28c6ef95 100644 --- a/packages/mco/utils/index.ts +++ b/packages/mco/utils/index.ts @@ -1,4 +1,3 @@ export * from './disaster-recovery'; -export * from './acm'; export * from './common'; export * from './doc-utils'; diff --git a/plugins/mco/console-plugin.json b/plugins/mco/console-plugin.json index fbb3f1497..a81c3fdc6 100644 --- a/plugins/mco/console-plugin.json +++ b/plugins/mco/console-plugin.json @@ -9,7 +9,7 @@ "exposedModules": { "features": "@odf/mco/features", "dataPolicies": "@odf/mco/components/data-policies/data-policies-page", - "createDataPolicy": "@odf/mco/components/disaster-recovery/create-dr-policy/create-dr-policy", + "createDataPolicy": "@odf/mco/components/create-dr-policy/create-dr-policy", "systemDashboard": "@odf/mco/components/mco-dashboard/storage-system/system-dashboard", "editPage": "@odf/core/components/ResourceEditor/GenericResourceEditor", "dataPolicyStatusPopover": "@odf/mco/components/modals/app-data-policies-status/acm-column-callback",