Skip to content

Commit 1f1025c

Browse files
Add provision to recipe parameters while enrolling applicaiton
https://issues.redhat.com/browse/DFBUGS-857 Signed-off-by: Timothy Asir Jeyasingh <[email protected]>
1 parent b8aea66 commit 1f1025c

File tree

6 files changed

+126
-4
lines changed

6 files changed

+126
-4
lines changed

locales/en/plugin__odf-console.json

+5-1
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,10 @@
9898
"Resource label": "Resource label",
9999
"Secure namespaces as per Recipe definition.": "Secure namespaces as per Recipe definition.",
100100
"Recipe": "Recipe",
101+
"Recipe parameters": "Recipe parameters",
102+
"Recipe parameters are a set of named inputs that substitute the placeholders in a recipe.": "Recipe parameters are a set of named inputs that substitute the placeholders in a recipe.",
103+
"Add parameter": "Add parameter",
104+
"Value (optional)": "Value (optional)",
101105
"Recipe list": "Recipe list",
102106
"Only recipes of the selected namespaces will appear in the list.": "Only recipes of the selected namespaces will appear in the list.",
103107
"Select a recipe": "Select a recipe",
@@ -147,6 +151,7 @@
147151
"Type:": "Type:",
148152
"Recipe name:": "Recipe name:",
149153
"Recipe namespace:": "Recipe namespace:",
154+
"Recipe Parameters:": "Recipe Parameters:",
150155
"Label expressions:": "Label expressions:",
151156
"PVC label selectors:": "PVC label selectors:",
152157
"Replication": "Replication",
@@ -1304,7 +1309,6 @@
13041309
"Use different criteria for tagging your bucket.": "Use different criteria for tagging your bucket.",
13051310
"No tags are attached to this bucket.": "No tags are attached to this bucket.",
13061311
"Add tag": "Add tag",
1307-
"Value (optional)": "Value (optional)",
13081312
"Enter a valid rule name": "Enter a valid rule name",
13091313
"A rule name is required.": "A rule name is required.",
13101314
"A rule with this name already exists. Type a different name.": "A rule with this name already exists. Type a different name.",

packages/mco/components/discovered-application-wizard/utils/k8s-utils.ts

+25-2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,28 @@ import {
2222
ProtectionMethodType,
2323
} from './reducer';
2424

