Skip to content

Commit

Permalink
[Part-3] Imperative application manage policy wizard
Browse files Browse the repository at this point in the history
Signed-off-by: Gowtham Shanmugasundaram <[email protected]>
  • Loading branch information
GowthamShanmugam committed Nov 14, 2023
1 parent 069c538 commit 8b1843f
Show file tree
Hide file tree
Showing 24 changed files with 1,082 additions and 70 deletions.
33 changes: 33 additions & 0 deletions locales/en/plugin__odf-console.json
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,16 @@
"Assign": "Assign",
"Next": "Next",
"Back": "Back",
"Kubernetes object replication interval": "Kubernetes object replication interval",
"Define the interval for Kubernetes object replication, this is only applicable for Kubernetes object and not application data.": "Define the interval for Kubernetes object replication, this is only applicable for Kubernetes object and not application data.",
"Interval": "Interval",
"Protect Kubernetes objects": "Protect Kubernetes objects",
"For your imperative applications, select a method to protect Kubernetes deployed dynamic objects": "For your imperative applications, select a method to protect Kubernetes deployed dynamic objects",
"Using resource label selector": "Using resource label selector",
"Protect all Kubernetes resources that use the selected resource label selector": "Protect all Kubernetes resources that use the selected resource label selector",
"Add another resource label selector": "Add another resource label selector",
"Using recipes": "Using recipes",
"Protect Kubernetes resources as per rules or in the order defined within the recipe": "Protect Kubernetes resources as per rules or in the order defined within the recipe",
"Manage list view alert": "Manage list view alert",
"Confirm unassign": "Confirm unassign",
"All placements": "All placements",
Expand All @@ -236,6 +246,13 @@
"View configurations": "View configurations",
"No activity": "No activity",
"No assigned data policy found": "No assigned data policy found",
"Policy assignment rule": "Policy assignment rule",
"Select the scope of your policy assignment": "Select the scope of your policy assignment",
"This is an OpenShift application type and has shared resources in the namespace.": "This is an OpenShift application type and has shared resources in the namespace.",
"Application-specific": "Application-specific",
"Use to secure only this application. Any existing applications, and new applications within the namespace[namespace] will need to be proactively secured.": "Use to secure only this application. Any existing applications, and new applications within the namespace[namespace] will need to be proactively secured.",
"Namespace-wide": "Namespace-wide",
"Use to secure all applications in the namesapce. All existing and newly created applications present in the namespace sharing the same PVC label selector will be protected when you assign a policy.<1><0>Namespace: ui-git-ansible</0></1>": "Use to secure all applications in the namesapce. All existing and newly created applications present in the namespace sharing the same PVC label selector will be protected when you assign a policy.<1><0>Namespace: ui-git-ansible</0></1>",
"Delete": "Delete",
"Required": "Required",
"Select a placement": "Select a placement",
Expand All @@ -245,8 +262,15 @@
"Use PVC label selectors to effortlessly specify the application resources that need protection.": "Use PVC label selectors to effortlessly specify the application resources that need protection.",
"Application resource": "Application resource",
"Add application resource": "Add application resource",
"Recipe": "Recipe",
"Select a recipe": "Select a recipe",
"Hide recipe details": "Hide recipe details",
"Show recipe details": "Show recipe details",
"{{count}} placements_one": "{{count}} placements",
"{{count}} placements_other": "{{count}} placements",
"Policy rule": "Policy rule",
"Policy assignment rule:": "Policy assignment rule:",
"Namesapce:": "Namesapce:",
"Data policy": "Data policy",
"Policy name:": "Policy name:",
"Clusters:": "Clusters:",
Expand All @@ -255,6 +279,13 @@
"PVC details": "PVC details",
"Application resource:": "Application resource:",
"PVC label selector:": "PVC label selector:",
"Dynamic objects": "Dynamic objects",
"Protection method:": "Protection method:",
"Resource label selector": "Resource label selector",
"Recipe name:": "Recipe name:",
"Recipe namespace:": "Recipe namespace:",
"Resource label selector:": "Resource label selector:",
"Replication interval:": "Replication interval:",
"Replication type: {{type}}, Interval: {{interval}}, Clusters: {{clusters}}": "Replication type: {{type}}, Interval: {{interval}}, Clusters: {{clusters}}",
"Replication type: {{type}}, Clusters: {{clusters}}": "Replication type: {{type}}, Clusters: {{clusters}}",
"Status: {{status}}": "Status: {{status}}",
Expand All @@ -271,6 +302,8 @@
"Unable to unassign all selected policies for the application.": "Unable to unassign all selected policies for the application.",
"My assigned policies": "My assigned policies",
"You haven't set a data policy for your application yet. To protect your application, click the 'Assign data policy' button and select a policy from the available templates.": "You haven't set a data policy for your application yet. To protect your application, click the 'Assign data policy' button and select a policy from the available templates.",
"There was an error while getting the managed resource.": "There was an error while getting the managed resource.",
"Request for ManagedClusterView: {{viewName}} on cluster: {{clusterName}} failed.": "Request for ManagedClusterView: {{viewName}} on cluster: {{clusterName}} failed.",
"Relocate in progress": "Relocate in progress",
"Failover in progress": "Failover in progress",
"List all the connected applications under a policy.": "List all the connected applications under a policy.",
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@
"webpack": "5.74.0",
"webpack-bundle-analyzer": "^4.6.1",
"webpack-cli": "4.5.x",
"yup": "^0.32.11"
"yup": "^0.32.11",
"sha1": "1.1.1"
},
"devDependencies": {
"@cypress/webpack-preprocessor": "^5.9.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,19 @@ import {
AssignPolicyStepsNames,
} from '@odf/mco/constants';
import { ModalBody } from '@odf/shared/modals';
import { getName } from '@odf/shared/selectors';
import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook';
import { getErrorMessage } from '@odf/shared/utils';
import { TFunction } from 'i18next';
import { Wizard, WizardStep, AlertVariant } from '@patternfly/react-core';
import { AssignPolicyViewFooter } from './helper/assign-policy-view-footer';
import { DynamicObjectWizardContent } from './helper/dynamic-object-wizard-content';
import { PolicyRuleWizardContent } from './helper/policy-rule-wizard-content';
import { PVCDetailsWizardContent } from './helper/pvc-details-wizard-content';
import { ReviewAndAssign } from './helper/review-and-assign';
import { SelectPolicyWizardContent } from './helper/select-policy-wizard-content';
import { assignPromises } from './utils/k8s-utils';
import { getClusterNamesFromPlacements } from './utils/parser-utils';
import {
AssignPolicyViewState,
ManagePolicyStateAction,
Expand All @@ -30,8 +34,10 @@ import {
} from './utils/types';

export const createSteps = (
appName: string,
appType: APPLICATION_TYPE,
workloadNamespace: string,
clusterNames: string[],
unProtectedPlacements: PlacementType[],
matchingPolicies: DRPolicyType[],
state: AssignPolicyViewState,
Expand Down Expand Up @@ -59,14 +65,47 @@ export const createSteps = (
pvcSelectors={state.persistentVolumeClaim.pvcSelectors}
unProtectedPlacements={unProtectedPlacements}
workloadNamespace={workloadNamespace}
clusterNames={clusterNames}
isValidationEnabled={isValidationEnabled}
dispatch={dispatch}
/>
),
},
reviewAndAssign: {
name: AssignPolicyStepsNames(t)[AssignPolicySteps.ReviewAndAssign],
component: <ReviewAndAssign state={state} />,
component: (
<ReviewAndAssign
state={state}
workloadNamespace={workloadNamespace}
appType={appType}
/>
),
},
};

