From 1909f1374c441973bdc325ebb0240f8d6126bab5 Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Fri, 3 Oct 2025 16:24:58 +0300 Subject: [PATCH 01/11] wip Signed-off-by: Daniil Loktev --- .../controller/vd/internal/source/step/wait_for_dv_step.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/images/virtualization-artifact/pkg/controller/vd/internal/source/step/wait_for_dv_step.go b/images/virtualization-artifact/pkg/controller/vd/internal/source/step/wait_for_dv_step.go index 9b6e8999ea..41fae553f2 100644 --- a/images/virtualization-artifact/pkg/controller/vd/internal/source/step/wait_for_dv_step.go +++ b/images/virtualization-artifact/pkg/controller/vd/internal/source/step/wait_for_dv_step.go @@ -133,6 +133,10 @@ func (s WaitForDVStep) setForFirstConsumerIsAwaited(ctx context.Context, vd *vir isWFFC := sc != nil && sc.VolumeBindingMode != nil && *sc.VolumeBindingMode == storagev1.VolumeBindingWaitForFirstConsumer if isWFFC && (s.dv.Status.Phase == cdiv1.PendingPopulation || s.dv.Status.Phase == cdiv1.WaitForFirstConsumer) { + if len(vd.Status.AttachedToVirtualMachines) > 0 { + return false, nil + } + vd.Status.Phase = virtv2.DiskWaitForFirstConsumer s.cb. Status(metav1.ConditionFalse). From 6ceef9b6805a925240fb2a705b9548ae744580dd Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Fri, 3 Oct 2025 17:19:23 +0300 Subject: [PATCH 02/11] wip Signed-off-by: Daniil Loktev --- .../vm/internal/block_device_condition.go | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/block_device_condition.go b/images/virtualization-artifact/pkg/controller/vm/internal/block_device_condition.go index 3adb173d1c..715988a0db 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/block_device_condition.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/block_device_condition.go @@ -21,8 +21,11 @@ import ( "fmt" "strings" + storagev1 "k8s.io/api/storage/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "github.com/deckhouse/virtualization-controller/pkg/common/object" "github.com/deckhouse/virtualization-controller/pkg/controller/conditions" "github.com/deckhouse/virtualization-controller/pkg/controller/vm/internal/state" "github.com/deckhouse/virtualization-controller/pkg/logger" @@ -54,14 +57,30 @@ func (h *BlockDeviceHandler) checkVirtualDisksToBeWFFC(ctx context.Context, s st } for _, vd := range vds { - if vd.Status.Phase == v1alpha2.DiskWaitForFirstConsumer { - return true, nil + sc, err := h.getStorageClass(ctx, vd.Status.StorageClassName) + if err != nil { + return false, err + } + + if sc != nil && sc.VolumeBindingMode != nil && *sc.VolumeBindingMode == storagev1.VolumeBindingWaitForFirstConsumer { + readyCondition, _ := conditions.GetCondition(vdcondition.ReadyType, vd.Status.Conditions) + if readyCondition.Status != metav1.ConditionTrue { + return true, nil + } } } return false, nil } +func (h *BlockDeviceHandler) getStorageClass(ctx context.Context, scName string) (*storagev1.StorageClass, error) { + sc, err := object.FetchObject(ctx, types.NamespacedName{Name: scName}, h.client, &storagev1.StorageClass{}) + if err != nil { + return nil, fmt.Errorf("fetch storage class %s: %w", scName, err) + } + return sc, nil +} + func (h *BlockDeviceHandler) handleVirtualDisksReadyForUse(ctx context.Context, s state.VirtualMachineState) (bool, error) { vm := s.VirtualMachine().Changed() From a79f8e90c3685c1a496c6c9a98ef28585766e7c1 Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Fri, 3 Oct 2025 17:44:21 +0300 Subject: [PATCH 03/11] wip Signed-off-by: Daniil Loktev --- .../vd/internal/source/step/wait_for_dv_step.go | 6 +----- .../vm/internal/block_device_condition.go | 13 +++---------- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/images/virtualization-artifact/pkg/controller/vd/internal/source/step/wait_for_dv_step.go b/images/virtualization-artifact/pkg/controller/vd/internal/source/step/wait_for_dv_step.go index 58a1cbe80a..b531095e3e 100644 --- a/images/virtualization-artifact/pkg/controller/vd/internal/source/step/wait_for_dv_step.go +++ b/images/virtualization-artifact/pkg/controller/vd/internal/source/step/wait_for_dv_step.go @@ -132,11 +132,7 @@ func (s WaitForDVStep) setForFirstConsumerIsAwaited(ctx context.Context, vd *v1a } isWFFC := sc != nil && sc.VolumeBindingMode != nil && *sc.VolumeBindingMode == storagev1.VolumeBindingWaitForFirstConsumer - if isWFFC && (s.dv.Status.Phase == cdiv1.PendingPopulation || s.dv.Status.Phase == cdiv1.WaitForFirstConsumer) { - if len(vd.Status.AttachedToVirtualMachines) > 0 { - return false, nil - } - + if isWFFC && (s.dv.Status.Phase == cdiv1.PendingPopulation || s.dv.Status.Phase == cdiv1.WaitForFirstConsumer) && len(vd.Status.AttachedToVirtualMachines) == 0 { vd.Status.Phase = v1alpha2.DiskWaitForFirstConsumer s.cb. Status(metav1.ConditionFalse). diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/block_device_condition.go b/images/virtualization-artifact/pkg/controller/vm/internal/block_device_condition.go index 715988a0db..b0a4bc3637 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/block_device_condition.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/block_device_condition.go @@ -57,9 +57,10 @@ func (h *BlockDeviceHandler) checkVirtualDisksToBeWFFC(ctx context.Context, s st } for _, vd := range vds { - sc, err := h.getStorageClass(ctx, vd.Status.StorageClassName) + scName := vd.Status.StorageClassName + sc, err := object.FetchObject(ctx, types.NamespacedName{Name: scName}, h.client, &storagev1.StorageClass{}) if err != nil { - return false, err + return false, fmt.Errorf("fetch storage class %s: %w", scName, err) } if sc != nil && sc.VolumeBindingMode != nil && *sc.VolumeBindingMode == storagev1.VolumeBindingWaitForFirstConsumer { @@ -73,14 +74,6 @@ func (h *BlockDeviceHandler) checkVirtualDisksToBeWFFC(ctx context.Context, s st return false, nil } -func (h *BlockDeviceHandler) getStorageClass(ctx context.Context, scName string) (*storagev1.StorageClass, error) { - sc, err := object.FetchObject(ctx, types.NamespacedName{Name: scName}, h.client, &storagev1.StorageClass{}) - if err != nil { - return nil, fmt.Errorf("fetch storage class %s: %w", scName, err) - } - return sc, nil -} - func (h *BlockDeviceHandler) handleVirtualDisksReadyForUse(ctx context.Context, s state.VirtualMachineState) (bool, error) { vm := s.VirtualMachine().Changed() From 99d306c43386c064b599965ac6511c956cac6ebf Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Fri, 3 Oct 2025 18:02:08 +0300 Subject: [PATCH 04/11] fix tests Signed-off-by: Daniil Loktev --- .../vm/internal/block_devices_test.go | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/block_devices_test.go b/images/virtualization-artifact/pkg/controller/vm/internal/block_devices_test.go index 6d521d5bbf..fe73ceca21 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/block_devices_test.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/block_devices_test.go @@ -24,6 +24,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" + storagev1 "k8s.io/api/storage/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" apiruntime "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" @@ -58,6 +59,7 @@ var _ = Describe("Test BlockDeviceReady condition", func() { v1alpha2.AddToScheme, virtv1.AddToScheme, corev1.AddToScheme, + storagev1.AddToScheme, } { err := f(scheme) Expect(err).NotTo(HaveOccurred(), "failed to add scheme: %s", err) @@ -203,7 +205,8 @@ var _ = Describe("Test BlockDeviceReady condition", func() { Namespace: namespacedName.Namespace, }, Status: v1alpha2.VirtualDiskStatus{ - Phase: v1alpha2.DiskWaitForFirstConsumer, + Phase: v1alpha2.DiskWaitForFirstConsumer, + StorageClassName: "wffc-storage", Target: v1alpha2.DiskTarget{ PersistentVolumeClaim: "testPvc", }, @@ -222,8 +225,19 @@ var _ = Describe("Test BlockDeviceReady condition", func() { } } + getWFFCStorageClass := func() *storagev1.StorageClass { + bindingMode := storagev1.VolumeBindingWaitForFirstConsumer + return &storagev1.StorageClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: "wffc-storage", + }, + VolumeBindingMode: &bindingMode, + } + } + DescribeTable("One wffc disk", func(vd *v1alpha2.VirtualDisk, vm *v1alpha2.VirtualMachine, status metav1.ConditionStatus, msg string) { - fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithObjects(vm, vd).Build() + sc := getWFFCStorageClass() + fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithObjects(vm, vd, sc).Build() vmResource := reconciler.NewResource(namespacedName, fakeClient, vmFactoryByVM(vm), vmStatusGetter) err := vmResource.Fetch(ctx) From 095eb4c1fc868c1dca2eb812f16c048fe4ed4bcf Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Thu, 16 Oct 2025 11:21:46 +0300 Subject: [PATCH 05/11] fix sourcery ai comments Signed-off-by: Daniil Loktev --- .../controller/vd/internal/source/step/wait_for_dv_step.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/images/virtualization-artifact/pkg/controller/vd/internal/source/step/wait_for_dv_step.go b/images/virtualization-artifact/pkg/controller/vd/internal/source/step/wait_for_dv_step.go index b531095e3e..e92ff012b5 100644 --- a/images/virtualization-artifact/pkg/controller/vd/internal/source/step/wait_for_dv_step.go +++ b/images/virtualization-artifact/pkg/controller/vd/internal/source/step/wait_for_dv_step.go @@ -126,6 +126,10 @@ func (s WaitForDVStep) setForProvisioning(vd *v1alpha2.VirtualDisk) (set bool) { } func (s WaitForDVStep) setForFirstConsumerIsAwaited(ctx context.Context, vd *v1alpha2.VirtualDisk) (set bool, err error) { + if vd.Status.StorageClassName == "" { + return false, nil + } + sc, err := object.FetchObject(ctx, types.NamespacedName{Name: vd.Status.StorageClassName}, s.client, &storagev1.StorageClass{}) if err != nil { return false, fmt.Errorf("get sc: %w", err) From 253200ed8f275c899ba55bd14f9bd3ffa284b269 Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Thu, 16 Oct 2025 17:09:42 +0300 Subject: [PATCH 06/11] wip Signed-off-by: Daniil Loktev --- .../pkg/controller/vd/internal/source/step/wait_for_dv_step.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/images/virtualization-artifact/pkg/controller/vd/internal/source/step/wait_for_dv_step.go b/images/virtualization-artifact/pkg/controller/vd/internal/source/step/wait_for_dv_step.go index e92ff012b5..5b474b1c73 100644 --- a/images/virtualization-artifact/pkg/controller/vd/internal/source/step/wait_for_dv_step.go +++ b/images/virtualization-artifact/pkg/controller/vd/internal/source/step/wait_for_dv_step.go @@ -135,8 +135,9 @@ func (s WaitForDVStep) setForFirstConsumerIsAwaited(ctx context.Context, vd *v1a return false, fmt.Errorf("get sc: %w", err) } + dvRunningCond, _ := conditions.GetDataVolumeCondition(conditions.DVRunningConditionType, s.dv.Status.Conditions) isWFFC := sc != nil && sc.VolumeBindingMode != nil && *sc.VolumeBindingMode == storagev1.VolumeBindingWaitForFirstConsumer - if isWFFC && (s.dv.Status.Phase == cdiv1.PendingPopulation || s.dv.Status.Phase == cdiv1.WaitForFirstConsumer) && len(vd.Status.AttachedToVirtualMachines) == 0 { + if isWFFC && (s.dv.Status.Phase == cdiv1.PendingPopulation || s.dv.Status.Phase == cdiv1.WaitForFirstConsumer) && dvRunningCond.Status == corev1.ConditionFalse && dvRunningCond.Reason == "" { vd.Status.Phase = v1alpha2.DiskWaitForFirstConsumer s.cb. Status(metav1.ConditionFalse). From 33dd5f1d9934ba44c878588729438e786fec9a1f Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Thu, 16 Oct 2025 20:13:25 +0300 Subject: [PATCH 07/11] wip Signed-off-by: Daniil Loktev --- .../controller/vd/internal/source/step/wait_for_dv_step.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/images/virtualization-artifact/pkg/controller/vd/internal/source/step/wait_for_dv_step.go b/images/virtualization-artifact/pkg/controller/vd/internal/source/step/wait_for_dv_step.go index 5b474b1c73..aed1d90cce 100644 --- a/images/virtualization-artifact/pkg/controller/vd/internal/source/step/wait_for_dv_step.go +++ b/images/virtualization-artifact/pkg/controller/vd/internal/source/step/wait_for_dv_step.go @@ -137,7 +137,11 @@ func (s WaitForDVStep) setForFirstConsumerIsAwaited(ctx context.Context, vd *v1a dvRunningCond, _ := conditions.GetDataVolumeCondition(conditions.DVRunningConditionType, s.dv.Status.Conditions) isWFFC := sc != nil && sc.VolumeBindingMode != nil && *sc.VolumeBindingMode == storagev1.VolumeBindingWaitForFirstConsumer - if isWFFC && (s.dv.Status.Phase == cdiv1.PendingPopulation || s.dv.Status.Phase == cdiv1.WaitForFirstConsumer) && dvRunningCond.Status == corev1.ConditionFalse && dvRunningCond.Reason == "" { + if !isWFFC || s.dv.Status.Phase != cdiv1.PendingPopulation && s.dv.Status.Phase != cdiv1.WaitForFirstConsumer { + return false, nil + } + + if dvRunningCond.Status == corev1.ConditionFalse && dvRunningCond.Reason == "" { vd.Status.Phase = v1alpha2.DiskWaitForFirstConsumer s.cb. Status(metav1.ConditionFalse). From d4cdc27c21169ab4f2004d27b76e4e12d7808ffd Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Thu, 16 Oct 2025 20:28:44 +0300 Subject: [PATCH 08/11] Revert "wip" This reverts commit 33dd5f1d9934ba44c878588729438e786fec9a1f. Signed-off-by: Daniil Loktev --- .../controller/vd/internal/source/step/wait_for_dv_step.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/images/virtualization-artifact/pkg/controller/vd/internal/source/step/wait_for_dv_step.go b/images/virtualization-artifact/pkg/controller/vd/internal/source/step/wait_for_dv_step.go index aed1d90cce..5b474b1c73 100644 --- a/images/virtualization-artifact/pkg/controller/vd/internal/source/step/wait_for_dv_step.go +++ b/images/virtualization-artifact/pkg/controller/vd/internal/source/step/wait_for_dv_step.go @@ -137,11 +137,7 @@ func (s WaitForDVStep) setForFirstConsumerIsAwaited(ctx context.Context, vd *v1a dvRunningCond, _ := conditions.GetDataVolumeCondition(conditions.DVRunningConditionType, s.dv.Status.Conditions) isWFFC := sc != nil && sc.VolumeBindingMode != nil && *sc.VolumeBindingMode == storagev1.VolumeBindingWaitForFirstConsumer - if !isWFFC || s.dv.Status.Phase != cdiv1.PendingPopulation && s.dv.Status.Phase != cdiv1.WaitForFirstConsumer { - return false, nil - } - - if dvRunningCond.Status == corev1.ConditionFalse && dvRunningCond.Reason == "" { + if isWFFC && (s.dv.Status.Phase == cdiv1.PendingPopulation || s.dv.Status.Phase == cdiv1.WaitForFirstConsumer) && dvRunningCond.Status == corev1.ConditionFalse && dvRunningCond.Reason == "" { vd.Status.Phase = v1alpha2.DiskWaitForFirstConsumer s.cb. Status(metav1.ConditionFalse). From 99449cccfdf10fd4a0568d3b4bc8e315cffecf6e Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Thu, 16 Oct 2025 20:31:40 +0300 Subject: [PATCH 09/11] wip Signed-off-by: Daniil Loktev --- .../controller/vd/internal/watcher/datavolume_watcher.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/images/virtualization-artifact/pkg/controller/vd/internal/watcher/datavolume_watcher.go b/images/virtualization-artifact/pkg/controller/vd/internal/watcher/datavolume_watcher.go index ffbaccf88d..70d83da98d 100644 --- a/images/virtualization-artifact/pkg/controller/vd/internal/watcher/datavolume_watcher.go +++ b/images/virtualization-artifact/pkg/controller/vd/internal/watcher/datavolume_watcher.go @@ -73,6 +73,13 @@ func (w *DataVolumeWatcher) Watch(mgr manager.Manager, ctr controller.Controller return true } + oldDVRunning, _ := conditions.GetDataVolumeCondition(conditions.DVRunningConditionType, e.ObjectOld.Status.Conditions) + newDVRunning, _ := conditions.GetDataVolumeCondition(conditions.DVRunningConditionType, e.ObjectNew.Status.Conditions) + + if oldDVRunning.Reason != newDVRunning.Reason { + return true + } + dvRunning := service.GetDataVolumeCondition(cdiv1.DataVolumeRunning, e.ObjectNew.Status.Conditions) return dvRunning != nil && (dvRunning.Reason == "Error" || dvRunning.Reason == "ImagePullFailed") }, From af28afe16fc179d4033f6c3baa92d4446e5ce7eb Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Thu, 16 Oct 2025 20:57:18 +0300 Subject: [PATCH 10/11] wip Signed-off-by: Daniil Loktev --- .../pkg/controller/vd/internal/source/sources.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/images/virtualization-artifact/pkg/controller/vd/internal/source/sources.go b/images/virtualization-artifact/pkg/controller/vd/internal/source/sources.go index aeb6ecc3ee..6ca60ae7a0 100644 --- a/images/virtualization-artifact/pkg/controller/vd/internal/source/sources.go +++ b/images/virtualization-artifact/pkg/controller/vd/internal/source/sources.go @@ -181,7 +181,9 @@ func setPhaseConditionForPVCProvisioningDisk( Message("Waiting for the pvc importer to be created") return nil } - if isStorageClassWFFC(sc) && (dv.Status.Phase == cdiv1.PendingPopulation || dv.Status.Phase == cdiv1.WaitForFirstConsumer) { + + dvRunningCond, _ := conditions.GetDataVolumeCondition(conditions.DVRunningConditionType, dv.Status.Conditions) + if isStorageClassWFFC(sc) && (dv.Status.Phase == cdiv1.PendingPopulation || dv.Status.Phase == cdiv1.WaitForFirstConsumer) && dvRunningCond.Status == corev1.ConditionFalse && dvRunningCond.Reason == "" { vd.Status.Phase = v1alpha2.DiskWaitForFirstConsumer cb. Status(metav1.ConditionFalse). From 01626ea7a2ec2e1567f77e921b3da779a722e442 Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Thu, 16 Oct 2025 21:25:15 +0300 Subject: [PATCH 11/11] fix tests Signed-off-by: Daniil Loktev --- .../controller/vd/internal/source/object_ref_cvi_test.go | 7 +++++++ .../controller/vd/internal/source/object_ref_vi_test.go | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/images/virtualization-artifact/pkg/controller/vd/internal/source/object_ref_cvi_test.go b/images/virtualization-artifact/pkg/controller/vd/internal/source/object_ref_cvi_test.go index a301316902..555d8c6e73 100644 --- a/images/virtualization-artifact/pkg/controller/vd/internal/source/object_ref_cvi_test.go +++ b/images/virtualization-artifact/pkg/controller/vd/internal/source/object_ref_cvi_test.go @@ -184,6 +184,13 @@ var _ = Describe("ObjectRef ClusterVirtualImage", func() { It("waits for the first consumer", func() { dv.Status.Phase = cdiv1.PendingPopulation + dv.Status.Conditions = []cdiv1.DataVolumeCondition{ + { + Type: cdiv1.DataVolumeRunning, + Status: corev1.ConditionFalse, + Reason: "", + }, + } sc.VolumeBindingMode = ptr.To(storagev1.VolumeBindingWaitForFirstConsumer) client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(pvc, dv, sc).Build() diff --git a/images/virtualization-artifact/pkg/controller/vd/internal/source/object_ref_vi_test.go b/images/virtualization-artifact/pkg/controller/vd/internal/source/object_ref_vi_test.go index cd640d4161..f7509c2d80 100644 --- a/images/virtualization-artifact/pkg/controller/vd/internal/source/object_ref_vi_test.go +++ b/images/virtualization-artifact/pkg/controller/vd/internal/source/object_ref_vi_test.go @@ -184,6 +184,13 @@ var _ = Describe("ObjectRef VirtualImage", func() { It("waits for the first consumer", func() { dv.Status.Phase = cdiv1.PendingPopulation + dv.Status.Conditions = []cdiv1.DataVolumeCondition{ + { + Type: cdiv1.DataVolumeRunning, + Status: corev1.ConditionFalse, + Reason: "", + }, + } sc.VolumeBindingMode = ptr.To(storagev1.VolumeBindingWaitForFirstConsumer) client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(pvc, dv, sc).Build()