Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug 1871742: can't delete a PVC created using a data upload #6407

Merged
merged 1 commit into from
Sep 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
29 changes: 27 additions & 2 deletions frontend/packages/console-plugin-sdk/src/typings/pvc.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { K8sResourceCommon } from '@console/internal/module/k8s';
import { PersistentVolumeClaimKind } from '@console/internal/module/k8s';
import { AlertVariant } from '@patternfly/react-core';
import { Extension, LazyLoader } from './base';

namespace ExtensionProperties {
Expand All @@ -11,7 +12,7 @@ namespace ExtensionProperties {
/** Loader for the corresponding Status component. */
loader: LazyLoader<T>;
/** predicate that tells if to render the status or not */
predicate: (pvc: K8sResourceCommon) => boolean;
predicate: (pvc: PersistentVolumeClaimKind) => boolean;
/** priority for the corresponding Status component. bigger is prioritized */
priority: number;
}
Expand All @@ -22,6 +23,22 @@ namespace ExtensionProperties {
/** path for the create prop */
path: string;
}

export interface PVCDelete {
/** predicate that tells if to use the extension or not */
predicate: (pvc: PersistentVolumeClaimKind) => boolean;
/** method for the pvc delete operation */
onPVCKill: (pvc: PersistentVolumeClaimKind) => Promise<void>;
/** alert for additional info */
alert?: {
/** alert title */
title: string;
/** alert variant */
type: AlertVariant;
/** alert body */
body: LazyLoader<{ pvc: PersistentVolumeClaimKind }>;
};
}
}

export interface PVCAlert<R = any> extends Extension<ExtensionProperties.PVCAlert<R>> {
Expand All @@ -36,6 +53,10 @@ export interface PVCCreateProp extends Extension<ExtensionProperties.PVCCreatePr
type: 'PVCCreateProp';
}

export interface PVCDelete extends Extension<ExtensionProperties.PVCDelete> {
type: 'PVCDelete';
}

