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
4 changes: 4 additions & 0 deletions libs/types/assisted-installer-service.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1838,6 +1838,10 @@ export interface InfraEnv {
* The type of network configuration for hosts: 'dhcp' for DHCP only, 'static' for static IP configuration.
*/
hostsNetworkConfigurationType?: 'dhcp' | 'static';
/**
* The pull secret obtained from Red Hat OpenShift Cluster Manager at console.redhat.com/openshift/install/pull-secret.
*/
pullSecret?: string;
}
export interface InfraEnvCreateParams {
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,15 @@ const ClusterDetails = ({ cluster, infraEnv }: ClusterDetailsProps) => {
const navigate = useNavigate();
const dispatch = useDispatch();
const { usedClusterNames } = useUsedClusterNames(cluster?.id || '');
const pullSecret = usePullSecret();
const {
error: errorOCPVersions,
loading: loadingOCPVersions,
latestVersions: versions,
} = useOpenShiftVersionsContext();
const location = useLocation();
const isSingleClusterFeatureEnabled = useFeature('ASSISTED_INSTALLER_SINGLE_CLUSTER_FEATURE');
const defaultPullSecret = usePullSecret();
const pullSecret = isSingleClusterFeatureEnabled ? infraEnv?.pullSecret || '' : defaultPullSecret;

const handleClusterUpdate = React.useCallback(
async (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,6 @@ export type ClusterWizardContextType = {
setInstallDisconnected: (enabled: boolean) => void;
disconnectedInfraEnv?: InfraEnv;
setDisconnectedInfraEnv: (infraEnv: InfraEnv | undefined) => void;
/** Last pull secret submitted in disconnected Optional configurations (restored when going back from Review) */
disconnectedFormPullSecret?: string;
setDisconnectedFormPullSecret: (value: string | undefined) => void;
/** Whether "Edit pull secret" was checked when user last clicked Next (restored when going back from Review) */
disconnectedFormEditPullSecret?: boolean;
setDisconnectedFormEditPullSecret: (value: boolean) => void;
};

export const ClusterWizardContext = React.createContext<ClusterWizardContextType | null>(null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,6 @@ const ClusterWizardContextProvider = ({
const [disconnectedInfraEnv, setDisconnectedInfraEnv] = React.useState<InfraEnv | undefined>(
infraEnv,
);
const [disconnectedFormPullSecret, setDisconnectedFormPullSecret] = React.useState<string>();
const [disconnectedFormEditPullSecret, setDisconnectedFormEditPullSecret] =
React.useState<boolean>();
const location = useLocation();
const locationState = location.state as ClusterWizardFlowStateType | undefined;
const {
Expand Down Expand Up @@ -334,10 +331,6 @@ const ClusterWizardContextProvider = ({
},
disconnectedInfraEnv,
setDisconnectedInfraEnv,
disconnectedFormPullSecret,
setDisconnectedFormPullSecret,
disconnectedFormEditPullSecret,
setDisconnectedFormEditPullSecret,
};
}, [
wizardStepIds,
Expand All @@ -353,8 +346,6 @@ const ClusterWizardContextProvider = ({
connectedWizardStepIds,
disconnectedWizardStepIds,
disconnectedInfraEnv,
disconnectedFormPullSecret,
disconnectedFormEditPullSecret,
]);

if (!contextValue) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import * as React from 'react';
import { Formik, useFormikContext } from 'formik';
import { Formik } from 'formik';
import { TFunction } from 'i18next';
import * as Yup from 'yup';
import {
ClusterWizardStep,
TechnologyPreview,
sshPublicKeyValidationSchema,
pullSecretValidationSchema,
getFormikErrorFields,
httpProxyValidationSchema,
noProxyValidationSchema,
Expand All @@ -15,16 +14,17 @@ import {
CheckboxField,
AdditionalNTPSourcesField,
ProxyFieldsType,
PullSecret,
pullSecretValidationSchema,
} from '../../../../common';
import { Split, SplitItem, Grid, GridItem, Form, Content, Checkbox } from '@patternfly/react-core';
import { Split, SplitItem, Grid, GridItem, Form, Content } from '@patternfly/react-core';
import { useClusterWizardContext } from '../ClusterWizardContext';
import ClusterWizardFooter from '../ClusterWizardFooter';
import ClusterWizardNavigation from '../ClusterWizardNavigation';
import { WithErrorBoundary } from '../../../../common/components/ErrorHandling/WithErrorBoundary';
import UploadSSH from '../../../../common/components/clusterConfiguration/UploadSSH';
import PullSecretField from '../../../../common/components/ui/formik/PullSecretField';
import { ProxyInputFields } from '../../../../common/components/clusterConfiguration/ProxyFields';
import { isInOcm, handleApiError, getApiErrorMessage } from '../../../../common/api';
import { handleApiError, getApiErrorMessage } from '../../../../common/api';
import { useAlerts } from '../../../../common/components/AlertsContextProvider';
import { AlertVariant } from '@patternfly/react-core';
import InfraEnvsService from '../../../services/InfraEnvsService';
Expand All @@ -43,22 +43,22 @@ import { HostsNetworkConfigurationType } from '../../../services/types';
import { useTranslation } from '../../../../common/hooks/use-translation-wrapper';
import { getDummyInfraEnvField } from '../../clusterConfiguration/staticIp/data/dummyData';
import { ntpSourceValidationSchema } from '../../../../common/validationSchemas/ntpValidation';
import { isInOcm } from '../../../../common/api';

const DEFAULT_CPU_ARCHITECTURE = 'x86_64' as const;
const DISCONNECTED_IMAGE_TYPE = 'disconnected-iso' as const;

type OptionalConfigurationsFormValues = ProxyFieldsType & {
sshPublicKey?: string;
pullSecret: string;
enableNtpSources: boolean;
additionalNtpSources?: string;
hostsNetworkConfigurationType: HostsNetworkConfigurationType;
rendezvousIp?: string;
pullSecret?: string;
};

const DEFAULT_INITIAL_VALUES: OptionalConfigurationsFormValues = {
sshPublicKey: '',
pullSecret: '',
enableProxy: false,
httpProxy: '',
httpsProxy: '',
Expand All @@ -67,18 +67,15 @@ const DEFAULT_INITIAL_VALUES: OptionalConfigurationsFormValues = {
additionalNtpSources: '',
hostsNetworkConfigurationType: HostsNetworkConfigurationType.DHCP,
rendezvousIp: '',
pullSecret: '',
};

/**
* Rehydrate form values from infraEnv and optional stored pull secret (API does not return pull secret).
* Rehydrate form values from infraEnv when navigating back to this step.
*/
const infraEnvToFormValues = (
infraEnv: InfraEnv,
storedPullSecret?: string,
): OptionalConfigurationsFormValues => ({
const infraEnvToFormValues = (infraEnv: InfraEnv): OptionalConfigurationsFormValues => ({
...DEFAULT_INITIAL_VALUES,
sshPublicKey: infraEnv.sshAuthorizedKey ?? '',
pullSecret: storedPullSecret ?? '',
enableProxy: !!(infraEnv.proxy?.httpProxy || infraEnv.proxy?.httpsProxy),
httpProxy: infraEnv.proxy?.httpProxy ?? '',
httpsProxy: infraEnv.proxy?.httpsProxy ?? '',
Expand All @@ -105,7 +102,6 @@ const buildInfraEnvParams = (values: OptionalConfigurationsFormValues) => {
const hasProxy = Object.keys(proxy).length > 0;

return {
pullSecret: values.pullSecret,
...(values.sshPublicKey && { sshAuthorizedKey: values.sshPublicKey }),
...(hasProxy && { proxy }),
...(values.additionalNtpSources && {
Expand All @@ -120,11 +116,10 @@ const buildInfraEnvParams = (values: OptionalConfigurationsFormValues) => {
};
};

const getValidationSchema = (t: TFunction) =>
const getValidationSchema = (t: TFunction, requirePullSecret: boolean) =>
Yup.lazy((values: OptionalConfigurationsFormValues) =>
Yup.object().shape({
sshPublicKey: sshPublicKeyValidationSchema(t),
pullSecret: pullSecretValidationSchema(t).required('Required field'),
enableProxy: Yup.boolean().required(),
httpProxy: httpProxyValidationSchema({
values,
Expand All @@ -151,40 +146,20 @@ const getValidationSchema = (t: TFunction) =>
'Not a valid IP address',
(value) => !value || ipValidationSchema(t).isValidSync(value),
),
...(requirePullSecret && {
pullSecret: pullSecretValidationSchema(t).required(t('ai:Required field')),
}),
}),
);

const PullSecretSync = () => {
const defaultPullSecret = usePullSecret();
const {
setFieldValue,
values: { pullSecret },
} = useFormikContext<OptionalConfigurationsFormValues>();

React.useEffect(() => {
if (defaultPullSecret !== undefined && pullSecret === '') {
setFieldValue('pullSecret', defaultPullSecret);
}
}, [defaultPullSecret, pullSecret, setFieldValue]);

return null;
};

const OptionalConfigurationsStep = () => {
const { clusterId } = useParams<{ clusterId: string }>();
const [cluster, setCluster] = React.useState<Cluster | null>(null);
const { t } = useTranslation();
const defaultPullSecret = usePullSecret();

const {
moveNext,
moveBack,
setDisconnectedInfraEnv,
disconnectedInfraEnv,
disconnectedFormPullSecret,
setDisconnectedFormPullSecret,
disconnectedFormEditPullSecret,
setDisconnectedFormEditPullSecret,
} = useClusterWizardContext();
const { moveNext, moveBack, setDisconnectedInfraEnv, disconnectedInfraEnv } =
useClusterWizardContext();
const { addAlert, clearAlerts } = useAlerts();

React.useEffect(() => {
Expand All @@ -209,14 +184,14 @@ const OptionalConfigurationsStep = () => {
}, [clusterId, addAlert, t]);

const initialValues: OptionalConfigurationsFormValues = disconnectedInfraEnv
? infraEnvToFormValues(disconnectedInfraEnv, disconnectedFormPullSecret)
? infraEnvToFormValues(disconnectedInfraEnv)
: DEFAULT_INITIAL_VALUES;

return (
<Formik
initialValues={initialValues}
validateOnMount
validationSchema={getValidationSchema(t)}
validationSchema={getValidationSchema(t, !isInOcm)}
onSubmit={async (values) => {
clearAlerts();
if (!cluster?.id) {
Expand All @@ -229,22 +204,21 @@ const OptionalConfigurationsStep = () => {
}

const commonParams = buildInfraEnvParams(values);
// Default pull secret only exists in OCM; when !isInOcm the user must provide it via the field
const pullSecretToUse = isInOcm ? defaultPullSecret : values.pullSecret;

try {
if (disconnectedInfraEnv?.id) {
// Update existing infraEnv
const updateParams: InfraEnvUpdateParams = {
...commonParams,
imageType: DISCONNECTED_IMAGE_TYPE,
pullSecret: pullSecretToUse,
};
const { data: updatedInfraEnv } = await InfraEnvsAPI.update(
disconnectedInfraEnv.id,
updateParams,
);
setDisconnectedFormPullSecret(values.pullSecret);
setDisconnectedFormEditPullSecret(
disconnectedFormEditPullSecret ?? !!disconnectedInfraEnv?.pullSecretSet,
);
setDisconnectedInfraEnv({
...updatedInfraEnv,
// infraEnv does not return the whole OCP version
Expand All @@ -258,13 +232,10 @@ const OptionalConfigurationsStep = () => {
openshiftVersion: cluster.openshiftVersion,
cpuArchitecture: DEFAULT_CPU_ARCHITECTURE,
imageType: DISCONNECTED_IMAGE_TYPE,
pullSecret: pullSecretToUse ?? '',
...commonParams,
};
const createdInfraEnv = await InfraEnvsService.create(createParams);
setDisconnectedFormPullSecret(values.pullSecret);
setDisconnectedFormEditPullSecret(
disconnectedFormEditPullSecret ?? !!disconnectedInfraEnv?.pullSecretSet,
);
setDisconnectedInfraEnv({
...createdInfraEnv,
// infraEnv does not return the whole OCP version
Expand Down Expand Up @@ -306,7 +277,6 @@ const OptionalConfigurationsStep = () => {
}
>
<WithErrorBoundary title="Failed to load Optional Configurations step">
<PullSecretSync />
<Grid hasGutter>
<GridItem>
<Split>
Expand All @@ -320,6 +290,9 @@ const OptionalConfigurationsStep = () => {
</GridItem>
<GridItem>
<Form id="wizard-cluster-optional-configurations__form">
{/* Pull secret (!OCM only) */}
{!isInOcm && <PullSecret defaultPullSecret={defaultPullSecret} isOcm={false} />}

{/* Rendezvous IP */}
<InputField
label={t('ai:Rendezvous IP')}
Expand All @@ -332,17 +305,6 @@ const OptionalConfigurationsStep = () => {
/>

<UploadSSH />
<Checkbox
label={t('ai:Edit pull secret')}
isChecked={
disconnectedFormEditPullSecret ?? !!disconnectedInfraEnv?.pullSecretSet
}
onChange={(_, checked) => setDisconnectedFormEditPullSecret(checked)}
id="edit-pull-secret-checkbox"
/>
{(disconnectedFormEditPullSecret ?? !!disconnectedInfraEnv?.pullSecretSet) && (
<PullSecretField isOcm={isInOcm} />
)}

{/* Proxy Settings */}
<CheckboxField
Expand Down
Loading