Skip to content

Commit

Permalink
Separate concerns in vsphere create form
Browse files Browse the repository at this point in the history
Signed-off-by: yzamir <[email protected]>
  • Loading branch information
yaacov committed Mar 27, 2024
1 parent 1ca8305 commit 816a85c
Show file tree
Hide file tree
Showing 7 changed files with 228 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
"404: Not Found": "404: Not Found",
"7 days": "7 days",
"A CA certificate to be trusted when connecting to Openshift API endpoint. Ensure the CA certificate format is in a PEM encoded X.509 format. To use a CA certificate, drag the file to the text box or browse for it. To use the system CA certificate, leave the field empty.": "A CA certificate to be trusted when connecting to Openshift API endpoint. Ensure the CA certificate format is in a PEM encoded X.509 format. To use a CA certificate, drag the file to the text box or browse for it. To use the system CA certificate, leave the field empty.",
"A CA certificate to be trusted when connecting to the ESXi API endpoint. Ensure the CA certificate format is in a PEM encoded X.509 format. To use a CA certificate, drag the file to the text box or browse for it. To use the system CA certificate, leave the field empty.": "A CA certificate to be trusted when connecting to the ESXi API endpoint. Ensure the CA certificate format is in a PEM encoded X.509 format. To use a CA certificate, drag the file to the text box or browse for it. To use the system CA certificate, leave the field empty.",
"A CA certificate to be trusted when connecting to the OpenStack Identity (Keystone) endpoint. Ensure the CA certificate format is valid. To use a CA certificate, drag the file to the text box or browse for it. To use the system CA certificate, leave the field empty.": "A CA certificate to be trusted when connecting to the OpenStack Identity (Keystone) endpoint. Ensure the CA certificate format is valid. To use a CA certificate, drag the file to the text box or browse for it. To use the system CA certificate, leave the field empty.",
"A CA certificate to be trusted when connecting to the Red Hat Virtualization Manager (RHVM) API endpoint. Ensure the CA certificate format is in a PEM encoded X.509 format. To use a CA certificate, drag the file to the text box or browse for it. To use the system CA certificate, leave the field empty.": "A CA certificate to be trusted when connecting to the Red Hat Virtualization Manager (RHVM) API endpoint. Ensure the CA certificate format is in a PEM encoded X.509 format. To use a CA certificate, drag the file to the text box or browse for it. To use the system CA certificate, leave the field empty.",
"A CA certificate to be trusted when connecting to the vCenter API endpoint. Ensure the CA certificate format is in a PEM encoded X.509 format. To use a CA certificate, drag the file to the text box or browse for it. To use the system CA certificate, leave the field empty.": "A CA certificate to be trusted when connecting to the vCenter API endpoint. Ensure the CA certificate format is in a PEM encoded X.509 format. To use a CA certificate, drag the file to the text box or browse for it. To use the system CA certificate, leave the field empty.",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import { validateURL, ValidationMsg } from '../../common';

export const defaultEsxiUrlMsg = {
msg: 'The URL of the ESXi API endpoint for example: https://host-example.com/sdk .',
type: 'default',
};

