Skip to content

Commit 05437cb

Browse files
Merge pull request #15643 from cajieh/volume-attributes-class-implementation
CONSOLE-4814: Add UI for creating VACs and also applying VACs to PVCs
2 parents 569b5f4 + 40dc00e commit 05437cb

File tree

20 files changed

+863
-8
lines changed

20 files changed

+863
-8
lines changed

frontend/packages/console-app/console-extensions.json

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,20 @@
424424
"provider": { "$codeRef": "volumeSnapshotProvider.useVolumeSnapshotActionsProvider" }
425425
}
426426
},
427+
{
428+
"type": "console.action/resource-provider",
429+
"properties": {
430+
"model": {
431+
"version": "v1",
432+
"kind": "VolumeAttributesClass",
433+
"group": "storage.k8s.io"
434+
},
435+
"provider": { "$codeRef": "volumeAttributesClassProvider.useVolumeAttributesClassActionsProvider" }
436+
},
437+
"flags": {
438+
"required": ["VAC_PLATFORM_SUPPORT"]
439+
}
440+
},
427441
{
428442
"type": "console.action/resource-provider",
429443
"properties": {
@@ -1255,13 +1269,31 @@
12551269
}
12561270
}
12571271
},
1272+
{
1273+
"type": "console.navigation/resource-cluster",
1274+
"properties": {
1275+
"perspective": "admin",
1276+
"section": "storage",
1277+
"id": "volumeattributesclasses",
1278+
"insertAfter": "storageclasses",
1279+
"name": "%console-app~VolumeAttributesClasses%",
1280+
"model": {
1281+
"group": "storage.k8s.io",
1282+
"version": "v1",
1283+
"kind": "VolumeAttributesClass"
1284+
}
1285+
},
1286+
"flags": {
1287+
"required": ["VAC_PLATFORM_SUPPORT"]
1288+
}
1289+
},
12581290
{
12591291
"type": "console.navigation/resource-ns",
12601292
"properties": {
12611293
"perspective": "admin",
12621294
"section": "storage",
12631295
"id": "volumesnapshots",
1264-
"insertAfter": "storageclasses",
1296+
"insertAfter": "volumeattributesclasses",
12651297
"name": "%console-app~VolumeSnapshots%",
12661298
"model": {
12671299
"group": "snapshot.storage.k8s.io",
@@ -2406,6 +2438,12 @@
24062438
"handler": { "$codeRef": "getConsoleOperatorConfigFlag" }
24072439
}
24082440
},
2441+
{
2442+
"type": "console.flag/hookProvider",
2443+
"properties": {
2444+
"handler": { "$codeRef": "vacPlatformSupport.useVACPlatformSupportProvider" }
2445+
}
2446+
},
24092447
{
24102448
"type": "console.action/resource-provider",
24112449
"properties": {

frontend/packages/console-app/locales/en/console-app.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
"PersistentVolumes": "PersistentVolumes",
6868
"PersistentVolumeClaims": "PersistentVolumeClaims",
6969
"StorageClasses": "StorageClasses",
70+
"VolumeAttributesClasses": "VolumeAttributesClasses",
7071
"VolumeSnapshotClasses": "VolumeSnapshotClasses",
7172
"BuildConfigs": "BuildConfigs",
7273
"ImageStreams": "ImageStreams",
@@ -227,6 +228,8 @@
227228
"Create snapshot": "Create snapshot",
228229
"PVC is not Bound": "PVC is not Bound",
229230
"Clone PVC": "Clone PVC",
231+
"Modify VolumeAttributesClass": "Modify VolumeAttributesClass",
232+
"PVC must be Bound to modify VolumeAttributesClass": "PVC must be Bound to modify VolumeAttributesClass",
230233
"Rollback": "Rollback",
231234
"Cancel rollout": "Cancel rollout",
232235
"Are you sure you want to cancel this rollout?": "Are you sure you want to cancel this rollout?",

frontend/packages/console-app/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@
4747
"prometheusProvider": "src/actions/providers/prometheus-provider.ts",
4848
"groupProvider": "src/actions/providers/group-provider.ts",
4949
"volumeSnapshotProvider": "src/actions/providers/volume-snaphot-provider.ts",
50+
"volumeAttributesClassProvider": "src/actions/providers/volumeattributesclass-provider.ts",
51+
"vacPlatformSupport": "src/features/vac-platform-support.ts",
5052
"podProvider": "src/actions/providers/pod-provider.ts",
5153
"podDisruptionBudgetProvider": "src/actions/providers/pdb-provider.ts",
5254
"perspectiveStateProvider": "src/actions/providers/perspective-state-provider.ts",

frontend/packages/console-app/src/actions/hooks/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export enum PVCActionCreator {
3030
PVCSnapshot = 'PVCSnapshot',
3131
ClonePVC = 'ClonePVC',
3232
DeletePVC = 'DeletePVC',
33+
ModifyVAC = 'ModifyVAC',
3334
}
3435

3536
export enum VolumeSnapshotActionCreator {

frontend/packages/console-app/src/actions/hooks/usePVCActions.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { useMemo } from 'react';
22
import { useTranslation } from 'react-i18next';
3+
import { ModifyVACModal } from '@console/app/src/components/modals/modify-vac-modal';
34
import { Action } from '@console/dynamic-plugin-sdk';
5+
import { useOverlay } from '@console/dynamic-plugin-sdk/src/app/modal-support/useOverlay';
46
import { useDeepCompareMemoize } from '@console/dynamic-plugin-sdk/src/utils/k8s/hooks/useDeepCompareMemoize';
57
import { clonePVCModal, expandPVCModal } from '@console/internal/components/modals';
68
import deletePVCModal from '@console/internal/components/modals/delete-pvc-modal';
@@ -34,6 +36,7 @@ export const usePVCActions = (
3436
filterActions?: PVCActionCreator[],
3537
): Action[] => {
3638
const { t } = useTranslation();
39+
const launchModal = useOverlay();
3740

3841
const memoizedFilterActions = useDeepCompareMemoize(filterActions);
3942

@@ -71,6 +74,17 @@ export const usePVCActions = (
7174
}),
7275
accessReview: asAccessReview(PersistentVolumeClaimModel, obj, 'create'),
7376
}),
77+
[PVCActionCreator.ModifyVAC]: () => ({
78+
id: 'modify-vac',
79+
label: t('console-app~Modify VolumeAttributesClass'),
80+
disabled: obj?.status?.phase !== 'Bound',
81+
tooltip:
82+
obj?.status?.phase !== 'Bound'
83+
? t('console-app~PVC must be Bound to modify VolumeAttributesClass')
84+
: '',
85+
cta: () => launchModal(ModifyVACModal, { resource: obj }),
86+
accessReview: asAccessReview(PersistentVolumeClaimModel, obj, 'patch'),
87+
}),
7488
[PVCActionCreator.DeletePVC]: () => ({
7589
id: 'delete-pvc',
7690
label: t('public~Delete PersistentVolumeClaim'),
@@ -81,15 +95,21 @@ export const usePVCActions = (
8195
accessReview: asAccessReview(PersistentVolumeClaimModel, obj, 'delete'),
8296
}),
8397
}),
84-
[t, obj],
98+
[t, obj, launchModal],
8599
);
86100

