Skip to content

Commit

Permalink
Merge pull request kubev2v#861 from sgratch/enhance-openstack-provide…
Browse files Browse the repository at this point in the history
…r-help-text-fields

Enhance OpenStack provider help text fields
  • Loading branch information
yaacov authored Jan 30, 2024
2 parents b966c02 + c65e5a2 commit dc2b7ce
Show file tree
Hide file tree
Showing 12 changed files with 470 additions and 226 deletions.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import { Trans } from 'react-i18next';
import { useForkliftTranslation } from 'src/utils/i18n';

import { ProviderModel } from '@kubev2v/types';
Expand All @@ -13,20 +14,65 @@ import { EditProviderURLModalProps } from './EditProviderURLModal';
export const OpenstackEditURLModal: React.FC<EditProviderURLModalProps> = (props) => {
const { t } = useForkliftTranslation();

const urlValidationHook: ValidationHookType = (value) => {
const isValidURL = validateURL(value.toString().trim());
const helperTextMsgs = {
error: (
<div className="forklift-edit-modal-field-error-validation">
<Trans t={t} ns="plugin__forklift-console-plugin">
Error: The format of the provided URL is invalid. Ensure the URL includes a scheme, a
domain name, and a path. For example:{' '}
<strong>https://identity_service.com:5000/v3</strong>.
</Trans>
</div>
),
warning: (
<div className="forklift--edit-modal-field-warning-validation">
<Trans t={t} ns="plugin__forklift-console-plugin">
Warning: The provided URL does not end with the API endpoint path:
<strong>
{'"'}/v3{'"'}
</strong>
{'. '}
Ensure the URL includes the correct path. For example:{' '}
<strong>https://identity_service.com:5000/v3</strong>.
</Trans>
</div>
),
success: (
<div className="forklift-edit-modal-field-success-validation">
<Trans t={t} ns="plugin__forklift-console-plugin">
For example: <strong>https://identity_service.com:5000/v3</strong>.
</Trans>
</div>
),
default: (
<div className="forklift-edit-modal-field-default-validation">
<Trans t={t} ns="plugin__forklift-console-plugin">
For example: <strong>https://identity_service.com:5000/v3</strong>.
</Trans>
</div>
),
};

return isValidURL
? {
validationHelpText: undefined,
validated: 'success',
}
: {
validationHelpText: t(
'URL must start with https:// or http:// and contain valid hostname and path',
),
validated: 'error',
};
const urlValidationHook: ValidationHookType = (value) => {
const trimmedUrl: string = value.toString().trim();
const isValidURL = validateURL(trimmedUrl);
// error
if (!isValidURL)
return {
validationHelpText: helperTextMsgs.error,
validated: 'error',
};
// warning
if (!trimmedUrl.endsWith('v3') && !trimmedUrl.endsWith('v3/'))
return {
validationHelpText: helperTextMsgs.warning,
validated: 'warning',
};
// success
return {
validationHelpText: helperTextMsgs.success,
validated: 'success',
};
};

return (
Expand All @@ -37,10 +83,8 @@ export const OpenstackEditURLModal: React.FC<EditProviderURLModalProps> = (props
label={props?.label || t('URL')}
model={ProviderModel}
variant={ModalVariant.large}
body={t(
'Specify OpenStack Identity (Keystone) endpoint, for example, http://controller:5000/v3.',
)}
helperText={t('Please enter URL for OpenStack services REST APIs.')}
body={t('URL of the OpenStack Identity (Keystone) endpoint.')}
helperText={helperTextMsgs.default}
onConfirmHook={patchProviderURL}
validationHook={urlValidationHook}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { useCallback, useReducer } from 'react';
import { Trans } from 'react-i18next';
import { validateURL, Validation } from 'src/modules/Providers/utils';
import { useForkliftTranslation } from 'src/utils/i18n';

Expand All @@ -18,9 +19,50 @@ export const OpenstackProviderCreateForm: React.FC<OpenstackProviderCreateFormPr

const url = provider?.spec?.url || '';

const urlHelperTextMsgs = {
error: (
<div className="forklift--create-provider-field-error-validation">
<Trans t={t} ns="plugin__forklift-console-plugin">
Error: The format of the provided URL is invalid. Ensure the URL includes a scheme, a
domain name, and a path. For example:{' '}
<strong>https://identity_service.com:5000/v3</strong>.
</Trans>
</div>
),
warning: (
<div className="forklift--create-provider-field-warning-validation">
<Trans t={t} ns="plugin__forklift-console-plugin">
Warning: The provided URL does not end with the API endpoint path:{' '}
<strong>
{'"'}/v3{'"'}
</strong>
. Ensure the URL includes the correct path. For example:{' '}
<strong>https://identity_service.com:5000/v3</strong>.
</Trans>
</div>
),
success: (
<div className="forklift--create-provider-field-success-validation">
<Trans t={t} ns="plugin__forklift-console-plugin">
URL of the OpenStack Identity (Keystone) endpoint. For example:{' '}
<strong>https://identity_service.com:5000/v3</strong>.
</Trans>
</div>
),
default: (
<div className="forklift--create-provider-field-default-validation">
<Trans t={t} ns="plugin__forklift-console-plugin">
URL of the OpenStack Identity (Keystone) endpoint. For example: {'<strong>'}
https://identity_service.com:5000/v3{'</strong>'}.
</Trans>
</div>
),
};

const initialState = {
validation: {
url: 'default' as Validation,
urlHelperText: urlHelperTextMsgs.default,
},
};

Expand All @@ -43,27 +85,44 @@ export const OpenstackProviderCreateForm: React.FC<OpenstackProviderCreateFormPr

const handleChange = useCallback(
(id, value) => {
const trimmedValue = value.trim();
if (id !== 'url') return;

const trimmedValue: string = value.trim();
const validationState = getURLValidationState(trimmedValue);

if (id === 'url') {
const validationState = validateURL(trimmedValue) ? 'success' : 'error';
dispatch({ type: 'SET_FIELD_VALIDATED', payload: { field: id, validationState } });
dispatch({
type: 'SET_FIELD_VALIDATED',
payload: { field: 'url', validationState },
});

onChange({ ...provider, spec: { ...provider.spec, url: trimmedValue } });
}
dispatch({
type: 'SET_FIELD_VALIDATED',
payload: {
field: 'urlHelperText',
validationState: urlHelperTextMsgs[validationState],
},
});

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

const getURLValidationState = (url: string): Validation => {
if (!validateURL(url)) return 'error';
if (!url.endsWith('v3') && !url.endsWith('v3/')) return 'warning';
return 'success';
};

return (
<Form isWidthLimited className="forklift-section-provider-edit">
<FormGroup
label={t('URL')}
isRequired
fieldId="url"
helperText={t('URL of the provider')}
helperText={state.validation.urlHelperText}
validated={state.validation.url}
helperTextInvalid={t('Error: URL is required and must be valid.')}
helperTextInvalid={state.validation.urlHelperText}
>
<TextInput
isRequired
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { useCallback, useReducer } from 'react';
import { Trans } from 'react-i18next';
import { Base64 } from 'js-base64';
import {
openstackSecretFieldValidator,
Expand All @@ -7,7 +8,16 @@ import {
} from 'src/modules/Providers/utils';
import { useForkliftTranslation } from 'src/utils/i18n';

import { Divider, FileUpload, Form, FormGroup, Radio, Switch } from '@patternfly/react-core';
import {
Divider,
FileUpload,
Form,
FormGroup,
Popover,
Radio,
Switch,
} from '@patternfly/react-core';
import HelpIcon from '@patternfly/react-icons/dist/esm/icons/help-icon';

import { EditComponentProps } from '../BaseCredentialsSection';

Expand All @@ -22,6 +32,29 @@ import {
export const OpenstackCredentialsEdit: React.FC<EditComponentProps> = ({ secret, onChange }) => {
const { t } = useForkliftTranslation();

const insecureSkipVerifyHelperTextMsgs = {
error: t('Error: this field must be set to a boolean value.'),
successAndSkipped: t("The provider's CA certificate won't be validated."),
successAndNotSkipped: t("The provider's CA certificate will be validated."),
};

const cacertHelperTextMsgs = {
error: t(
'Error: The format of the provided CA Certificate is invalid. Ensure the CA certificate format is valid.',
),
success: t(
'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 certificates, leave the field empty.',
),
};

const insecureSkipVerifyHelperTextPopover = (
<Trans t={t} ns="plugin__forklift-console-plugin">
Note: If {"'"}Skip certificate validation{"'"} is selected, migrations from this provider will
not be secure.{'<br><br>'}Insecure migration means that the transferred data is sent over an
insecure connection and potentially sensitive data could be exposed.
</Trans>
);

const authType = safeBase64Decode(secret?.data?.authType || '');
const username = safeBase64Decode(secret?.data?.username || '');
const insecureSkipVerify = safeBase64Decode(secret?.data?.insecureSkipVerify || '') === 'true';
Expand Down Expand Up @@ -62,6 +95,7 @@ export const OpenstackCredentialsEdit: React.FC<EditComponentProps> = ({ secret,
authenticationType: authenticationType,
validation: {
cacert: 'default' as Validation,
insecureSkipVerify: 'default' as Validation,
},
};

Expand Down Expand Up @@ -151,7 +185,9 @@ export const OpenstackCredentialsEdit: React.FC<EditComponentProps> = ({ secret,
role="radiogroup"
fieldId="authType"
label={t('Authentication type')}
helperText={t('Type of authentication to use when connecting to OpenStack REST API.')}
helperText={t(
'Method of authentication to use when connecting to the OpenStack Identity (Keystone) server.',
)}
>
<Radio
name="authType"
Expand Down Expand Up @@ -212,6 +248,21 @@ export const OpenstackCredentialsEdit: React.FC<EditComponentProps> = ({ secret,

<FormGroup
label={t('Skip certificate validation')}
labelIcon={
<Popover
headerContent={<div>Skip certificate validation</div>}
bodyContent={<div>{insecureSkipVerifyHelperTextPopover}</div>}
alertSeverityVariant="info"
>
<button
type="button"
onClick={(e) => e.preventDefault()}
className="pf-c-form__group-label-help"
>
<HelpIcon noVerticalAlign />
</button>
</Popover>
}
fieldId="insecureSkipVerify"
validated={state.validation.insecureSkipVerify}
helperTextInvalid={t('Error: Insecure Skip Verify must be a boolean value.')}
Expand All @@ -220,9 +271,9 @@ export const OpenstackCredentialsEdit: React.FC<EditComponentProps> = ({ secret,
className="forklift-section-secret-edit-switch"
id="insecureSkipVerify"
name="insecureSkipVerify"
label={t("The provider's REST API TLS certificate won't be validated.")}
labelOff={t("The provider's REST API TLS certificate will be validated.")}
aria-label={t("If true, the provider's REST API TLS certificate won't be validated.")}
label={insecureSkipVerifyHelperTextMsgs.successAndSkipped}
labelOff={insecureSkipVerifyHelperTextMsgs.successAndNotSkipped}
aria-label={insecureSkipVerifyHelperTextMsgs.successAndSkipped}
isChecked={insecureSkipVerify}
hasCheckIcon
onChange={(value) => handleChange('insecureSkipVerify', value ? 'true' : 'false')}
Expand All @@ -231,15 +282,13 @@ export const OpenstackCredentialsEdit: React.FC<EditComponentProps> = ({ secret,
<FormGroup
label={
insecureSkipVerify
? t('CA certificate - disabled when skip certificate validation is checked')
: t('CA certificate - leave empty to use system certificates')
? t("CA certificate - disabled when 'Skip certificate validation' is checked")
: t('CA certificate - leave empty to use system CA certificates')
}
fieldId="cacert"
helperText={t(
'Custom certification used to verify the OpenStack REST API server, when empty use system certificate.',
)}
helperText={cacertHelperTextMsgs.success}
validated={state.validation.cacert}
helperTextInvalid={t('Error: CA Certificate must be valid.')}
helperTextInvalid={cacertHelperTextMsgs.error}
>
<FileUpload
id="cacert"
Expand Down
Loading

0 comments on commit dc2b7ce

Please sign in to comment.