From 310da178ce69df683a4e4517eeb719f3eb9aab8c Mon Sep 17 00:00:00 2001 From: Roman Sysoev Date: Tue, 6 May 2025 18:03:15 +0300 Subject: [PATCH] feat(vd): remove finalizer from unmounted vd `vd-protection` will be removed from the virtual disk if the virtual machine is stopped. Signed-off-by: Roman Sysoev --- .../pkg/controller/vd/internal/protection.go | 11 ++ .../controller/vd/internal/protection_test.go | 109 ++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 images/virtualization-artifact/pkg/controller/vd/internal/protection_test.go diff --git a/images/virtualization-artifact/pkg/controller/vd/internal/protection.go b/images/virtualization-artifact/pkg/controller/vd/internal/protection.go index 9a103c5d90..eb96798012 100644 --- a/images/virtualization-artifact/pkg/controller/vd/internal/protection.go +++ b/images/virtualization-artifact/pkg/controller/vd/internal/protection.go @@ -43,6 +43,17 @@ func (h ProtectionHandler) Handle(ctx context.Context, vd *virtv2.VirtualDisk) ( case len(vd.Status.AttachedToVirtualMachines) == 0: log.Debug("Allow virtual disk deletion") controllerutil.RemoveFinalizer(vd, virtv2.FinalizerVDProtection) + case len(vd.Status.AttachedToVirtualMachines) != 0: + unmounted := true + for _, vm := range vd.Status.AttachedToVirtualMachines { + if vm.Mounted { + unmounted = false + } + } + if unmounted { + log.Debug("Allow virtual disk deletion") + controllerutil.RemoveFinalizer(vd, virtv2.FinalizerVDProtection) + } case vd.DeletionTimestamp == nil: log.Debug("Protect virtual disk from deletion") controllerutil.AddFinalizer(vd, virtv2.FinalizerVDProtection) diff --git a/images/virtualization-artifact/pkg/controller/vd/internal/protection_test.go b/images/virtualization-artifact/pkg/controller/vd/internal/protection_test.go new file mode 100644 index 0000000000..640cbd8f44 --- /dev/null +++ b/images/virtualization-artifact/pkg/controller/vd/internal/protection_test.go @@ -0,0 +1,109 @@ +/* +Copyright 2025 Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package internal + +import ( + "context" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + virtv1 "kubevirt.io/api/core/v1" + + virtv2 "github.com/deckhouse/virtualization/api/core/v1alpha2" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("The protection handler test", func() { + const vdProtection = "virtualization.deckhouse.io/vd-protection" + + var ( + schema *runtime.Scheme + ctx context.Context + handler *ProtectionHandler + ) + + BeforeEach(func() { + schema = runtime.NewScheme() + Expect(clientgoscheme.AddToScheme(schema)).To(Succeed()) + Expect(virtv2.AddToScheme(schema)).To(Succeed()) + Expect(virtv1.AddToScheme(schema)).To(Succeed()) + + ctx = context.TODO() + }) + + Context("`VirtualDisk`", func() { + When("has the `AttachedToVirtualMachines` status with the `Mounted` false value", func() { + It("should remove the `vd-protection` finalizer from the `VirtualDisk`", func() { + vd := &virtv2.VirtualDisk{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-virtual-disk", + Namespace: "default", + Finalizers: []string{ + vdProtection, + }, + }, + Status: virtv2.VirtualDiskStatus{ + Conditions: []metav1.Condition{}, + AttachedToVirtualMachines: []virtv2.AttachedVirtualMachine{ + { + Name: "test-virtual-machine", + Mounted: false, + }, + }, + }, + } + + handler = &ProtectionHandler{} + result, err := handler.Handle(ctx, vd) + Expect(err).NotTo(HaveOccurred()) + Expect(result.IsZero()).To(BeTrue()) + Expect(vd.Finalizers).NotTo(ContainElement(vdProtection)) + }) + }) + + When("has the `AttachedToVirtualMachines` status with the `Mounted` true value", func() { + It("should not remove the `vd-protection` finalizer from the `VirtualDisk`", func() { + vd := &virtv2.VirtualDisk{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-virtual-disk", + Namespace: "default", + Finalizers: []string{ + vdProtection, + }, + }, + Status: virtv2.VirtualDiskStatus{ + Conditions: []metav1.Condition{}, + AttachedToVirtualMachines: []virtv2.AttachedVirtualMachine{ + { + Name: "test-virtual-machine", + Mounted: true, + }, + }, + }, + } + + handler = &ProtectionHandler{} + result, err := handler.Handle(ctx, vd) + Expect(err).NotTo(HaveOccurred()) + Expect(result.IsZero()).To(BeTrue()) + Expect(vd.Finalizers).To(ContainElement(vdProtection)) + }) + }) + }) +})