From 9a3c02da3b40cab2be2952b639ce19c56fe98fd7 Mon Sep 17 00:00:00 2001 From: Valeriy Khorunzhin Date: Thu, 19 Dec 2024 17:58:17 +0300 Subject: [PATCH] add Signed-off-by: Valeriy Khorunzhin --- api/core/v1alpha2/vicondition/condition.go | 2 + .../pkg/controller/vi/internal/source/http.go | 8 +++ .../vi/internal/source/object_ref.go | 8 +++ .../vi/internal/source/object_ref_vd.go | 8 +++ .../internal/source/object_ref_vi_on_pvc.go | 8 +++ .../controller/vi/internal/source/registry.go | 8 +++ .../controller/vi/internal/source/sources.go | 52 +++++++++++++++++++ .../controller/vi/internal/source/upload.go | 8 +++ 8 files changed, 102 insertions(+) diff --git a/api/core/v1alpha2/vicondition/condition.go b/api/core/v1alpha2/vicondition/condition.go index f450d1e701..fdefb7f233 100644 --- a/api/core/v1alpha2/vicondition/condition.go +++ b/api/core/v1alpha2/vicondition/condition.go @@ -79,6 +79,8 @@ const ( StorageClassNotReady ReadyReason = "StorageClassNotReady" // Ready indicates that the import process is complete and the `VirtualImage` is ready for use. Ready ReadyReason = "Ready" + // QuotaExceeded indicates that the VirtualImage is reached project quotas and can not be provisioned. + QuotaExceeded ReadyReason = "QuotaExceeded" // Lost indicates that the underlying PersistentVolumeClaim has been lost and the `VirtualImage` can no longer be used. Lost ReadyReason = "PVCLost" diff --git a/images/virtualization-artifact/pkg/controller/vi/internal/source/http.go b/images/virtualization-artifact/pkg/controller/vi/internal/source/http.go index 8eeecc7d46..fa4e4876f9 100644 --- a/images/virtualization-artifact/pkg/controller/vi/internal/source/http.go +++ b/images/virtualization-artifact/pkg/controller/vi/internal/source/http.go @@ -210,6 +210,8 @@ func (ds HTTPDataSource) StoreToPVC(ctx context.Context, vi *virtv2.VirtualImage return reconcile.Result{}, err } + quotaExceeded, quotaExceededMessage := checkIfQuotaExceeded(dv) + switch { case isDiskProvisioningFinished(condition): log.Info("Image provisioning finished: clean up") @@ -329,6 +331,12 @@ func (ds HTTPDataSource) StoreToPVC(ctx context.Context, vi *virtv2.VirtualImage Message("DVCR Provisioner not found: create the new one.") return reconcile.Result{Requeue: true}, nil + case quotaExceeded: + cb. + Status(metav1.ConditionFalse). + Reason(vicondition.QuotaExceeded). + Message(quotaExceededMessage) + return reconcile.Result{}, nil case pvc == nil: vi.Status.Phase = virtv2.ImageProvisioning cb. diff --git a/images/virtualization-artifact/pkg/controller/vi/internal/source/object_ref.go b/images/virtualization-artifact/pkg/controller/vi/internal/source/object_ref.go index b312d7ea44..79b21d4aa9 100644 --- a/images/virtualization-artifact/pkg/controller/vi/internal/source/object_ref.go +++ b/images/virtualization-artifact/pkg/controller/vi/internal/source/object_ref.go @@ -131,6 +131,8 @@ func (ds ObjectRefDataSource) StoreToPVC(ctx context.Context, vi *virtv2.Virtual return reconcile.Result{}, err } + quotaExceeded, quotaExceededMessage := checkIfQuotaExceeded(dv) + switch { case isDiskProvisioningFinished(condition): log.Info("Disk provisioning finished: clean up") @@ -200,6 +202,12 @@ func (ds ObjectRefDataSource) StoreToPVC(ctx context.Context, vi *virtv2.Virtual Message("PVC Provisioner not found: create the new one.") return reconcile.Result{Requeue: true}, nil + case quotaExceeded: + cb. + Status(metav1.ConditionFalse). + Reason(vicondition.QuotaExceeded). + Message(quotaExceededMessage) + return reconcile.Result{}, nil case pvc == nil: vi.Status.Phase = virtv2.ImageProvisioning cb. diff --git a/images/virtualization-artifact/pkg/controller/vi/internal/source/object_ref_vd.go b/images/virtualization-artifact/pkg/controller/vi/internal/source/object_ref_vd.go index 9e857fa4ed..a3242c4a2e 100644 --- a/images/virtualization-artifact/pkg/controller/vi/internal/source/object_ref_vd.go +++ b/images/virtualization-artifact/pkg/controller/vi/internal/source/object_ref_vd.go @@ -224,6 +224,8 @@ func (ds ObjectRefVirtualDisk) StoreToPVC(ctx context.Context, vi *virtv2.Virtua return reconcile.Result{}, err } + quotaExceeded, quotaExceededMessage := checkIfQuotaExceeded(dv) + switch { case isDiskProvisioningFinished(cb.Condition()): log.Info("Disk provisioning finished: clean up") @@ -274,6 +276,12 @@ func (ds ObjectRefVirtualDisk) StoreToPVC(ctx context.Context, vi *virtv2.Virtua Message("PVC Provisioner not found: create the new one.") return reconcile.Result{Requeue: true}, nil + case quotaExceeded: + cb. + Status(metav1.ConditionFalse). + Reason(vicondition.QuotaExceeded). + Message(quotaExceededMessage) + return reconcile.Result{}, nil case pvc == nil: vi.Status.Phase = virtv2.ImageProvisioning cb. diff --git a/images/virtualization-artifact/pkg/controller/vi/internal/source/object_ref_vi_on_pvc.go b/images/virtualization-artifact/pkg/controller/vi/internal/source/object_ref_vi_on_pvc.go index b41cc43e7f..f6c021d9a1 100644 --- a/images/virtualization-artifact/pkg/controller/vi/internal/source/object_ref_vi_on_pvc.go +++ b/images/virtualization-artifact/pkg/controller/vi/internal/source/object_ref_vi_on_pvc.go @@ -203,6 +203,8 @@ func (ds ObjectRefDataVirtualImageOnPVC) StoreToPVC(ctx context.Context, vi, viR return reconcile.Result{}, err } + quotaExceeded, quotaExceededMessage := checkIfQuotaExceeded(dv) + switch { case isDiskProvisioningFinished(cb.Condition()): log.Info("Disk provisioning finished: clean up") @@ -259,6 +261,12 @@ func (ds ObjectRefDataVirtualImageOnPVC) StoreToPVC(ctx context.Context, vi, viR Message("PVC Provisioner not found: create the new one.") return reconcile.Result{Requeue: true}, nil + case quotaExceeded: + cb. + Status(metav1.ConditionFalse). + Reason(vicondition.QuotaExceeded). + Message(quotaExceededMessage) + return reconcile.Result{}, nil case pvc == nil: vi.Status.Phase = virtv2.ImageProvisioning cb. diff --git a/images/virtualization-artifact/pkg/controller/vi/internal/source/registry.go b/images/virtualization-artifact/pkg/controller/vi/internal/source/registry.go index b26c64c46c..ee719e7726 100644 --- a/images/virtualization-artifact/pkg/controller/vi/internal/source/registry.go +++ b/images/virtualization-artifact/pkg/controller/vi/internal/source/registry.go @@ -100,6 +100,8 @@ func (ds RegistryDataSource) StoreToPVC(ctx context.Context, vi *virtv2.VirtualI return reconcile.Result{}, err } + quotaExceeded, quotaExceededMessage := checkIfQuotaExceeded(dv) + switch { case isDiskProvisioningFinished(condition): log.Info("Disk provisioning finished: clean up") @@ -217,6 +219,12 @@ func (ds RegistryDataSource) StoreToPVC(ctx context.Context, vi *virtv2.VirtualI Message("PVC Provisioner not found: create the new one.") return reconcile.Result{Requeue: true}, nil + case quotaExceeded: + cb. + Status(metav1.ConditionFalse). + Reason(vicondition.QuotaExceeded). + Message(quotaExceededMessage) + return reconcile.Result{}, nil case pvc == nil: vi.Status.Phase = virtv2.ImageProvisioning cb. diff --git a/images/virtualization-artifact/pkg/controller/vi/internal/source/sources.go b/images/virtualization-artifact/pkg/controller/vi/internal/source/sources.go index ffb3538945..6c6e330a78 100644 --- a/images/virtualization-artifact/pkg/controller/vi/internal/source/sources.go +++ b/images/virtualization-artifact/pkg/controller/vi/internal/source/sources.go @@ -279,3 +279,55 @@ func setQuotaExceededPhaseCondition(cb *conditions.ConditionBuilder, phase *virt cb.Message(fmt.Sprintf("Quota exceeded: %s; Retry in %d minute.", err, retryPeriod)) return reconcile.Result{RequeueAfter: retryPeriod * time.Minute} } + +const ( + DVBoundConditionType string = "Bound" + DVRunningConditionType string = "Running" + DVReadyConditionType string = "Ready" + DVErrExceededQuotaReason string = "ErrExceededQuota" +) + +func checkIfQuotaExceeded(dv *cdiv1.DataVolume) (bool, string) { + if dv == nil { + return false, "" + } + + boundCondition, ok := getDVCondition(dv, DVBoundConditionType) + if !ok { + return false, "" + } + + readyDVCondition, ok := getDVCondition(dv, DVReadyConditionType) + if !ok { + return false, "" + } + + runningCondition, ok := getDVCondition(dv, DVRunningConditionType) + if !ok { + return false, "" + } + + if boundCondition.Reason == DVErrExceededQuotaReason { + return true, service.CapitalizeFirstLetter(boundCondition.Message) + } + + if readyDVCondition.Reason == DVErrExceededQuotaReason { + return true, service.CapitalizeFirstLetter(readyDVCondition.Message) + } + + if runningCondition.Reason == DVErrExceededQuotaReason { + return true, service.CapitalizeFirstLetter(runningCondition.Message) + } + + return false, "" +} + +func getDVCondition(dv *cdiv1.DataVolume, conditionType string) (*cdiv1.DataVolumeCondition, bool) { + for _, cond := range dv.Status.Conditions { + if string(cond.Type) == conditionType { + return &cond, true + } + } + + return nil, false +} diff --git a/images/virtualization-artifact/pkg/controller/vi/internal/source/upload.go b/images/virtualization-artifact/pkg/controller/vi/internal/source/upload.go index 802cbc5d2a..27d1b51a50 100644 --- a/images/virtualization-artifact/pkg/controller/vi/internal/source/upload.go +++ b/images/virtualization-artifact/pkg/controller/vi/internal/source/upload.go @@ -103,6 +103,8 @@ func (ds UploadDataSource) StoreToPVC(ctx context.Context, vi *virtv2.VirtualIma return reconcile.Result{}, err } + quotaExceeded, quotaExceededMessage := checkIfQuotaExceeded(dv) + switch { case isDiskProvisioningFinished(condition): log.Info("Disk provisioning finished: clean up") @@ -249,6 +251,12 @@ func (ds UploadDataSource) StoreToPVC(ctx context.Context, vi *virtv2.VirtualIma Message("PVC Provisioner not found: create the new one.") return reconcile.Result{Requeue: true}, nil + case quotaExceeded: + cb. + Status(metav1.ConditionFalse). + Reason(vicondition.QuotaExceeded). + Message(quotaExceededMessage) + return reconcile.Result{}, nil case pvc == nil: vi.Status.Phase = virtv2.ImageProvisioning cb.