|
1 | | -import { useMemo, useCallback, Suspense } from 'react'; |
| 1 | +import { useMemo, useCallback, Suspense, useState, useEffect } from 'react'; |
2 | 2 | import * as _ from 'lodash'; |
3 | 3 | import i18next, { TFunction } from 'i18next'; |
4 | 4 | import { useTranslation } from 'react-i18next'; |
5 | 5 | import { useDispatch, useSelector } from 'react-redux'; |
6 | 6 | import { |
| 7 | + Alert, |
| 8 | + AlertActionCloseButton, |
7 | 9 | DescriptionList, |
8 | 10 | DescriptionListDescription, |
9 | 11 | DescriptionListGroup, |
@@ -76,6 +78,30 @@ const tableColumnInfo = [ |
76 | 78 | { id: '' }, |
77 | 79 | ]; |
78 | 80 |
|
| 81 | +/** |
| 82 | + * Determines the VAC (VolumeAttributesClass) modification state for a PVC |
| 83 | + * @param pvc - The PersistentVolumeClaim resource |
| 84 | + * @returns Object containing error condition and pending state |
| 85 | + */ |
| 86 | +const getVACAlertState = (pvc: PersistentVolumeClaimKind) => { |
| 87 | + const volumeAttributesClassName = pvc?.spec?.volumeAttributesClassName; |
| 88 | + const currentVolumeAttributesClassName = pvc?.status?.currentVolumeAttributesClassName; |
| 89 | + const conditions = pvc?.status?.conditions; |
| 90 | + |
| 91 | + // Check for explicit ModifyVolumeError condition |
| 92 | + const vacErrorCondition = conditions?.find( |
| 93 | + (condition) => condition.type === 'ModifyVolumeError' && condition.status === 'True', |
| 94 | + ); |
| 95 | + |
| 96 | + // Determine if modification is pending |
| 97 | + const isVacPending = |
| 98 | + !vacErrorCondition && |
| 99 | + volumeAttributesClassName && |
| 100 | + volumeAttributesClassName !== currentVolumeAttributesClassName; |
| 101 | + |
| 102 | + return { vacErrorCondition, isVacPending }; |
| 103 | +}; |
| 104 | + |
79 | 105 | export const PVCStatusComponent: React.FCC<PVCStatusProps> = ({ pvc }) => { |
80 | 106 | const { t } = useTranslation(); |
81 | 107 | const [pvcStatusExtensions, resolved] = useResolvedExtensions<PVCStatus>(isPVCStatus); |
@@ -249,6 +275,16 @@ const PVCDetails: React.FCC<PVCDetailsProps> = ({ obj: pvc }) => { |
249 | 275 | const volumeMode = pvc?.spec?.volumeMode; |
250 | 276 | const conditions = pvc?.status?.conditions; |
251 | 277 |
|
| 278 | + // State to track dismissed alerts |
| 279 | + const [isErrorAlertDismissed, setIsErrorAlertDismissed] = useState(false); |
| 280 | + const [isInfoAlertDismissed, setIsInfoAlertDismissed] = useState(false); |
| 281 | + |
| 282 | + // Reset alert dismiss states when PVC changes |
| 283 | + useEffect(() => { |
| 284 | + setIsErrorAlertDismissed(false); |
| 285 | + setIsInfoAlertDismissed(false); |
| 286 | + }, [pvc?.metadata?.uid]); |
| 287 | + |
252 | 288 | const query = |
253 | 289 | name && namespace |
254 | 290 | ? `kubelet_volume_stats_used_bytes{persistentvolumeclaim='${name}',namespace='${namespace}'}` |
@@ -288,10 +324,51 @@ const PVCDetails: React.FCC<PVCDetailsProps> = ({ obj: pvc }) => { |
288 | 324 | ({ properties: { alert: AlertComponent }, uid }) => <AlertComponent key={uid} pvc={pvc} />, |
289 | 325 | ); |
290 | 326 |
|
| 327 | + // Get VAC modification state using helper function |
| 328 | + const { vacErrorCondition, isVacPending } = getVACAlertState(pvc); |
| 329 | + |
291 | 330 | return ( |
292 | 331 | <> |
293 | 332 | <PaneBody> |
294 | 333 | {alertComponents} |
| 334 | + {isVACSupported && vacErrorCondition && !isErrorAlertDismissed && ( |
| 335 | + <Alert |
| 336 | + isInline |
| 337 | + variant="danger" |
| 338 | + title={t('public~VolumeAttributesClass modification failed')} |
| 339 | + className="co-alert co-alert--margin-bottom-sm" |
| 340 | + actionClose={<AlertActionCloseButton onClose={() => setIsErrorAlertDismissed(true)} />} |
| 341 | + > |
| 342 | + {t( |
| 343 | + 'public~VolumeAttributesClass modification failed. Your volume settings could not be updated. Please try again.', |
| 344 | + )} |
| 345 | + </Alert> |
| 346 | + )} |
| 347 | + {isVACSupported && isVacPending && !isInfoAlertDismissed && ( |
| 348 | + <Alert |
| 349 | + isInline |
| 350 | + variant="info" |
| 351 | + title={ |
| 352 | + volumeAttributesClassName && !currentVolumeAttributesClassName |
| 353 | + ? t('public~VolumeAttributesClass application pending') |
| 354 | + : t('public~VolumeAttributesClass modification in progress') |
| 355 | + } |
| 356 | + className="co-alert co-alert--margin-bottom-sm" |
| 357 | + actionClose={<AlertActionCloseButton onClose={() => setIsInfoAlertDismissed(true)} />} |
| 358 | + > |
| 359 | + {!currentVolumeAttributesClassName |
| 360 | + ? t('public~VolumeAttributesClass "{{target}}" is pending application.', { |
| 361 | + target: volumeAttributesClassName, |
| 362 | + }) |
| 363 | + : t( |
| 364 | + 'public~Your volume settings are being updated from "{{current}}" to "{{target}}". This may take a few moments.', |
| 365 | + { |
| 366 | + current: currentVolumeAttributesClassName, |
| 367 | + target: volumeAttributesClassName, |
| 368 | + }, |
| 369 | + )} |
| 370 | + </Alert> |
| 371 | + )} |
295 | 372 | <SectionHeading text={t('public~PersistentVolumeClaim details')} /> |
296 | 373 | {totalCapacityMetric && !loading && ( |
297 | 374 | <div className="co-pvc-donut"> |
@@ -383,19 +460,34 @@ const PVCDetails: React.FCC<PVCDetailsProps> = ({ obj: pvc }) => { |
383 | 460 | )} |
384 | 461 | </DescriptionListDescription> |
385 | 462 | </DescriptionListGroup> |
386 | | - {isVACSupported && |
387 | | - !!volumeAttributesClassName && |
388 | | - volumeAttributesClassName === currentVolumeAttributesClassName && ( |
389 | | - <DescriptionListGroup> |
390 | | - <DescriptionListTerm>{t('public~VolumeAttributesClass')}</DescriptionListTerm> |
391 | | - <DescriptionListDescription> |
| 463 | + {isVACSupported && volumeAttributesClassName !== currentVolumeAttributesClassName && ( |
| 464 | + <DescriptionListGroup> |
| 465 | + <DescriptionListTerm> |
| 466 | + {t('public~Requested VolumeAttributesClass')} |
| 467 | + </DescriptionListTerm> |
| 468 | + <DescriptionListDescription data-test-id="pvc-requested-vac"> |
| 469 | + {volumeAttributesClassName ? ( |
392 | 470 | <ResourceLink |
393 | 471 | kind={referenceFor(VolumeAttributesClassModel)} |
394 | 472 | name={volumeAttributesClassName} |
395 | 473 | /> |
396 | | - </DescriptionListDescription> |
397 | | - </DescriptionListGroup> |
398 | | - )} |
| 474 | + ) : ( |
| 475 | + DASH |
| 476 | + )} |
| 477 | + </DescriptionListDescription> |
| 478 | + </DescriptionListGroup> |
| 479 | + )} |
| 480 | + {isVACSupported && !!currentVolumeAttributesClassName && ( |
| 481 | + <DescriptionListGroup> |
| 482 | + <DescriptionListTerm>{t('public~VolumeAttributesClass')}</DescriptionListTerm> |
| 483 | + <DescriptionListDescription data-test-id="pvc-current-vac"> |
| 484 | + <ResourceLink |
| 485 | + kind={referenceFor(VolumeAttributesClassModel)} |
| 486 | + name={currentVolumeAttributesClassName} |
| 487 | + /> |
| 488 | + </DescriptionListDescription> |
| 489 | + </DescriptionListGroup> |
| 490 | + )} |
399 | 491 | {volumeName && canListPV && ( |
400 | 492 | <DescriptionListGroup> |
401 | 493 | <DescriptionListTerm>{t('public~PersistentVolumes')}</DescriptionListTerm> |
|
0 commit comments