Skip to content

Commit 5cd9dee

Browse files
committed
CNV-89816: Cancel all pending uploads when VM creation is canceled or VM is deleted
1 parent 3d579c1 commit 5cd9dee

23 files changed

Lines changed: 550 additions & 27 deletions

locales/en/plugin__kubevirt-plugin.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
"{{count}} Node Field_other": "{{count}} Node Fields",
3434
"{{count}} successful checks_one": "{{count}} successful check",
3535
"{{count}} successful checks_other": "{{count}} successful checks",
36+
"{{count}} uploads were aborted_one": "{{count}} uploads were aborted",
37+
"{{count}} uploads were aborted_other": "{{count}} uploads were aborteds",
3638
"{{count}} VirtualMachine_one": "{{count}} VirtualMachine",
3739
"{{count}} VirtualMachine_other": "{{count}} VirtualMachines",
3840
"{{count}} Volumes_one": "{{count}} Volume",
@@ -2210,6 +2212,7 @@
22102212
"Upload of {{fileName}} aborted": "Upload of {{fileName}} aborted",
22112213
"Upload of {{fileName}} completed": "Upload of {{fileName}} completed",
22122214
"Upload of {{fileName}} failed": "Upload of {{fileName}} failed",
2215+
"Upload of {{fileName}} was aborted": "Upload of {{fileName}} was aborted",
22132216
"Upload progress for {{fileName}}": "Upload progress for {{fileName}}",
22142217
"Upload PVC image": "Upload PVC image",
22152218
"Upload to registry": "Upload to registry",

locales/es/plugin__kubevirt-plugin.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@
4141
"{{count}} successful checks_one": "{{count}} comprobaciones exitosas_one",
4242
"{{count}} successful checks_many": "{{count}} successful checks",
4343
"{{count}} successful checks_other": "{{count}} comprobaciones exitosas_other",
44+
"{{count}} uploads were aborted_one": "{{count}} uploads were aborted",
45+
"{{count}} uploads were aborted_many": "{{count}} uploads were aborted",
46+
"{{count}} uploads were aborted_other": "{{count}} uploads were aborted",
4447
"{{count}} VirtualMachine_one": "{{count}} VirtualMachine_one",
4548
"{{count}} VirtualMachine_many": "{{count}} VirtualMachine",
4649
"{{count}} VirtualMachine_other": "{{count}} VirtualMachine_other",
@@ -2231,6 +2234,7 @@
22312234
"Upload of {{fileName}} aborted": "Upload of {{fileName}} aborted",
22322235
"Upload of {{fileName}} completed": "Upload of {{fileName}} completed",
22332236
"Upload of {{fileName}} failed": "Upload of {{fileName}} failed",
2237+
"Upload of {{fileName}} was aborted": "Upload of {{fileName}} was aborted",
22342238
"Upload progress for {{fileName}}": "Upload progress for {{fileName}}",
22352239
"Upload PVC image": "Cargar imagen de PVC",
22362240
"Upload to registry": "Cargar al registro",

locales/fr/plugin__kubevirt-plugin.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@
4141
"{{count}} successful checks_one": "{{count}} vérifications réussies_une",
4242
"{{count}} successful checks_many": "{{count}} successful checks",
4343
"{{count}} successful checks_other": "{{count}} vérifications réussies_autres",
44+
"{{count}} uploads were aborted_one": "{{count}} uploads were aborted",
45+
"{{count}} uploads were aborted_many": "{{count}} uploads were aborted",
46+
"{{count}} uploads were aborted_other": "{{count}} uploads were aborted",
4447
"{{count}} VirtualMachine_one": "{{count}} Machine virtuelle_une",
4548
"{{count}} VirtualMachine_many": "{{count}} VirtualMachine",
4649
"{{count}} VirtualMachine_other": "{{count}} Machine virtuelle_autre",
@@ -2231,6 +2234,7 @@
22312234
"Upload of {{fileName}} aborted": "Upload of {{fileName}} aborted",
22322235
"Upload of {{fileName}} completed": "Upload of {{fileName}} completed",
22332236
"Upload of {{fileName}} failed": "Upload of {{fileName}} failed",
2237+
"Upload of {{fileName}} was aborted": "Upload of {{fileName}} was aborted",
22342238
"Upload progress for {{fileName}}": "Upload progress for {{fileName}}",
22352239
"Upload PVC image": "Télécharger l'image PVC",
22362240
"Upload to registry": "Télécharger dans le registre",

