From 7729ed6eb058d002ab12100f0549107b6f9871dd Mon Sep 17 00:00:00 2001 From: Aviv Turgeman Date: Tue, 12 Sep 2023 12:53:25 +0300 Subject: [PATCH] CNV-23832: Apply boot StorageClass from Template's boot source Signed-off-by: Aviv Turgeman --- .../useVmTemplateSource.ts | 5 +++ .../TemplatesCatalogDrawerCreateForm.tsx | 14 +++++++-- .../TemplatesCatalogDrawerFooter.tsx | 8 ++++- .../TemplatesCatalogDrawer/utils/helpers.ts | 22 +++++++++++++ src/views/catalog/utils/quick-create-vm.ts | 31 +++++++++++++++++-- 5 files changed, 75 insertions(+), 5 deletions(-) create mode 100644 src/views/catalog/templatescatalog/components/TemplatesCatalogDrawer/utils/helpers.ts diff --git a/src/utils/resources/template/hooks/useVmTemplateSource/useVmTemplateSource.ts b/src/utils/resources/template/hooks/useVmTemplateSource/useVmTemplateSource.ts index 9bc6b14560..c749a58c8d 100644 --- a/src/utils/resources/template/hooks/useVmTemplateSource/useVmTemplateSource.ts +++ b/src/utils/resources/template/hooks/useVmTemplateSource/useVmTemplateSource.ts @@ -65,6 +65,11 @@ export const useVmTemplateSource = (template: V1Template): useVmTemplateSourceVa setTemplateBootSource({ source: { pvc: dataSource?.spec?.source?.pvc, + sourceRef: { + kind: dataSource?.kind, + name, + namespace, + }, }, storageClassName: pvc?.spec?.storageClassName, type: BOOT_SOURCE.DATA_SOURCE, diff --git a/src/views/catalog/templatescatalog/components/TemplatesCatalogDrawer/TemplatesCatalogDrawerCreateForm.tsx b/src/views/catalog/templatescatalog/components/TemplatesCatalogDrawer/TemplatesCatalogDrawerCreateForm.tsx index 83455d3285..a6ba067ff4 100644 --- a/src/views/catalog/templatescatalog/components/TemplatesCatalogDrawer/TemplatesCatalogDrawerCreateForm.tsx +++ b/src/views/catalog/templatescatalog/components/TemplatesCatalogDrawer/TemplatesCatalogDrawerCreateForm.tsx @@ -28,6 +28,7 @@ import { LABEL_USED_TEMPLATE_NAME, LABEL_USED_TEMPLATE_NAMESPACE, } from '@kubevirt-utils/resources/template'; +import { TemplateBootSource } from '@kubevirt-utils/resources/template/hooks/useVmTemplateSource/utils'; import { getMemoryCPU } from '@kubevirt-utils/resources/vm'; import { ensurePath, isEmpty } from '@kubevirt-utils/utils/utils'; import { k8sCreate } from '@openshift-console/dynamic-plugin-sdk'; @@ -61,6 +62,7 @@ type TemplatesCatalogDrawerCreateFormProps = { onCancel: () => void; subscriptionData: RHELAutomaticSubscriptionData; template: V1Template; + templateBootSource: TemplateBootSource; }; export const TemplatesCatalogDrawerCreateForm: FC = memo( @@ -73,6 +75,7 @@ export const TemplatesCatalogDrawerCreateForm: FC { const history = useHistory(); const { t } = useKubevirtTranslation(); @@ -90,7 +93,7 @@ export const TemplatesCatalogDrawerCreateForm: FC { + const onQuickCreate = async () => { setIsQuickCreating(true); setQuickCreateError(undefined); @@ -126,7 +129,14 @@ export const TemplatesCatalogDrawerCreateForm: FC { diff --git a/src/views/catalog/templatescatalog/components/TemplatesCatalogDrawer/TemplatesCatalogDrawerFooter.tsx b/src/views/catalog/templatescatalog/components/TemplatesCatalogDrawer/TemplatesCatalogDrawerFooter.tsx index e6169a2745..b41c55ec8e 100644 --- a/src/views/catalog/templatescatalog/components/TemplatesCatalogDrawer/TemplatesCatalogDrawerFooter.tsx +++ b/src/views/catalog/templatescatalog/components/TemplatesCatalogDrawer/TemplatesCatalogDrawerFooter.tsx @@ -43,7 +43,12 @@ export const TemplatesCatalogDrawerFooter: FC template, }) => { const { t } = useKubevirtTranslation(); - const { isBootSourceAvailable, loaded: bootSourceLoaded } = useVmTemplateSource(template); + const { + isBootSourceAvailable, + loaded: bootSourceLoaded, + templateBootSource, + } = useVmTemplateSource(template); + const [authorizedSSHKeys, updateAuthorizedSSHKeys, userSettingsLoaded] = useKubevirtUserSettings('ssh'); const { loaded: loadedRHELSubscription, subscriptionData } = useRHELAutomaticSubscription(); @@ -109,6 +114,7 @@ export const TemplatesCatalogDrawerFooter: FC onCancel={onCancel} subscriptionData={subscriptionData} template={template} + templateBootSource={templateBootSource} /> diff --git a/src/views/catalog/templatescatalog/components/TemplatesCatalogDrawer/utils/helpers.ts b/src/views/catalog/templatescatalog/components/TemplatesCatalogDrawer/utils/helpers.ts new file mode 100644 index 0000000000..53fd92f6a2 --- /dev/null +++ b/src/views/catalog/templatescatalog/components/TemplatesCatalogDrawer/utils/helpers.ts @@ -0,0 +1,22 @@ +import { StorageClassModel } from '@kubevirt-ui/kubevirt-api/console'; +import { IoK8sApiStorageV1StorageClassList } from '@kubevirt-ui/kubevirt-api/kubernetes'; +import { V1VirtualMachine } from '@kubevirt-ui/kubevirt-api/kubevirt'; +import { isDefaultStorageClass } from '@kubevirt-utils/components/DiskModal/DiskFormFields/utils/helpers'; +import { getDataVolumeTemplates } from '@kubevirt-utils/resources/vm'; +import { isEmpty } from '@kubevirt-utils/utils/utils'; +import { k8sGet } from '@openshift-console/dynamic-plugin-sdk'; + +export const hasStorageClass = async (vm: V1VirtualMachine) => { + const vmSCExists = getDataVolumeTemplates(vm)?.some( + (dvt) => !!dvt?.spec?.storage?.storageClassName, + ); + if (vmSCExists) return true; + + const storageClasses = await k8sGet({ + model: StorageClassModel, + }); + + const defaultSC = storageClasses?.items?.find((item) => isDefaultStorageClass(item)); + + return !isEmpty(defaultSC); +}; diff --git a/src/views/catalog/utils/quick-create-vm.ts b/src/views/catalog/utils/quick-create-vm.ts index d77d4ea30d..b8a4633a76 100644 --- a/src/views/catalog/utils/quick-create-vm.ts +++ b/src/views/catalog/utils/quick-create-vm.ts @@ -1,9 +1,11 @@ import produce from 'immer'; +import { hasStorageClass } from '@catalog/templatescatalog/components/TemplatesCatalogDrawer/utils/helpers'; import { ProcessedTemplatesModel, V1Template } from '@kubevirt-ui/kubevirt-api/console'; import VirtualMachineModel from '@kubevirt-ui/kubevirt-api/console/models/VirtualMachineModel'; -import { V1VirtualMachine } from '@kubevirt-ui/kubevirt-api/kubevirt'; +import { V1DataVolumeTemplateSpec, V1VirtualMachine } from '@kubevirt-ui/kubevirt-api/kubevirt'; import { updateCloudInitRHELSubscription } from '@kubevirt-utils/components/CloudinitModal/utils/cloudinit-utils'; +import { isEqualObject } from '@kubevirt-utils/components/NodeSelectorModal/utils/helpers'; import { applyCloudDriveCloudInitVolume } from '@kubevirt-utils/components/SSHSecretSection/utils/utils'; import { addSecretToVM } from '@kubevirt-utils/components/SSHSecretSection/utils/utils'; import { DEFAULT_NAMESPACE } from '@kubevirt-utils/constants/constants'; @@ -13,7 +15,9 @@ import { LABEL_USED_TEMPLATE_NAMESPACE, replaceTemplateVM, } from '@kubevirt-utils/resources/template'; +import { TemplateBootSource } from '@kubevirt-utils/resources/template/hooks/useVmTemplateSource/utils'; import { getTemplateVirtualMachineObject } from '@kubevirt-utils/resources/template/utils/selectors'; +import { getDataVolumeTemplates } from '@kubevirt-utils/resources/vm'; import { isEmpty } from '@kubevirt-utils/utils/utils'; import { k8sCreate, K8sModel } from '@openshift-console/dynamic-plugin-sdk'; @@ -27,13 +31,21 @@ type QuickCreateVMType = (inputs: { namespace: string; startVM: boolean; subscriptionData: RHELAutomaticSubscriptionData; + templateBootSource: TemplateBootSource; }; template: V1Template; }) => Promise; export const quickCreateVM: QuickCreateVMType = async ({ models, - overrides: { authorizedSSHKey, name, namespace = DEFAULT_NAMESPACE, startVM, subscriptionData }, + overrides: { + authorizedSSHKey, + name, + namespace = DEFAULT_NAMESPACE, + startVM, + subscriptionData, + templateBootSource, + }, template, }) => { const processedTemplate = await k8sCreate({ @@ -47,6 +59,8 @@ export const quickCreateVM: QuickCreateVMType = async ({ const vm = getTemplateVirtualMachineObject(processedTemplate); + const hasSC = await hasStorageClass(vm); + const overridedVM = produce(vm, (draftVM) => { draftVM.metadata.namespace = namespace; draftVM.metadata.name = name; @@ -57,6 +71,19 @@ export const quickCreateVM: QuickCreateVMType = async ({ draftVM.spec.running = true; } + if (!hasSC && templateBootSource?.storageClassName) { + const dataVolumeTemplates: V1DataVolumeTemplateSpec[] = getDataVolumeTemplates(vm).map( + (dvt) => + isEqualObject(dvt?.spec?.sourceRef, templateBootSource?.source?.sourceRef) + ? produce(dvt, (draftDVT) => { + draftDVT.spec.storage.storageClassName = templateBootSource?.storageClassName; + }) + : dvt, + ); + + draftVM.spec.dataVolumeTemplates = dataVolumeTemplates; + } + const updatedVolumes = applyCloudDriveCloudInitVolume(vm); draftVM.spec.template.spec.volumes = isRHELTemplate(processedTemplate) ? updateCloudInitRHELSubscription(updatedVolumes, subscriptionData)