25+
export const convertToRecipeParameters = (
26+
params: Record<string, string>
27+
): Record<string, string[]> => {
28+
return Object.fromEntries(
29+
Object.entries(params || {}).map(([key, value]) => [
30+
key,
31+
value
32+
.split(',')
33+
.map((v) => v.trim())
34+
.filter(Boolean),
35+
])
36+
);
37+
};
38+
39+
export const formatRecipeParametersForDisplay = (
40+
params: Record<string, string[]>
41+
): string => {
42+
return Object.entries(params || {})
43+
.map(([key, values]) => `${key}: ${values.join(', ')}`)
44+
.join('; ');
45+
};
46+
2547
export const getDRPCKindObj = (props: {
2648
name: string;
2749
preferredCluster: string;
@@ -31,10 +53,10 @@ export const getDRPCKindObj = (props: {
3153
k8sResourceReplicationInterval: string;
3254
recipeName?: string;
3355
recipeNamespace?: string;
56+
recipeParameters?: Record<string, string[]>;
3457
k8sResourceLabelExpressions?: MatchExpression[];
3558
pvcLabelExpressions?: MatchExpression[];
3659
placementName: string;
37-
recipeParameters?: Record<string, string[]>;
3860
labels?: ObjectMetadata['labels'];
3961
}): DRPlacementControlKind => ({
4062
apiVersion: getAPIVersionForModel(DRPlacementControlModel),
@@ -106,7 +128,7 @@ export const createPromise = (
106128
const { namespace, configuration, replication } = state;
107129
const { clusterName, namespaces, name } = namespace;
108130
const { protectionMethod, recipe, resourceLabels } = configuration;
109-
const { recipeName, recipeNamespace } = recipe;
131+
const { recipeName, recipeNamespace, recipeParameters } = recipe;
110132
const { k8sResourceLabelExpressions, pvcLabelExpressions } = resourceLabels;
111133
const { drPolicy, k8sResourceReplicationInterval } = replication;
112134
const namespaceList = namespaces.map(getName);
@@ -132,6 +154,7 @@ export const createPromise = (
132154
protectionMethod,
133155
recipeName,
134156
recipeNamespace,
157+
recipeParameters: convertToRecipeParameters(recipeParameters),
135158
k8sResourceLabelExpressions,
136159
pvcLabelExpressions,
137160
drPolicyName: getName(drPolicy),

packages/mco/components/discovered-application-wizard/utils/reducer.ts

+20
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export enum EnrollDiscoveredApplicationStateType {
1616
SET_NAMESPACES = 'NAMESPACE/SET_NAMESPACES',
1717
SET_PROTECTION_METHOD = 'CONFIGURATION/SET_PROTECTION_METHOD',
1818
SET_RECIPE_NAME_NAMESPACE = 'CONFIGURATION/RECIPE/SET_RECIPE_NAME_NAMESPACE',
19+
SET_RECIPE_PARAMETERS = 'SET_RECIPE_PARAMETERS',
1920
SET_K8S_RESOURCE_LABEL_EXPRESSIONS = 'CONFIGURATION/RESOURCE_LABEL/SET_K8S_RESOURCE_LABEL_EXPRESSIONS',
2021
SET_PVC_LABEL_EXPRESSIONS = 'CONFIGURATION/RESOURCE_LABEL/SET_PVC_LABEL_EXPRESSIONS',
2122
SET_POLICY = 'REPLICATION/SET_POLICY',
@@ -40,6 +41,7 @@ export type EnrollDiscoveredApplicationState = {
4041
recipeName: string;
4142
// recipe CR namespace
4243
recipeNamespace: string;
44+
recipeParameters: Record<string, string>;
4345
};
4446
resourceLabels: {
4547
k8sResourceLabelExpressions: MatchExpression[];
@@ -70,6 +72,7 @@ export const initialState: EnrollDiscoveredApplicationState = {
7072
recipe: {
7173
recipeName: '',
7274
recipeNamespace: '',
75+
recipeParameters: {},
7376
},
7477
resourceLabels: {
7578
k8sResourceLabelExpressions: [],
@@ -112,6 +115,10 @@ export type EnrollDiscoveredApplicationAction =
112115
type: EnrollDiscoveredApplicationStateType.SET_POLICY;
113116
payload: DRPolicyKind;
114117
}
118+
| {
119+
type: EnrollDiscoveredApplicationStateType.SET_RECIPE_PARAMETERS;
120+
payload: Record<string, string>;
121+
}
115122
| {
116123
type: EnrollDiscoveredApplicationStateType.SET_K8S_RESOURCE_REPLICATION_INTERVAL;
117124
payload: string;
@@ -159,6 +166,18 @@ export const reducer: EnrollReducer = (state, action) => {
159166
},
160167
};
161168
}
169+
case EnrollDiscoveredApplicationStateType.SET_RECIPE_PARAMETERS: {
170+
return {
171+
...state,
172+
configuration: {
173+
...state.configuration,
174+
recipe: {
175+
...state.configuration.recipe,
176+
recipeParameters: action.payload,
177+
},
178+
},
179+
};
180+
}
162181
case EnrollDiscoveredApplicationStateType.SET_RECIPE_NAME_NAMESPACE: {
163182
const [recipeName, recipeNamespace] = action.payload.split(
164183
NAME_NAMESPACE_SPLIT_CHAR
@@ -168,6 +187,7 @@ export const reducer: EnrollReducer = (state, action) => {
168187
configuration: {
169188
...state.configuration,
170189
recipe: {
190+
...state.configuration.recipe,
171191
recipeName,
172192
recipeNamespace,
173193
},
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import * as React from 'react';
2+
import { FieldLevelHelp, useCustomTranslation } from '@odf/shared';
3+
import { LazyNameValueEditor } from '@odf/shared/utils/NameValueEditor';
4+
import { FormGroup } from '@patternfly/react-core';
5+
import {
6+
EnrollDiscoveredApplicationAction,
7+
EnrollDiscoveredApplicationStateType,
8+
} from '../../utils/reducer';
9+
10+
export const RecipeParameterInput: React.FC<RecipeParameterInputProps> = ({
11+
dispatch,
12+
}) => {
13+
const { t } = useCustomTranslation();
14+
const [recipeParams, setRecipeParams] = React.useState<[string, string][]>([
15+
['', ''],
16+
]);
17+
18+
const updateRecipeParams = ({
19+
nameValuePairs,
20+
}: {
21+
nameValuePairs: [string, string][];
22+
}) => {
23+
setRecipeParams(nameValuePairs);
24+
const validPairs = nameValuePairs.filter(([key]) => key.trim());
25+
dispatch({
26+
type: EnrollDiscoveredApplicationStateType.SET_RECIPE_PARAMETERS,
27+
payload: Object.fromEntries(validPairs.map(([k, v]) => [k.trim(), v])),
28+
});
29+
};
30+
31+
return (
32+
<FormGroup
33+
label={t('Recipe parameters')}
34+
fieldId="recipe-parameters"
35+
labelIcon={
36+
<FieldLevelHelp>
37+
{t(
38+
'Recipe parameters are a set of named inputs that substitute the placeholders in a recipe.'
39+
)}
40+
</FieldLevelHelp>
41+
}
42+
data-test="recipe-parameters-form-group"
43+
>
44+
<LazyNameValueEditor
45+
data-test="recipe-parameters"
46+
nameValuePairs={recipeParams}
47+
updateParentData={updateRecipeParams}
48+
addString={t('Add parameter')}
49+
valueString={t('Value (optional)')}
50+
nameMaxLength={128}
51+
valueMaxLength={256}
52+
/>
53+
</FormGroup>
54+
);
55+
};
56+
57+
type RecipeParameterInputProps = {
58+
dispatch: React.Dispatch<EnrollDiscoveredApplicationAction>;
59+
};

packages/mco/components/discovered-application-wizard/wizard-steps/configuration-step/recipe-selection.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
EnrollDiscoveredApplicationStateType,
2222
} from '../../utils/reducer';
2323
import './configuration-step.scss';
24+
import { RecipeParameterInput } from './recipe-parameter-input';
2425

2526
const getRecipeOptions = (
2627
searchResultItem: SearchResultItemType[]
@@ -117,6 +118,8 @@ export const RecipeSelection: React.FC<RecipeSelectionProps> = ({
117118
) : (
118119
<StatusBox loaded={searchLoaded} loadError={searchError} />
119120
)}
121+
122+
{recipeNameNamespace && <RecipeParameterInput dispatch={dispatch} />}
120123
</>
121124
);
122125
};

packages/mco/components/discovered-application-wizard/wizard-steps/review-step/review-step.tsx

+14-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ import {
1212
} from '@odf/shared/review-and-create-step';
1313
import { getName } from '@odf/shared/selectors';
1414
import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook';
15+
import * as _ from 'lodash-es';
16+
import {
17+
formatRecipeParametersForDisplay,
18+
convertToRecipeParameters,
19+
} from '../../utils/k8s-utils';
1520
import {
1621
EnrollDiscoveredApplicationState,
1722
ProtectionMethodType,
@@ -23,7 +28,7 @@ export const Review: React.FC<ReviewProps> = ({ state }) => {
2328
const { namespace, configuration, replication } = state;
2429
const { clusterName, namespaces, name } = namespace;
2530
const { protectionMethod, recipe, resourceLabels } = configuration;
26-
const { recipeName, recipeNamespace } = recipe;
31+
const { recipeName, recipeNamespace, recipeParameters } = recipe;
2732
const { k8sResourceLabelExpressions, pvcLabelExpressions } = resourceLabels;
2833
const { drPolicy, k8sResourceReplicationInterval } = replication;
2934

@@ -37,6 +42,9 @@ export const Review: React.FC<ReviewProps> = ({ state }) => {
3742
drPolicy.spec.schedulingInterval === '0m'
3843
? REPLICATION_DISPLAY_TEXT(t).sync
3944
: REPLICATION_DISPLAY_TEXT(t).async;
45+
const displayRecipeParameters = formatRecipeParametersForDisplay(
46+
convertToRecipeParameters(recipeParameters)
47+
);
4048
const [unitVal, interval] = parseSyncInterval(k8sResourceReplicationInterval);
4149

4250
return (
@@ -62,6 +70,11 @@ export const Review: React.FC<ReviewProps> = ({ state }) => {
6270
<ReviewAndCreationItem label={t('Recipe namespace:')}>
6371
{recipeNamespace}
6472
</ReviewAndCreationItem>
73+
{!_.isEmpty(recipeParameters) && (
74+
<ReviewAndCreationItem label={t('Recipe Parameters:')}>
75+
{displayRecipeParameters}
76+
</ReviewAndCreationItem>
77+
)}
6578
</>
6679
)}
6780
{protectionMethod === ProtectionMethodType.RESOURCE_LABEL && (

0 commit comments

Comments
 (0)