diff --git a/src/utils/resources/bootableresources/helpers.ts b/src/utils/resources/bootableresources/helpers.ts index c2c592782..b38702d2d 100644 --- a/src/utils/resources/bootableresources/helpers.ts +++ b/src/utils/resources/bootableresources/helpers.ts @@ -7,6 +7,7 @@ import DataSourceModel, { } from '@kubevirt-ui/kubevirt-api/console/models/DataSourceModel'; import DataVolumeModel from '@kubevirt-ui/kubevirt-api/console/models/DataVolumeModel'; import { + V1beta1DataImportCron, V1beta1DataSource, V1beta1DataVolume, } from '@kubevirt-ui/kubevirt-api/containerized-data-importer/models'; @@ -14,7 +15,7 @@ import { IoK8sApiCoreV1PersistentVolumeClaim } from '@kubevirt-ui/kubevirt-api/k import { isEmpty, kubevirtConsole } from '@kubevirt-utils/utils/utils'; import { k8sDelete } from '@openshift-console/dynamic-plugin-sdk'; -import { getLabel } from '../shared'; +import { getLabel, getName, getNamespace } from '../shared'; import { deprecatedOSNames, KUBEVIRT_ISO_LABEL } from './constants'; import { BootableVolume } from './types'; @@ -73,3 +74,13 @@ export const isBootableVolumeISO = (bootableVolume: BootableVolume): boolean => getLabel(bootableVolume, KUBEVIRT_ISO_LABEL) === 'true'; export const isDeprecated = (bootVolumeName: string) => deprecatedOSNames.includes(bootVolumeName); + +export const getDataImportCronFromDataSource = ( + dataImportCrons: V1beta1DataImportCron[], + dataSource: V1beta1DataSource, +): V1beta1DataImportCron => + dataImportCrons?.find( + (cron) => + cron?.spec?.managedDataSource === getName(dataSource) && + getNamespace(dataSource) === getNamespace(cron), + ); diff --git a/src/utils/resources/bootableresources/hooks/useBootableVolumes.ts b/src/utils/resources/bootableresources/hooks/useBootableVolumes.ts index eb6e94983..e0bd96454 100644 --- a/src/utils/resources/bootableresources/hooks/useBootableVolumes.ts +++ b/src/utils/resources/bootableresources/hooks/useBootableVolumes.ts @@ -8,7 +8,11 @@ import { PersistentVolumeClaimModel, VolumeSnapshotModel, } from '@kubevirt-ui/kubevirt-api/console'; -import { V1beta1DataSource } from '@kubevirt-ui/kubevirt-api/containerized-data-importer/models'; +import DataImportCronModel from '@kubevirt-ui/kubevirt-api/console/models/DataImportCronModel'; +import { + V1beta1DataImportCron, + V1beta1DataSource, +} from '@kubevirt-ui/kubevirt-api/containerized-data-importer/models'; import { IoK8sApiCoreV1PersistentVolumeClaim } from '@kubevirt-ui/kubevirt-api/kubernetes'; import { VolumeSnapshotKind } from '@kubevirt-utils/components/SelectSnapshot/types'; import { ALL_PROJECTS } from '@kubevirt-utils/hooks/constants'; @@ -36,6 +40,14 @@ const useBootableVolumes: UseBootableVolumes = (namespace) => { }, }); + const [dataImportCrons, loadedDataImportCrons, dataImportCronsError] = useK8sWatchResource< + V1beta1DataImportCron[] + >({ + groupVersionKind: modelToGroupVersionKind(DataImportCronModel), + isList: true, + namespace: projectsNamespace, + }); + // getting all pvcs since there could be a case where a DS has the label and it's underlying PVC does not const [pvcs, loadedPVCs, loadErrorPVCs] = useK8sWatchResource< IoK8sApiCoreV1PersistentVolumeClaim[] @@ -52,16 +64,19 @@ const useBootableVolumes: UseBootableVolumes = (namespace) => { namespace: projectsNamespace, }); - const error = useMemo(() => dataSourcesError || loadErrorPVCs, [dataSourcesError, loadErrorPVCs]); + const error = useMemo( + () => dataSourcesError || loadErrorPVCs || dataImportCronsError, + [dataSourcesError, loadErrorPVCs, dataImportCronsError], + ); const loaded = useMemo( - () => (error ? true : loadedDataSources && loadedPVCs), - [error, loadedDataSources, loadedPVCs], + () => (error ? true : loadedDataSources && loadedPVCs && loadedDataImportCrons), + [error, loadedDataSources, loadedPVCs, loadedDataImportCrons], ); const readyOrCloningDataSources = useMemo( - () => getReadyOrCloningOrUploadingDataSources(dataSources), - [dataSources], + () => getReadyOrCloningOrUploadingDataSources(dataSources, dataImportCrons), + [dataSources, dataImportCrons], ); const pvcSources = useMemo(() => convertResourceArrayToMap(pvcs, true), [pvcs]); @@ -89,6 +104,7 @@ const useBootableVolumes: UseBootableVolumes = (namespace) => { return { bootableVolumes, + dataImportCrons, error, loaded, pvcSources, diff --git a/src/utils/resources/shared.ts b/src/utils/resources/shared.ts index f5827bf37..c1324adf5 100644 --- a/src/utils/resources/shared.ts +++ b/src/utils/resources/shared.ts @@ -1,5 +1,8 @@ import { modelToGroupVersionKind } from '@kubevirt-ui/kubevirt-api/console'; -import { V1beta1DataSource } from '@kubevirt-ui/kubevirt-api/containerized-data-importer/models'; +import { + V1beta1DataImportCron, + V1beta1DataSource, +} from '@kubevirt-ui/kubevirt-api/containerized-data-importer/models'; import { V1alpha1Condition, V1VirtualMachine } from '@kubevirt-ui/kubevirt-api/kubevirt'; import { ALL_NAMESPACES_SESSION_KEY } from '@kubevirt-utils/hooks/constants'; import { TemplateModel } from '@kubevirt-utils/models'; @@ -16,6 +19,7 @@ import { import { isDataSourceReady } from '../../views/datasources/utils'; import { isEmpty } from './../utils/utils'; +import { getDataImportCronFromDataSource } from './bootableresources/helpers'; import { isDataSourceCloning, isDataSourceUploading, @@ -327,25 +331,28 @@ export const convertResourceArrayToMap = dataSources?.filter((dataSource) => isDataSourceReady(dataSource)); +export const isDataImportCronProgressing = (dataImportCron: V1beta1DataImportCron): boolean => + dataImportCron?.status?.conditions?.find((condition) => condition.type === 'UpToDate')?.reason === + 'ImportProgressing'; + /** * function to get all V1beta1DataSource objects with condition type 'Ready'and status 'True' * and/or also those with 'False' status but only 'CloneScheduled' or 'CloneInProgress' reason (cloning of the DS in progress) * @param {V1beta1DataSource[]} dataSources list of DataSources to be filtered + * @param {V1beta1DataImportCron[]} dataImportCrons list of DataImportCrons related to DataSources * @returns list of available/ready/cloning DataSources */ -export const getAvailableOrCloningDataSources = ( - dataSources: V1beta1DataSource[], -): V1beta1DataSource[] => - dataSources?.filter( - (dataSource) => isDataSourceReady(dataSource) || isDataSourceCloning(dataSource), - ); - export const getReadyOrCloningOrUploadingDataSources = ( dataSources: V1beta1DataSource[], + dataImportCrons: V1beta1DataImportCron[], ): V1beta1DataSource[] => - dataSources?.filter( - (dataSource) => + dataSources?.filter((dataSource) => { + const dataImportCron = getDataImportCronFromDataSource(dataImportCrons, dataSource); + + return ( isDataSourceReady(dataSource) || isDataSourceCloning(dataSource) || - isDataSourceUploading(dataSource), - ); + isDataSourceUploading(dataSource) || + isDataImportCronProgressing(dataImportCron) + ); + }); diff --git a/src/views/bootablevolumes/list/BootableVolumesList.tsx b/src/views/bootablevolumes/list/BootableVolumesList.tsx index 9fb2a22ac..af72bdda7 100644 --- a/src/views/bootablevolumes/list/BootableVolumesList.tsx +++ b/src/views/bootablevolumes/list/BootableVolumesList.tsx @@ -32,7 +32,7 @@ const BootableVolumesList: FC = () => { const { ns: namespace } = useParams<{ ns: string }>(); const { t } = useKubevirtTranslation(); - const { bootableVolumes, error, loaded } = useBootableVolumes(namespace); + const { bootableVolumes, dataImportCrons, error, loaded } = useBootableVolumes(namespace); const [preferences] = useClusterPreferences(); const rowFilters = useBootableVolumesFilters(); @@ -117,6 +117,7 @@ const BootableVolumesList: FC = () => { )} rowData={{ + dataImportCrons, preferences, }} columns={activeColumns} diff --git a/src/views/bootablevolumes/list/components/BootableVolumesRow.tsx b/src/views/bootablevolumes/list/components/BootableVolumesRow.tsx index 3c4b394ed..e420078c0 100644 --- a/src/views/bootablevolumes/list/components/BootableVolumesRow.tsx +++ b/src/views/bootablevolumes/list/components/BootableVolumesRow.tsx @@ -2,16 +2,24 @@ import React, { FC } from 'react'; import DataSourceActions from 'src/views/datasources/actions/DataSourceActions'; import DataSourceModel from '@kubevirt-ui/kubevirt-api/console/models/DataSourceModel'; -import { V1beta1DataSource } from '@kubevirt-ui/kubevirt-api/containerized-data-importer/models'; +import { + V1beta1DataImportCron, + V1beta1DataSource, +} from '@kubevirt-ui/kubevirt-api/containerized-data-importer/models'; import { V1beta1VirtualMachineClusterPreference } from '@kubevirt-ui/kubevirt-api/kubevirt'; import DeprecatedBadge from '@kubevirt-utils/components/badges/DeprecatedBadge/DeprecatedBadge'; import { useKubevirtTranslation } from '@kubevirt-utils/hooks/useKubevirtTranslation'; import { getBootableVolumeGroupVersionKind, + getDataImportCronFromDataSource, isBootableVolumePVCKind, isDeprecated, } from '@kubevirt-utils/resources/bootableresources/helpers'; -import { getName, getNamespace } from '@kubevirt-utils/resources/shared'; +import { + getName, + getNamespace, + isDataImportCronProgressing, +} from '@kubevirt-utils/resources/shared'; import { ANNOTATIONS } from '@kubevirt-utils/resources/template'; import { isDataSourceCloning } from '@kubevirt-utils/resources/template/hooks/useVmTemplateSource/utils'; import { NO_DATA_DASH } from '@kubevirt-utils/resources/vm/utils/constants'; @@ -29,15 +37,21 @@ const BootableVolumesRow: FC< RowProps< BootableResource, { + dataImportCrons: V1beta1DataImportCron[]; preferences: V1beta1VirtualMachineClusterPreference[]; } > -> = ({ activeColumnIDs, obj, rowData: { preferences } }) => { +> = ({ activeColumnIDs, obj, rowData: { dataImportCrons, preferences } }) => { const { t } = useKubevirtTranslation(); const bootableVolumeName = getName(obj); const bootableVolumeNamespace = getNamespace(obj); + const dataImportCron = getDataImportCronFromDataSource(dataImportCrons, obj as V1beta1DataSource); + + const isCloning = + isDataSourceCloning(obj as V1beta1DataSource) || isDataImportCronProgressing(dataImportCron); + return ( <> @@ -49,9 +63,7 @@ const BootableVolumesRow: FC< namespace={bootableVolumeNamespace} /> {isDeprecated(bootableVolumeName) && } - {obj.kind === DataSourceModel.kind && isDataSourceCloning(obj as V1beta1DataSource) && ( - - )} + {obj.kind === DataSourceModel.kind && isCloning && } diff --git a/src/views/catalog/CreateFromInstanceTypes/components/BootableVolumeList/components/BootableVolumeRow/BootableVolumeRow.tsx b/src/views/catalog/CreateFromInstanceTypes/components/BootableVolumeList/components/BootableVolumeRow/BootableVolumeRow.tsx index a48acb0a9..042fbecc3 100644 --- a/src/views/catalog/CreateFromInstanceTypes/components/BootableVolumeList/components/BootableVolumeRow/BootableVolumeRow.tsx +++ b/src/views/catalog/CreateFromInstanceTypes/components/BootableVolumeList/components/BootableVolumeRow/BootableVolumeRow.tsx @@ -3,7 +3,10 @@ import React, { FC, MouseEvent } from 'react'; import { useInstanceTypeVMStore } from '@catalog/CreateFromInstanceTypes/state/useInstanceTypeVMStore'; import { InstanceTypeVMStore } from '@catalog/CreateFromInstanceTypes/state/utils/types'; import { getTemplateOSIcon, getVolumeNameOSIcon } from '@catalog/templatescatalog/utils/os-icons'; -import { V1beta1DataSource } from '@kubevirt-ui/kubevirt-api/containerized-data-importer/models'; +import { + V1beta1DataImportCron, + V1beta1DataSource, +} from '@kubevirt-ui/kubevirt-api/containerized-data-importer/models'; import { IoK8sApiCoreV1PersistentVolumeClaim } from '@kubevirt-ui/kubevirt-api/kubernetes'; import { V1beta1VirtualMachineClusterPreference } from '@kubevirt-ui/kubevirt-api/kubevirt'; import DeprecatedBadge from '@kubevirt-utils/components/badges/DeprecatedBadge/DeprecatedBadge'; @@ -18,7 +21,11 @@ import { getVolumeSnapshotStorageClass, } from '@kubevirt-utils/resources/bootableresources/selectors'; import { BootableVolume } from '@kubevirt-utils/resources/bootableresources/types'; -import { getName, getNamespace } from '@kubevirt-utils/resources/shared'; +import { + getName, + getNamespace, + isDataImportCronProgressing, +} from '@kubevirt-utils/resources/shared'; import { ANNOTATIONS } from '@kubevirt-utils/resources/template'; import { isDataSourceCloning, @@ -38,6 +45,7 @@ type BootableVolumeRowProps = { bootableVolume: BootableVolume; rowData: { bootableVolumeSelectedState: [BootableVolume, InstanceTypeVMStore['onSelectCreatedVolume']]; + dataImportCron: V1beta1DataImportCron; favorites: [isFavorite: boolean, updaterFavorites: (val: boolean) => void]; preference: V1beta1VirtualMachineClusterPreference; pvcSource: IoK8sApiCoreV1PersistentVolumeClaim; @@ -50,6 +58,7 @@ const BootableVolumeRow: FC = ({ bootableVolume, rowData: { bootableVolumeSelectedState: [selectedBootableVolume, setSelectedBootableVolume], + dataImportCron, favorites, preference, pvcSource, @@ -74,6 +83,9 @@ const BootableVolumeRow: FC = ({ }; const { volumeListNamespace } = useInstanceTypeVMStore(); + const isCloning = + isDataImportCronProgressing(dataImportCron) || + isDataSourceCloning(bootableVolume as V1beta1DataSource); return ( = ({ {bootVolumeName} {isDeprecated(bootVolumeName) && } - {isDataSourceCloning(bootableVolume as V1beta1DataSource) && ( - - )} + {isCloning && } {isDataSourceUploading(bootableVolume as V1beta1DataSource) && ( )} diff --git a/src/views/catalog/CreateFromInstanceTypes/components/BootableVolumeList/components/BootableVolumeTable/BootableVolumeTable.tsx b/src/views/catalog/CreateFromInstanceTypes/components/BootableVolumeList/components/BootableVolumeTable/BootableVolumeTable.tsx index 72b346c00..ad07c8efe 100644 --- a/src/views/catalog/CreateFromInstanceTypes/components/BootableVolumeList/components/BootableVolumeTable/BootableVolumeTable.tsx +++ b/src/views/catalog/CreateFromInstanceTypes/components/BootableVolumeList/components/BootableVolumeTable/BootableVolumeTable.tsx @@ -5,9 +5,13 @@ import { UseBootableVolumesValues, } from '@catalog/CreateFromInstanceTypes/state/utils/types'; import { DEFAULT_PREFERENCE_LABEL } from '@catalog/CreateFromInstanceTypes/utils/constants'; +import { V1beta1DataSource } from '@kubevirt-ui/kubevirt-api/containerized-data-importer/models'; import { V1beta1VirtualMachineClusterPreference } from '@kubevirt-ui/kubevirt-api/kubevirt'; import { UserSettingFavorites } from '@kubevirt-utils/hooks/useKubevirtUserSettings/utils/types'; -import { getBootableVolumePVCSource } from '@kubevirt-utils/resources/bootableresources/helpers'; +import { + getBootableVolumePVCSource, + getDataImportCronFromDataSource, +} from '@kubevirt-utils/resources/bootableresources/helpers'; import { BootableVolume } from '@kubevirt-utils/resources/bootableresources/types'; import { getLabel, getName } from '@kubevirt-utils/resources/shared'; import { TableColumn } from '@openshift-console/dynamic-plugin-sdk'; @@ -38,7 +42,7 @@ const BootableVolumeTable: FC = ({ sortedPaginatedData, }) => { const [volumeFavorites, updateFavorites] = favorites; - const { pvcSources, volumeSnapshotSources } = bootableVolumesData; + const { dataImportCrons, pvcSources, volumeSnapshotSources } = bootableVolumesData; return ( @@ -63,6 +67,10 @@ const BootableVolumeTable: FC = ({ diff --git a/src/views/catalog/CreateFromInstanceTypes/state/utils/types.ts b/src/views/catalog/CreateFromInstanceTypes/state/utils/types.ts index 48cefda64..b8b0f3243 100644 --- a/src/views/catalog/CreateFromInstanceTypes/state/utils/types.ts +++ b/src/views/catalog/CreateFromInstanceTypes/state/utils/types.ts @@ -1,5 +1,6 @@ import { Dispatch } from 'react'; +import { V1beta1DataImportCron } from '@kubevirt-ui/kubevirt-api/containerized-data-importer/models'; import { IoK8sApiCoreV1PersistentVolumeClaim } from '@kubevirt-ui/kubevirt-api/kubernetes'; import { V1beta1VirtualMachineClusterInstancetype, @@ -27,6 +28,7 @@ export type UseInstanceTypeAndPreferencesValues = { export type UseBootableVolumesValues = { bootableVolumes: BootableVolume[]; + dataImportCrons: V1beta1DataImportCron[]; error: Error; loaded: boolean; pvcSources: {