Skip to content

Commit fbca5b1

Browse files
Merge pull request #13549 from TimothyAsirJeyasing/cross-storage-clone
RHSTOR-4130: Cross storage class clone /restore
2 parents 13aeba4 + 58c5ff4 commit fbca5b1

6 files changed

Lines changed: 210 additions & 135 deletions

File tree

frontend/packages/console-app/src/components/modals/clone/clone-pvc-modal.tsx

Lines changed: 130 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import * as React from 'react';
22
import {
3-
Form,
43
FormGroup,
54
FormHelperText,
65
HelperText,
@@ -34,6 +33,7 @@ import {
3433
} from '@console/internal/components/utils';
3534
import { useK8sGet } from '@console/internal/components/utils/k8s-get-hook';
3635
import { HandlePromiseProps } from '@console/internal/components/utils/promise-component';
36+
import { StorageClassDropdown } from '@console/internal/components/utils/storage-class-dropdown';
3737
import {
3838
NamespaceModel,
3939
PersistentVolumeClaimModel,
@@ -46,7 +46,7 @@ import {
4646
StorageClassResourceKind,
4747
} from '@console/internal/module/k8s';
4848
import { RedExclamationCircleIcon, isCephProvisioner } from '@console/shared';
49-
import { getRequestedPVCSize } from '@console/shared/src/selectors';
49+
import { getName, getRequestedPVCSize, onlyPvcSCs } from '@console/shared/src/selectors';
5050
import { getPVCAccessModes, AccessModeSelector } from '../../access-modes/access-mode';
5151

5252
import './_clone-pvc-modal.scss';
@@ -65,6 +65,12 @@ const ClonePVCModal = withHandlePromise((props: ClonePVCModalProps) => {
6565
const [requestedUnit, setRequestedUnit] = React.useState(defaultSize[1] || 'Ti');
6666
const [validSize, setValidSize] = React.useState(true);
6767
const pvcAccessMode = getPVCAccessModes(resource, 'title');
68+
const [pvcSC, setPVCStorageClass] = React.useState('');
69+
const [updatedProvisioner, setUpdatedProvisioner] = React.useState('');
70+
const handleStorageClass = (updatedStorageClass: StorageClassResourceKind) => {
71+
setPVCStorageClass(getName(updatedStorageClass) || '');
72+
setUpdatedProvisioner(updatedStorageClass?.provisioner);
73+
};
6874

6975
const [scResource, scResourceLoaded, scResourceLoadError] = useK8sGet<StorageClassResourceKind>(
7076
StorageClassModel,
@@ -103,7 +109,7 @@ const ClonePVCModal = withHandlePromise((props: ClonePVCModalProps) => {
103109
namespace: resource.metadata.namespace,
104110
},
105111
spec: {
106-
storageClassName: resource.spec.storageClassName,
112+
storageClassName: pvcSC,
107113
dataSource: {
108114
name: pvcName,
109115
kind: PersistentVolumeClaimModel.kind,
@@ -126,125 +132,139 @@ const ClonePVCModal = withHandlePromise((props: ClonePVCModalProps) => {
126132
};
127133

128134
return (
129-
<Form onSubmit={submit}>
130-
<div className="modal-content">
131-
<ModalTitle>{t('console-app~Clone')}</ModalTitle>
132-
<ModalBody>
133-
<FormGroup
134-
label={t('console-app~Name')}
135-
isRequired
136-
fieldId="clone-pvc-modal__name"
137-
className="co-clone-pvc-modal__form--space"
138-
>
139-
<TextInput
140-
type="text"
141-
className="co-clone-pvc-modal__name--margin"
142-
value={clonePVCName}
143-
onChange={(_event, value) => setClonePVCName(value)}
144-
aria-label={t('console-app~Clone PVC')}
145-
/>
146-
</FormGroup>
147-
<AccessModeSelector
148-
onChange={setCloneAccessMode}
149-
className="co-clone-pvc-modal__form--space"
150-
pvcResource={resource}
151-
provisioner={scResource?.provisioner}
152-
loaded={scResourceLoaded}
153-
loadError={scResourceLoadError}
154-
filterByVolumeMode
135+
<form onSubmit={submit} name="form" className="modal-content">
136+
<ModalTitle>{t('console-app~Clone')}</ModalTitle>
137+
<ModalBody>
138+
<FormGroup
139+
label={t('console-app~Name')}
140+
isRequired
141+
fieldId="clone-pvc-modal__name"
142+
className="co-clone-pvc-modal__form--space"
143+
>
144+
<TextInput
145+
type="text"
146+
className="co-clone-pvc-modal__name--margin"
147+
data-test="pvc-name"
148+
value={clonePVCName}
149+
onChange={(_event, value) => setClonePVCName(value)}
150+
aria-label={t('console-app~Clone PVC')}
155151
/>
156-
<FormGroup
157-
label={t('console-app~Size')}
158-
isRequired
159-
fieldId="clone-pvc-modal__size"
160-
className="co-clone-pvc-modal__form--space"
161-
>
162-
{scResourceLoaded ? (
163-
<RequestSizeInput
164-
name="requestSize"
165-
testID="input-request-size"
166-
onChange={requestedSizeInputChange}
167-
defaultRequestSizeUnit={requestedUnit}
168-
defaultRequestSizeValue={requestedSize}
169-
dropdownUnits={dropdownUnits}
170-
isInputDisabled={scResourceLoadError || isCephProvisioner(scResource?.provisioner)}
171-
required
172-
/>
173-
) : (
174-
<div className="skeleton-text" />
175-
)}
152+
</FormGroup>
153+
<AccessModeSelector
154+
onChange={setCloneAccessMode}
155+
className="co-clone-pvc-modal__form--space"
156+
pvcResource={resource}
157+
provisioner={updatedProvisioner}
158+
loaded={scResourceLoaded}
159+
loadError={scResourceLoadError}
160+
filterByVolumeMode
161+
/>
162+
<FormGroup
163+
label={t('console-app~Size')}
164+
isRequired
165+
fieldId="clone-pvc-modal__size"
166+
className="co-clone-pvc-modal__form--space"
167+
>
168+
{scResourceLoaded ? (
169+
<RequestSizeInput
170+
name="requestSize"
171+
testID="input-request-size"
172+
onChange={requestedSizeInputChange}
173+
defaultRequestSizeUnit={requestedUnit}
174+
defaultRequestSizeValue={requestedSize}
175+
dropdownUnits={dropdownUnits}
176+
isInputDisabled={scResourceLoadError || isCephProvisioner(scResource?.provisioner)}
177+
required
178+
/>
179+
) : (
180+
<div className="skeleton-text" />
181+
)}
176182

177-
{!validSize && (
178-
<FormHelperText>
179-
<HelperText>
180-
<HelperTextItem variant="error" icon={<RedExclamationCircleIcon />}>
181-
{t(
182-
'console-app~Size should be equal or greater than the requested size of PVC.',
183-
)}
184-
</HelperTextItem>
185-
</HelperText>
186-
</FormHelperText>
187-
)}
188-
</FormGroup>
189-
<div className="co-clone-pvc-modal__details">
190-
<p className="text-muted">{t('console-app~PVC details')}</p>
191-
<div className="co-clone-pvc-modal__details-section">
183+
{!validSize && (
184+
<FormHelperText>
185+
<HelperText>
186+
<HelperTextItem variant="error" icon={<RedExclamationCircleIcon />}>
187+
{t('console-app~Size should be equal or greater than the requested size of PVC.')}
188+
</HelperTextItem>
189+
</HelperText>
190+
</FormHelperText>
191+
)}
192+
</FormGroup>
193+
<FormGroup
194+
fieldId="clone-pvc-modal__storage-class"
195+
className="co-clone-pvc-modal__form--space"
196+
>
197+
{!scResourceLoaded ? (
198+
<div className="skeleton-text" />
199+
) : (
200+
<StorageClassDropdown
201+
onChange={handleStorageClass}
202+
filter={(scObj: StorageClassResourceKind) =>
203+
onlyPvcSCs(scObj, scResourceLoadError, scResource)
204+
}
205+
id="clone-storage-class"
206+
data-test="storage-class-dropdown"
207+
required
208+
selectedKey={getName(scResource)}
209+
/>
210+
)}
211+
</FormGroup>
212+
<div className="co-clone-pvc-modal__details">
213+
<p className="text-muted">{t('console-app~PVC details')}</p>
214+
<div className="co-clone-pvc-modal__details-section">
215+
<div>
192216
<div>
193-
<div>
194-
<p className="co-clone-pvc-modal__pvc-details">{t('console-app~Namespace')}</p>
195-
<p>
196-
<ResourceIcon kind={NamespaceModel.kind} />
197-
{resource.metadata.namespace}
198-
</p>
199-
</div>
200-
<div>
201-
<p className="co-clone-pvc-modal__pvc-details">{t('console-app~StorageClass')}</p>
202-
<p>
203-
<ResourceIcon kind={StorageClassModel.kind} />
204-
{resource.spec?.storageClassName || '-'}
205-
</p>
206-
</div>
217+
<p className="co-clone-pvc-modal__pvc-details">{t('console-app~Namespace')}</p>
218+
<p>
219+
<ResourceIcon kind={NamespaceModel.kind} />
220+
{resource.metadata.namespace}
221+
</p>
207222
</div>
208223
<div>
209-
<div>
210-
<p className="co-clone-pvc-modal__pvc-details">
211-
{t('console-app~Requested capacity')}
212-
</p>
213-
<p>{pvcRequestedSize}</p>
214-
</div>
215-
<div>
216-
<p className="co-clone-pvc-modal__pvc-details">
217-
{t('console-app~Used capacity')}
218-
</p>
219-
<div>
220-
{!loading && !error && pvcUsedCapacity}
221-
{loading && <LoadingInline />}
222-
{!loading && error && '-'}
223-
</div>
224-
</div>
224+
<p className="co-clone-pvc-modal__pvc-details">{t('console-app~StorageClass')}</p>
225+
<p>
226+
<ResourceIcon kind={StorageClassModel.kind} />
227+
{pvcSC || '-'}
228+
</p>
225229
</div>
230+
</div>
231+
<div>
226232
<div>
233+
<p className="co-clone-pvc-modal__pvc-details">
234+
{t('console-app~Requested capacity')}
235+
</p>
236+
<p>{pvcRequestedSize}</p>
237+
</div>
238+
<div>
239+
<p className="co-clone-pvc-modal__pvc-details">{t('console-app~Used capacity')}</p>
227240
<div>
228-
<p className="co-clone-pvc-modal__pvc-details">{t('console-app~Access mode')}</p>
229-
<p>{pvcAccessMode.join(', ') || '-'}</p>
230-
</div>
231-
<div>
232-
<p className="co-clone-pvc-modal__pvc-details">{t('console-app~Volume mode')}</p>
233-
<p>{resource.spec.volumeMode}</p>
241+
{!loading && !error && pvcUsedCapacity}
242+
{loading && <LoadingInline />}
243+
{!loading && error && '-'}
234244
</div>
235245
</div>
236246
</div>
247+
<div>
248+
<div>
249+
<p className="co-clone-pvc-modal__pvc-details">{t('console-app~Access mode')}</p>
250+
<p>{pvcAccessMode.join(', ') || '-'}</p>
251+
</div>
252+
<div>
253+
<p className="co-clone-pvc-modal__pvc-details">{t('console-app~Volume mode')}</p>
254+
<p>{resource.spec.volumeMode}</p>
255+
</div>
256+
</div>
237257
</div>
238-
</ModalBody>
239-
<ModalSubmitFooter
240-
inProgress={inProgress}
241-
submitDisabled={!validSize || !resource.spec?.storageClassName}
242-
errorMessage={errorMessage}
243-
submitText={t('console-app~Clone')}
244-
cancel={cancel}
245-
/>
246-
</div>
247-
</Form>
258+
</div>
259+
</ModalBody>
260+
<ModalSubmitFooter
261+
inProgress={inProgress}
262+
submitDisabled={!validSize || !pvcSC}
263+
errorMessage={errorMessage}
264+
submitText={t('console-app~Clone')}
265+
cancel={cancel}
266+
/>
267+
</form>
248268
);
249269
});
250270

frontend/packages/console-app/src/components/modals/restore-pvc/restore-pvc-modal.tsx

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,13 @@ import {
4040
NamespaceModel,
4141
PersistentVolumeClaimModel,
4242
VolumeSnapshotModel,
43-
VolumeSnapshotClassModel,
43+
StorageClassModel,
4444
} from '@console/internal/models';
4545
import {
4646
k8sCreate,
4747
VolumeSnapshotKind,
4848
StorageClassResourceKind,
4949
PersistentVolumeClaimKind,
50-
VolumeSnapshotClassKind,
5150
} from '@console/internal/module/k8s';
5251
import {
5352
getName,
@@ -56,6 +55,7 @@ import {
5655
isCephProvisioner,
5756
getAnnotations,
5857
RedExclamationCircleIcon,
58+
onlyPvcSCs,
5959
} from '@console/shared';
6060
import { AccessModeSelector } from '../../access-modes/access-mode';
6161

@@ -85,21 +85,13 @@ const RestorePVCModal = withHandlePromise<RestorePVCModalProps>(
8585
PersistentVolumeClaimKind
8686
>(PersistentVolumeClaimModel, resource?.spec?.source?.persistentVolumeClaimName, namespace);
8787

88-
const [
89-
snapshotClassResource,
90-
snapshotClassResourceLoaded,
91-
snapshotClassResourceLoadError,
92-
] = useK8sGet<VolumeSnapshotClassKind>(
93-
VolumeSnapshotClassModel,
94-
resource?.spec?.volumeSnapshotClassName,
88+
const pvcStorageClassName = pvcResource?.spec?.storageClassName;
89+
const [scResource, scResourceLoaded, scResourceLoadError] = useK8sGet<StorageClassResourceKind>(
90+
StorageClassModel,
91+
pvcStorageClassName,
9592
);
9693

9794
const [volumeMode, setVolumeMode] = React.useState('');
98-
const onlyPvcSCs = (scObj: StorageClassResourceKind) =>
99-
!snapshotClassResourceLoadError
100-
? scObj.provisioner.includes(snapshotClassResource?.driver)
101-
: true;
102-
10395
const requestedSizeInputChange = ({ value, unit }) => {
10496
setRequestedSize(value);
10597
setRequestedUnit(unit);
@@ -178,12 +170,14 @@ const RestorePVCModal = withHandlePromise<RestorePVCModalProps>(
178170
/>
179171
</FormGroup>
180172
<FormGroup fieldId="restore-storage-class" className="co-restore-pvc-modal__input">
181-
{!snapshotClassResourceLoaded ? (
173+
{!pvcStorageClassName || !scResourceLoaded ? (
182174
<div className="skeleton-text" />
183175
) : (
184176
<StorageClassDropdown
185177
onChange={handleStorageClass}
186-
filter={onlyPvcSCs}
178+
filter={(scObj: StorageClassResourceKind) =>
179+
onlyPvcSCs(scObj, scResourceLoadError, scResource)
180+
}
187181
id="restore-storage-class"
188182
required
189183
selectedKey={volumeSnapshotAnnotations?.[snapshotPVCStorageClassAnnotation]}
@@ -217,16 +211,14 @@ const RestorePVCModal = withHandlePromise<RestorePVCModalProps>(
217211
fieldId="pvc-size"
218212
className="co-restore-pvc-modal__input co-restore-pvc-modal__ocs-size"
219213
>
220-
{snapshotClassResourceLoaded ? (
214+
{!!pvcStorageClassName && scResourceLoaded ? (
221215
<RequestSizeInput
222216
name="requestSize"
223217
onChange={requestedSizeInputChange}
224218
defaultRequestSizeUnit={requestedUnit}
225219
defaultRequestSizeValue={requestedSize}
226220
dropdownUnits={dropdownUnits}
227-
isInputDisabled={
228-
snapshotClassResourceLoadError || isCephProvisioner(snapshotClassResource?.driver)
229-
}
221+
isInputDisabled={scResourceLoadError || isCephProvisioner(scResource?.provisioner)}
230222
required
231223
/>
232224
) : (
Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
1-
import { K8sResourceKind } from '@console/internal/module/k8s';
1+
import { K8sResourceKind, StorageClassResourceKind } from '@console/internal/module/k8s';
22

33
export const getRequestedPVCSize = (pvc: K8sResourceKind): string =>
44
pvc?.spec?.resources?.requests?.storage;
5+
6+
export const onlyPvcSCs = (
7+
scObj: StorageClassResourceKind,
8+
scResourceLoadError: boolean,
9+
scResource: StorageClassResourceKind | null,
10+
) =>
11+
!scResourceLoadError
12+
? scObj.provisioner.includes(scResource?.provisioner) &&
13+
scObj.parameters?.encrypted === scResource?.parameters?.encrypted
14+
: true;

0 commit comments

Comments
 (0)