Skip to content

Commit 72d8bfa

Browse files
authored
ACM-9268 KubeVirt hosted cluster creation wizard (stolostron#3274)
* ACM-9268 KubeVirt hosted cluster creation wizard Signed-off-by: zlayne <zlayne@redhat.com> * fix lint errors Signed-off-by: zlayne <zlayne@redhat.com> * fix credentials test Signed-off-by: zlayne <zlayne@redhat.com> * Update controlDataKubeVirt test Signed-off-by: zlayne <zlayne@redhat.com> * fix lint Signed-off-by: zlayne <zlayne@redhat.com> * add tests Signed-off-by: zlayne <zlayne@redhat.com> --------- Signed-off-by: zlayne <zlayne@redhat.com>
1 parent 4a8b881 commit 72d8bfa

22 files changed

+1864
-102
lines changed

frontend/public/locales/en/translation.json

+30
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,7 @@
516516
"Attempts to delete objects known to be created by the policy when the policy is deleted.": "Attempts to delete objects known to be created by the policy when the policy is deleted.",
517517
"Attribute filters": "Attribute filters",
518518
"Attributes": "Attributes",
519+
"Auto repair": "Auto repair",
519520
"Automatically create namespace if it does not exist": "Automatically create namespace if it does not exist",
520521
"Automatically runs remediation action that is defined in the source, if this feature is supported.": "Automatically runs remediation action that is defined in the source, if this feature is supported.",
521522
"Automatically sync when cluster state changes": "Automatically sync when cluster state changes",
@@ -793,6 +794,7 @@
793794
"Copy button": "Copy button",
794795
"Copy this private URL to share": "Copy this private URL to share",
795796
"Copy to clipboard": "Copy to clipboard",
797+
"Core": "Core",
796798
"Could not find the AgentClusterInstall resource.": "Could not find the AgentClusterInstall resource.",
797799
"Could not process request because of invalid data.": "Could not process request because of invalid data.",
798800
"count.labels": "{{count}} label",
@@ -986,6 +988,7 @@
986988
"creation.ocp.instance.type": "Instance type",
987989
"creation.ocp.machine.cidr": "Machine CIDR",
988990
"creation.ocp.machine.cidr.placeholder": "Enter machine CIDR",
991+
"creation.ocp.memoryGB": "Memory (GiB)",
989992
"creation.ocp.memoryMB": "Memory (MiB)",
990993
"creation.ocp.name": "Cluster name",
991994
"creation.ocp.name.placeholder": "Enter cluster name",
@@ -994,6 +997,7 @@
994997
"creation.ocp.node.controlplane.pool.title": "Control plane pool",
995998
"creation.ocp.node.network.info": "Specify at least one network. Multiple are required for IPv6.",
996999
"creation.ocp.node.network.title": "Network {{0}}",
1000+
"creation.ocp.node.pool.title": "Node pool {{0}}",
9971001
"creation.ocp.node.worker.pool.info": "One or more worker nodes will be created to run the container workloads in this cluster.",
9981002
"creation.ocp.node.worker.pool.title": "Worker pool {{0}}",
9991003
"creation.ocp.pool.name": "Pool name",
@@ -1282,13 +1286,15 @@
12821286
"Enter job tag with \",\" or \"enter\"": "Enter job tag with \",\" or \"enter\"",
12831287
"Enter memory limit": "Enter memory limit",
12841288
"Enter memory request": "Enter memory request",
1289+
"Enter node pool name": "Enter node pool name",
12851290
"Enter or select a Git URL": "Enter or select a Git URL",
12861291
"Enter or select a Helm URL": "Enter or select a Helm URL",
12871292
"Enter or select a repository path": "Enter or select a repository path",
12881293
"Enter or select a tracking revision": "Enter or select a tracking revision",
12891294
"Enter Red Hat OpenShift Container Platform pull secret": "Enter Red Hat OpenShift Container Platform pull secret",
12901295
"Enter search text": "Enter search text",
12911296
"Enter skip tag with \",\" or \"enter\"": "Enter skip tag with \",\" or \"enter\"",
1297+
"Enter Storage Class": "Enter Storage Class",
12921298
"Enter the Amazon Web Services credentials": "Enter the Amazon Web Services credentials",
12931299
"Enter the Ansible Automation Platform credentials": "Enter the Ansible Automation Platform credentials",
12941300
"Enter the Ansible Tower host URL": "Enter the Ansible Tower host URL",
@@ -1360,6 +1366,7 @@
13601366
"Error querying resource logs:": "Error querying resource logs:",
13611367
"Error querying search results": "Error querying search results",
13621368
"error.create.syntax": "Syntax error: {{0}}",
1369+
"Etcd storage class": "Etcd storage class",
13631370
"Ev3-series": "Ev3-series",
13641371
"EveryEvent": "EveryEvent",
13651372
"Examples: 512Mi, 2Gi": "Examples: 512Mi, 2Gi",
@@ -1589,6 +1596,7 @@
15891596
"Infrastructure provider credentials": "Infrastructure provider credentials",
15901597
"Insights data": "Insights data",
15911598
"install": "Install",
1599+
"Install operator": "Install operator",
15921600
"Install Plan Approval": "Install Plan Approval",
15931601
"Install plan approved": "Install plan approved",
15941602
"Install the operator": "Install the operator",
@@ -1651,6 +1659,7 @@
16511659
"Limits": "Limits",
16521660
"ListBox input field": "ListBox input field",
16531661
"Loading": "Loading",
1662+
"Loading etcd storage classes...": "Loading etcd storage classes...",
16541663
"Loading more...": "Loading more...",
16551664
"Loading namespaces...": "Loading namespaces...",
16561665
"Loading...": "Loading...",
@@ -1834,6 +1843,7 @@
18341843
"No clusters available": "No clusters available",
18351844
"No clusters found": "No clusters found",
18361845
"No conditions found": "No conditions found",
1846+
"No etcd storage classes. Enter an etcd storage classes name.": "No etcd storage classes. Enter an etcd storage classes name.",
18371847
"No history": "No history",
18381848
"No infrastructure environments found": "No infrastructure environments found",
18391849
"No infrastructure environments yet": "No infrastructure environments yet",
@@ -1861,6 +1871,7 @@
18611871
"no.running.clusters.in.pool": "There are no running clusters in the pool",
18621872
"Node pool": "Node pool",
18631873
"Node pool name": "Node pool name",
1874+
"Node pool replica": "Node pool replica",
18641875
"Node pools": "Node pools",
18651876
"Node pools cannot be added during hosted cluster upgrade.": "Node pools cannot be added during hosted cluster upgrade.",
18661877
"Node pools cannot be managed during hosted cluster upgrade.": "Node pools cannot be managed during hosted cluster upgrade.",
@@ -1918,6 +1929,7 @@
19181929
"OpenShift GitOps Operator is required to create ApplicationSets.": "OpenShift GitOps Operator is required to create ApplicationSets.",
19191930
"OpenShift release images unavailable": "OpenShift release images unavailable",
19201931
"OpenShift version": "OpenShift version",
1932+
"OpenShift Virtualization operator is required to create a cluster.": "OpenShift Virtualization operator is required to create a cluster.",
19211933
"openshift.cluster.manager": "OpenShift Cluster Manager",
19221934
"OpenStack clouds.yaml": "OpenStack clouds.yaml",
19231935
"Operator": "Operator",
@@ -1975,6 +1987,7 @@
19751987
"Permanently destroy clusters?": "Permanently destroy clusters?",
19761988
"Permanently remove node pool {{name}}?": "Permanently remove node pool {{name}}?",
19771989
"Persistent volume": "Persistent volume",
1990+
"Persistent volume storage class for etcd data volumes": "Persistent volume storage class for etcd data volumes",
19781991
"Personalize this view": "Personalize this view",
19791992
"Phase": "Phase",
19801993
"placeholder.creation.ocp.baseDomain": "Enter base DNS domain",
@@ -2220,7 +2233,9 @@
22202233
"Revision": "Revision",
22212234
"Role": "Role",
22222235
"Root volume": "Root volume",
2236+
"Root volume option": "Root volume option",
22232237
"Root volume size": "Root volume size",
2238+
"Root Volume Storage Class": "Root Volume Storage Class",
22242239
"Root volume type": "Root volume type",
22252240
"Rules": "Rules",
22262241
"Run an OpenShift cluster where the control plane and data plane are coupled. The control plane is hosted by a dedicated group of physical or virtual nodes and the network stack is shared.": "Run an OpenShift cluster where the control plane and data plane are coupled. The control plane is hosted by a dedicated group of physical or virtual nodes and the network stack is shared.",
@@ -2273,11 +2288,14 @@
22732288
"Select a namespace for the credential": "Select a namespace for the credential",
22742289
"Select a namespace to be able to select policies in that namespace.": "Select a namespace to be able to select policies in that namespace.",
22752290
"Select a policy set": "Select a policy set",
2291+
"Select a storage class": "Select a storage class",
22762292
"Select a template": "Select a template",
2293+
"Select a volume mode": "Select a volume mode",
22772294
"Select all": "Select all",
22782295
"Select all ({{count}} items)": "Select all ({{count}} item)",
22792296
"Select all ({{count}} items)_plural": "Select all ({{count}} items)",
22802297
"Select all clusters to be added to the cluster set and deselect all clusters to be removed from the cluster set.": "Select all clusters to be added to the cluster set and deselect all clusters to be removed from the cluster set.",
2298+
"Select an access mode": "Select an access mode",
22812299
"Select an active cloud name for the credential.": "Select an active cloud name for the credential.",
22822300
"Select an inventory": "Select an inventory",
22832301
"Select at least one cluster set to deploy application resources.": "Select at least one cluster set to deploy application resources.",
@@ -2286,6 +2304,7 @@
22862304
"Select clusters from the clusters in selected cluster sets using cluster labels. For a cluster to be be selected, the cluster must match all label selectors, label expressions, and claim expressions.": "Select clusters from the clusters in selected cluster sets using cluster labels. For a cluster to be be selected, the cluster must match all label selectors, label expressions, and claim expressions.",
22872305
"Select configured clusters from the chosen cluster set by using cluster labels, which filters on the clusters. To use cluster labels, your cluster must match all label selectors, label expressions, and claim expressions.": "Select configured clusters from the chosen cluster set by using cluster labels, which filters on the clusters. To use cluster labels, your cluster must match all label selectors, label expressions, and claim expressions.",
22882306
"Select container": "Select container",
2307+
"Select etcd storage class": "Select etcd storage class",
22892308
"Select label value": "Select label value",
22902309
"Select namespace": "Select namespace",
22912310
"Select none": "Select none",
@@ -2336,6 +2355,7 @@
23362355
"Simple Table": "Simple Table",
23372356
"Single cluster": "Single cluster",
23382357
"Single node OpenShift": "Single node OpenShift",
2358+
"Size (GiB)": "Size (GiB)",
23392359
"Skip tags": "Skip tags",
23402360
"Some policies have the Prune parameter set.": "Some policies have the Prune parameter set.",
23412361
"Some resources failed to deploy. Use View resource YAML link to view the details.": "Some resources failed to deploy. Use View resource YAML link to view the details.",
@@ -2901,6 +2921,15 @@
29012921
"tooltip.creation.ocp.machine.cidr": "A block of IP addresses used by the OpenShift Container Platform hosts. The address block must not overlap any other network block. The default value is 10.0.0.0/16.",
29022922
"tooltip.creation.ocp.memoryMB": "Optional. Memory of the VM in MiB.",
29032923
"tooltip.creation.ocp.name": "The unique name of your cluster. The value must be a string that contains lowercase alphanumeric values, such as dev. Cannot be changed after creation.",
2924+
"tooltip.creation.ocp.node.pool.autorepair": "Enables machine auto-repair with machine health checks",
2925+
"tooltip.creation.ocp.node.pool.core.count": "Number of cores assigned to each worker node VM",
2926+
"tooltip.creation.ocp.node.pool.memoryGB": "Memory assigned to each worker node VM",
2927+
"tooltip.creation.ocp.node.pool.name": "The unique name of your node pool. The value must be a string that contains lowercase alphanumeric values, such as dev. Cannot be changed after creation.",
2928+
"tooltip.creation.ocp.node.pool.replica.count": "The number of Worker Nodes in the cluster",
2929+
"tooltip.creation.ocp.node.pool.root.vol.accessmode": "Access mode used for the worker node VM’s root volume",
2930+
"tooltip.creation.ocp.node.pool.root.vol.size": "The size of the underlying root volume used for the worker node VM",
2931+
"tooltip.creation.ocp.node.pool.root.vol.storageclass": "Storage class used for the worker node VM’s root volume",
2932+
"tooltip.creation.ocp.node.pool.root.vol.volumemode": "Volume Mode used for the worker node VM’s root volume",
29042933
"tooltip.creation.ocp.otp.instance.type": "The OpenStack compute flavor.",
29052934
"tooltip.creation.ocp.ovirt.diskSizeGB": "Required if you use <machine-pool>.platform.ovirt.osDisk. Size of the disk in GiB.",
29062935
"tooltip.creation.ocp.pool.name": "The name for your worker pool.",
@@ -3073,6 +3102,7 @@
30733102
"Violations": "Violations",
30743103
"VM size": "VM size",
30753104
"VMware": "VMware",
3105+
"Volume mode": "Volume mode",
30763106
"vSphere cluster name": "vSphere cluster name",
30773107
"vSphere datacenter": "vSphere datacenter",
30783108
"vSphere default datastore": "vSphere default datastore",

frontend/src/NavigationPath.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import { LocationDescriptor } from 'history'
44
import { useContext, useMemo } from 'react'
55
import { generatePath } from 'react-router'
66
import { useHistory, useLocation } from 'react-router-dom'
7-
import { Cluster } from './resources'
87
import { LostChangesContext } from './components/LostChanges'
8+
import { Cluster } from './resources'
99

1010
export const UNKNOWN_NAMESPACE = '~managed-cluster'
1111

@@ -52,6 +52,7 @@ export enum NavigationPath {
5252
createDiscoverHost = '/multicloud/infrastructure/clusters/create/discover-host',
5353
createCluster = '/multicloud/infrastructure/clusters/create',
5454
createAWSCLI = '/multicloud/infrastructure/clusters/create/aws/cli',
55+
createKubeVirt = '/multicloud/infrastructure/clusters/create/kubevirt',
5556
createKubeVirtCLI = '/multicloud/infrastructure/clusters/create/kubevirt/cli',
5657
editCluster = '/multicloud/infrastructure/clusters/edit/:namespace/:name',
5758
clusterDetails = '/multicloud/infrastructure/clusters/details/:namespace/:name',

frontend/src/components/TemplateEditor/controls/ControlPanel.js

+23-11
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,28 @@
11
/* Copyright Contributors to the Open Cluster Management project */
22
'use strict'
33

4-
import React from 'react'
5-
import PropTypes from 'prop-types'
64
import { Alert, Button } from '@patternfly/react-core'
75
import { PlusCircleIcon, TrashIcon } from '@patternfly/react-icons'
86
import classNames from 'classnames'
7+
import PropTypes from 'prop-types'
8+
import React from 'react'
9+
import '../css/control-panel.css'
910
import ControlPanelAccordion from './ControlPanelAccordion'
10-
import ControlPanelTextInput from './ControlPanelTextInput'
11+
import ControlPanelBoolean from './ControlPanelBoolean'
12+
import ControlPanelCards from './ControlPanelCards'
13+
import ControlPanelCheckbox from './ControlPanelCheckbox'
1114
import ControlPanelComboBox from './ControlPanelComboBox'
12-
import ControlPanelTextArea from './ControlPanelTextArea'
15+
import ControlPanelLabels from './ControlPanelLabels'
16+
import ControlPanelMultiSelect from './ControlPanelMultiSelect'
1317
import ControlPanelNumber from './ControlPanelNumber'
14-
import ControlPanelCheckbox from './ControlPanelCheckbox'
18+
import ControlPanelPrompt from './ControlPanelPrompt'
1519
import ControlPanelSingleSelect from './ControlPanelSingleSelect'
20+
import ControlPanelSkeleton from './ControlPanelSkeleton'
21+
import ControlPanelTextArea from './ControlPanelTextArea'
22+
import ControlPanelTextInput from './ControlPanelTextInput'
1623
import ControlPanelTreeSelect from './ControlPanelTreeSelect'
17-
import ControlPanelMultiSelect from './ControlPanelMultiSelect'
18-
import ControlPanelCards from './ControlPanelCards'
19-
import ControlPanelLabels from './ControlPanelLabels'
2024
import ControlPanelValues from './ControlPanelValues'
2125
import ControlPanelWizard from './ControlPanelWizard'
22-
import ControlPanelPrompt from './ControlPanelPrompt'
23-
import ControlPanelSkeleton from './ControlPanelSkeleton'
24-
import '../css/control-panel.css'
2526

2627
class ControlPanel extends React.Component {
2728
static propTypes = {
@@ -546,6 +547,17 @@ class ControlPanel extends React.Component {
546547
i18n={i18n}
547548
/>
548549
)
550+
case 'boolean':
551+
return (
552+
<ControlPanelBoolean
553+
key={controlId}
554+
controlId={controlId}
555+
control={control}
556+
controlData={controlData}
557+
handleChange={this.handleControlChange.bind(this, control)}
558+
i18n={i18n}
559+
/>
560+
)
549561
case 'checkbox':
550562
case 'radio':
551563
return (
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/* Copyright Contributors to the Open Cluster Management project */
2+
'use strict'
3+
4+
import { Radio } from '@patternfly/react-core'
5+
import PropTypes from 'prop-types'
6+
import React from 'react'
7+
import ControlPanelFormGroup from './ControlPanelFormGroup'
8+
9+
class ControlPanelBoolean extends React.Component {
10+
static propTypes = {
11+
control: PropTypes.object,
12+
controlId: PropTypes.string,
13+
handleChange: PropTypes.func,
14+
i18n: PropTypes.func,
15+
}
16+
17+
constructor(props) {
18+
super(props)
19+
this.state = {}
20+
}
21+
22+
setControlRef = (control, ref) => {
23+
control.ref = ref
24+
}
25+
26+
render() {
27+
const { controlId, control, controlData, handleChange, i18n } = this.props
28+
const { tip, isTrue } = control
29+
30+
const onChange = () => {
31+
control.isTrue = !isTrue
32+
control.active = !isTrue
33+
handleChange()
34+
}
35+
36+
return (
37+
<React.Fragment>
38+
<div
39+
style={{ flexDirection: 'column', alignItems: 'flex-start' }}
40+
className="creation-view-controls-number"
41+
ref={this.setControlRef.bind(this, control)}
42+
>
43+
<ControlPanelFormGroup i18n={i18n} controlId={controlId} control={control} controlData={controlData}>
44+
<div style={{ display: 'flex' }}>
45+
<div style={{ marginRight: '40px' }}>
46+
<Radio
47+
aria-label={`${controlId}-true`}
48+
id={`${controlId}-true`}
49+
label={'True'}
50+
isChecked={isTrue}
51+
onChange={onChange}
52+
data-testid={`radio-${controlId}`}
53+
style={{ marginRight: 0 }}
54+
/>
55+
</div>
56+
<div>
57+
<Radio
58+
aria-label={`${controlId}-false`}
59+
id={`${controlId}-false`}
60+
label={'False'}
61+
isChecked={!isTrue}
62+
onChange={onChange}
63+
data-testid={`radio-${controlId}`}
64+
style={{ marginRight: 0 }}
65+
/>
66+
</div>
67+
</div>
68+
</ControlPanelFormGroup>
69+
</div>
70+
<div style={{ fontSize: '14px', fontWeight: 'normal' }}>{tip}</div>
71+
</React.Fragment>
72+
)
73+
}
74+
}
75+
76+
export default ControlPanelBoolean
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/* Copyright Contributors to the Open Cluster Management project */
2+
'use strict'
3+
4+
import { render } from '@testing-library/react'
5+
import userEvent from '@testing-library/user-event'
6+
import ControlPanelBoolean from './ControlPanelBoolean'
7+
8+
import i18n from 'i18next'
9+
10+
const t = i18n.t.bind(i18n)
11+
12+
export const control = {
13+
active: false,
14+
name: 'Name',
15+
tooltip: 'Application name',
16+
controlData: [],
17+
id: 'boolean',
18+
type: 'boolean',
19+
isTrue: false,
20+
}
21+
22+
const fn = jest.fn()
23+
24+
describe('ControlPanelBoolean component', () => {
25+
it('renders as expected', () => {
26+
const Component = () => {
27+
return <ControlPanelBoolean key={'key'} control={control} controlId={'controlId'} handleChange={fn} i18n={t} />
28+
}
29+
30+
const { getByTestId, asFragment, rerender } = render(<Component />)
31+
expect(asFragment()).toMatchSnapshot()
32+
33+
userEvent.click(getByTestId('controlId-true'))
34+
expect(control.active).toBe(true)
35+
expect(control.isTrue).toBe(true)
36+
37+
// control.active = 'true'
38+
rerender(<Component />)
39+
userEvent.click(getByTestId('controlId-false'))
40+
expect(control.active).toBe(false)
41+
expect(control.isTrue).toBe(false)
42+
})
43+
})

0 commit comments

Comments
 (0)