export const isPVCAlert = (e: Extension): e is PVCAlert => {
return e.type === 'PVCAlert';
};
Expand All @@ -47,3 +68,7 @@ export const isPVCStatus = (e: Extension): e is PVCStatus => {
export const isPVCCreateProp = (e: Extension): e is PVCCreateProp => {
return e.type === 'PVCCreateProp';
};

export const isPVCDelete = (e: Extension): e is PVCDelete => {
return e.type === 'PVCDelete';
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ export enum UPLOAD_STATUS {

export const CDI_UPLOAD_POD_ANNOTATION = 'cdi.kubevirt.io/storage.pod.phase';
export const CDI_UPLOAD_POD_NAME_ANNOTATION = 'cdi.kubevirt.io/storage.uploadPodName';
export const CDI_BOUND_PVC_ANNOTATION = 'cdi.kubevirt.io/storage.condition.bound';
export const CDI_UPLOAD_RUNNING = 'Running';
export const CDI_UPLOAD_OS_URL_PARAM = 'os';
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import * as React from 'react';
import { k8sKill, PersistentVolumeClaimKind } from '@console/internal/module/k8s';
import { DataVolumeModel } from '../../models';
import {
useK8sWatchResource,
WatchK8sResource,
} from '@console/internal/components/utils/k8s-watch-hook';
import { PersistentVolumeClaimModel } from '@console/internal/models';
import { TEMPLATE_VM_GOLDEN_OS_NAMESPACE } from '../../constants';

export const killCDIBoundPVC = (pvc: PersistentVolumeClaimKind) =>
k8sKill(DataVolumeModel, {
metadata: {
name: pvc?.metadata?.name,
namespace: pvc?.metadata?.namespace,
},
});

export const PVCDeleteAlertExtension: React.FC<{ pvc: PersistentVolumeClaimKind }> = ({ pvc }) => {
const goldenPvcsResource: WatchK8sResource = {
isList: true,
optional: true,
kind: PersistentVolumeClaimModel.kind,
namespace: TEMPLATE_VM_GOLDEN_OS_NAMESPACE,
};
const [goldenPvcs, loadedPvcs, errorPvcs] = useK8sWatchResource<PersistentVolumeClaimKind[]>(
goldenPvcsResource,
);

const isGolden =
pvc?.metadata?.namespace === TEMPLATE_VM_GOLDEN_OS_NAMESPACE &&
goldenPvcs.find((goldenPvc) => goldenPvc?.metadata?.name === pvc?.metadata?.name);

return (
<>
<p>
Deleting this PVC will also delete{' '}
<strong className="co-break-word">{pvc?.metadata?.name}</strong> Data Volume
</p>
{!loadedPvcs && <p>Checking for usages of this PVC...</p>}
{errorPvcs && <p>Error checking for usages of this PVC.</p>}
{isGolden && (
<p>
<strong className="co-break-word">WARNING:</strong> this PVC is used as a base operating
system image. New VMs will not be able to clone this image
</p>
)}
</>
);
};
26 changes: 24 additions & 2 deletions frontend/packages/kubevirt-plugin/src/plugin.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from 'react';
import * as _ from 'lodash';
import * as virtualMachineIcon from './images/virtual-machine.svg';
import { AlertVariant } from '@patternfly/react-core';
import {
Plugin,
ResourceNSNavItem,
Expand All @@ -21,6 +22,7 @@ import {
PVCCreateProp,
PVCAlert,
PVCStatus,
PVCDelete,
} from '@console/plugin-sdk';
import { DashboardsStorageCapacityDropdownItem } from '@console/ceph-storage-plugin';
import { TemplateModel, PodModel, PersistentVolumeClaimModel } from '@console/internal/models';
Expand All @@ -42,8 +44,9 @@ import {
CDIUploadContext,
useCDIUploadHook,
} from './components/cdi-upload-provider/cdi-upload-provider';
import { killCDIBoundPVC } from './components/cdi-upload-provider/pvc-delete-extension';
import { isPvcBoundToCDI, isPvcUploading } from './selectors/pvc/selectors';
import './style.scss';
import { isPvcUploading } from './selectors/pvc/selectors';

type ConsumedExtensions =
| ResourceNSNavItem
Expand All @@ -66,7 +69,8 @@ type ConsumedExtensions =
| ContextProvider
| PVCCreateProp
| PVCAlert
| PVCStatus;
| PVCStatus
| PVCDelete;

export const FLAG_KUBEVIRT = 'KUBEVIRT';

Expand Down Expand Up @@ -544,6 +548,24 @@ const plugin: Plugin<ConsumedExtensions> = [
required: [FLAG_KUBEVIRT],
},
},
{
type: 'PVCDelete',
properties: {
predicate: isPvcBoundToCDI,
onPVCKill: killCDIBoundPVC,
alert: {
type: AlertVariant.warning,
title: 'PVC Delete',
body: () =>
import('./components/cdi-upload-provider/pvc-delete-extension').then(
(m) => m.PVCDeleteAlertExtension,
),
},
},
flags: {
required: [FLAG_KUBEVIRT],
},
},
];

export default plugin;
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
CDI_UPLOAD_RUNNING,
CDI_UPLOAD_POD_ANNOTATION,
CDI_UPLOAD_POD_NAME_ANNOTATION,
CDI_BOUND_PVC_ANNOTATION,
} from '../../components/cdi-upload-provider/consts';
import { STORAGE_IMPORT_POD_LABEL } from '../../constants';

Expand All @@ -24,3 +25,5 @@ export const getPvcUploadPhase = (pvc) => getAnnotation(pvc, CDI_UPLOAD_POD_ANNO

export const isPvcUploading = (pvc) =>
getPvcUploadPodName(pvc) && getPvcUploadPhase(pvc) === CDI_UPLOAD_RUNNING;

export const isPvcBoundToCDI = (pvc) => getAnnotation(pvc, CDI_BOUND_PVC_ANNOTATION) === 'true';
72 changes: 72 additions & 0 deletions frontend/public/components/modals/delete-pvc-modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import * as React from 'react';
import { Alert, Stack, StackItem } from '@patternfly/react-core';
import { AsyncComponent, HandlePromiseProps, withHandlePromise } from '../utils';
import { getName, YellowExclamationTriangleIcon } from '@console/shared';
import {
createModalLauncher,
ModalTitle,
ModalBody,
ModalSubmitFooter,
ModalComponentProps,
} from '../factory';
import { k8sKill, PersistentVolumeClaimKind } from '@console/internal/module/k8s';
import { PersistentVolumeClaimModel } from '../../models';
import { isPVCDelete, PVCDelete, useExtensions } from '@console/plugin-sdk';

const DeletePVCModal = withHandlePromise<DeletePVCModalProps>((props) => {
const { pvc, inProgress, errorMessage, handlePromise, close, cancel } = props;
const pvcDeleteExtensions = useExtensions<PVCDelete>(isPVCDelete);
const pvcName = getName(pvc);

const submit = (e) => {
e.preventDefault();

const promise = k8sKill(PersistentVolumeClaimModel, pvc);
const extensionPromises = pvcDeleteExtensions.map(
({ properties: { predicate, onPVCKill } }) => predicate(pvc) && onPVCKill(pvc),
);
return handlePromise(Promise.all([promise, ...extensionPromises]), close);
};

const alertComponents = pvcDeleteExtensions.map(
({ properties: { predicate, alert }, uid }) =>
predicate(pvc) && (
<StackItem key={uid}>
<Alert className="co-m-form-row" isInline variant={alert?.type} title={alert?.title}>
<AsyncComponent loader={alert?.body} pvc={pvc} />
</Alert>
</StackItem>
),
);

return (
<form onSubmit={submit} className="modal-content">
<ModalTitle>
<YellowExclamationTriangleIcon className="co-icon-space-r" /> Delete Persistent Volume Claim
</ModalTitle>
<ModalBody>
<Stack hasGutter>
{alertComponents}
<StackItem>
Are you sure you want to delete <strong className="co-break-word">{pvcName}</strong>{' '}
Persistent Volume Claim?
</StackItem>
</Stack>
</ModalBody>
<ModalSubmitFooter
errorMessage={errorMessage}
inProgress={inProgress}
submitText="Delete"
submitDanger
cancel={cancel}
/>
</form>
);
});

export type DeletePVCModalProps = {
pvc: PersistentVolumeClaimKind;
} & ModalComponentProps &
HandlePromiseProps;

export default createModalLauncher(DeletePVCModal);
16 changes: 14 additions & 2 deletions frontend/public/components/persistent-volume-claim.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,32 @@ import {
humanizeBinaryBytes,
convertToBaseValue,
AsyncComponent,
asAccessReview,
} from './utils';
import { ResourceEventStream } from './events';
import { PersistentVolumeClaimModel } from '../models';
import { setPVCMetrics } from '../actions/ui';
import { PrometheusEndpoint } from './graphs/helpers';
import { usePrometheusPoll } from './graphs/prometheus-poll-hook';
import deletePVCModal from './modals/delete-pvc-modal';

const { common, ExpandPVC, PVCSnapshot, ClonePVC } = Kebab.factory;
const { ModifyLabels, ModifyAnnotations, Edit, ExpandPVC, PVCSnapshot, ClonePVC } = Kebab.factory;
const menuActions = [
...Kebab.getExtensionsActionsForKind(PersistentVolumeClaimModel),
ExpandPVC,
PVCSnapshot,
ClonePVC,
...common,
ModifyLabels,
ModifyAnnotations,
Edit,
(kind, obj) => ({
label: `Delete ${kind.label}`,
callback: () =>
deletePVCModal({
pvc: obj,
}),
accessReview: asAccessReview(kind, obj, 'delete'),
}),
];

export const PVCStatus = ({ pvc }) => {
Expand Down