const imperativeApplicationSteps = {
policyRule: {
name: AssignPolicyStepsNames(t)[AssignPolicySteps.PolicyRule],
component: (
<PolicyRuleWizardContent
policyRule={state.policyRule}
dispatch={dispatch}
/>
),
},
dynamicObjects: {
name: AssignPolicyStepsNames(t)[AssignPolicySteps.DynamicObjects],
component: (
<DynamicObjectWizardContent
appName={appName}
workLoadNamespace={workloadNamespace}
clusterNames={clusterNames}
policyRule={state.policyRule}
dynamicObjects={state.dynamicObjects}
isValidationEnabled={isValidationEnabled}
dispatch={dispatch}
/>
),
},
};

Expand All @@ -90,6 +129,34 @@ export const createSteps = (
canJumpTo: stepIdReached >= 3,
},
];
case APPLICATION_TYPE.OPENSHIFT:
return [
{
id: 1,
...imperativeApplicationSteps.policyRule,
canJumpTo: stepIdReached >= 1,
},
{
id: 2,
...commonSteps.policy,
canJumpTo: stepIdReached >= 2,
},
{
id: 3,
...commonSteps.persistentVolumeClaim,
canJumpTo: stepIdReached >= 3,
},
{
id: 4,
...imperativeApplicationSteps.dynamicObjects,
canJumpTo: stepIdReached >= 4,
},
{
id: 5,
...commonSteps.reviewAndAssign,
canJumpTo: stepIdReached >= 5,
},
];
default:
return [];
}
Expand All @@ -107,8 +174,11 @@ export const AssignPolicyView: React.FC<AssignPolicyViewProps> = ({
const { t } = useCustomTranslation();
const [stepIdReached, setStepIdReached] = React.useState(1);
const [isValidationEnabled, setIsValidationEnabled] = React.useState(false);

const { type: appType, workloadNamespace, placements } = applicaitonInfo;
const clusterNames = React.useMemo(
() => getClusterNamesFromPlacements(placements),
[placements]
);

const resetAssignState = () =>
dispatch({
Expand Down Expand Up @@ -139,7 +209,7 @@ export const AssignPolicyView: React.FC<AssignPolicyViewProps> = ({
resetAssignState();
};
// assign DRPolicy
const promises = assignPromises(state, applicaitonInfo.placements);
const promises = assignPromises(state, appType, applicaitonInfo.placements);
await Promise.all(promises)
.then(() => {
updateContext(
Expand Down Expand Up @@ -171,8 +241,10 @@ export const AssignPolicyView: React.FC<AssignPolicyViewProps> = ({
navAriaLabel={t('Assign policy nav')}
mainAriaLabel={t('Assign policy content')}
steps={createSteps(
appType,
getName(applicaitonInfo),
APPLICATION_TYPE.OPENSHIFT || appType,
workloadNamespace,
clusterNames,
placements,
matchingPolicies,
state,
Expand All @@ -184,7 +256,6 @@ export const AssignPolicyView: React.FC<AssignPolicyViewProps> = ({
footer={
<AssignPolicyViewFooter
state={state}
appType={appType}
stepIdReached={stepIdReached}
isValidationEnabled={isValidationEnabled}
setStepIdReached={setStepIdReached}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import * as React from 'react';
import {
APPLICATION_TYPE,
AssignPolicySteps,
AssignPolicyStepsNames,
} from '@odf/mco/constants';
import { AssignPolicySteps, AssignPolicyStepsNames } from '@odf/mco/constants';
import { isLabelOnlyOperator } from '@odf/shared/label-expression-selector';
import { getName } from '@odf/shared/selectors';
import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook';
import { TFunction } from 'i18next';
Expand All @@ -15,7 +12,12 @@ import {
Alert,
AlertVariant,
} from '@patternfly/react-core';
import { AssignPolicyViewState, PVCSelectorType } from '../utils/reducer';
import {
AssignPolicyViewState,
PVCSelectorType,
DynamicObjectType,
ObjectProtectionMethod,
} from '../utils/reducer';
import { DRPolicyType } from '../utils/types';
import '../../../../style.scss';
import '../style.scss';
Expand All @@ -26,6 +28,20 @@ const isPVCSelectorFound = (pvcSelectors: PVCSelectorType[]) =>

const isDRPolicySelected = (dataPolicy: DRPolicyType) => !!getName(dataPolicy);

const isValidKubeObjectProtection = ({
captureInterval,
objectProtectionMethod,
recipeInfo,
appResourceSelector,
}: DynamicObjectType) =>
!!captureInterval && objectProtectionMethod === ObjectProtectionMethod.Recipe
? !!recipeInfo
: appResourceSelector.every((selector) =>
isLabelOnlyOperator(selector.operator)
? !!selector.key
: !!selector.key && !!selector.values.length
);

const canJumpToNextStep = (
stepName: string,
state: AssignPolicyViewState,
Expand All @@ -36,6 +52,10 @@ const canJumpToNextStep = (
return isDRPolicySelected(state.policy);
case AssignPolicyStepsNames(t)[AssignPolicySteps.PersistentVolumeClaim]:
return isPVCSelectorFound(state.persistentVolumeClaim.pvcSelectors);
case AssignPolicyStepsNames(t)[AssignPolicySteps.PolicyRule]:
return !!state.policyRule;
case AssignPolicyStepsNames(t)[AssignPolicySteps.DynamicObjects]:
return isValidKubeObjectProtection(state.dynamicObjects);
default:
return false;
}
Expand All @@ -60,6 +80,7 @@ export const AssignPolicyViewFooter: React.FC<AssignPolicyViewFooterProps> = ({

const canJumpToNext = canJumpToNextStep(stepName, state, t);
const validationError = isValidationEnabled && !canJumpToNext;
const assignPolicyStepsNames = AssignPolicyStepsNames(t);

const moveToNextStep = () => {
if (canJumpToNext) {
Expand All @@ -73,7 +94,7 @@ export const AssignPolicyViewFooter: React.FC<AssignPolicyViewFooterProps> = ({

const handleNext = async () => {
switch (stepName) {
case AssignPolicyStepsNames(t)[AssignPolicySteps.ReviewAndAssign]:
case assignPolicyStepsNames[AssignPolicySteps.ReviewAndAssign]:
setRequestInProgress(true);
await onSubmit();
setRequestInProgress(false);
Expand Down Expand Up @@ -104,18 +125,15 @@ export const AssignPolicyViewFooter: React.FC<AssignPolicyViewFooterProps> = ({
onClick={handleNext}
>
{stepName ===
AssignPolicyStepsNames(t)[AssignPolicySteps.ReviewAndAssign]
assignPolicyStepsNames[AssignPolicySteps.ReviewAndAssign]
? t('Assign')
: t('Next')}
</Button>
{/* Disabling the back button for the first step (Policy) in wizard */}
<Button
variant="secondary"
onClick={onBack}
isDisabled={
stepName === AssignPolicyStepsNames(t)[AssignPolicySteps.Policy] ||
requestInProgress
}
isDisabled={stepId === 1 || requestInProgress}
>
{t('Back')}
</Button>
Expand All @@ -133,7 +151,6 @@ export const AssignPolicyViewFooter: React.FC<AssignPolicyViewFooterProps> = ({

type AssignPolicyViewFooterProps = {
state: AssignPolicyViewState;
appType: APPLICATION_TYPE;
stepIdReached: number;
isValidationEnabled: boolean;
setStepIdReached: React.Dispatch<React.SetStateAction<number>>;
Expand Down
Loading

0 comments on commit 8b1843f

Please sign in to comment.