locales/ja/plugin__kubevirt-plugin.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"{{count}} Node Field_other": "{{count}} ノードフィールド",
3434
"{{count}} successful checks_one": "{{count}} 件のチェックが成功しました",
3535
"{{count}} successful checks_other": "{{count}} 件のチェックが成功しました",
36+
"{{count}} uploads were aborted_other": "{{count}} uploads were aborted",
3637
"{{count}} VirtualMachine_one": "{{count}} VirtualMachine",
3738
"{{count}} VirtualMachine_other": "{{count}} VirtualMachine",
3839
"{{count}} Volumes_one": "{{count}} 台のボリューム",
@@ -2209,6 +2210,7 @@
22092210
"Upload of {{fileName}} aborted": "Upload of {{fileName}} aborted",
22102211
"Upload of {{fileName}} completed": "Upload of {{fileName}} completed",
22112212
"Upload of {{fileName}} failed": "Upload of {{fileName}} failed",
2213+
"Upload of {{fileName}} was aborted": "Upload of {{fileName}} was aborted",
22122214
"Upload progress for {{fileName}}": "Upload progress for {{fileName}}",
22132215
"Upload PVC image": "PVC イメージのアップロード",
22142216
"Upload to registry": "レジストリーへのアップロード",

locales/ko/plugin__kubevirt-plugin.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"{{count}} Node Field_other": "{{count}} 노드 필드",
3434
"{{count}} successful checks_one": "{{count}} 검사 성공",
3535
"{{count}} successful checks_other": "{{count}} 검사 성공",
36+
"{{count}} uploads were aborted_other": "{{count}} uploads were aborted",
3637
"{{count}} VirtualMachine_one": "{{count}} VirtualMachine",
3738
"{{count}} VirtualMachine_other": "{{count}} VirtualMachine",
3839
"{{count}} Volumes_one": "{{count}} 볼륨",
@@ -2209,6 +2210,7 @@
22092210
"Upload of {{fileName}} aborted": "Upload of {{fileName}} aborted",
22102211
"Upload of {{fileName}} completed": "Upload of {{fileName}} completed",
22112212
"Upload of {{fileName}} failed": "Upload of {{fileName}} failed",
2213+
"Upload of {{fileName}} was aborted": "Upload of {{fileName}} was aborted",
22122214
"Upload progress for {{fileName}}": "Upload progress for {{fileName}}",
22132215
"Upload PVC image": "PVC 이미지 업로드",
22142216
"Upload to registry": "레지스트리에 업로드",

locales/zh/plugin__kubevirt-plugin.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"{{count}} Node Field_other": "{{count}} 节点字段",
3434
"{{count}} successful checks_one": "{{count}} 个成功的检查",
3535
"{{count}} successful checks_other": "{{count}} 个成功的检查",
36+
"{{count}} uploads were aborted_other": "{{count}} uploads were aborted",
3637
"{{count}} VirtualMachine_one": "{{count}} 个虚拟机",
3738
"{{count}} VirtualMachine_other": "{{count}} 个虚拟机",
3839
"{{count}} Volumes_one": "{{count}} 个卷_one",
@@ -2209,6 +2210,7 @@
22092210
"Upload of {{fileName}} aborted": "Upload of {{fileName}} aborted",
22102211
"Upload of {{fileName}} completed": "Upload of {{fileName}} completed",
22112212
"Upload of {{fileName}} failed": "Upload of {{fileName}} failed",
2213+
"Upload of {{fileName}} was aborted": "Upload of {{fileName}} was aborted",
22122214
"Upload progress for {{fileName}}": "Upload progress for {{fileName}}",
22132215
"Upload PVC image": "上传 PVC 镜像",
22142216
"Upload to registry": "上传到 registry",