export const validateEsxiURL = (url: string | number): ValidationMsg => {
// Sanity check
if (typeof url !== 'string') {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import { validateURL, ValidationMsg } from '../../common';

export const defaultVCenterUrlMsg = {
msg: 'The URL of the vCenter API endpoint for example: https://host-example.com/sdk .',
type: 'default',
};

export const validateVCenterURL = (url: string | number): ValidationMsg => {
// Sanity check
if (typeof url !== 'string') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ import {
} from '../../details';

import { EditProviderSectionHeading } from './EditProviderSectionHeading';
import { EsxiProviderCreateForm } from './EsxiProviderCreateForm';
import { OpenshiftProviderFormCreate } from './OpenshiftProviderCreateForm';
import { OpenstackProviderCreateForm } from './OpenstackProviderCreateForm';
import { OVAProviderCreateForm } from './OVAProviderCreateForm';
import { OvirtProviderCreateForm } from './OvirtProviderCreateForm';
import { ProvidersCreateFormProps } from './ProviderCreateForm';
import { VSphereProviderCreateForm } from './VSphereProviderCreateForm';
import { VCenterProviderCreateForm } from './VCenterProviderCreateForm';

export const EditProvider: React.FC<ProvidersCreateFormProps> = ({
newProvider,
Expand Down Expand Up @@ -61,7 +62,7 @@ export const EditProvider: React.FC<ProvidersCreateFormProps> = ({
case 'esxi':
return (
<>
<VSphereProviderCreateForm provider={newProvider} onChange={onNewProviderChange} />
<EsxiProviderCreateForm provider={newProvider} onChange={onNewProviderChange} />

<EditProviderSectionHeading text={t('Provider credentials')} />
<EsxiCredentialsEdit secret={newSecret} onChange={onNewSecretChange} />
Expand All @@ -70,7 +71,7 @@ export const EditProvider: React.FC<ProvidersCreateFormProps> = ({
default:
return (
<>
<VSphereProviderCreateForm provider={newProvider} onChange={onNewProviderChange} />
<VCenterProviderCreateForm provider={newProvider} onChange={onNewProviderChange} />

<EditProviderSectionHeading text={t('Provider credentials')} />
<VCenterCredentialsEdit secret={newSecret} onChange={onNewSecretChange} />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
import React, { useCallback, useReducer } from 'react';
import { validateEsxiURL, validateVDDKImage } from 'src/modules/Providers/utils';
import { ForkliftTrans, useForkliftTranslation } from 'src/utils/i18n';

import { ExternalLink } from '@kubev2v/common';
import { V1beta1Provider } from '@kubev2v/types';
import { Form, FormGroup, Popover, Radio, TextInput } from '@patternfly/react-core';
import HelpIcon from '@patternfly/react-icons/dist/esm/icons/help-icon';

const CREATE_VDDK_HELP_LINK =
'https://access.redhat.com/documentation/en-us/migration_toolkit_for_virtualization/2.5/html-single/installing_and_using_the_migration_toolkit_for_virtualization/index#creating-vddk-image_mtv';

export interface EsxiProviderCreateFormProps {
provider: V1beta1Provider;
onChange: (newValue: V1beta1Provider) => void;
}

export const EsxiProviderCreateForm: React.FC<EsxiProviderCreateFormProps> = ({
provider,
onChange,
}) => {
const { t } = useForkliftTranslation();

const url = provider?.spec?.url || '';
const vddkInitImage = provider?.spec?.settings?.['vddkInitImage'] || '';
const sdkEndpoint = provider?.spec?.settings?.['sdkEndpoint'] || '';

const vddkHelperTextPopover = (
<ForkliftTrans>
<p>
VMware Virtual Disk Development Kit (VDDK) image in a secure registry that is accessible to
all clusters, for example: quay.io/kubev2v/vddk:latest .
</p>
<p>
It is strongly recommended to create a VDDK init image to accelerate migrations. For more
information, see{' '}
<ExternalLink isInline href={CREATE_VDDK_HELP_LINK}>
Creating VDDK image
</ExternalLink>
.
</p>
</ForkliftTrans>
);

const initialState = {
validation: {
url: {
msg: 'The URL of the ESXi API endpoint for example: https://host-example.com/sdk .',
type: 'default',
},
vddkInitImage: {
type: 'default',
msg: 'VMware Virtual Disk Development Kit (VDDK) image, for example: quay.io/kubev2v/vddk:latest .',
},
},
};

const reducer = (state, action) => {
switch (action.type) {
case 'SET_FIELD_VALIDATED':
return {
...state,
validation: {
...state.validation,
[action.payload.field]: action.payload.validationState,
},
};
default:
return state;
}
};

const [state, dispatch] = useReducer(reducer, initialState);

const handleChange = useCallback(
(id, value) => {
const trimmedValue = value.trim();

if (id == 'vddkInitImage') {
const validationState = validateVDDKImage(trimmedValue);

dispatch({
type: 'SET_FIELD_VALIDATED',
payload: { field: 'vddkInitImage', validationState },
});

onChange({
...provider,
spec: {
type: provider.spec.type,
url: provider.spec.url,
...provider?.spec,
settings: {
...(provider?.spec?.settings as object),
vddkInitImage: trimmedValue || undefined,
},
},
});
}

if (id == 'sdkEndpoint') {
const sdkEndpoint = trimmedValue || undefined;

// Revalidate URL - VCenter or ESXi
const trimmedURL = provider?.spec?.url?.trim() || '';
const validationState = validateEsxiURL(trimmedURL);

dispatch({ type: 'SET_FIELD_VALIDATED', payload: { field: 'url', validationState } });

onChange({
...provider,
spec: {
type: provider.spec.type,
url: provider.spec.url,
...provider?.spec,
settings: {
...(provider?.spec?.settings as object),
sdkEndpoint: sdkEndpoint,
},
},
});
}

if (id === 'url') {
// Validate URL - VCenter of ESXi
const validationState = validateEsxiURL(trimmedValue);

dispatch({ type: 'SET_FIELD_VALIDATED', payload: { field: 'url', validationState } });

onChange({ ...provider, spec: { ...provider.spec, url: trimmedValue } });
}
},
[provider],
);

return (
<Form isWidthLimited className="forklift-section-provider-edit">
<FormGroup
role="radiogroup"
fieldId="sdkEndpoint"
label={t('Endpoint type')}
helperText={t('Select vSphere provider endpoint type.')}
>
<Radio
name="sdkEndpoint"
label="vCenter"
id="sdkEndpoint-vcenter"
isChecked={!sdkEndpoint || sdkEndpoint === 'vcenter'}
onChange={() => handleChange('sdkEndpoint', 'vcenter')}
/>
<Radio
name="sdkEndpoint"
label="ESXi"
id="sdkEndpoint-esxi"
isChecked={sdkEndpoint === 'esxi'}
onChange={() => handleChange('sdkEndpoint', 'esxi')}
/>
</FormGroup>

<FormGroup
label={t('URL')}
isRequired
fieldId="url"
helperText={state.validation.url.msg}
helperTextInvalid={state.validation.url.msg}
validated={state.validation.url.type}
>
<TextInput
isRequired
type="text"
id="url"
name="url"
value={url}
validated={state.validation.url.type}
onChange={(value) => handleChange('url', value)}
/>
</FormGroup>

<FormGroup
label={t('VDDK init image')}
fieldId="vddkInitImage"
helperText={state.validation.vddkInitImage.msg}
helperTextInvalid={state.validation.vddkInitImage.msg}
validated={state.validation.vddkInitImage.type}
labelIcon={
<Popover
headerContent={t('VDDK init image')}
bodyContent={vddkHelperTextPopover}
alertSeverityVariant="info"
>
<button
type="button"
onClick={(e) => e.preventDefault()}
className="pf-c-form__group-label-help"
>
<HelpIcon />
</button>
</Popover>
}
>
<TextInput
type="text"
id="vddkInitImage"
name="vddkInitImage"
value={vddkInitImage}
validated={state.validation.vddkInitImage.type}
onChange={(value) => handleChange('vddkInitImage', value)}
/>
</FormGroup>
</Form>
);
};
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
import React, { useCallback, useReducer } from 'react';
import {
defaultEsxiUrlMsg,
defaultVCenterUrlMsg,
validateEsxiURL,
validateVCenterURL,
validateVDDKImage,
ValidationMsg,
} from 'src/modules/Providers/utils';
import { validateVCenterURL, validateVDDKImage } from 'src/modules/Providers/utils';
import { ForkliftTrans, useForkliftTranslation } from 'src/utils/i18n';

import { ExternalLink } from '@kubev2v/common';
Expand All @@ -17,12 +10,12 @@ import HelpIcon from '@patternfly/react-icons/dist/esm/icons/help-icon';
const CREATE_VDDK_HELP_LINK =
'https://access.redhat.com/documentation/en-us/migration_toolkit_for_virtualization/2.5/html-single/installing_and_using_the_migration_toolkit_for_virtualization/index#creating-vddk-image_mtv';

export interface VSphereProviderCreateFormProps {
export interface VCenterProviderCreateFormProps {
provider: V1beta1Provider;
onChange: (newValue: V1beta1Provider) => void;
}

export const VSphereProviderCreateForm: React.FC<VSphereProviderCreateFormProps> = ({
export const VCenterProviderCreateForm: React.FC<VCenterProviderCreateFormProps> = ({
provider,
onChange,
}) => {
Expand All @@ -49,11 +42,12 @@ export const VSphereProviderCreateForm: React.FC<VSphereProviderCreateFormProps>
</ForkliftTrans>
);

const defaultUrlMsg = sdkEndpoint === 'esxi' ? defaultEsxiUrlMsg : defaultVCenterUrlMsg;

const initialState = {
validation: {
url: defaultUrlMsg,
url: {
msg: 'The URL of the vCenter API endpoint for example: https://host-example.com/sdk .',
type: 'default',
},
vddkInitImage: {
type: 'default',
msg: 'VMware Virtual Disk Development Kit (VDDK) image, for example: quay.io/kubev2v/vddk:latest .',
Expand Down Expand Up @@ -107,15 +101,9 @@ export const VSphereProviderCreateForm: React.FC<VSphereProviderCreateFormProps>
if (id == 'sdkEndpoint') {
const sdkEndpoint = trimmedValue || undefined;

let validationState: ValidationMsg;

// Revalidate URL - VCenter or ESXi
const trimmedURL = provider?.spec?.url?.trim() || '';
if (sdkEndpoint === 'esxi') {
validationState = validateEsxiURL(trimmedURL);
} else {
validationState = validateVCenterURL(trimmedURL);
}
const validationState = validateVCenterURL(trimmedURL);

dispatch({ type: 'SET_FIELD_VALIDATED', payload: { field: 'url', validationState } });

Expand All @@ -134,15 +122,8 @@ export const VSphereProviderCreateForm: React.FC<VSphereProviderCreateFormProps>
}

if (id === 'url') {
let validationState: ValidationMsg;

// Validate URL - VCenter of ESXi
const sdkEndpoint = provider?.spec?.settings?.['sdkEndpoint'] || '';
if (sdkEndpoint === 'esxi') {
validationState = validateEsxiURL(trimmedValue);
} else {
validationState = validateVCenterURL(trimmedValue);
}
const validationState = validateVCenterURL(trimmedValue);

dispatch({ type: 'SET_FIELD_VALIDATED', payload: { field: 'url', validationState } });

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
// @index(['./*', /style/g], f => `export * from '${f.path}';`)
export * from './EditProvider';
export * from './EditProviderSectionHeading';
export * from './EsxiProviderCreateForm';
export * from './OpenshiftProviderCreateForm';
export * from './OpenstackProviderCreateForm';
export * from './OVAProviderCreateForm';
export * from './OvirtProviderCreateForm';
export * from './providerCardItems';
export * from './ProviderCreateForm';
export * from './VSphereProviderCreateForm';
export * from './VCenterProviderCreateForm';
// @endindex

0 comments on commit 816a85c

Please sign in to comment.