@openshift-assisted/ui-lib 2.55.2 → 2.55.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/build/@types/ocm/components/clusterConfiguration/staticIp/components/StaticIpPage.d.ts.map +1 -1
  2. package/build/@types/ocm/components/clusterWizard/ClusterDetailsForm.d.ts.map +1 -1
  3. package/build/@types/ocm/components/clusterWizard/ClusterWizardContextProvider.d.ts.map +1 -1
  4. package/build/@types/ocm/components/clusterWizard/ClusterWizardNavigation.d.ts +7 -0
  5. package/build/@types/ocm/components/clusterWizard/ClusterWizardNavigation.d.ts.map +1 -1
  6. package/build/@types/ocm/components/clusterWizard/NewClusterWizard.d.ts.map +1 -1
  7. package/build/@types/ocm/components/clusterWizard/StaticIp.d.ts +1 -1
  8. package/build/@types/ocm/components/clusterWizard/StaticIp.d.ts.map +1 -1
  9. package/build/@types/ocm/components/clusterWizard/disconnected/BasicStep.d.ts +1 -1
  10. package/build/@types/ocm/components/clusterWizard/disconnected/BasicStep.d.ts.map +1 -1
  11. package/build/@types/ocm/components/clusterWizard/disconnected/DisconnectedStaticIp.d.ts +4 -0
  12. package/build/@types/ocm/components/clusterWizard/disconnected/DisconnectedStaticIp.d.ts.map +1 -0
  13. package/build/@types/ocm/components/clusterWizard/disconnected/OptionalConfigurationsStep.d.ts.map +1 -1
  14. package/build/@types/ocm/components/clusterWizard/disconnected/ReviewStep.d.ts.map +1 -1
  15. package/build/@types/ocm/components/clusterWizard/utils.d.ts +7 -0
  16. package/build/@types/ocm/components/clusterWizard/utils.d.ts.map +1 -0
  17. package/build/@types/ocm/components/clusters/AssistedInstallerHeader.d.ts.map +1 -1
  18. package/build/cjs/common/components/clusterWizard/ClusterWizardStep.css +9 -0
  19. package/build/cjs/common/validationSchemas/addressValidation.js +2 -2
  20. package/build/cjs/common/validationSchemas/addressValidation.js.map +1 -1
  21. package/build/cjs/ocm/components/clusterConfiguration/staticIp/components/StaticIpPage.js +3 -1
  22. package/build/cjs/ocm/components/clusterConfiguration/staticIp/components/StaticIpPage.js.map +1 -1
  23. package/build/cjs/ocm/components/clusterWizard/ClusterDetailsForm.js +1 -2
  24. package/build/cjs/ocm/components/clusterWizard/ClusterDetailsForm.js.map +1 -1
  25. package/build/cjs/ocm/components/clusterWizard/ClusterWizardContextProvider.js +47 -47
  26. package/build/cjs/ocm/components/clusterWizard/ClusterWizardContextProvider.js.map +1 -1
  27. package/build/cjs/ocm/components/clusterWizard/ClusterWizardNavigation.js +31 -19
  28. package/build/cjs/ocm/components/clusterWizard/ClusterWizardNavigation.js.map +1 -1
  29. package/build/cjs/ocm/components/clusterWizard/NewClusterWizard.js +5 -0
  30. package/build/cjs/ocm/components/clusterWizard/NewClusterWizard.js.map +1 -1
  31. package/build/cjs/ocm/components/clusterWizard/StaticIp.js.map +1 -1
  32. package/build/cjs/ocm/components/clusterWizard/disconnected/BasicStep.js +2 -3
  33. package/build/cjs/ocm/components/clusterWizard/disconnected/BasicStep.js.map +1 -1
  34. package/build/cjs/ocm/components/clusterWizard/disconnected/DisconnectedStaticIp.js +26 -0
  35. package/build/cjs/ocm/components/clusterWizard/disconnected/DisconnectedStaticIp.js.map +1 -0
  36. package/build/cjs/ocm/components/clusterWizard/disconnected/OptionalConfigurationsStep.js +52 -19
  37. package/build/cjs/ocm/components/clusterWizard/disconnected/OptionalConfigurationsStep.js.map +1 -1
  38. package/build/cjs/ocm/components/clusterWizard/disconnected/ReviewStep.js +4 -5
  39. package/build/cjs/ocm/components/clusterWizard/disconnected/ReviewStep.js.map +1 -1
  40. package/build/cjs/ocm/components/clusterWizard/utils.js +63 -0
  41. package/build/cjs/ocm/components/clusterWizard/utils.js.map +1 -0
  42. package/build/cjs/ocm/components/clusters/AssistedInstallerHeader.js +0 -3
  43. package/build/cjs/ocm/components/clusters/AssistedInstallerHeader.js.map +1 -1
  44. package/lib/common/components/clusterWizard/ClusterWizardStep.css +9 -0
  45. package/lib/common/validationSchemas/_validationSchemas.test.ts +1 -1
  46. package/lib/common/validationSchemas/addressValidation.tsx +2 -2
  47. package/lib/ocm/components/clusterConfiguration/staticIp/components/StaticIpPage.tsx +4 -1
  48. package/lib/ocm/components/clusterWizard/ClusterDetailsForm.tsx +0 -2
  49. package/lib/ocm/components/clusterWizard/ClusterWizardContextProvider.tsx +48 -71
  50. package/lib/ocm/components/clusterWizard/ClusterWizardNavigation.tsx +39 -16
  51. package/lib/ocm/components/clusterWizard/NewClusterWizard.tsx +5 -0
  52. package/lib/ocm/components/clusterWizard/StaticIp.tsx +1 -1
  53. package/lib/ocm/components/clusterWizard/_wizardTransition.test.ts +60 -1
  54. package/lib/ocm/components/clusterWizard/disconnected/BasicStep.tsx +2 -3
  55. package/lib/ocm/components/clusterWizard/disconnected/DisconnectedStaticIp.tsx +29 -0
  56. package/lib/ocm/components/clusterWizard/disconnected/OptionalConfigurationsStep.tsx +72 -12
  57. package/lib/ocm/components/clusterWizard/disconnected/ReviewStep.tsx +9 -11
  58. package/lib/ocm/components/clusterWizard/utils.ts +75 -0
  59. package/lib/ocm/components/clusters/AssistedInstallerHeader.tsx +1 -9
  60. package/package.json +3 -3