src/utils/components/DiskModal/UploadDiskModal.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ import { useCDIUpload } from '@kubevirt-utils/hooks/useCDIUpload/useCDIUpload';
66
import { useKubevirtTranslation } from '@kubevirt-utils/hooks/useKubevirtTranslation';
77
import { cancelTrackedUploadOnModalClose } from '@kubevirt-utils/hooks/useUploadProgressToast/utils/modalUploadCancel';
88
import { completeVmDiskUpload } from '@kubevirt-utils/hooks/useUploadProgressToast/utils/uploadCompletion';
9-
import { getVmDiskUploadKey } from '@kubevirt-utils/hooks/useUploadProgressToast/utils/uploadKeys';
9+
import {
10+
getUploadClusterForVm,
11+
getVmDiskUploadKey,
12+
} from '@kubevirt-utils/hooks/useUploadProgressToast/utils/uploadKeys';
1013
import { getName, getNamespace } from '@kubevirt-utils/resources/shared';
1114
import { getCluster } from '@multicluster/helpers/selectors';
1215
import { isRunning } from '@virtualmachines/utils';
@@ -57,7 +60,7 @@ const UploadDiskModal: FC<V1SubDiskModalProps> = ({
5760
onClose={() => {
5861
const diskName = getValues('disk.name');
5962
const uploadKey = diskName
60-
? getVmDiskUploadKey(getCluster(vm), getNamespace(vm), getName(vm), diskName)
63+
? getVmDiskUploadKey(getUploadClusterForVm(vm), getNamespace(vm), getName(vm), diskName)
6164
: undefined;
6265

6366
cancelTrackedUploadOnModalClose({ upload, uploadKey });
@@ -66,7 +69,7 @@ const UploadDiskModal: FC<V1SubDiskModalProps> = ({
6669
onSubmit={() =>
6770
handleSubmit(async (data) => {
6871
const uploadKey = getVmDiskUploadKey(
69-
getCluster(vm),
72+
getUploadClusterForVm(vm),
7073
getNamespace(vm),
7174
getName(vm),
7275
data.disk.name,

src/utils/components/DiskModal/utils/submit.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
import { UploadDataProps } from '@kubevirt-utils/hooks/useCDIUpload/types';
1212
import { getVmDiskUploadSuccessLinks } from '@kubevirt-utils/hooks/useUploadProgressToast/utils/uploadLinks';
1313
import { PersistentVolumeClaimModel } from '@kubevirt-utils/models';
14-
import { getName, getNamespace } from '@kubevirt-utils/resources/shared';
14+
import { getName, getNamespace, getUID } from '@kubevirt-utils/resources/shared';
1515
import {
1616
getBootDisk,
1717
getDataVolumeTemplates,
@@ -63,15 +63,16 @@ export const uploadDataVolume = async (
6363
uploadKey && file
6464
? {
6565
abortTooltip,
66-
contextLinks: t
67-
? getVmDiskUploadSuccessLinks(
68-
t,
69-
vm,
70-
data.disk?.name,
71-
dataVolume.metadata.name,
72-
isCDROM,
73-
)
74-
: undefined,
66+
contextLinks:
67+
t && getUID(vm)
68+
? getVmDiskUploadSuccessLinks(
69+
t,
70+
vm,
71+
data.disk?.name,
72+
dataVolume.metadata.name,
73+
isCDROM,
74+
)
75+
: undefined,
7576
dvCluster: getCluster(vm),
7677
dvName: dataVolume.metadata.name,
7778
dvNamespace: getNamespace(dataVolume),

src/utils/hooks/useUploadProgressToast/UploadProgressToastListener.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import { FC, useEffect } from 'react';
1+
import { FC, useEffect, useRef } from 'react';
22
import { useNavigate } from 'react-router';
33

44
import useKubevirtToast from '@kubevirt-utils/hooks/useKubevirtToast';
55
import { useKubevirtTranslation } from '@kubevirt-utils/hooks/useKubevirtTranslation';
66

7+
import { cleanupRemovedUploads } from './utils/cleanupRemovedUploads';
78
import { useUploadProgressStore } from './uploadProgressStore';
89
import { syncUploadToasts } from './uploadToastSync';
910

@@ -18,8 +19,17 @@ const UploadProgressToastListener: FC = () => {
1819
);
1920
const trySetToastId = useUploadProgressStore((state) => state.trySetToastId);
2021
const removeUpload = useUploadProgressStore((state) => state.removeUpload);
22+
const prevUploadsRef = useRef(useUploadProgressStore.getState().uploads);
2123

2224
useEffect(() => {
25+
cleanupRemovedUploads({
26+
addWarningToast,
27+
currentUploads: uploads,
28+
previousUploads: prevUploadsRef.current,
29+
removeToast,
30+
t,
31+
});
32+
2333
syncUploadToasts(uploads, {
2434
addDangerToast,
2535
addInfoToast,
@@ -32,6 +42,8 @@ const UploadProgressToastListener: FC = () => {
3242
tryMarkTerminalToastShown,
3343
trySetToastId,
3444
});
45+
46+
prevUploadsRef.current = uploads;
3547
}, [
3648
addDangerToast,
3749
addInfoToast,

src/utils/hooks/useUploadProgressToast/uploadProgressStore.test.ts

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import { cancelUploadPVC } from '@kubevirt-utils/hooks/useCDIUpload/utils';
22

33
import { UPLOAD_PROGRESS_STATUS } from './utils/constants';
4+
import {
5+
getBootableVolumeUploadKey,
6+
getVmCdromUploadKey,
7+
getVmDiskUploadKey,
8+
} from './utils/uploadKeys';
49
import { useUploadProgressStore } from './uploadProgressStore';
510

611
jest.mock('@kubevirt-utils/hooks/useCDIUpload/utils', () => ({
@@ -18,6 +23,12 @@ const ERROR_CANCEL_FAILED = 'cancel failed';
1823
const DV_NAME = 'upload-dv';
1924
const NAMESPACE = 'default';
2025
const CLUSTER = 'local-cluster';
26+
const VM_NAME = 'test-vm';
27+
const VM_DISK_NAME = 'rootdisk';
28+
const CDROM_NAME = 'cdrom-1';
29+
const OTHER_VM_NAME = 'other-vm';
30+
const BOOTABLE_VOLUME_NAMESPACE = 'openshift-virtualization-os-images';
31+
const BOOTABLE_VOLUME_NAME = 'fedora-40';
2132
const LINK_LABEL_VIEW_DISK = 'View disk';
2233
const LINK_URL_DISK = '/disk';
2334
const LINK_LABEL_EXISTING = 'Existing link';
@@ -249,4 +260,127 @@ describe('useUploadProgressStore', () => {
249260
expect(cancelUploadPVC).not.toHaveBeenCalled();
250261
});
251262
});
263+
264+
describe('cancelUploadsForVm', () => {
265+
it('should cancel all vm-disk and vm-cdrom uploads for the VM', async () => {
266+
const diskUploadKey = getVmDiskUploadKey(CLUSTER, NAMESPACE, VM_NAME, VM_DISK_NAME);
267+
const cdromUploadKey = getVmCdromUploadKey(CLUSTER, NAMESPACE, VM_NAME, CDROM_NAME);
268+
const bootableVolumeUploadKey = getBootableVolumeUploadKey(
269+
BOOTABLE_VOLUME_NAMESPACE,
270+
BOOTABLE_VOLUME_NAME,
271+
);
272+
const otherVmUploadKey = getVmDiskUploadKey(CLUSTER, NAMESPACE, OTHER_VM_NAME, VM_DISK_NAME);
273+
const diskCancelUpload = jest.fn(async () => undefined);
274+
const cdromCancelUpload = jest.fn(async () => undefined);
275+
const bootableCancelUpload = jest.fn(async () => undefined);
276+
const otherVmCancelUpload = jest.fn(async () => undefined);
277+
278+
useUploadProgressStore.getState().startUpload(diskUploadKey, {
279+
cancelUpload: diskCancelUpload,
280+
fileName: FILE_IMAGE_ISO,
281+
});
282+
useUploadProgressStore.getState().startUpload(cdromUploadKey, {
283+
cancelUpload: cdromCancelUpload,
284+
fileName: FILE_IMAGE_ISO,
285+
});
286+
useUploadProgressStore.getState().startUpload(bootableVolumeUploadKey, {
287+
cancelUpload: bootableCancelUpload,
288+
fileName: FILE_IMAGE_ISO,
289+
});
290+
useUploadProgressStore.getState().startUpload(otherVmUploadKey, {
291+
cancelUpload: otherVmCancelUpload,
292+
fileName: FILE_IMAGE_ISO,
293+
});
294+
295+
await useUploadProgressStore.getState().cancelUploadsForVm(CLUSTER, NAMESPACE, VM_NAME);
296+
297+
expect(diskCancelUpload).toHaveBeenCalledTimes(1);
298+
expect(cdromCancelUpload).toHaveBeenCalledTimes(1);
299+
expect(bootableCancelUpload).not.toHaveBeenCalled();
300+
expect(otherVmCancelUpload).not.toHaveBeenCalled();
301+
expect(useUploadProgressStore.getState().getUpload(diskUploadKey)).toBeUndefined();
302+
expect(useUploadProgressStore.getState().getUpload(cdromUploadKey)).toBeUndefined();
303+
expect(useUploadProgressStore.getState().getUpload(bootableVolumeUploadKey)?.status).toBe(
304+
UPLOAD_PROGRESS_STATUS.UPLOADING,
305+
);
306+
expect(useUploadProgressStore.getState().getUpload(otherVmUploadKey)?.status).toBe(
307+
UPLOAD_PROGRESS_STATUS.UPLOADING,
308+
);
309+
});
310+
311+
it('should no-op when no uploads match the VM', async () => {
312+
await useUploadProgressStore.getState().cancelUploadsForVm(CLUSTER, NAMESPACE, VM_NAME);
313+
314+
expect(cancelUploadPVC).not.toHaveBeenCalled();
315+
});
316+
317+
it('should not cancel completed uploads for the VM', async () => {
318+
const diskUploadKey = getVmDiskUploadKey(CLUSTER, NAMESPACE, VM_NAME, VM_DISK_NAME);
319+
const diskCancelUpload = jest.fn(async () => undefined);
320+
321+
useUploadProgressStore.getState().startUpload(diskUploadKey, {
322+
cancelUpload: diskCancelUpload,
323+
fileName: FILE_IMAGE_ISO,
324+
});
325+
useUploadProgressStore.getState().completeUpload(diskUploadKey);
326+
327+
await useUploadProgressStore.getState().cancelUploadsForVm(CLUSTER, NAMESPACE, VM_NAME);
328+
329+
expect(diskCancelUpload).not.toHaveBeenCalled();
330+
expect(useUploadProgressStore.getState().getUpload(diskUploadKey)?.status).toBe(
331+
UPLOAD_PROGRESS_STATUS.SUCCESS,
332+
);
333+
});
334+
335+
it('should cancel empty-cluster uploads when deleting an ACM fleet VM', async () => {
336+
const emptyClusterUploadKey = getVmDiskUploadKey('', NAMESPACE, VM_NAME, VM_DISK_NAME);
337+
const cancelUpload = jest.fn(async () => undefined);
338+
339+
useUploadProgressStore.getState().startUpload(emptyClusterUploadKey, {
340+
cancelUpload,
341+
fileName: FILE_IMAGE_ISO,
342+
});
343+
344+
await useUploadProgressStore.getState().cancelUploadsForVm(CLUSTER, NAMESPACE, VM_NAME);
345+
346+
expect(cancelUpload).toHaveBeenCalledTimes(1);
347+
expect(useUploadProgressStore.getState().getUpload(emptyClusterUploadKey)).toBeUndefined();
348+
});
349+
});
350+
351+
describe('cancelAllPendingUploads', () => {
352+
it('should cancel and remove all in-progress uploads', async () => {
353+
const diskUploadKey = getVmDiskUploadKey(CLUSTER, NAMESPACE, VM_NAME, VM_DISK_NAME);
354+
const bootableVolumeUploadKey = getBootableVolumeUploadKey(
355+
BOOTABLE_VOLUME_NAMESPACE,
356+
BOOTABLE_VOLUME_NAME,
357+
);
358+
const completedUploadKey = 'completed-upload-key';
359+
const diskCancelUpload = jest.fn(async () => undefined);
360+
const bootableCancelUpload = jest.fn(async () => undefined);
361+
362+
useUploadProgressStore.getState().startUpload(diskUploadKey, {
363+
cancelUpload: diskCancelUpload,
364+
fileName: FILE_IMAGE_ISO,
365+
});
366+
useUploadProgressStore.getState().startUpload(bootableVolumeUploadKey, {
367+
cancelUpload: bootableCancelUpload,
368+
fileName: FILE_IMAGE_ISO,
369+
});
370+
useUploadProgressStore.getState().startUpload(completedUploadKey, {
371+
fileName: FILE_IMAGE_ISO,
372+
});
373+
useUploadProgressStore.getState().completeUpload(completedUploadKey);
374+
375+
await useUploadProgressStore.getState().cancelAllPendingUploads();
376+
377+
expect(diskCancelUpload).toHaveBeenCalledTimes(1);
378+
expect(bootableCancelUpload).toHaveBeenCalledTimes(1);
379+
expect(useUploadProgressStore.getState().getUpload(diskUploadKey)).toBeUndefined();
380+
expect(useUploadProgressStore.getState().getUpload(bootableVolumeUploadKey)).toBeUndefined();
381+
expect(useUploadProgressStore.getState().getUpload(completedUploadKey)?.status).toBe(
382+
UPLOAD_PROGRESS_STATUS.SUCCESS,
383+
);
384+
});
385+
});
252386
});

0 commit comments

Comments
 (0)