Skip to content

Commit 3ce0917

Browse files
authored
MTV-2386: Clarify name template configuration (#1854)
Signed-off-by: Aviv Turgeman <[email protected]>
1 parent 36c5aad commit 3ce0917

File tree

27 files changed

+231
-134
lines changed

27 files changed

+231
-134
lines changed

locales/en/plugin__forklift-console-plugin.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@
259259
"Critical": "Critical",
260260
"Critical concerns": "Critical concerns",
261261
"Critical issues detected in your selected VMs will cause the migration to fail. Resolve these issues or remove the VMs from the plan before starting the migration.": "Critical issues detected in your selected VMs will cause the migration to fail. Resolve these issues or remove the VMs from the plan before starting the migration.",
262+
"Custom name template": "Custom name template",
262263
"Cutover": "Cutover",
263264
"Data centers": "Data centers",
264265
"Data is loading, please wait.": "Data is loading, please wait.",
@@ -268,6 +269,7 @@
268269
"Dates are compared in UTC. End of the interval is included.": "Dates are compared in UTC. End of the interval is included.",
269270
"Dedicated CPU": "Dedicated CPU",
270271
"Default": "Default",
272+
"Default name template": "Default name template",
271273
"Default network": "Default network",
272274
"Default transfer network": "Default transfer network",
273275
"Default transfer Network": "Default transfer Network",
@@ -470,6 +472,7 @@
470472
"incomplete": "incomplete",
471473
"Incomplete": "Incomplete",
472474
"Information concerns": "Information concerns",
475+
"Inherit plan wide setting": "Inherit plan wide setting",
473476
"Invalid application ID, spaces are not allowed": "Invalid application ID, spaces are not allowed",
474477
"Invalid application name, spaces are not allowed": "Invalid application name, spaces are not allowed",
475478
"Invalid application secret, spaces are not allowed": "Invalid application secret, spaces are not allowed",
@@ -720,10 +723,13 @@
720723
"Plan name must be unique across all namespaces.": "Plan name must be unique across all namespaces.",
721724
"Plan name must contain only lowercase alphanumeric characters or '-', and must start or end with lowercase alphanumeric character.": "Plan name must contain only lowercase alphanumeric characters or '-', and must start or end with lowercase alphanumeric character.",
722725
"Plan name:": "Plan name:",
726+
"Plan network name template": "Plan network name template",
723727
"Plan project": "Plan project",
724728
"Plan project is required.": "Plan project is required.",
725729
"Plan project:": "Plan project:",
730+
"Plan PVC name template": "Plan PVC name template",
726731
"Plan settings": "Plan settings",
732+
"Plan volume name template": "Plan volume name template",
727733
"Plan YAML": "Plan YAML",
728734
"Plans": "Plans",
729735
"Plans cannot be modified during migration": "Plans cannot be modified during migration",
@@ -1101,13 +1107,16 @@
11011107
"Virtual machine": "Virtual machine",
11021108
"Virtual machines": "Virtual machines",
11031109
"VM name": "VM name",
1110+
"VM network name template": "VM network name template",
1111+
"VM PVC name template": "VM PVC name template",
11041112
"VM target affinity rules": "VM target affinity rules",
11051113
"VM target labels": "VM target labels",
11061114
"VM target name": "VM target name",
11071115
"VM target name must be unique within a plan.": "VM target name must be unique within a plan.",
11081116
"VM target name must contain only lowercase alphanumeric characters or '-', and must start or end with lowercase alphanumeric character.": "VM target name must contain only lowercase alphanumeric characters or '-', and must start or end with lowercase alphanumeric character.",
11091117
"VM target node selector": "VM target node selector",
11101118
"VM target power state": "VM target power state",
1119+
"VM volume name template": "VM volume name template",
11111120
"VMs": "VMs",
11121121
"VMs included in cold migrations are shut down during migration.": "VMs included in cold migrations are shut down during migration.",
11131122
"VMs included in live migrations migrate without downtime.": "VMs included in live migrations migrate without downtime.",

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
"postinstall": "husky"
4242
},
4343
"dependencies": {
44-
"@kubev2v/types": "0.0.22",
44+
"@kubev2v/types": "0.0.24",
4545
"@openshift-console/dynamic-plugin-sdk": "1.8.0",
4646
"@patternfly/patternfly": "5.4.2",
4747
"@patternfly/quickstarts": "^5.4.5",

src/components/AffinityModal/utils/constants.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,14 @@ export const operatorSelectOptions = [
7272
{ label: t('In'), value: Operator.In.valueOf() },
7373
{ label: t('Not in'), value: Operator.NotIn.valueOf() },
7474
];
75+
76+
export const K8sIoApiCoreV1NodeSelectorRequirementOperatorEnum = {
77+
DoesNotExist: 'DoesNotExist',
78+
Exists: 'Exists',
79+
Gt: 'Gt',
80+
In: 'In',
81+
Lt: 'Lt',
82+
NotIn: 'NotIn',
83+
} as const;
84+
export type K8sIoApiCoreV1NodeSelectorRequirementOperatorEnum =
85+
(typeof K8sIoApiCoreV1NodeSelectorRequirementOperatorEnum)[keyof typeof K8sIoApiCoreV1NodeSelectorRequirementOperatorEnum];

src/components/AffinityModal/utils/rowsDataToAffinity.ts

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,26 @@
1-
import {
2-
type K8sIoApiCoreV1Affinity,
3-
type K8sIoApiCoreV1NodeSelectorRequirement,
4-
K8sIoApiCoreV1NodeSelectorRequirementOperatorEnum,
5-
type K8sIoApiCoreV1NodeSelectorTerm,
6-
type K8sIoApiCoreV1PodAffinityTerm,
7-
type K8sIoApiCoreV1PreferredSchedulingTerm,
8-
type K8sIoApiCoreV1WeightedPodAffinityTerm,
1+
import type {
2+
K8sIoApiCoreV1Affinity,
3+
K8sIoApiCoreV1NodeSelectorRequirement,
4+
K8sIoApiCoreV1NodeSelectorTerm,
5+
K8sIoApiCoreV1PodAffinityTerm,
6+
K8sIoApiCoreV1PreferredSchedulingTerm,
7+
K8sIoApiCoreV1WeightedPodAffinityTerm,
98
} from '@kubev2v/types';
109
import { isEmpty } from '@utils/helpers';
1110

11+
import { K8sIoApiCoreV1NodeSelectorRequirementOperatorEnum } from './constants';
1212
import { AffinityCondition, type AffinityLabel, type AffinityRowData, AffinityType } from './types';
1313

14+
type PickRowsMapper<T> = (rowData: AffinityRowData) => T;
15+
1416
const flattenExpressions = (
15-
affinityLabels: AffinityLabel[],
16-
): K8sIoApiCoreV1NodeSelectorRequirement[] =>
17-
affinityLabels?.map((aff) => {
17+
affinityLabels: AffinityLabel[] | undefined,
18+
): K8sIoApiCoreV1NodeSelectorRequirement[] => {
19+
if (!affinityLabels) {
20+
return [];
21+
}
22+
23+
return affinityLabels?.map((aff) => {
1824
const { id: _id, ...affinityWithoutID } = aff;
1925

2026
const affinityRequirement = { ...affinityWithoutID } as K8sIoApiCoreV1NodeSelectorRequirement;
@@ -23,6 +29,7 @@ const flattenExpressions = (
2329
? { ...affinityRequirement, values: [] }
2430
: affinityRequirement;
2531
});
32+
};
2633

2734
const getRequiredNodeTermFromRowData = ({
2835
expressions,
@@ -41,7 +48,7 @@ const getPreferredNodeTermFromRowData = ({
4148
matchExpressions: flattenExpressions(expressions),
4249
matchFields: flattenExpressions(fields),
4350
},
44-
weight,
51+
weight: weight!,
4552
});
4653

4754
const getRequiredPodTermFromRowData = ({
@@ -51,7 +58,7 @@ const getRequiredPodTermFromRowData = ({
5158
labelSelector: {
5259
matchExpressions: flattenExpressions(expressions),
5360
},
54-
topologyKey,
61+
topologyKey: topologyKey!,
5562
});
5663

5764
const getPreferredPodTermFromRowData = ({
@@ -63,20 +70,24 @@ const getPreferredPodTermFromRowData = ({
6370
labelSelector: {
6471
matchExpressions: flattenExpressions(expressions),
6572
},
66-
topologyKey,
73+
topologyKey: topologyKey!,
6774
},
68-
weight,
75+
weight: weight!,
6976
});
7077

7178
export const rowsDataToAffinity = (affinityRows: AffinityRowData[]) => {
7279
if (isEmpty(affinityRows)) {
7380
return null;
7481
}
7582

76-
const pickRows = (rowType, rowCondition, mapper) =>
83+
const pickRows = <T>(
84+
affinityType: AffinityType,
85+
condition: AffinityCondition,
86+
mapper: PickRowsMapper<T>,
87+
): T[] =>
7788
affinityRows
78-
.filter(({ condition, type }) => type === rowType && condition === rowCondition)
79-
.map((rowData) => mapper(rowData));
89+
.filter((row) => row.type === affinityType && row.condition === condition)
90+
.map(mapper);
8091

8192
const affinity = {} as K8sIoApiCoreV1Affinity;
8293

src/plans/create/utils/createPlan.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { getObjectRef } from 'src/modules/Providers/views/migrate/reducer/helpers';
2-
import type { EnhancedPlan } from 'src/plans/details/tabs/Details/components/SettingsSection/utils/types';
32

4-
import { PlanModel } from '@kubev2v/types';
3+
import { PlanModel, type V1beta1Plan } from '@kubev2v/types';
54
import { k8sCreate } from '@openshift-console/dynamic-plugin-sdk';
65

76
import { MigrationTypeValue } from '../steps/migration-type/constants';
@@ -33,7 +32,7 @@ export const createPlan = async ({
3332
transferNetwork,
3433
vms,
3534
}: CreatePlanParams) => {
36-
const plan: EnhancedPlan = {
35+
const plan: V1beta1Plan = {
3736
apiVersion: 'forklift.konveyor.io/v1beta1',
3837
kind: 'Plan',
3938
metadata: {

src/plans/details/tabs/Details/components/SettingsSection/SettingsSection.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,9 @@ const SettingsSection: FC<SettingsSectionProps> = ({ plan }) => {
5151
/>
5252
<RootDiskDetailsItem plan={plan} canPatch={canPatch} shouldRender={isVsphere} />
5353
<SharedDisksDetailsItem plan={plan} canPatch={canPatch} shouldRender={isVsphere} />
54-
<VolumeNameTemplateDetailsItem plan={plan} canPatch={canPatch} shouldRender={isVsphere} />
55-
<TransferNetworkDetailsItem plan={plan} canPatch={canPatch} />
5654
<PVCNameTemplateDetailsItem plan={plan} canPatch={canPatch} shouldRender={isVsphere} />
55+
<TransferNetworkDetailsItem plan={plan} canPatch={canPatch} />
56+
<VolumeNameTemplateDetailsItem plan={plan} canPatch={canPatch} shouldRender={isVsphere} />
5757
<PreserveStaticIPsDetailsItem plan={plan} canPatch={canPatch} shouldRender={isVsphere} />
5858
<NetworkNameTemplateDetailsItem plan={plan} canPatch={canPatch} shouldRender={isVsphere} />
5959
<PreserveClusterCpuModelDetailsItem

src/plans/details/tabs/Details/components/SettingsSection/components/EditNameTemplate/EditNameTemplate.tsx

Lines changed: 60 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,96 @@
11
import { type FC, type ReactNode, useState } from 'react';
22

3+
import Select from '@components/common/Select';
34
import ModalForm from '@components/ModalForm/ModalForm';
45
import type { V1beta1Plan } from '@kubev2v/types';
5-
import { Radio, TextInput } from '@patternfly/react-core';
6+
import { Form, FormGroup, SelectList, SelectOption, TextInput } from '@patternfly/react-core';
7+
import { isEmpty } from '@utils/helpers';
68

7-
import { NameTemplateRadioOptions } from './utils/constants';
9+
import { NameTemplateOptions, type NameTemplateOptionType } from './utils/types';
10+
import {
11+
getNameTemplateOptions,
12+
getNameTemplateStateLabel,
13+
getSelectedOption,
14+
} from './utils/utils';
815

916
type EditNameTemplateProps = {
17+
allowInherit?: boolean;
18+
inheritValue?: string;
1019
title: string;
1120
value: string | undefined;
1221
onConfirm: (value: string | undefined) => Promise<V1beta1Plan>;
1322
body?: ReactNode;
1423
helperText?: ReactNode;
24+
fieldName: string;
1525
};
1626

1727
const EditNameTemplate: FC<EditNameTemplateProps> = ({
28+
allowInherit = true,
1829
body,
30+
fieldName,
1931
helperText,
32+
inheritValue,
2033
onConfirm,
2134
title,
2235
value,
2336
}) => {
24-
const [selected, setSelected] = useState<NameTemplateRadioOptions>(
25-
value
26-
? NameTemplateRadioOptions.customNameTemplate
27-
: NameTemplateRadioOptions.defaultNameTemplate,
37+
const [selected, setSelected] = useState<NameTemplateOptions>(
38+
getSelectedOption(value, allowInherit),
2839
);
2940
const [inputValue, setInputValue] = useState(value ?? '');
3041

3142
return (
3243
<ModalForm
3344
title={title}
3445
onConfirm={async () => {
35-
return selected === NameTemplateRadioOptions.customNameTemplate
46+
if (selected === NameTemplateOptions.customNameTemplate && isEmpty(inputValue.trim())) {
47+
throw new Error('Name template cannot be empty');
48+
}
49+
return selected === NameTemplateOptions.customNameTemplate
3650
? onConfirm(inputValue)
3751
: onConfirm(undefined);
3852
}}
53+
isDisabled={
54+
selected === NameTemplateOptions.customNameTemplate &&
55+
(inputValue === value || isEmpty(inputValue.trim()))
56+
}
3957
>
4058
{body}
41-
<Radio
42-
isChecked={selected === NameTemplateRadioOptions.defaultNameTemplate}
43-
name="name-template"
44-
onChange={() => {
45-
setSelected(NameTemplateRadioOptions.defaultNameTemplate);
46-
}}
47-
label="Use default naming template"
48-
id="default-naming-template"
49-
/>
50-
<Radio
51-
isChecked={selected === NameTemplateRadioOptions.customNameTemplate}
52-
name="name-template"
53-
onChange={() => {
54-
setSelected(NameTemplateRadioOptions.customNameTemplate);
55-
}}
56-
label="Enter custom naming template"
57-
id="custom-naming-template"
58-
/>
59-
<TextInput
60-
isDisabled={selected === NameTemplateRadioOptions.defaultNameTemplate}
61-
value={inputValue}
62-
onChange={(_, val) => {
63-
setInputValue(val);
64-
}}
65-
/>
66-
{helperText}
59+
<Form>
60+
<FormGroup label={fieldName} fieldId="nameTemplate" isRequired>
61+
<Select
62+
id="nameTemplate"
63+
value={getNameTemplateStateLabel(selected, allowInherit)}
64+
onSelect={(_event, val) => {
65+
setSelected((val as unknown as NameTemplateOptionType)?.value);
66+
}}
67+
>
68+
<SelectList>
69+
{getNameTemplateOptions(allowInherit).map((option) => (
70+
<SelectOption
71+
key={option.value}
72+
value={option}
73+
isSelected={selected === option.value}
74+
description={option.getInheritToDescription?.(inheritValue)}
75+
>
76+
{option?.label}
77+
</SelectOption>
78+
))}
79+
</SelectList>
80+
</Select>
81+
</FormGroup>
82+
{selected === NameTemplateOptions.customNameTemplate && (
83+
<FormGroup>
84+
<TextInput
85+
value={inputValue}
86+
onChange={(_, val) => {
87+
setInputValue(val);
88+
}}
89+
/>
90+
{helperText}
91+
</FormGroup>
92+
)}
93+
</Form>
6794
</ModalForm>
6895
);
6996
};

src/plans/details/tabs/Details/components/SettingsSection/components/EditNameTemplate/components/NameTemplateBody.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
} from '@patternfly/react-core';
1010

1111
type NameTemplateBodyProps = {
12-
bodyText: string[];
12+
bodyText: string;
1313
allowedVariables: string[];
1414
};
1515

src/plans/details/tabs/Details/components/SettingsSection/components/EditNameTemplate/utils/constants.ts

Lines changed: 0 additions & 4 deletions
This file was deleted.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export enum NameTemplateOptions {
2+
defaultNameTemplate = 'defaultNameTemplate',
3+
customNameTemplate = 'customNameTemplate',
4+
inheritPlanWideSetting = 'inheritPlanWideSetting',
5+
}
6+
7+
export type NameTemplateOptionType = {
8+
value: NameTemplateOptions;
9+
label: string;
10+
getInheritToDescription?: (inheritValue: string | undefined) => string;
11+
};

0 commit comments

Comments
 (0)