Skip to content
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
1 change: 1 addition & 0 deletions libs/locales/lib/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
"ai:Adding hosts instructions": "Adding hosts instructions",
"ai:Adding...": "Adding...",
"ai:Additional certificates": "Additional certificates",
"ai:Additional NTP sources": "Additional NTP sources",
"ai:Additional NTP Sources": "Additional NTP sources",
"ai:Address": "Address",
"ai:Address is the host/ip that the NodePort service is exposed over.": "Address is the host or IP address that exposes the NodePort service.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ export const ProxyInputFields = () => {
}
};
const { t } = useTranslation();
const hasHttp = !!values.httpProxy?.trim();
const hasHttps = !!values.httpsProxy?.trim();

return (
<Grid hasGutter>
<InputField
Expand All @@ -31,6 +34,7 @@ export const ProxyInputFields = () => {
}
name="httpProxy"
placeholder="http://<user>:<password>@<ipaddr>:<port>"
isRequired={values.enableProxy && !hasHttps}
helperText={
<div>
<Trans
Expand All @@ -54,6 +58,7 @@ export const ProxyInputFields = () => {
}
name="httpsProxy"
placeholder="http://<user>:<password>@<ipaddr>:<port>"
isRequired={values.enableProxy && !hasHttp}
helperText={
<div>
<Trans
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const AdditionalNTPSourcesForm = ({

const getValidationSchema = (t: TFunction) =>
Yup.object().shape({
additionalNtpSource: ntpSourceValidationSchema(t).required(t('ai:Required field')),
additionalNtpSource: ntpSourceValidationSchema(t, false),
});

const { t } = useTranslation();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ const TextAreaField: React.FC<React.PropsWithChildren<TextAreaFieldProps>> = ({
validated={isValid ? 'default' : 'error'}
isRequired={isRequired}
aria-describedby={`${fieldId}-helper`}
onChange={(event) => field.onChange(event)}
onChange={(event) => {
field.onChange(event);
restProps.onChange?.(event);
}}
disabled={isDisabled}
/>
{(errorMessage || helperText) && (
Expand Down
1 change: 1 addition & 0 deletions libs/ui-lib/lib/common/config/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export const clusterFieldLabels = (t: TFunction): { [key in string]: string } =>
httpProxy: t('ai:HTTP proxy'),
httpsProxy: t('ai:HTTPS proxy'),
noProxy: t('ai:No proxy'),
additionalNtpSources: t('ai:Additional NTP sources'),
machineNetworks: t('ai:Machine networks'),
clusterNetworks: t('ai:Cluster networks'),
serviceNetworks: t('ai:Service networks'),
Expand Down
20 changes: 13 additions & 7 deletions libs/ui-lib/lib/common/validationSchemas/ntpValidation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,34 @@ import { trimCommaSeparatedList } from '../components/ui/formik/utils';
import { isIPorDN } from './utils';
import { TFunction } from 'i18next';

export const ntpSourceValidationSchema = (t: TFunction) =>
Yup.string()
export const ntpSourceValidationSchema = (t: TFunction, allowEmpty = true) => {
let schema = Yup.string();

if (!allowEmpty) {
schema = schema.trim().required(t('ai:Required field'));
}

return schema
.test(
'ntp-source-validation',
t('ai:Provide a comma separated list of valid DNS names or IP addresses.'),
(value?: string) => {
if (!value || value === '') {
if (!value || value.trim() === '') {
return true;
}
return trimCommaSeparatedList(value)
.split(',')
.every((v) => isIPorDN(v));
const parts = trimCommaSeparatedList(value).split(',');
return parts.length > 0 && parts.every((v) => isIPorDN(v));
},
)
.test(
'ntp-source-validation-unique',
t('ai:DNS names and IP addresses must be unique.'),
(value?: string) => {
if (!value || value === '') {
if (!value || value.trim() === '') {
return true;
}
const arr = trimCommaSeparatedList(value).split(',');
return arr.length === new Set(arr).size;
},
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -102,19 +102,30 @@ const shouldSendDummyStaticConfig = (
(typeof infraEnv.staticNetworkConfig === 'string' &&
isDummyYaml(infraEnv.staticNetworkConfig)));

/**
* Builds common infrastructure environment params from form values.
* Proxy and NTP are only included when their respective checkboxes are checked.
*/
const buildInfraEnvParams = (
values: OptionalConfigurationsFormValues,
disconnectedInfraEnv: InfraEnv | null,
): InfraEnvUpdateParams => {
const proxy = {
...(values.httpProxy && { httpProxy: values.httpProxy }),
...(values.httpsProxy && { httpsProxy: values.httpsProxy }),
...(values.noProxy && { noProxy: values.noProxy }),
};
const proxy =
values.enableProxy && (values.httpProxy || values.httpsProxy || values.noProxy)
? {
...(values.httpProxy && { httpProxy: values.httpProxy }),
...(values.httpsProxy && { httpsProxy: values.httpsProxy }),
...(values.noProxy && { noProxy: values.noProxy }),
}
: undefined;

return {
...(values.sshPublicKey && { sshAuthorizedKey: values.sshPublicKey }),
...(Object.keys(proxy).length > 0 && { proxy }),
...(values.additionalNtpSources && { additionalNtpSources: values.additionalNtpSources }),
...(proxy && { proxy }),
...(values.enableNtpSources &&
values.additionalNtpSources?.trim() && {
additionalNtpSources: values.additionalNtpSources.trim(),
}),
...(values.rendezvousIp && { rendezvousIp: values.rendezvousIp }),
...(shouldSendDummyStaticConfig(values, disconnectedInfraEnv) && {
staticNetworkConfig: getDummyInfraEnvField(),
Expand Down Expand Up @@ -148,21 +159,27 @@ const getValidationSchema = (t: TFunction) =>
Yup.object().shape({
sshPublicKey: sshPublicKeyValidationSchema(t),
enableProxy: Yup.boolean().required(),
httpProxy: httpProxyValidationSchema({
values,
pairValueName: 'httpsProxy',
allowEmpty: true,
t,
}),
httpsProxy: httpProxyValidationSchema({
values,
pairValueName: 'httpProxy',
allowEmpty: true,
t,
}),
noProxy: noProxyValidationSchema(t),
httpProxy: values.enableProxy
? httpProxyValidationSchema({
values,
pairValueName: 'httpsProxy',
allowEmpty: false,
t,
})
: Yup.string().optional(),
httpsProxy: values.enableProxy
? httpProxyValidationSchema({
values,
pairValueName: 'httpProxy',
allowEmpty: false,
t,
})
: Yup.string().optional(),
noProxy: values.enableProxy ? noProxyValidationSchema(t) : Yup.string().optional(),
enableNtpSources: Yup.boolean().required(),
additionalNtpSources: ntpSourceValidationSchema(t),
additionalNtpSources: values.enableNtpSources
? ntpSourceValidationSchema(t, false)
: Yup.string().optional(),
hostsNetworkConfigurationType: Yup.string()
.oneOf(Object.values(HostsNetworkConfigurationType))
.required(),
Expand All @@ -187,11 +204,50 @@ const OptionalConfigurationsStepForm = ({
}: OptionalConfigurationsStepFormProps) => {
const { t } = useTranslation();
const { moveNext, moveBack, disconnectedInfraEnv } = useClusterWizardContext();
const { isValid, errors, touched, isSubmitting, values } =
useFormikContext<OptionalConfigurationsFormValues>();
const {
isValid,
errors,
touched,
isSubmitting,
values,
setFieldValue,
setFieldError,
setFieldTouched,
validateForm,
} = useFormikContext<OptionalConfigurationsFormValues>();

const errorFields = getFormikErrorFields(errors, touched);

const onProxyCheckboxChange = React.useCallback(
(checked: boolean) => {
if (!checked) {
setFieldValue('httpProxy', '');
setFieldValue('httpsProxy', '');
setFieldValue('noProxy', '');
setFieldError('httpProxy', undefined);
setFieldError('httpsProxy', undefined);
setFieldError('noProxy', undefined);
setFieldTouched('httpProxy', false);
setFieldTouched('httpsProxy', false);
setFieldTouched('noProxy', false);
setTimeout(() => void validateForm(), 0);
}
},
[setFieldValue, setFieldError, setFieldTouched, validateForm],
);

const onNtpCheckboxChange = React.useCallback(
(checked: boolean) => {
if (!checked) {
setFieldValue('additionalNtpSources', '');
setFieldError('additionalNtpSources', undefined);
setFieldTouched('additionalNtpSources', false);
setTimeout(() => void validateForm(), 0);
}
},
[setFieldValue, setFieldError, setFieldTouched, validateForm],
);

return (
<>
{cluster && disconnectedInfraEnv && <FormikAutoSave debounce={0} />}
Expand Down Expand Up @@ -240,6 +296,7 @@ const OptionalConfigurationsStepForm = ({
<CheckboxField
label={t('ai:Configure proxy settings')}
name="enableProxy"
onChange={onProxyCheckboxChange}
helperText={
<p>
{t(
Expand All @@ -254,6 +311,7 @@ const OptionalConfigurationsStepForm = ({
<CheckboxField
label={t('ai:Add your own NTP (Network Time Protocol) sources')}
name="enableNtpSources"
onChange={onNtpCheckboxChange}
helperText={
<p>
{t(
Expand Down
Loading