87101
// filter and initialize requested actions or construct list of all PVCActions
88102
const actions = useMemo<Action[]>(() => {
89103
if (memoizedFilterActions) {
90104
return memoizedFilterActions.map((creator) => factory[creator]());
91105
}
92-
return [factory.ExpandPVC(), factory.PVCSnapshot(), factory.ClonePVC(), factory.DeletePVC()];
106+
return [
107+
factory.ExpandPVC(),
108+
factory.PVCSnapshot(),
109+
factory.ClonePVC(),
110+
factory.ModifyVAC(),
111+
factory.DeletePVC(),
112+
];
93113
}, [factory, memoizedFilterActions]);
94114
return actions;
95115
};

frontend/packages/console-app/src/actions/providers/persistent-volume-claim-provider.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { useMemo } from 'react';
22
import { usePVCActions } from '@console/app/src/actions/hooks/usePVCActions';
33
import { Action } from '@console/dynamic-plugin-sdk';
44
import { referenceFor, PersistentVolumeClaimKind } from '@console/internal/module/k8s';
5+
import { FLAGS } from '@console/shared/src/constants/common';
6+
import { useFlag } from '@console/shared/src/hooks/flag';
57
import { useK8sModel } from '@console/shared/src/hooks/useK8sModel';
68
import { CommonActionCreator, PVCActionCreator } from '../hooks/types';
79
import { useCommonActions } from '../hooks/useCommonActions';
@@ -10,11 +12,20 @@ export const usePVCActionsProvider = (
1012
resource: PersistentVolumeClaimKind,
1113
): [Action[], boolean, boolean] => {
1214
const [kindObj, inFlight] = useK8sModel(referenceFor(resource));
13-
const pvcActions = usePVCActions(resource, [
14-
PVCActionCreator.ExpandPVC,
15-
PVCActionCreator.PVCSnapshot,
16-
PVCActionCreator.ClonePVC,
17-
]);
15+
const isVACSupported = useFlag(FLAGS.VAC_PLATFORM_SUPPORT);
16+
17+
const pvcActionsList = useMemo(() => {
18+
const baseActions = [
19+
PVCActionCreator.ExpandPVC,
20+
PVCActionCreator.PVCSnapshot,
21+
PVCActionCreator.ClonePVC,
22+
];
23+
24+
// Conditionally check platform support for VAC
25+
return isVACSupported ? [...baseActions, PVCActionCreator.ModifyVAC] : baseActions;
26+
}, [isVACSupported]);
27+
28+
const pvcActions = usePVCActions(resource, pvcActionsList);
1829
const pvcDeleteAction = usePVCActions(resource, [PVCActionCreator.DeletePVC]);
1930
const [commonActions] = useCommonActions(kindObj, resource, [
2031
CommonActionCreator.ModifyLabels,
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { Action } from '@console/dynamic-plugin-sdk';
2+
import { K8sResourceCommon, referenceFor } from '@console/internal/module/k8s';
3+
import { useK8sModel } from '@console/shared/src/hooks/useK8sModel';
4+
import { useCommonResourceActions } from '../hooks/useCommonResourceActions';
5+
6+
export const useVolumeAttributesClassActionsProvider = (
7+
volumeAttributesClass: K8sResourceCommon,
8+
): [Action[], boolean] => {
9+
const [volumeAttributesClassModel, inFlight] = useK8sModel(referenceFor(volumeAttributesClass));
10+
const commonActions = useCommonResourceActions(volumeAttributesClassModel, volumeAttributesClass);
11+
12+
return [commonActions, !inFlight];
13+
};
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import { useState } from 'react';
2+
import { Form, FormGroup } from '@patternfly/react-core';
3+
import { useTranslation } from 'react-i18next';
4+
import { k8sPatchResource } from '@console/dynamic-plugin-sdk/src/utils/k8s';
5+
import {
6+
ModalBody,
7+
ModalSubmitFooter,
8+
ModalTitle,
9+
ModalWrapper,
10+
} from '@console/internal/components/factory/modal';
11+
import { VolumeAttributesClassDropdown } from '@console/internal/components/utils/volume-attributes-class-dropdown';
12+
import { PersistentVolumeClaimModel } from '@console/internal/models';
13+
import { PersistentVolumeClaimKind } from '@console/internal/module/k8s';
14+
import { usePromiseHandler } from '@console/shared/src/hooks/promise-handler';
15+
16+
const ModifyVACModalComponent: React.FCC<ModifyVACModalComponentProps> = ({
17+
resource,
18+
close,
19+
cancel,
20+
}) => {
21+
const { t } = useTranslation();
22+
const [volumeAttributesClass, setVolumeAttributesClass] = useState(
23+
resource?.spec?.volumeAttributesClassName || '',
24+
);
25+
const [handlePromise, inProgress, errorMessage] = usePromiseHandler();
26+
27+
const handleSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
28+
event.preventDefault();
29+
30+
const hasExistingVAC = !!resource?.spec?.volumeAttributesClassName;
31+
32+
const patch = [
33+
{
34+
op: hasExistingVAC ? 'replace' : 'add',
35+
path: '/spec/volumeAttributesClassName',
36+
value: volumeAttributesClass,
37+
},
38+
];
39+
40+
handlePromise(
41+
k8sPatchResource({
42+
model: PersistentVolumeClaimModel,
43+
resource,
44+
data: patch,
45+
}),
46+
)
47+
.then(() => close())
48+
.catch(() => {});
49+
};
50+
51+
return (
52+
<Form onSubmit={handleSubmit}>
53+
<ModalTitle>{t('console-app~Modify VolumeAttributesClass')}</ModalTitle>
54+
<ModalBody>
55+
<FormGroup fieldId="vac-dropdown">
56+
<VolumeAttributesClassDropdown
57+
onChange={setVolumeAttributesClass}
58+
selectedKey={volumeAttributesClass}
59+
id="vac-dropdown"
60+
dataTest="modify-vac-dropdown"
61+
required
62+
noSelection
63+
/>
64+
</FormGroup>
65+
</ModalBody>
66+
<ModalSubmitFooter
67+
submitText={t('console-app~Save')}
68+
submitDisabled={!volumeAttributesClass}
69+
cancel={cancel}
70+
inProgress={inProgress}
71+
errorMessage={errorMessage}
72+
/>
73+
</Form>
74+
);
75+
};
76+
77+
export const ModifyVACModal: React.FC<ModifyVACModalProps> = ({ closeOverlay, resource }) => {
78+
return (
79+
<ModalWrapper blocking onClose={closeOverlay}>
80+
<ModifyVACModalComponent close={closeOverlay} cancel={closeOverlay} resource={resource} />
81+
</ModalWrapper>
82+
);
83+
};
84+
85+
export type ModifyVACModalProps = {
86+
resource: PersistentVolumeClaimKind;
87+
closeOverlay: () => void;
88+
};
89+
90+
type ModifyVACModalComponentProps = {
91+
resource: PersistentVolumeClaimKind;
92+
close: () => void;
93+
cancel: () => void;
94+
};
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { useEffect } from 'react';
2+
import { SetFeatureFlag } from '@console/dynamic-plugin-sdk';
3+
import { useK8sGet } from '@console/internal/components/utils/k8s-get-hook';
4+
import { InfrastructureModel } from '@console/internal/models';
5+
import { K8sResourceKind } from '@console/internal/module/k8s';
6+
import { FLAGS } from '@console/shared/src/constants/common';
7+
import { getInfrastructurePlatform } from '@console/shared/src/selectors/infrastructure';
8+
9+
/**
10+
* Platforms that support VolumeAttributesClass (VAC) modifications on a PVC.
11+
* VAC is only supported on AWS and GCP platforms.
12+
*/
13+
const VAC_SUPPORTED_PLATFORMS = ['AWS', 'GCP'] as const;
14+
15+
/**
16+
* Hook provider for VolumeAttributesClass platform support detection.
17+
* VAC is only supported on AWS and GCP platforms.
18+
*/
19+
export const useVACPlatformSupportProvider = (setFeatureFlag: SetFeatureFlag) => {
20+
const [infrastructure, loaded, error] = useK8sGet<K8sResourceKind>(
21+
InfrastructureModel,
22+
'cluster',
23+
);
24+
25+
useEffect(() => {
26+
if (loaded && !error) {
27+
const platform = getInfrastructurePlatform(infrastructure);
28+
const isSupported = VAC_SUPPORTED_PLATFORMS.includes(
29+
platform as typeof VAC_SUPPORTED_PLATFORMS[number],
30+
);
31+
32+
setFeatureFlag(FLAGS.VAC_PLATFORM_SUPPORT, isSupported);
33+
} else if (error) {
34+
// eslint-disable-next-line no-console
35+
console.warn('Failed to detect platform for VAC support:', error);
36+
setFeatureFlag(FLAGS.VAC_PLATFORM_SUPPORT, false);
37+
}
38+
}, [error, infrastructure, loaded, setFeatureFlag]);
39+
};

frontend/packages/console-shared/src/constants/common.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ export enum FLAGS {
9494
CONSOLE_CAPABILITY_GETTINGSTARTEDBANNER_IS_ENABLED = 'CONSOLE_CAPABILITY_GETTINGSTARTEDBANNER_IS_ENABLED',
9595
LIGHTSPEED_IS_AVAILABLE_TO_INSTALL = 'LIGHTSPEED_IS_AVAILABLE_TO_INSTALL',
9696
DEVCONSOLE_PROXY = 'DEVCONSOLE_PROXY',
97+
VAC_PLATFORM_SUPPORT = 'VAC_PLATFORM_SUPPORT',
9798
}
9899

99100
export const CONFIG_STORAGE_CONSOLE = 'console';

0 commit comments

Comments
 (0)