@@ -13,6 +13,30 @@ import { staticIpFormViewSubSteps, wizardStepNames } from './constants';
13
13
  import WizardNavItem from '../../../common/components/ui/WizardNavItem';
14
14
  import { Cluster } from '@openshift-assisted/types/assisted-installer-service';
15
15
 
16
+ export type WizardNavEntry = {
17
+ stepId: ClusterWizardStepsType;
18
+ visualNumber: number;
19
+ isStaticIpFormGroup: boolean;
20
+ };
21
+
22
+ export const getWizardNavEntries = (wizardStepIds: ClusterWizardStepsType[]): WizardNavEntry[] => {
23
+ const entries: WizardNavEntry[] = [];
24
+ let i = 0;
25
+ let visualNumber = 1;
26
+ while (i < wizardStepIds.length) {
27
+ const stepId = wizardStepIds[i];
28
+ if (stepId === 'static-ip-network-wide-configurations') {
29
+ entries.push({ stepId, visualNumber, isStaticIpFormGroup: true });
30
+ i += 2;
31
+ } else {
32
+ entries.push({ stepId, visualNumber, isStaticIpFormGroup: false });
33
+ i += 1;
34
+ }
35
+ visualNumber += 1;
36
+ }
37
+ return entries;
38
+ };
39
+
16
40
  const isStepValid = (stepId: ClusterWizardStepsType, cluster?: Cluster): boolean => {
17
41
  if (!cluster) {
18
42
  return true;
@@ -48,7 +72,11 @@ const ClusterWizardNavigation = ({ cluster }: { cluster?: Cluster }) => {
48
72
  return stepId === 'cluster-details' ? false : isStepIdxAfterCurrent(idx);
49
73
  };
50
74
 
51
- const getNavItem = (idx: number, stepId: ClusterWizardStepsType): ReactNode => {
75
+ const getNavItem = (
76
+ idx: number,
77
+ stepId: ClusterWizardStepsType,
78
+ visualNumber: number,
79
+ ): ReactNode => {
52
80
  return (
53
81
  <WizardNavItem
54
82
  stepIndex={idx}
@@ -60,11 +88,12 @@ const ClusterWizardNavigation = ({ cluster }: { cluster?: Cluster }) => {
60
88
  isDisabled={isStepDisabled(idx, stepId)}
61
89
  isValid={() => isStepValid(stepId, cluster)}
62
90
  data-testid={`cluster-wizard-nav-item-${stepId}`}
91
+ style={{ '--cluster-wizard-step-number': `"${visualNumber}"` } as React.CSSProperties}
63
92
  />
64
93
  );
65
94
  };
66
95
 
67
- const getStaticIpFormViewNavItem = (idx: number): ReactNode => {
96
+ const getStaticIpFormViewNavItem = (idx: number, visualNumber: number): ReactNode => {
68
97
  return (
69
98
  <WizardNavItem
70
99
  stepIndex={idx}
@@ -74,13 +103,14 @@ const ClusterWizardNavigation = ({ cluster }: { cluster?: Cluster }) => {
74
103
  isCurrent={isStaticIpStep(clusterWizardContext.currentStepId)}
75
104
  isDisabled={isStepIdxAfterCurrent(idx)}
76
105
  data-testid={`cluster-wizard-nav-item-static-network-configuration-form-view`}
106
+ style={{ '--cluster-wizard-step-number': `"${visualNumber}"` } as React.CSSProperties}
77
107
  >
78
108
  <WizardNav
79
109
  isInnerList
80
110
  data-testid="cluster-wizard-nav-item-static-network-configuration-form-view-inner-list"
81
111
  >
82
112
  {staticIpFormViewSubSteps.map((stepId, subIdx) => {
83
- return getNavItem(idx + subIdx, stepId);
113
+ return getNavItem(idx + subIdx, stepId, visualNumber);
84
114
  })}
85
115
  </WizardNav>
86
116
  </WizardNavItem>
@@ -88,20 +118,13 @@ const ClusterWizardNavigation = ({ cluster }: { cluster?: Cluster }) => {
88
118
  };
89
119
 
90
120
  const getWizardNavItems = (): ReactNode[] => {
91
- const navItems: ReactNode[] = [];
92
- let i = 0;
93
- while (i < clusterWizardContext.wizardStepIds.length) {
94
- const stepId = clusterWizardContext.wizardStepIds[i];
95
- if (stepId !== 'static-ip-network-wide-configurations') {
96
- navItems.push(getNavItem(i, stepId));
97
- i += 1;
98
- } else {
99
- navItems.push(getStaticIpFormViewNavItem(i));
100
- //skip iteration on form view sub steps
101
- i += 2;
121
+ return getWizardNavEntries(clusterWizardContext.wizardStepIds).map((entry) => {
122
+ const idx = clusterWizardContext.wizardStepIds.indexOf(entry.stepId);
123
+ if (entry.isStaticIpFormGroup) {
124
+ return getStaticIpFormViewNavItem(idx, entry.visualNumber);
102
125
  }
103
- }
104
- return navItems;
126
+ return getNavItem(idx, entry.stepId, entry.visualNumber);
127
+ });
105
128
  };
106
129
 
107
130
  return <WizardNav data-testid="cluster-wizard-nav">{getWizardNavItems()}</WizardNav>;
@@ -5,6 +5,7 @@ import { useClusterWizardContext } from './ClusterWizardContext';
5
5
  import ReviewStep from './disconnected/ReviewStep';
6
6
  import BasicStep from './disconnected/BasicStep';
7
7
  import OptionalConfigurationsStep from './disconnected/OptionalConfigurationsStep';
8
+ import DisconnectedStaticIp from './disconnected/DisconnectedStaticIp';
8
9
  import { ClusterWizardStepsType } from './wizardTransition';
9
10
  import { useInfraEnv } from '../../hooks';
10
11
  import { CpuArchitecture } from '../../../common/types';
@@ -18,6 +19,10 @@ const getCurrentStep = (currentStepId: ClusterWizardStepsType, infraEnv?: InfraE
18
19
  return <OptionalConfigurationsStep />;
19
20
  case 'disconnected-basic':
20
21
  return <BasicStep />;
22
+ case 'static-ip-yaml-view':
23
+ case 'static-ip-network-wide-configurations':
24
+ case 'static-ip-host-configurations':
25
+ return <DisconnectedStaticIp />;
21
26
  default:
22
27
  return <ClusterDetails infraEnv={infraEnv} />;
23
28
  }
@@ -23,7 +23,7 @@ const getInitialFormStateProps = () => {
23
23
  isEmpty: true,
24
24
  };
25
25
  };
26
- const StaticIp: React.FC<StaticIpProps & { cluster: Cluster }> = ({
26
+ const StaticIp: React.FC<StaticIpProps & { cluster?: Cluster }> = ({
27
27
  cluster,
28
28
  infraEnv,
29
29
  updateInfraEnv,
@@ -8,7 +8,15 @@ import {
8
8
  Validation as HostValidation,
9
9
  } from '../../../common/types/hosts';
10
10
 
11
- import { canNextClusterDetails, canNextHostDiscovery, canNextStorage } from './wizardTransition';
11
+ import {
12
+ canNextClusterDetails,
13
+ canNextHostDiscovery,
14
+ canNextStorage,
15
+ disconnectedSteps,
16
+ } from './wizardTransition';
17
+ import { getWizardNavEntries } from './ClusterWizardNavigation';
18
+ import { getDisconnectedWizardStepIds } from './utils';
19
+ import { StaticIpView } from '../clusterConfiguration/staticIp/data/dataTypes';
12
20
  import { Cluster, Host } from '@openshift-assisted/types/assisted-installer-service';
13
21
 
14
22
  const clusterBase: Cluster = {
@@ -441,3 +449,54 @@ describe('OCM wizard transition of Storage', () => {
441
449
  expect(canNextStorage({ cluster })).toBe(true);
442
450
  });
443
451
  });
452
+
453
+ describe('Disconnected wizard step ids', () => {
454
+ test('starts with the three base disconnected steps', () => {
455
+ expect(getDisconnectedWizardStepIds()).toEqual(disconnectedSteps);
456
+ expect(getDisconnectedWizardStepIds('dhcp-selected')).toEqual(disconnectedSteps);
457
+ });
458
+
459
+ test('inserts static IP form steps and removes them again for DHCP', () => {
460
+ const withStatic = getDisconnectedWizardStepIds(StaticIpView.FORM);
461
+ expect(withStatic).toEqual([
462
+ 'disconnected-basic',
463
+ 'disconnected-optional-configurations',
464
+ 'static-ip-network-wide-configurations',
465
+ 'static-ip-host-configurations',
466
+ 'disconnected-review',
467
+ ]);
468
+ expect(getDisconnectedWizardStepIds('dhcp-selected')).toEqual(disconnectedSteps);
469
+ });
470
+
471
+ test('inserts the YAML static IP step', () => {
472
+ expect(getDisconnectedWizardStepIds(StaticIpView.YAML)).toEqual([
473
+ 'disconnected-basic',
474
+ 'disconnected-optional-configurations',
475
+ 'static-ip-yaml-view',
476
+ 'disconnected-review',
477
+ ]);
478
+ });
479
+ });
480
+
481
+ describe('Wizard nav visual numbers', () => {
482
+ test('numbers review as 3 when static IP steps are absent', () => {
483
+ const entries = getWizardNavEntries(disconnectedSteps);
484
+ expect(entries.map((entry) => [entry.stepId, entry.visualNumber])).toEqual([
485
+ ['disconnected-basic', 1],
486
+ ['disconnected-optional-configurations', 2],
487
+ ['disconnected-review', 3],
488
+ ]);
489
+ });
490
+
491
+ test('keeps a single number for form-view static IP and numbers review as 4', () => {
492
+ const entries = getWizardNavEntries(getDisconnectedWizardStepIds(StaticIpView.FORM));
493
+ expect(
494
+ entries.map((entry) => [entry.stepId, entry.visualNumber, entry.isStaticIpFormGroup]),
495
+ ).toEqual([
496
+ ['disconnected-basic', 1, false],
497
+ ['disconnected-optional-configurations', 2, false],
498
+ ['static-ip-network-wide-configurations', 3, true],
499
+ ['disconnected-review', 4, false],
500
+ ]);
501
+ });
502
+ });
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { ClusterWizardStep, TechnologyPreview, StaticTextField } from '../../../../common';
2
+ import { ClusterWizardStep, StaticTextField } from '../../../../common';
3
3
  import { Flex, Grid, GridItem, Form, Content } from '@patternfly/react-core';
4
4
  import { useClusterWizardContext } from '../ClusterWizardContext';
5
5
  import ClusterWizardFooter from '../ClusterWizardFooter';
@@ -7,7 +7,7 @@ import ClusterWizardNavigation from '../ClusterWizardNavigation';
7
7
  import { WithErrorBoundary } from '../../../../common/components/ErrorHandling/WithErrorBoundary';
8
8
  import InstallDisconnectedSwitch from './InstallDisconnectedSwitch';
9
9
 
10
- export const DISCONNECTED_OPENSHIFT_VERSION = '4.21.27';
10
+ export const DISCONNECTED_OPENSHIFT_VERSION = '4.22.13';
11
11
 
12
12
  const BasicStep = () => {
13
13
  const { moveNext } = useClusterWizardContext();
@@ -26,7 +26,6 @@ const BasicStep = () => {
26
26
  <Flex alignItems={{ default: 'alignItemsCenter' }} gap={{ default: 'gapSm' }}>
27
27
  <InstallDisconnectedSwitch />
28
28
  <span>I'm installing on a disconnected/air-gapped/secured environment</span>
29
- <TechnologyPreview />
30
29
  </Flex>
31
30
  </GridItem>
32
31
  <GridItem>
@@ -0,0 +1,29 @@
1
+ import React from 'react';
2
+ import { InfraEnvUpdateParams } from '@openshift-assisted/types/assisted-installer-service';
3
+ import { useClusterWizardContext } from '../ClusterWizardContext';
4
+ import { InfraEnvsAPI } from '../../../services/apis';
5
+ import StaticIp from '../StaticIp';
6
+
7
+ export const DisconnectedStaticIp: React.FC = () => {
8
+ const { disconnectedInfraEnv, setDisconnectedInfraEnv } = useClusterWizardContext();
9
+
10
+ const updateInfraEnv = React.useCallback(
11
+ async (params: InfraEnvUpdateParams) => {
12
+ if (!disconnectedInfraEnv?.id) {
13
+ throw new Error('No disconnected infraEnv available');
14
+ }
15
+ const { data: updatedInfraEnv } = await InfraEnvsAPI.update(disconnectedInfraEnv.id, params);
16
+ setDisconnectedInfraEnv(updatedInfraEnv);
17
+ return updatedInfraEnv;
18
+ },
19
+ [disconnectedInfraEnv, setDisconnectedInfraEnv],
20
+ );
21
+
22
+ if (!disconnectedInfraEnv) {
23
+ return null;
24
+ }
25
+
26
+ return <StaticIp infraEnv={disconnectedInfraEnv} updateInfraEnv={updateInfraEnv} />;
27
+ };
28
+
29
+ export default DisconnectedStaticIp;
@@ -1,8 +1,13 @@
1
1
  import * as React from 'react';
2
2
  import { Formik, useFormikContext } from 'formik';
3
3
  import * as Yup from 'yup';
4
- import { AlertVariant } from '@patternfly/react-core';
5
- import { Form, Grid, GridItem, Content } from '@patternfly/react-core';
4
+ import { AlertVariant, Form, Grid, GridItem, Content } from '@patternfly/react-core';
5
+ import {
6
+ HostStaticNetworkConfig,
7
+ InfraEnv,
8
+ InfraEnvUpdateParams,
9
+ ImageType,
10
+ } from '@openshift-assisted/types/assisted-installer-service';
6
11
  import {
7
12
  ClusterWizardStep,
8
13
  PullSecret,
@@ -14,15 +19,21 @@ import {
14
19
  sshPublicKeyValidationSchema,
15
20
  useAlerts,
16
21
  useTranslation,
22
+ InputField,
23
+ ipValidationSchema,
24
+ getFormikErrorFields,
17
25
  } from '../../../../common';
18
26
  import { WithErrorBoundary } from '../../../../common/components/ErrorHandling/WithErrorBoundary';
19
27
  import { useClusterWizardContext } from '../ClusterWizardContext';
20
28
  import ClusterWizardFooter from '../ClusterWizardFooter';
21
29
  import ClusterWizardNavigation from '../ClusterWizardNavigation';
22
30
  import { InfraEnvsAPI, ClustersAPI } from '../../../services/apis';
23
- import { ImageType } from '@openshift-assisted/types/assisted-installer-service';
24
31
  import usePullSecret from '../../../hooks/usePullSecret';
25
32
  import { DISCONNECTED_OPENSHIFT_VERSION } from './BasicStep';
33
+ import { HostsNetworkConfigurationControlGroup } from '../../clusterConfiguration/HostsNetworkConfigurationControlGroup';
34
+ import { HostsNetworkConfigurationType } from '../../../services/types';
35
+ import { getDummyInfraEnvField } from '../../clusterConfiguration/staticIp/data/dummyData';
36
+ import { getStaticNetworkConfig } from '../../clusterConfiguration/staticIp/data/fromInfraEnv';
26
37
 
27
38
  const DISCONNECTED_IMAGE_TYPE: ImageType = 'disconnected-iso';
28
39
  const DISCONNECTED_CLUSTER_NAME = 'disconnected-cluster';
@@ -30,6 +41,8 @@ const DISCONNECTED_CLUSTER_NAME = 'disconnected-cluster';
30
41
  type OptionalConfigurationsValues = {
31
42
  sshPublicKey: string;
32
43
  pullSecret: string;
44
+ rendezvousIp: string;
45
+ hostsNetworkConfigurationType: HostsNetworkConfigurationType;
33
46
  };
34
47
 
35
48
  type OptionalConfigurationsFormProps = {
@@ -42,7 +55,8 @@ const OptionalConfigurationsForm: React.FC<OptionalConfigurationsFormProps> = ({
42
55
  isSubmitting,
43
56
  }) => {
44
57
  const { moveBack } = useClusterWizardContext();
45
- const { isValid, submitForm } = useFormikContext<OptionalConfigurationsValues>();
58
+ const { isValid, submitForm, errors, touched } = useFormikContext<OptionalConfigurationsValues>();
59
+ const errorFields = getFormikErrorFields(errors, touched);
46
60
 
47
61
  return (
48
62
  <ClusterWizardStep
@@ -53,6 +67,7 @@ const OptionalConfigurationsForm: React.FC<OptionalConfigurationsFormProps> = ({
53
67
  onBack={moveBack}
54
68
  isSubmitting={isSubmitting}
55
69
  isNextDisabled={!isValid}
70
+ errorFields={errorFields}
56
71
  />
57
72
  }
58
73
  >
@@ -63,8 +78,15 @@ const OptionalConfigurationsForm: React.FC<OptionalConfigurationsFormProps> = ({
63
78
  </GridItem>
64
79
  <GridItem>
65
80
  <Form id="wizard-cluster-optional-config__form">
81
+ <InputField
82
+ label="Rendezvous IP"
83
+ name="rendezvousIp"
84
+ helperText="The IP address that hosts will use to communicate with the bootstrap node during installation."
85
+ maxLength={45}
86
+ />
66
87
  <UploadSSH />
67
88
  {!isInOcm && <PullSecret isOcm={false} defaultPullSecret={defaultPullSecret} />}
89
+ <HostsNetworkConfigurationControlGroup clusterExists={false} isDisabled={false} />
68
90
  </Form>
69
91
  </GridItem>
70
92
  </Grid>
@@ -73,6 +95,29 @@ const OptionalConfigurationsForm: React.FC<OptionalConfigurationsFormProps> = ({
73
95
  );
74
96
  };
75
97
 
98
+ const getStaticNetworkConfigUpdate = (
99
+ values: OptionalConfigurationsValues,
100
+ infraEnv: InfraEnv | undefined,
101
+ ): HostStaticNetworkConfig[] => {
102
+ if (values.hostsNetworkConfigurationType === HostsNetworkConfigurationType.DHCP) {
103
+ return [];
104
+ }
105
+ // Static IP selected — resend existing config, or seed with dummy if none exists yet.
106
+ if (!infraEnv?.staticNetworkConfig) {
107
+ return getDummyInfraEnvField();
108
+ }
109
+ return getStaticNetworkConfig(infraEnv) ?? getDummyInfraEnvField();
110
+ };
111
+
112
+ const buildInfraEnvUpdateParams = (
113
+ values: OptionalConfigurationsValues,
114
+ disconnectedInfraEnv: InfraEnv | undefined,
115
+ ): InfraEnvUpdateParams => ({
116
+ sshAuthorizedKey: values.sshPublicKey,
117
+ rendezvousIp: values.rendezvousIp,
118
+ staticNetworkConfig: getStaticNetworkConfigUpdate(values, disconnectedInfraEnv),
119
+ });
120
+
76
121
  const OptionalConfigurationsStep = () => {
77
122
  const { t } = useTranslation();
78
123
  const {
@@ -91,6 +136,9 @@ const OptionalConfigurationsStep = () => {
91
136
  Yup.object({
92
137
  sshPublicKey: sshPublicKeyValidationSchema(t),
93
138
  pullSecret: isInOcm ? Yup.string() : pullSecretValidationSchema(t),
139
+ rendezvousIp: Yup.string()
140
+ .max(45, 'IP address must be at most 45 characters')
141
+ .concat(ipValidationSchema(t)),
94
142
  }),
95
143
  [t],
96
144
  );
@@ -98,6 +146,10 @@ const OptionalConfigurationsStep = () => {
98
146
  const initialValues: OptionalConfigurationsValues = {
99
147
  sshPublicKey: disconnectedInfraEnv?.sshAuthorizedKey ?? '',
100
148
  pullSecret: defaultPullSecret ?? '',
149
+ rendezvousIp: disconnectedInfraEnv?.rendezvousIp ?? '',
150
+ hostsNetworkConfigurationType: disconnectedInfraEnv?.staticNetworkConfig
151
+ ? HostsNetworkConfigurationType.STATIC
152
+ : HostsNetworkConfigurationType.DHCP,
101
153
  };
102
154
 
103
155
  const handleNext = React.useCallback(
@@ -107,12 +159,9 @@ const OptionalConfigurationsStep = () => {
107
159
  try {
108
160
  const pullSecretToUse = isInOcm ? defaultPullSecret ?? '' : values.pullSecret;
109
161
 
110
- if (disconnectedCluster?.id && disconnectedInfraEnv?.id) {
111
- const { data: updatedInfraEnv } = await InfraEnvsAPI.update(disconnectedInfraEnv.id, {
112
- sshAuthorizedKey: values.sshPublicKey,
113
- });
114
- setDisconnectedInfraEnv(updatedInfraEnv);
115
- } else {
162
+ let infraEnvToUse: InfraEnv | undefined = disconnectedInfraEnv;
163
+
164
+ if (!disconnectedCluster?.id || !infraEnvToUse?.id) {
116
165
  const { data: cluster } = await ClustersAPI.registerDisconnected({
117
166
  name: DISCONNECTED_CLUSTER_NAME,
118
167
  openshiftVersion: DISCONNECTED_OPENSHIFT_VERSION,
@@ -126,10 +175,22 @@ const OptionalConfigurationsStep = () => {
126
175
  imageType: DISCONNECTED_IMAGE_TYPE,
127
176
  openshiftVersion: DISCONNECTED_OPENSHIFT_VERSION,
128
177
  sshAuthorizedKey: values.sshPublicKey || undefined,
178
+ rendezvousIp: values.rendezvousIp || undefined,
179
+ staticNetworkConfig:
180
+ values.hostsNetworkConfigurationType === HostsNetworkConfigurationType.STATIC
181
+ ? getDummyInfraEnvField()
182
+ : undefined,
129
183
  });
130
- setDisconnectedInfraEnv(createdInfraEnv);
184
+ infraEnvToUse = createdInfraEnv;
185
+ } else {
186
+ const { data: updatedInfraEnv } = await InfraEnvsAPI.update(
187
+ infraEnvToUse.id,
188
+ buildInfraEnvUpdateParams(values, infraEnvToUse),
189
+ );
190
+ infraEnvToUse = updatedInfraEnv;
131
191
  }
132
192
 
193
+ setDisconnectedInfraEnv(infraEnvToUse);
133
194
  moveNext();
134
195
  } catch (error) {
135
196
  handleApiError(error, () => {
@@ -158,7 +219,6 @@ const OptionalConfigurationsStep = () => {
158
219
  return (
159
220
  <Formik<OptionalConfigurationsValues>
160
221
  initialValues={initialValues}
161
- enableReinitialize
162
222
  validationSchema={validationSchema}
163
223
  onSubmit={(values) => void handleNext(values)}
164
224
  >
@@ -2,8 +2,6 @@ import * as React from 'react';
2
2
  import { saveAs } from 'file-saver';
3
3
  import { useNavigate } from 'react-router';
4
4
  import {
5
- Split,
6
- SplitItem,
7
5
  Alert,
8
6
  Grid,
9
7
  DescriptionList,
@@ -18,7 +16,6 @@ import {
18
16
  } from '@patternfly/react-core';
19
17
  import {
20
18
  ClusterWizardStep,
21
- TechnologyPreview,
22
19
  ExternalLink,
23
20
  getDisconnectedDocsLink,
24
21
  getMajorMinorVersion,
@@ -53,14 +50,7 @@ const ReviewStep = () => {
53
50
  >
54
51
  <WithErrorBoundary title="Failed to load Review step">
55
52
  <Grid hasGutter>
56
- <Split>
57
- <SplitItem>
58
- <Content component="h2">Review and download ISO</Content>
59
- </SplitItem>
60
- <SplitItem>
61
- <TechnologyPreview />
62
- </SplitItem>
63
- </Split>
53
+ <Content component="h2">Review and download ISO</Content>
64
54
  <Alert isInline variant="info" title="ISO boot instructions">
65
55
  <List component={ListComponent.ol} type={OrderType.number}>
66
56
  <ListItem>Download the ISO.</ListItem>
@@ -98,6 +88,14 @@ const ReviewStep = () => {
98
88
  {disconnectedInfraEnv?.openshiftVersion ?? DISCONNECTED_OPENSHIFT_VERSION}
99
89
  </DescriptionListDescription>
100
90
  </DescriptionListGroup>
91
+ {disconnectedInfraEnv?.rendezvousIp && (
92
+ <DescriptionListGroup>
93
+ <DescriptionListTerm>Rendezvous IP</DescriptionListTerm>
94
+ <DescriptionListDescription>
95
+ {disconnectedInfraEnv.rendezvousIp}
96
+ </DescriptionListDescription>
97
+ </DescriptionListGroup>
98
+ )}
101
99
  <DescriptionListGroup>
102
100
  <DescriptionListTerm>CPU architecture</DescriptionListTerm>
103
101
  <DescriptionListDescription>
@@ -0,0 +1,75 @@
1
+ import { defaultWizardSteps, staticIpFormViewSubSteps } from './constants';
2
+ import { StaticIpView } from '../clusterConfiguration/staticIp/data/dataTypes';
3
+ import { ClusterWizardStepsType, disconnectedSteps } from './wizardTransition';
4
+
5
+ export const addStepToClusterWizard = (
6
+ wizardStepIds: ClusterWizardStepsType[],
7
+ addAfterStep: ClusterWizardStepsType,
8
+ itemsToAdd: ClusterWizardStepsType[],
9
+ ): ClusterWizardStepsType[] => {
10
+ const stepsIds = [...wizardStepIds];
11
+ const referencePosition = stepsIds.findIndex((step) => step === addAfterStep);
12
+ const found = wizardStepIds.filter((step) => step === itemsToAdd[0]);
13
+ if (referencePosition !== -1 && found.length === 0) {
14
+ stepsIds.splice(referencePosition + 1, 0, ...itemsToAdd);
15
+ }
16
+ return stepsIds;
17
+ };
18
+
19
+ export const removeStepFromClusterWizard = (
20
+ wizardStepIds: ClusterWizardStepsType[],
21
+ itemToRemove: ClusterWizardStepsType,
22
+ numberItemsToRemove: number,
23
+ ): ClusterWizardStepsType[] => {
24
+ const stepsIds = [...wizardStepIds];
25
+ const position = stepsIds.findIndex((step) => step === itemToRemove);
26
+ if (position !== -1) {
27
+ stepsIds.splice(position, numberItemsToRemove);
28
+ }
29
+ return stepsIds;
30
+ };
31
+
32
+ export const getWizardStepIds = (
33
+ wizardStepIds: ClusterWizardStepsType[] | undefined,
34
+ staticIpView?: StaticIpView | 'dhcp-selected',
35
+ isSingleClusterFeatureEnabled?: boolean,
36
+ ): ClusterWizardStepsType[] => {
37
+ let stepsCopy = wizardStepIds ? [...wizardStepIds] : [...defaultWizardSteps];
38
+ if (staticIpView === StaticIpView.YAML) {
39
+ stepsCopy = removeStepFromClusterWizard(stepsCopy, 'static-ip-network-wide-configurations', 2);
40
+ stepsCopy = addStepToClusterWizard(stepsCopy, 'cluster-details', ['static-ip-yaml-view']);
41
+ } else if (staticIpView === StaticIpView.FORM) {
42
+ stepsCopy = removeStepFromClusterWizard(stepsCopy, 'static-ip-yaml-view', 1);
43
+ stepsCopy = addStepToClusterWizard(stepsCopy, 'cluster-details', staticIpFormViewSubSteps);
44
+ } else if (staticIpView === 'dhcp-selected') {
45
+ stepsCopy = removeStepFromClusterWizard(stepsCopy, 'static-ip-network-wide-configurations', 2);
46
+ }
47
+
48
+ if (isSingleClusterFeatureEnabled && !stepsCopy.includes('credentials-download')) {
49
+ stepsCopy = addStepToClusterWizard(stepsCopy, 'custom-manifests', ['credentials-download']);
50
+ }
51
+
52
+ return stepsCopy;
53
+ };
54
+
55
+ export const getDisconnectedWizardStepIds = (
56
+ staticIpView?: StaticIpView | 'dhcp-selected',
57
+ ): ClusterWizardStepsType[] => {
58
+ // Always rebuild from the base disconnected steps so toggling static IP / DHCP
59
+ // cannot leave leftover sub-steps in the list.
60
+ const copy = [...disconnectedSteps];
61
+
62
+ if (staticIpView === StaticIpView.YAML) {
63
+ return addStepToClusterWizard(copy, 'disconnected-optional-configurations', [
64
+ 'static-ip-yaml-view',
65
+ ]);
66
+ }
67
+ if (staticIpView === StaticIpView.FORM) {
68
+ return addStepToClusterWizard(copy, 'disconnected-optional-configurations', [
69
+ 'static-ip-network-wide-configurations',
70
+ 'static-ip-host-configurations',
71
+ ]);
72
+ }
73
+
74
+ return copy;
75
+ };
@@ -1,25 +1,17 @@
1
1
  import React from 'react';
2
2
  import { Content, Split, SplitItem } from '@patternfly/react-core';
3
- import {
4
- ASSISTED_INSTALLER_DOCUMENTATION_LINK,
5
- TechnologyPreview,
6
- ExternalLink,
7
- isInOcm,
8
- } from '../../../common';
9
- import { useFeature } from '../../hooks/use-feature';
3
+ import { ASSISTED_INSTALLER_DOCUMENTATION_LINK, ExternalLink, isInOcm } from '../../../common';
10
4
 
11
5
  type AssistedInstallerHeaderProps = {
12
6
  clusterName?: string;
13
7
  };
14
8
 
15
9
  export const AssistedInstallerHeader = ({ clusterName }: AssistedInstallerHeaderProps) => {
16
- const isSingleClusterFeatureEnabled = useFeature('ASSISTED_INSTALLER_SINGLE_CLUSTER_FEATURE');
17
10
  return (
18
11
  <>
19
12
  <Content component="h1" className="pf-v6-u-display-inline">
20
13
  {clusterName || 'Install OpenShift with the Assisted Installer'}
21
14
  </Content>
22
- {isSingleClusterFeatureEnabled && <TechnologyPreview />}
23
15
  <Split hasGutter>
24
16
  <SplitItem>
25
17
  <ExternalLink href={ASSISTED_INSTALLER_DOCUMENTATION_LINK}>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "dependencies": {
3
- "@openshift-assisted/locales": "2.55.2",
4
- "@openshift-assisted/types": "2.55.2",
3
+ "@openshift-assisted/locales": "2.55.4",
4
+ "@openshift-assisted/types": "2.55.4",
5
5
  "@openshift-console/dynamic-plugin-sdk": "^4.22.0",
6
6
  "@patternfly/patternfly": "6.4.0",
7
7
  "@patternfly/react-code-editor": "6.4.1",
@@ -136,5 +136,5 @@
136
136
  ]
137
137
  }
138
138
  },
139
- "version": "2.55.2"
139
+ "version": "2.55.4"
140
140
  }