Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance OpenStack provider help text fields #861

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

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) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

urlValidationHook is a validator, it should be implemented in the validations part of our project, please move it to currect directory.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was not added as part of this PR, I just added more logic to that method.
In addition, this validation method exists for all providers as part of the modal code (ocp, oVirt,vsphere), so better do that in a separate follow up PR

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"you touch it you own it", it was not in the right place before you added to it, and it's not in it's right place now. Since you added to it, can you move it to the currect place ?
p.s.
you will also need to rename to fit the validation methods naming convention

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm ok with a folow up PR to fix in all places this method is used out of place

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.',

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First ooccurance: please change to "CA certificate"

),
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.',
),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is system CA certificates supposed to be plural? [I don't know]

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

both works for me, it's one certificate per our provider, but the system may have more then one for other servers, so both singular and plural are ok with me.

Copy link
Collaborator Author

@sgratch sgratch Jan 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll change to a singular phrasing since the current text refers to the ca assigned to our provider:
"To use the system CA certificate, 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.
Copy link

@RichardHoch RichardHoch Jan 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"not be secure[tags], meaning that the transferred data..."

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not be secure => be inscure
when I talkd to people i say the connection is inscure, "the connnection is not secure" sounds strange to my ears

</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")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"is selected"

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RichardHoch This is a Switch component for selecting the options, so I wonder which action is commonly used for a Switch clicking. I checked other documentations and saw that they use checked but I guess that select is ok as well....

: t('CA certificate - leave empty to use system CA certificates')
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CA certificates or cetrtificate?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here I'll leave the plural phrasing...

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
Loading