Skip to content

Commit

Permalink
Bug 1889213: The error message of uploading failure is not clear enough
Browse files Browse the repository at this point in the history
  • Loading branch information
glekner committed Dec 17, 2020
1 parent 8e5b41a commit 6b367b8
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,30 @@ import {
Alert,
AlertVariant,
Spinner,
Checkbox,
Split,
SplitItem,
} from '@patternfly/react-core';
import { ErrorCircleOIcon, InProgressIcon } from '@patternfly/react-icons';
import { history, resourcePath } from '@console/internal/components/utils';
import { getName, getNamespace } from '@console/shared';
import { PodModel } from '@console/internal/models';
import { DataUpload } from '../cdi-upload-provider';
import { getProgressVariant } from '../upload-pvc-popover';
import { killUploadPVC } from '../../../k8s/requests/cdi-upload/cdi-upload-requests';
import { V1alpha1DataVolume } from '../../../types/vm/disk/V1alpha1DataVolume';
import { UPLOAD_STATUS } from '../consts';

export enum uploadErrorType {
MISSING = 'missing',
ALLOCATE = 'allocate',
CERT = 'cert',
CDI_INIT = 'cdi_init',
}

export const UploadPVCFormStatus: React.FC<UploadPVCFormStatusProps> = ({
upload,
dataVolume,
isSubmitting,
isAllocating,
allocateError,
Expand Down Expand Up @@ -53,6 +68,7 @@ export const UploadPVCFormStatus: React.FC<UploadPVCFormStatusProps> = ({
<EmptyState>
<DataUploadStatus
upload={upload}
dataVolume={dataVolume}
error={error}
isAllocating={isAllocating}
onErrorClick={onErrorClick}
Expand All @@ -66,13 +82,23 @@ export const UploadPVCFormStatus: React.FC<UploadPVCFormStatusProps> = ({

const DataUploadStatus: React.FC<DataUploadStatus> = ({
upload,
dataVolume,
error,
onErrorClick,
isAllocating,
onSuccessClick,
onCancelClick,
}) => {
if (error) return <ErrorStatus error={error} onErrorClick={onErrorClick} />;
if (error)
return error === uploadErrorType.CDI_INIT ? (
<CDIInitErrorStatus
onErrorClick={onErrorClick}
pvcName={getName(dataVolume)}
namespace={getNamespace(dataVolume)}
/>
) : (
<ErrorStatus error={error} onErrorClick={onErrorClick} />
);
if (isAllocating) return <AllocatingStatus />;
if (upload?.uploadStatus === UPLOAD_STATUS.CANCELED) return <CancellingStatus />;
return (
Expand Down Expand Up @@ -120,6 +146,69 @@ const ErrorStatus: React.FC<ErrorStatusProps> = ({ error, onErrorClick }) => (
</>
);

const CDIInitErrorStatus: React.FC<CDIInitErrorStatus> = ({ onErrorClick, pvcName, namespace }) => {
const [shouldKillDv, setShouldKillDv] = React.useState(true);
return (
<>
<EmptyStateIcon icon={ErrorCircleOIcon} color="#cf1010" />
<Title headingLevel="h4" size="lg">
CDI Error: Could not initiate Data Volume
</Title>
<EmptyStateBody>
<Stack hasGutter>
<StackItem>
Data Volume failed to initiate upload, you can either delete the Data Volume and try
again, or check logs
</StackItem>
<StackItem>
<Split>
<SplitItem isFilled />
<Checkbox
id="approve-checkbox"
isChecked={shouldKillDv}
aria-label="kill datavolume checkbox"
label={`Delete Data Volume: ${pvcName}`}
onChange={(v) => setShouldKillDv(v)}
/>
<SplitItem isFilled />
</Split>
</StackItem>
</Stack>
</EmptyStateBody>
<Button
id="cdi-upload-error-btn"
variant="primary"
onClick={
shouldKillDv
? () => {
killUploadPVC(pvcName, namespace)
.then(() => {
onErrorClick();
})
.catch(() => {
onErrorClick();
});
}
: onErrorClick
}
>
Back to Form {shouldKillDv && '(Deletes DV)'}
</Button>
<EmptyStateSecondaryActions>
<Button
id="cdi-upload-check-logs"
onClick={() =>
history.push(`${resourcePath(PodModel.kind, `cdi-upload-${pvcName}`, namespace)}/logs`)
}
variant="link"
>
Check Logs
</Button>
</EmptyStateSecondaryActions>
</>
);
};

const UploadingStatus: React.FC<UploadingStatusProps> = ({
upload,
onSuccessClick,
Expand Down Expand Up @@ -170,14 +259,15 @@ const UploadingStatus: React.FC<UploadingStatusProps> = ({

type UploadingStatusProps = {
upload: DataUpload;
dataVolume?: V1alpha1DataVolume;
onSuccessClick?: () => void;
onCancelClick?: () => void;
};

export type UploadPVCFormStatusProps = UploadingStatusProps & {
isSubmitting: boolean;
isAllocating: boolean;
allocateError: React.ReactNode;
allocateError: any;
onErrorClick: () => void;
};

Expand All @@ -186,6 +276,12 @@ type ErrorStatusProps = {
onErrorClick: () => void;
};

type CDIInitErrorStatus = {
onErrorClick: () => void;
pvcName: string;
namespace: string;
};

export type DataUploadStatus = UploadingStatusProps &
ErrorStatusProps & {
isAllocating: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,12 @@ import {
} from '@console/internal/components/utils/k8s-watch-hook';
import { useBaseImages } from '../../../hooks/use-base-images';
import { DataVolumeModel } from '../../../models';
import { createUploadPVC } from '../../../k8s/requests/cdi-upload/cdi-upload-requests';
import {
createUploadPVC,
PVCInitError,
} from '../../../k8s/requests/cdi-upload/cdi-upload-requests';
import { CDIUploadContext } from '../cdi-upload-provider';
import { UploadPVCFormStatus } from './upload-pvc-form-status';
import { UploadPVCFormStatus, uploadErrorType } from './upload-pvc-form-status';
import { PersistentVolumeClaimModel, TemplateModel } from '@console/internal/models';
import { getName, getNamespace } from '@console/shared';
import { V1alpha1DataVolume } from '../../../types/vm/disk/V1alpha1DataVolume';
Expand Down Expand Up @@ -80,12 +83,6 @@ const templatesResource: WatchK8sResource = {
},
};

export enum uploadErrorType {
MISSING = 'missing',
ALLOCATE = 'allocate',
CERT = 'cert',
}

export const uploadErrorMessage = (t: TFunction) => ({
[uploadErrorType.MISSING]: t('kubevirt-plugin~File input is missing'),
[uploadErrorType.ALLOCATE]: t('kubevirt-plugin~Could not create persistent volume claim'),
Expand Down Expand Up @@ -521,7 +518,7 @@ export const UploadPVCPage: React.FC<UploadPVCPageProps> = (props) => {
setError('');
setIsAllocating(true);
setIsSubmitting(true);
return createUploadPVC(dvObj, t);
return createUploadPVC(dvObj);
})
.then(({ token }) => {
setIsAllocating(false);
Expand All @@ -532,9 +529,11 @@ export const UploadPVCPage: React.FC<UploadPVCPageProps> = (props) => {
namespace,
});
})
.catch(({ message }: { message: string }) => {
.catch((err) => {
setIsAllocating(false);
setError(message || uploadErrorType.ALLOCATE);
err instanceof PVCInitError
? setError(uploadErrorType.CDI_INIT)
: setError(err?.message || uploadErrorType.ALLOCATE);
});
}
};
Expand Down Expand Up @@ -626,9 +625,10 @@ export const UploadPVCPage: React.FC<UploadPVCPageProps> = (props) => {
upload={uploads.find(
(upl) => upl.pvcName === getName(dvObj) && upl.namespace === namespace,
)}
dataVolume={dvObj}
isSubmitting={isSubmitting}
isAllocating={isAllocating}
allocateError={errorMessage}
allocateError={error}
onErrorClick={() => {
setIsSubmitting(false);
setError('');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ import { Alert, Button, ActionGroup, Stack, StackItem } from '@patternfly/react-
import { useAccessReview2, LoadingBox } from '@console/internal/components/utils';
import { coFetch } from '@console/internal/co-fetch';

import { UploadPVCFormStatus } from '../../cdi-upload-provider/upload-pvc-form/upload-pvc-form-status';
import {
UploadPVCFormStatus,
uploadErrorType,
} from '../../cdi-upload-provider/upload-pvc-form/upload-pvc-form-status';
import { createUploadPVC } from '../../../k8s/requests/cdi-upload/cdi-upload-requests';
import {
TEMPLATE_BASE_IMAGE_NAME_PARAMETER,
Expand All @@ -28,10 +31,7 @@ import { getRootDataVolume } from '../../../k8s/requests/vm/create/simple-create
import { ProvisionSource } from '../../../constants/vm/provision-source';
import { useErrorTranslation } from '../../../hooks/use-error-translation';
import { CDI_UPLOAD_URL_BUILDER } from '../../cdi-upload-provider/consts';
import {
uploadErrorMessage,
uploadErrorType,
} from '../../cdi-upload-provider/upload-pvc-form/upload-pvc-form';
import { uploadErrorMessage } from '../../cdi-upload-provider/upload-pvc-form/upload-pvc-form';

const getAction = (t: TFunction, dataSource: string): string => {
switch (ProvisionSource.fromString(dataSource)) {
Expand Down Expand Up @@ -147,7 +147,7 @@ export const AddTemplateSourceModal: React.FC<ModalComponentProps &
try {
setAllocating(true);
setSubmitting(true);
const { token } = await createUploadPVC(dvObj, t);
const { token } = await createUploadPVC(dvObj);
setAllocating(false);
uploadData({
file: file.value?.value,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
/* eslint-disable camelcase, @typescript-eslint/camelcase,no-await-in-loop */
import { TFunction } from 'i18next';
import { getName, getNamespace } from '@console/shared';
import { DataVolumeModel, UploadTokenRequestModel } from '@console/kubevirt-plugin/src/models';
import { V1alpha1DataVolume } from '@console/kubevirt-plugin/src/types/vm/disk/V1alpha1DataVolume';
Expand All @@ -18,7 +17,7 @@ const UPLOAD_STATES = {
READY: 'UploadReady',
};

class PVCInitError extends Error {
export class PVCInitError extends Error {
constructor() {
// t('kubevirt-plugin~Data Volume failed to initiate upload, and has been deleted.')
super('kubevirt-plugin~Data Volume failed to initiate upload, and has been deleted.');
Expand Down Expand Up @@ -65,7 +64,7 @@ const createUploadToken = async (pvcName: string, namespace: string): Promise<st
}
};

export const createUploadPVC = async (dataVolume: V1alpha1DataVolume, t: TFunction) => {
export const createUploadPVC = async (dataVolume: V1alpha1DataVolume) => {
const dataVolumeName = getName(dataVolume);
const namespace = getNamespace(dataVolume);

Expand All @@ -77,8 +76,7 @@ export const createUploadPVC = async (dataVolume: V1alpha1DataVolume, t: TFuncti
return { token };
} catch (error) {
if (error instanceof PVCInitError) {
await killUploadPVC(dataVolumeName, namespace);
throw new Error(t(error.message));
throw new PVCInitError();
}
throw new Error(error.message);
}
Expand Down

0 comments on commit 6b367b8

Please sign in to comment.