From 4476a33e9a13d37dbaed640e685fc70872d5f021 Mon Sep 17 00:00:00 2001 From: Yaroslav Borbat Date: Tue, 5 Aug 2025 16:04:22 +0300 Subject: [PATCH 01/39] add maintenance Signed-off-by: Yaroslav Borbat Signed-off-by: Daniil Loktev --- api/core/v1alpha2/vmcondition/condition.go | 4 ++++ .../pkg/controller/vm/vm_reconciler.go | 21 +++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/api/core/v1alpha2/vmcondition/condition.go b/api/core/v1alpha2/vmcondition/condition.go index 452c9405a5..6fb32d774c 100644 --- a/api/core/v1alpha2/vmcondition/condition.go +++ b/api/core/v1alpha2/vmcondition/condition.go @@ -45,6 +45,8 @@ const ( TypeNeedsEvict Type = "NeedsEvict" TypeNetworkReady Type = "NetworkReady" + + TypeMaintenance Type = "Maintenance" ) type Reason string @@ -122,4 +124,6 @@ const ( ReasonNetworkReady Reason = "NetworkReady" ReasonNetworkNotReady Reason = "NetworkNotReady" ReasonSDNModuleDisable Reason = "SDNModuleDisable" + + ReasonMaintenanceRestore Reason = "RestoreInProgress" ) diff --git a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go index cc0212d79f..b0f2903186 100644 --- a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go +++ b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go @@ -21,6 +21,8 @@ import ( "fmt" "reflect" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + virtv1 "kubevirt.io/api/core/v1" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/handler" @@ -28,11 +30,14 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/source" + "github.com/deckhouse/virtualization-controller/pkg/common/object" + "github.com/deckhouse/virtualization-controller/pkg/controller/conditions" "github.com/deckhouse/virtualization-controller/pkg/controller/reconciler" "github.com/deckhouse/virtualization-controller/pkg/controller/vm/internal/state" "github.com/deckhouse/virtualization-controller/pkg/controller/vm/internal/watcher" "github.com/deckhouse/virtualization-controller/pkg/logger" virtv2 "github.com/deckhouse/virtualization/api/core/v1alpha2" + "github.com/deckhouse/virtualization/api/core/v1alpha2/vmcondition" ) type Handler interface { @@ -92,6 +97,22 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco return reconcile.Result{}, err } + maintenance, _ := conditions.GetCondition(vmcondition.TypeMaintenance, vm.Changed().Status.Conditions) + if maintenance.Status == metav1.ConditionTrue { + log.Info("Reconcile observe an VirtualMachine in maintenance mode, stopped vm") + kvvm := &virtv1.VirtualMachine{} + err = r.client.Get(ctx, req.NamespacedName, kvvm) + if err != nil { + return reconcile.Result{}, client.IgnoreNotFound(err) + } + err = object.CleanupObject(ctx, r.client, kvvm) + if err != nil { + return reconcile.Result{}, err + } + + return reconcile.Result{}, nil + } + if vm.IsEmpty() { log.Info("Reconcile observe an absent VirtualMachine: it may be deleted") return reconcile.Result{}, nil From b26c7bec8b61b032e2e0f2f23d54d5307439a56a Mon Sep 17 00:00:00 2001 From: Yaroslav Borbat Date: Tue, 5 Aug 2025 16:07:17 +0300 Subject: [PATCH 02/39] add maintenance Signed-off-by: Yaroslav Borbat Signed-off-by: Daniil Loktev --- .../pkg/controller/vm/vm_reconciler.go | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go index b0f2903186..e6d1d2920a 100644 --- a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go +++ b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go @@ -22,7 +22,6 @@ import ( "reflect" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - virtv1 "kubevirt.io/api/core/v1" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/handler" @@ -97,13 +96,21 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco return reconcile.Result{}, err } + if vm.IsEmpty() { + log.Info("Reconcile observe an absent VirtualMachine: it may be deleted") + return reconcile.Result{}, nil + } + + s := state.New(r.client, vm) maintenance, _ := conditions.GetCondition(vmcondition.TypeMaintenance, vm.Changed().Status.Conditions) if maintenance.Status == metav1.ConditionTrue { log.Info("Reconcile observe an VirtualMachine in maintenance mode, stopped vm") - kvvm := &virtv1.VirtualMachine{} - err = r.client.Get(ctx, req.NamespacedName, kvvm) + kvvm, err := s.KVVM(ctx) if err != nil { - return reconcile.Result{}, client.IgnoreNotFound(err) + return reconcile.Result{}, err + } + if kvvm == nil { + return reconcile.Result{}, nil } err = object.CleanupObject(ctx, r.client, kvvm) if err != nil { @@ -113,13 +120,6 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco return reconcile.Result{}, nil } - if vm.IsEmpty() { - log.Info("Reconcile observe an absent VirtualMachine: it may be deleted") - return reconcile.Result{}, nil - } - - s := state.New(r.client, vm) - rec := reconciler.NewBaseReconciler[Handler](r.handlers) rec.SetHandlerExecutor(func(ctx context.Context, h Handler) (reconcile.Result, error) { return h.Handle(ctx, s) From 97f522250b09f9867ecc91b3544d033325e1d6cb Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Thu, 7 Aug 2025 16:01:28 +0300 Subject: [PATCH 03/39] add maintenance Signed-off-by: Daniil Loktev --- .../pkg/controller/vm/internal/lifecycle.go | 6 ++++ .../pkg/controller/vm/vm_reconciler.go | 33 ++++++++++++++++--- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/lifecycle.go b/images/virtualization-artifact/pkg/controller/vm/internal/lifecycle.go index c33e233376..9f362f29a2 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/lifecycle.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/lifecycle.go @@ -77,6 +77,12 @@ func (h *LifeCycleHandler) Handle(ctx context.Context, s state.VirtualMachineSta return reconcile.Result{}, nil } + // Remove maintenance condition if it's false + maintenance, _ := conditions.GetCondition(vmcondition.TypeMaintenance, changed.Status.Conditions) + if maintenance.Status == metav1.ConditionFalse { + conditions.RemoveCondition(vmcondition.TypeMaintenance, &changed.Status.Conditions) + } + if updated := addAllUnknown(changed, vmcondition.TypeRunning); updated || changed.Status.Phase == "" { changed.Status.Phase = virtv2.MachinePending return reconcile.Result{Requeue: true}, nil diff --git a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go index e6d1d2920a..c8071161b6 100644 --- a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go +++ b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go @@ -21,6 +21,7 @@ import ( "fmt" "reflect" + k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" @@ -104,18 +105,42 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco s := state.New(r.client, vm) maintenance, _ := conditions.GetCondition(vmcondition.TypeMaintenance, vm.Changed().Status.Conditions) if maintenance.Status == metav1.ConditionTrue { - log.Info("Reconcile observe an VirtualMachine in maintenance mode, stopped vm") + log.Info("Reconcile observe an VirtualMachine in maintenance mode, cleaning up resources") + kvvm, err := s.KVVM(ctx) if err != nil { return reconcile.Result{}, err } - if kvvm == nil { - return reconcile.Result{}, nil + if kvvm != nil { + err = object.CleanupObject(ctx, r.client, kvvm) + if err != nil { + return reconcile.Result{}, err + } + } + + kvvmi, err := s.KVVMI(ctx) + if err != nil { + return reconcile.Result{}, err + } + if kvvmi != nil { + err = r.client.Delete(ctx, kvvmi) + if err != nil && !k8serrors.IsNotFound(err) { + return reconcile.Result{}, fmt.Errorf("delete KVVMI in maintenance mode: %w", err) + } } - err = object.CleanupObject(ctx, r.client, kvvm) + + pods, err := s.Pods(ctx) if err != nil { return reconcile.Result{}, err } + if pods != nil { + for i := range pods.Items { + err = r.client.Delete(ctx, &pods.Items[i]) + if err != nil && !k8serrors.IsNotFound(err) { + return reconcile.Result{}, fmt.Errorf("delete Pod in maintenance mode: %w", err) + } + } + } return reconcile.Result{}, nil } From 957d57cffb0dd5bf5d26f3cd3c3913237edfcefc Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Tue, 12 Aug 2025 14:51:43 +0300 Subject: [PATCH 04/39] fixes Signed-off-by: Daniil Loktev --- .../pkg/controller/vm/internal/lifecycle.go | 1 - .../pkg/controller/vm/vm_reconciler.go | 9 ++++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/lifecycle.go b/images/virtualization-artifact/pkg/controller/vm/internal/lifecycle.go index 9f362f29a2..b1ff992d6e 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/lifecycle.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/lifecycle.go @@ -77,7 +77,6 @@ func (h *LifeCycleHandler) Handle(ctx context.Context, s state.VirtualMachineSta return reconcile.Result{}, nil } - // Remove maintenance condition if it's false maintenance, _ := conditions.GetCondition(vmcondition.TypeMaintenance, changed.Status.Conditions) if maintenance.Status == metav1.ConditionFalse { conditions.RemoveCondition(vmcondition.TypeMaintenance, &changed.Status.Conditions) diff --git a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go index c8071161b6..a1304cca91 100644 --- a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go +++ b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go @@ -21,7 +21,6 @@ import ( "fmt" "reflect" - k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" @@ -123,8 +122,8 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco return reconcile.Result{}, err } if kvvmi != nil { - err = r.client.Delete(ctx, kvvmi) - if err != nil && !k8serrors.IsNotFound(err) { + err = object.DeleteObject(ctx, r.client, kvvmi) + if err != nil { return reconcile.Result{}, fmt.Errorf("delete KVVMI in maintenance mode: %w", err) } } @@ -135,8 +134,8 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco } if pods != nil { for i := range pods.Items { - err = r.client.Delete(ctx, &pods.Items[i]) - if err != nil && !k8serrors.IsNotFound(err) { + err = object.DeleteObject(ctx, r.client, &pods.Items[i]) + if err != nil { return reconcile.Result{}, fmt.Errorf("delete Pod in maintenance mode: %w", err) } } From 312c8557201373ebb5b910640ca26f77f1be1ca0 Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Wed, 13 Aug 2025 10:42:38 +0300 Subject: [PATCH 05/39] add annotation for testing Signed-off-by: Daniil Loktev --- .../pkg/common/annotations/annotations.go | 2 ++ .../pkg/controller/vm/internal/lifecycle.go | 20 +++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/images/virtualization-artifact/pkg/common/annotations/annotations.go b/images/virtualization-artifact/pkg/common/annotations/annotations.go index d684959455..9c16aabc15 100644 --- a/images/virtualization-artifact/pkg/common/annotations/annotations.go +++ b/images/virtualization-artifact/pkg/common/annotations/annotations.go @@ -93,6 +93,8 @@ const ( AnnVMRestore = AnnAPIGroupV + "/vmrestore" // AnnVMOPEvacuation is an annotation on vmop that represents a vmop created by evacuation controller AnnVMOPEvacuation = AnnAPIGroupV + "/evacuation" + // DELETE ME + AnnVMMaintenance = AnnAPIGroupV + "/maintenance" // LabelsPrefix is a prefix for virtualization-controller labels. LabelsPrefix = "virtualization.deckhouse.io" diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/lifecycle.go b/images/virtualization-artifact/pkg/controller/vm/internal/lifecycle.go index b1ff992d6e..3a53dc3ed6 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/lifecycle.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/lifecycle.go @@ -77,6 +77,26 @@ func (h *LifeCycleHandler) Handle(ctx context.Context, s state.VirtualMachineSta return reconcile.Result{}, nil } + // DELETE ME + if current.Annotations[annotations.AnnVMMaintenance] == "true" { + // Set maintenance condition if annotation is present + cb := conditions.NewConditionBuilder(vmcondition.TypeMaintenance). + Generation(current.GetGeneration()). + Status(metav1.ConditionTrue). + Reason(vmcondition.ReasonMaintenanceRestore). + Message("VM is in maintenance mode") + conditions.SetCondition(cb, &changed.Status.Conditions) + } else if current.Annotations[annotations.AnnVMMaintenance] == "false" { + // Explicitly set maintenance to false if annotation is "false" + cb := conditions.NewConditionBuilder(vmcondition.TypeMaintenance). + Generation(current.GetGeneration()). + Status(metav1.ConditionFalse). + Reason(vmcondition.ReasonMaintenanceRestore). + Message("VM maintenance mode disabled") + conditions.SetCondition(cb, &changed.Status.Conditions) + } + + // Original logic for removing false maintenance condition maintenance, _ := conditions.GetCondition(vmcondition.TypeMaintenance, changed.Status.Conditions) if maintenance.Status == metav1.ConditionFalse { conditions.RemoveCondition(vmcondition.TypeMaintenance, &changed.Status.Conditions) From def0204263ce01a381945ff0d20dc0b4e3e99f38 Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Wed, 13 Aug 2025 15:57:38 +0300 Subject: [PATCH 06/39] fixes Signed-off-by: Daniil Loktev --- .../pkg/controller/vm/vm_reconciler.go | 73 ++++++++++++++----- 1 file changed, 53 insertions(+), 20 deletions(-) diff --git a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go index a1304cca91..30c03e50af 100644 --- a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go +++ b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go @@ -20,7 +20,9 @@ import ( "context" "fmt" "reflect" + "time" + k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" @@ -29,6 +31,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/source" + "github.com/deckhouse/virtualization-controller/pkg/builder/vmop" "github.com/deckhouse/virtualization-controller/pkg/common/object" "github.com/deckhouse/virtualization-controller/pkg/controller/conditions" "github.com/deckhouse/virtualization-controller/pkg/controller/reconciler" @@ -104,40 +107,70 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco s := state.New(r.client, vm) maintenance, _ := conditions.GetCondition(vmcondition.TypeMaintenance, vm.Changed().Status.Conditions) if maintenance.Status == metav1.ConditionTrue { - log.Info("Reconcile observe an VirtualMachine in maintenance mode, cleaning up resources") + log.Info("Reconcile observe an VirtualMachine in maintenance mode, stopped vm") - kvvm, err := s.KVVM(ctx) + kvvmi, err := s.KVVMI(ctx) if err != nil { - return reconcile.Result{}, err + return reconcile.Result{}, fmt.Errorf("get KVVMI: %w", err) } - if kvvm != nil { - err = object.CleanupObject(ctx, r.client, kvvm) + + if kvvmi != nil { + vmops, err := s.VMOPs(ctx) if err != nil { - return reconcile.Result{}, err + return reconcile.Result{}, fmt.Errorf("get VMOPs: %w", err) + } + + stopInProgress := false + for _, vmop := range vmops { + if vmop.Spec.Type == virtv2.VMOPTypeStop && + vmop.Status.Phase != virtv2.VMOPPhaseCompleted && + vmop.Status.Phase != virtv2.VMOPPhaseFailed { + stopInProgress = true + break + } + } + + vmName := vm.Name().Name + if !stopInProgress { + stopOp := vmop.New( + vmop.WithGenerateName(fmt.Sprintf("%s-maintenance-stop-", vmName)), + vmop.WithNamespace(vm.Name().Namespace), + vmop.WithType(virtv2.VMOPTypeStop), + vmop.WithVirtualMachine(vmName), + ) + + log.Info("Creating VMOP to stop VM for maintenance") + err = r.client.Create(ctx, stopOp) + if err != nil && !k8serrors.IsAlreadyExists(err) { + return reconcile.Result{}, fmt.Errorf("create stop VMOP: %w", err) + } } + + log.Info("Waiting for VM to stop") + return reconcile.Result{RequeueAfter: 5 * time.Second}, nil } - kvvmi, err := s.KVVMI(ctx) + pods, err := s.Pods(ctx) if err != nil { - return reconcile.Result{}, err + return reconcile.Result{}, fmt.Errorf("get pods: %w", err) } - if kvvmi != nil { - err = object.DeleteObject(ctx, r.client, kvvmi) - if err != nil { - return reconcile.Result{}, fmt.Errorf("delete KVVMI in maintenance mode: %w", err) + if pods != nil && len(pods.Items) > 0 { + // If pods still exist but KVVMI is gone, force delete them + for _, pod := range pods.Items { + object.CleanupObject(ctx, r.client, &pod) } } - pods, err := s.Pods(ctx) + kvvm, err := s.KVVM(ctx) if err != nil { - return reconcile.Result{}, err + return reconcile.Result{}, fmt.Errorf("get KVVM: %w", err) } - if pods != nil { - for i := range pods.Items { - err = object.DeleteObject(ctx, r.client, &pods.Items[i]) - if err != nil { - return reconcile.Result{}, fmt.Errorf("delete Pod in maintenance mode: %w", err) - } + + if kvvm != nil { + log.Info("Deleting KVVM for maintenance mode") + err = object.CleanupObject(ctx, r.client, kvvm) + if err != nil { + return reconcile.Result{}, fmt.Errorf("delete KVVM: %w", err) } } From 43b0042ce566e72820982afedf7f76872f0ad1a9 Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Wed, 13 Aug 2025 16:46:46 +0300 Subject: [PATCH 07/39] remove vmop Signed-off-by: Daniil Loktev --- .../pkg/controller/vm/vm_reconciler.go | 38 ++++--------------- 1 file changed, 8 insertions(+), 30 deletions(-) diff --git a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go index 30c03e50af..0c4be4da4e 100644 --- a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go +++ b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go @@ -22,7 +22,6 @@ import ( "reflect" "time" - k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" @@ -31,9 +30,9 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/source" - "github.com/deckhouse/virtualization-controller/pkg/builder/vmop" "github.com/deckhouse/virtualization-controller/pkg/common/object" "github.com/deckhouse/virtualization-controller/pkg/controller/conditions" + "github.com/deckhouse/virtualization-controller/pkg/controller/powerstate" "github.com/deckhouse/virtualization-controller/pkg/controller/reconciler" "github.com/deckhouse/virtualization-controller/pkg/controller/vm/internal/state" "github.com/deckhouse/virtualization-controller/pkg/controller/vm/internal/watcher" @@ -107,7 +106,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco s := state.New(r.client, vm) maintenance, _ := conditions.GetCondition(vmcondition.TypeMaintenance, vm.Changed().Status.Conditions) if maintenance.Status == metav1.ConditionTrue { - log.Info("Reconcile observe an VirtualMachine in maintenance mode, stopped vm") + log.Info("Reconcile observe an VirtualMachine in maintenance mode") kvvmi, err := s.KVVMI(ctx) if err != nil { @@ -115,34 +114,13 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco } if kvvmi != nil { - vmops, err := s.VMOPs(ctx) - if err != nil { - return reconcile.Result{}, fmt.Errorf("get VMOPs: %w", err) - } - - stopInProgress := false - for _, vmop := range vmops { - if vmop.Spec.Type == virtv2.VMOPTypeStop && - vmop.Status.Phase != virtv2.VMOPPhaseCompleted && - vmop.Status.Phase != virtv2.VMOPPhaseFailed { - stopInProgress = true - break - } - } + if kvvmi.DeletionTimestamp == nil { + log.Info("Stopping VM for maintenance mode") - vmName := vm.Name().Name - if !stopInProgress { - stopOp := vmop.New( - vmop.WithGenerateName(fmt.Sprintf("%s-maintenance-stop-", vmName)), - vmop.WithNamespace(vm.Name().Namespace), - vmop.WithType(virtv2.VMOPTypeStop), - vmop.WithVirtualMachine(vmName), - ) - - log.Info("Creating VMOP to stop VM for maintenance") - err = r.client.Create(ctx, stopOp) - if err != nil && !k8serrors.IsAlreadyExists(err) { - return reconcile.Result{}, fmt.Errorf("create stop VMOP: %w", err) + force := new(bool) + *force = false + if err := powerstate.StopVM(ctx, r.client, kvvmi, force); err != nil { + return reconcile.Result{}, fmt.Errorf("stop VM: %w", err) } } From bb19ec05d555e0c5f7b85d554e67acf43a6e86c0 Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Wed, 13 Aug 2025 17:48:15 +0300 Subject: [PATCH 08/39] fixes Signed-off-by: Daniil Loktev --- .../pkg/controller/vm/vm_reconciler.go | 49 +++++++++++-------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go index 0c4be4da4e..3b4eaf6344 100644 --- a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go +++ b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go @@ -23,6 +23,7 @@ import ( "time" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + v1 "kubevirt.io/api/core/v1" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/handler" @@ -113,31 +114,20 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco return reconcile.Result{}, fmt.Errorf("get KVVMI: %w", err) } - if kvvmi != nil { - if kvvmi.DeletionTimestamp == nil { - log.Info("Stopping VM for maintenance mode") - - force := new(bool) - *force = false - if err := powerstate.StopVM(ctx, r.client, kvvmi, force); err != nil { - return reconcile.Result{}, fmt.Errorf("stop VM: %w", err) - } + if kvvmi != nil && kvvmi.Status.Phase != v1.Failed && kvvmi.Status.Phase != v1.Succeeded { + log.Info("Stopping VM for maintenance mode") + + force := new(bool) + *force = false + if err := powerstate.StopVM(ctx, r.client, kvvmi, force); err != nil { + return reconcile.Result{}, fmt.Errorf("stop VM: %w", err) } log.Info("Waiting for VM to stop") - return reconcile.Result{RequeueAfter: 5 * time.Second}, nil + return reconcile.Result{}, nil } - pods, err := s.Pods(ctx) - if err != nil { - return reconcile.Result{}, fmt.Errorf("get pods: %w", err) - } - if pods != nil && len(pods.Items) > 0 { - // If pods still exist but KVVMI is gone, force delete them - for _, pod := range pods.Items { - object.CleanupObject(ctx, r.client, &pod) - } - } + log.Info("Cleanup resources for maintenance mode") kvvm, err := s.KVVM(ctx) if err != nil { @@ -152,6 +142,25 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco } } + if kvvmi != nil { + log.Info("Deleting KVVMI for maintenance mode") + err = object.CleanupObject(ctx, r.client, kvvmi) + if err != nil { + return reconcile.Result{}, fmt.Errorf("delete KVVM: %w", err) + } + } + + pods, err := s.Pods(ctx) + if err != nil { + return reconcile.Result{}, fmt.Errorf("get pods: %w", err) + } + if pods != nil && len(pods.Items) > 0 { + log.Info("Deleting pods for maintenance mode") + for _, pod := range pods.Items { + object.CleanupObject(ctx, r.client, &pod) + } + } + return reconcile.Result{}, nil } From 0b0c4d857df47425818366481f9111c1fa9f077c Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Thu, 14 Aug 2025 09:51:26 +0300 Subject: [PATCH 09/39] fixes Signed-off-by: Daniil Loktev --- .../virtualization-artifact/pkg/controller/vm/vm_reconciler.go | 1 - 1 file changed, 1 deletion(-) diff --git a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go index 3b4eaf6344..857f5ff861 100644 --- a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go +++ b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go @@ -20,7 +20,6 @@ import ( "context" "fmt" "reflect" - "time" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" v1 "kubevirt.io/api/core/v1" From 4a22538eead57718b341238828a83ba0416fb2c5 Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Thu, 14 Aug 2025 11:23:26 +0300 Subject: [PATCH 10/39] fixes Signed-off-by: Daniil Loktev --- .../pkg/controller/vm/internal/lifecycle.go | 25 ---------------- .../pkg/controller/vm/vm_reconciler.go | 30 +++++++++++++++++-- 2 files changed, 27 insertions(+), 28 deletions(-) diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/lifecycle.go b/images/virtualization-artifact/pkg/controller/vm/internal/lifecycle.go index 3a53dc3ed6..c33e233376 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/lifecycle.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/lifecycle.go @@ -77,31 +77,6 @@ func (h *LifeCycleHandler) Handle(ctx context.Context, s state.VirtualMachineSta return reconcile.Result{}, nil } - // DELETE ME - if current.Annotations[annotations.AnnVMMaintenance] == "true" { - // Set maintenance condition if annotation is present - cb := conditions.NewConditionBuilder(vmcondition.TypeMaintenance). - Generation(current.GetGeneration()). - Status(metav1.ConditionTrue). - Reason(vmcondition.ReasonMaintenanceRestore). - Message("VM is in maintenance mode") - conditions.SetCondition(cb, &changed.Status.Conditions) - } else if current.Annotations[annotations.AnnVMMaintenance] == "false" { - // Explicitly set maintenance to false if annotation is "false" - cb := conditions.NewConditionBuilder(vmcondition.TypeMaintenance). - Generation(current.GetGeneration()). - Status(metav1.ConditionFalse). - Reason(vmcondition.ReasonMaintenanceRestore). - Message("VM maintenance mode disabled") - conditions.SetCondition(cb, &changed.Status.Conditions) - } - - // Original logic for removing false maintenance condition - maintenance, _ := conditions.GetCondition(vmcondition.TypeMaintenance, changed.Status.Conditions) - if maintenance.Status == metav1.ConditionFalse { - conditions.RemoveCondition(vmcondition.TypeMaintenance, &changed.Status.Conditions) - } - if updated := addAllUnknown(changed, vmcondition.TypeRunning); updated || changed.Status.Phase == "" { changed.Status.Phase = virtv2.MachinePending return reconcile.Result{Requeue: true}, nil diff --git a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go index 857f5ff861..daba84835f 100644 --- a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go +++ b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go @@ -22,7 +22,6 @@ import ( "reflect" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - v1 "kubevirt.io/api/core/v1" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/handler" @@ -30,6 +29,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/source" + "github.com/deckhouse/virtualization-controller/pkg/common/annotations" "github.com/deckhouse/virtualization-controller/pkg/common/object" "github.com/deckhouse/virtualization-controller/pkg/controller/conditions" "github.com/deckhouse/virtualization-controller/pkg/controller/powerstate" @@ -104,7 +104,31 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco } s := state.New(r.client, vm) - maintenance, _ := conditions.GetCondition(vmcondition.TypeMaintenance, vm.Changed().Status.Conditions) + + current := s.VirtualMachine().Current() + changed := s.VirtualMachine().Changed() + + // DELETE ME + if current.Annotations[annotations.AnnVMMaintenance] == "true" { + cb := conditions.NewConditionBuilder(vmcondition.TypeMaintenance). + Generation(current.GetGeneration()). + Status(metav1.ConditionTrue). + Reason(vmcondition.ReasonMaintenanceRestore). + Message("VM is in maintenance mode") + conditions.SetCondition(cb, &changed.Status.Conditions) + } else if current.Annotations[annotations.AnnVMMaintenance] == "false" { + cb := conditions.NewConditionBuilder(vmcondition.TypeMaintenance). + Generation(current.GetGeneration()). + Status(metav1.ConditionFalse). + Reason(vmcondition.ReasonMaintenanceRestore). + Message("VM maintenance mode disabled") + conditions.SetCondition(cb, &changed.Status.Conditions) + } + + maintenance, _ := conditions.GetCondition(vmcondition.TypeMaintenance, changed.Status.Conditions) + if maintenance.Status == metav1.ConditionFalse { + conditions.RemoveCondition(vmcondition.TypeMaintenance, &changed.Status.Conditions) + } if maintenance.Status == metav1.ConditionTrue { log.Info("Reconcile observe an VirtualMachine in maintenance mode") @@ -113,7 +137,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco return reconcile.Result{}, fmt.Errorf("get KVVMI: %w", err) } - if kvvmi != nil && kvvmi.Status.Phase != v1.Failed && kvvmi.Status.Phase != v1.Succeeded { + if kvvmi != nil && !kvvmi.IsFinal() { log.Info("Stopping VM for maintenance mode") force := new(bool) From 625f8747c4110ea2d4624d1e90f391c774af0dd2 Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Thu, 14 Aug 2025 14:14:23 +0300 Subject: [PATCH 11/39] fixes Signed-off-by: Daniil Loktev --- .../pkg/controller/vm/vm_reconciler.go | 58 +++++++++---------- 1 file changed, 28 insertions(+), 30 deletions(-) diff --git a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go index daba84835f..cf85690f14 100644 --- a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go +++ b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go @@ -146,45 +146,43 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco return reconcile.Result{}, fmt.Errorf("stop VM: %w", err) } - log.Info("Waiting for VM to stop") - return reconcile.Result{}, nil - } + } else { + log.Info("VM is stopped, cleaning up resources for maintenance mode") - log.Info("Cleanup resources for maintenance mode") + kvvm, err := s.KVVM(ctx) + if err != nil { + return reconcile.Result{}, fmt.Errorf("get KVVM: %w", err) + } - kvvm, err := s.KVVM(ctx) - if err != nil { - return reconcile.Result{}, fmt.Errorf("get KVVM: %w", err) - } + if kvvm != nil { + log.Info("Deleting KVVM for maintenance mode") + err = object.CleanupObject(ctx, r.client, kvvm) + if err != nil { + return reconcile.Result{}, fmt.Errorf("delete KVVM: %w", err) + } + } - if kvvm != nil { - log.Info("Deleting KVVM for maintenance mode") - err = object.CleanupObject(ctx, r.client, kvvm) - if err != nil { - return reconcile.Result{}, fmt.Errorf("delete KVVM: %w", err) + if kvvmi != nil { + log.Info("Deleting KVVMI for maintenance mode") + err = object.CleanupObject(ctx, r.client, kvvmi) + if err != nil { + return reconcile.Result{}, fmt.Errorf("delete KVVMI: %w", err) + } } - } - if kvvmi != nil { - log.Info("Deleting KVVMI for maintenance mode") - err = object.CleanupObject(ctx, r.client, kvvmi) + pods, err := s.Pods(ctx) if err != nil { - return reconcile.Result{}, fmt.Errorf("delete KVVM: %w", err) + return reconcile.Result{}, fmt.Errorf("get pods: %w", err) } - } - - pods, err := s.Pods(ctx) - if err != nil { - return reconcile.Result{}, fmt.Errorf("get pods: %w", err) - } - if pods != nil && len(pods.Items) > 0 { - log.Info("Deleting pods for maintenance mode") - for _, pod := range pods.Items { - object.CleanupObject(ctx, r.client, &pod) + if pods != nil && len(pods.Items) > 0 { + log.Info("Deleting pods for maintenance mode") + for _, pod := range pods.Items { + object.CleanupObject(ctx, r.client, &pod) + } } - } - return reconcile.Result{}, nil + return reconcile.Result{}, nil + } } rec := reconciler.NewBaseReconciler[Handler](r.handlers) From fecf8b0ed2f44f5f0bf4489526b25a6ec4cadd77 Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Thu, 14 Aug 2025 14:42:03 +0300 Subject: [PATCH 12/39] fixes Signed-off-by: Daniil Loktev --- .../pkg/controller/vm/vm_reconciler.go | 70 +++++++++---------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go index cf85690f14..9d427e56b1 100644 --- a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go +++ b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go @@ -30,7 +30,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/source" "github.com/deckhouse/virtualization-controller/pkg/common/annotations" - "github.com/deckhouse/virtualization-controller/pkg/common/object" + // "github.com/deckhouse/virtualization-controller/pkg/common/object" "github.com/deckhouse/virtualization-controller/pkg/controller/conditions" "github.com/deckhouse/virtualization-controller/pkg/controller/powerstate" "github.com/deckhouse/virtualization-controller/pkg/controller/reconciler" @@ -147,40 +147,40 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco } } else { - log.Info("VM is stopped, cleaning up resources for maintenance mode") - - kvvm, err := s.KVVM(ctx) - if err != nil { - return reconcile.Result{}, fmt.Errorf("get KVVM: %w", err) - } - - if kvvm != nil { - log.Info("Deleting KVVM for maintenance mode") - err = object.CleanupObject(ctx, r.client, kvvm) - if err != nil { - return reconcile.Result{}, fmt.Errorf("delete KVVM: %w", err) - } - } - - if kvvmi != nil { - log.Info("Deleting KVVMI for maintenance mode") - err = object.CleanupObject(ctx, r.client, kvvmi) - if err != nil { - return reconcile.Result{}, fmt.Errorf("delete KVVMI: %w", err) - } - } - - pods, err := s.Pods(ctx) - if err != nil { - return reconcile.Result{}, fmt.Errorf("get pods: %w", err) - } - if pods != nil && len(pods.Items) > 0 { - log.Info("Deleting pods for maintenance mode") - for _, pod := range pods.Items { - object.CleanupObject(ctx, r.client, &pod) - } - } - + // log.Info("VM is stopped, cleaning up resources for maintenance mode") + // + // kvvm, err := s.KVVM(ctx) + // if err != nil { + // return reconcile.Result{}, fmt.Errorf("get KVVM: %w", err) + // } + // + // if kvvm != nil { + // log.Info("Deleting KVVM for maintenance mode") + // err = object.CleanupObject(ctx, r.client, kvvm) + // if err != nil { + // return reconcile.Result{}, fmt.Errorf("delete KVVM: %w", err) + // } + // } + // + // if kvvmi != nil { + // log.Info("Deleting KVVMI for maintenance mode") + // err = object.CleanupObject(ctx, r.client, kvvmi) + // if err != nil { + // return reconcile.Result{}, fmt.Errorf("delete KVVMI: %w", err) + // } + // } + // + // pods, err := s.Pods(ctx) + // if err != nil { + // return reconcile.Result{}, fmt.Errorf("get pods: %w", err) + // } + // if pods != nil && len(pods.Items) > 0 { + // log.Info("Deleting pods for maintenance mode") + // for _, pod := range pods.Items { + // object.CleanupObject(ctx, r.client, &pod) + // } + // } + // return reconcile.Result{}, nil } } From 651942ae653a8ecc58d73f2303f217f46a8a1d76 Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Thu, 14 Aug 2025 14:59:22 +0300 Subject: [PATCH 13/39] fixes Signed-off-by: Daniil Loktev --- .../virtualization-artifact/pkg/controller/vm/vm_reconciler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go index 9d427e56b1..ae3db2d1f2 100644 --- a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go +++ b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go @@ -137,7 +137,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco return reconcile.Result{}, fmt.Errorf("get KVVMI: %w", err) } - if kvvmi != nil && !kvvmi.IsFinal() { + if kvvmi != nil && changed.Status.Phase != virtv2.MachineStopped { log.Info("Stopping VM for maintenance mode") force := new(bool) From 61849319af517da4dd5b36f16bd885e1760575be Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Thu, 14 Aug 2025 15:36:12 +0300 Subject: [PATCH 14/39] fixes Signed-off-by: Daniil Loktev --- .../pkg/controller/vm/internal/sync_power_state.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/sync_power_state.go b/images/virtualization-artifact/pkg/controller/vm/internal/sync_power_state.go index 0a45005ee8..b138128650 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/sync_power_state.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/sync_power_state.go @@ -376,6 +376,14 @@ func (h *SyncPowerStateHandler) checkNeedStartVM( isConfigurationApplied bool, runPolicy virtv2.RunPolicy, ) bool { + vm := s.VirtualMachine().Changed() + if vm != nil { + maintenance, _ := conditions.GetCondition(vmcondition.TypeMaintenance, vm.Status.Conditions) + if maintenance.Status == metav1.ConditionTrue { + return false + } + } + if isConfigurationApplied && (kvvm.Annotations[annotations.AnnVMStartRequested] == "true" || kvvm.Annotations[annotations.AnnVMRestartRequested] == "true") { h.recordStartEventf(ctx, s.VirtualMachine().Current(), "Start initiated by controller for %v policy", runPolicy) From 13da17c8b889cd79a6a52bb8b1ccd457d1021d7c Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Thu, 14 Aug 2025 15:50:39 +0300 Subject: [PATCH 15/39] fixes Signed-off-by: Daniil Loktev --- .../pkg/controller/vm/vm_reconciler.go | 73 ++++++++++--------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go index ae3db2d1f2..7a5122a05d 100644 --- a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go +++ b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go @@ -146,43 +146,44 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco return reconcile.Result{}, fmt.Errorf("stop VM: %w", err) } - } else { - // log.Info("VM is stopped, cleaning up resources for maintenance mode") - // - // kvvm, err := s.KVVM(ctx) - // if err != nil { - // return reconcile.Result{}, fmt.Errorf("get KVVM: %w", err) - // } - // - // if kvvm != nil { - // log.Info("Deleting KVVM for maintenance mode") - // err = object.CleanupObject(ctx, r.client, kvvm) - // if err != nil { - // return reconcile.Result{}, fmt.Errorf("delete KVVM: %w", err) - // } - // } - // - // if kvvmi != nil { - // log.Info("Deleting KVVMI for maintenance mode") - // err = object.CleanupObject(ctx, r.client, kvvmi) - // if err != nil { - // return reconcile.Result{}, fmt.Errorf("delete KVVMI: %w", err) - // } - // } - // - // pods, err := s.Pods(ctx) - // if err != nil { - // return reconcile.Result{}, fmt.Errorf("get pods: %w", err) - // } - // if pods != nil && len(pods.Items) > 0 { - // log.Info("Deleting pods for maintenance mode") - // for _, pod := range pods.Items { - // object.CleanupObject(ctx, r.client, &pod) - // } - // } - // - return reconcile.Result{}, nil } + // } else { + // // log.Info("VM is stopped, cleaning up resources for maintenance mode") + // // + // // kvvm, err := s.KVVM(ctx) + // // if err != nil { + // // return reconcile.Result{}, fmt.Errorf("get KVVM: %w", err) + // // } + // // + // // if kvvm != nil { + // // log.Info("Deleting KVVM for maintenance mode") + // // err = object.CleanupObject(ctx, r.client, kvvm) + // // if err != nil { + // // return reconcile.Result{}, fmt.Errorf("delete KVVM: %w", err) + // // } + // // } + // // + // // if kvvmi != nil { + // // log.Info("Deleting KVVMI for maintenance mode") + // // err = object.CleanupObject(ctx, r.client, kvvmi) + // // if err != nil { + // // return reconcile.Result{}, fmt.Errorf("delete KVVMI: %w", err) + // // } + // // } + // // + // // pods, err := s.Pods(ctx) + // // if err != nil { + // // return reconcile.Result{}, fmt.Errorf("get pods: %w", err) + // // } + // // if pods != nil && len(pods.Items) > 0 { + // // log.Info("Deleting pods for maintenance mode") + // // for _, pod := range pods.Items { + // // object.CleanupObject(ctx, r.client, &pod) + // // } + // // } + // // + // return reconcile.Result{}, nil + // } } rec := reconciler.NewBaseReconciler[Handler](r.handlers) From c0abc7b618f7f044b33120d5d34a50d0a6745510 Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Thu, 14 Aug 2025 16:04:54 +0300 Subject: [PATCH 16/39] fixes Signed-off-by: Daniil Loktev --- .../vm/internal/sync_power_state.go | 8 -- .../pkg/controller/vm/vm_reconciler.go | 74 +++++++++---------- 2 files changed, 36 insertions(+), 46 deletions(-) diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/sync_power_state.go b/images/virtualization-artifact/pkg/controller/vm/internal/sync_power_state.go index b138128650..0a45005ee8 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/sync_power_state.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/sync_power_state.go @@ -376,14 +376,6 @@ func (h *SyncPowerStateHandler) checkNeedStartVM( isConfigurationApplied bool, runPolicy virtv2.RunPolicy, ) bool { - vm := s.VirtualMachine().Changed() - if vm != nil { - maintenance, _ := conditions.GetCondition(vmcondition.TypeMaintenance, vm.Status.Conditions) - if maintenance.Status == metav1.ConditionTrue { - return false - } - } - if isConfigurationApplied && (kvvm.Annotations[annotations.AnnVMStartRequested] == "true" || kvvm.Annotations[annotations.AnnVMRestartRequested] == "true") { h.recordStartEventf(ctx, s.VirtualMachine().Current(), "Start initiated by controller for %v policy", runPolicy) diff --git a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go index 7a5122a05d..b81dbdd778 100644 --- a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go +++ b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go @@ -30,7 +30,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/source" "github.com/deckhouse/virtualization-controller/pkg/common/annotations" - // "github.com/deckhouse/virtualization-controller/pkg/common/object" + "github.com/deckhouse/virtualization-controller/pkg/common/object" "github.com/deckhouse/virtualization-controller/pkg/controller/conditions" "github.com/deckhouse/virtualization-controller/pkg/controller/powerstate" "github.com/deckhouse/virtualization-controller/pkg/controller/reconciler" @@ -145,45 +145,43 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco if err := powerstate.StopVM(ctx, r.client, kvvmi, force); err != nil { return reconcile.Result{}, fmt.Errorf("stop VM: %w", err) } + } else { + log.Info("VM is stopped, cleaning up resources for maintenance mode") + kvvm, err := s.KVVM(ctx) + if err != nil { + return reconcile.Result{}, fmt.Errorf("get KVVM: %w", err) + } + + if kvvm != nil { + log.Info("Deleting KVVM for maintenance mode") + err = object.CleanupObject(ctx, r.client, kvvm) + if err != nil { + return reconcile.Result{}, fmt.Errorf("delete KVVM: %w", err) + } + } + + if kvvmi != nil { + log.Info("Deleting KVVMI for maintenance mode") + err = object.CleanupObject(ctx, r.client, kvvmi) + if err != nil { + return reconcile.Result{}, fmt.Errorf("delete KVVMI: %w", err) + } + } + + pods, err := s.Pods(ctx) + if err != nil { + return reconcile.Result{}, fmt.Errorf("get pods: %w", err) + } + if pods != nil && len(pods.Items) > 0 { + log.Info("Deleting pods for maintenance mode") + for _, pod := range pods.Items { + object.CleanupObject(ctx, r.client, &pod) + } + } + + return reconcile.Result{}, nil } - // } else { - // // log.Info("VM is stopped, cleaning up resources for maintenance mode") - // // - // // kvvm, err := s.KVVM(ctx) - // // if err != nil { - // // return reconcile.Result{}, fmt.Errorf("get KVVM: %w", err) - // // } - // // - // // if kvvm != nil { - // // log.Info("Deleting KVVM for maintenance mode") - // // err = object.CleanupObject(ctx, r.client, kvvm) - // // if err != nil { - // // return reconcile.Result{}, fmt.Errorf("delete KVVM: %w", err) - // // } - // // } - // // - // // if kvvmi != nil { - // // log.Info("Deleting KVVMI for maintenance mode") - // // err = object.CleanupObject(ctx, r.client, kvvmi) - // // if err != nil { - // // return reconcile.Result{}, fmt.Errorf("delete KVVMI: %w", err) - // // } - // // } - // // - // // pods, err := s.Pods(ctx) - // // if err != nil { - // // return reconcile.Result{}, fmt.Errorf("get pods: %w", err) - // // } - // // if pods != nil && len(pods.Items) > 0 { - // // log.Info("Deleting pods for maintenance mode") - // // for _, pod := range pods.Items { - // // object.CleanupObject(ctx, r.client, &pod) - // // } - // // } - // // - // return reconcile.Result{}, nil - // } } rec := reconciler.NewBaseReconciler[Handler](r.handlers) From bbc4fbdd2a3aef48d6a09ce1d5d41cf7fff8d07d Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Thu, 14 Aug 2025 16:28:26 +0300 Subject: [PATCH 17/39] add check for syncPowerState Signed-off-by: Daniil Loktev --- .../vm/internal/sync_power_state.go | 37 +++++++++++-------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/sync_power_state.go b/images/virtualization-artifact/pkg/controller/vm/internal/sync_power_state.go index 0a45005ee8..4c2c35a73f 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/sync_power_state.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/sync_power_state.go @@ -126,23 +126,30 @@ func (h *SyncPowerStateHandler) syncPowerState( shutdownInfo = s.ShutdownInfo }) - isConfigurationApplied := checkVirtualMachineConfiguration(s.VirtualMachine().Changed()) + changed := s.VirtualMachine().Changed() + isConfigurationApplied := checkVirtualMachineConfiguration(changed) + maintenance, _ := conditions.GetCondition(vmcondition.TypeMaintenance, changed.Status.Conditions) + var vmAction VMAction - switch runPolicy { - case virtv2.AlwaysOffPolicy: - vmAction = h.handleAlwaysOffPolicy(ctx, s, kvvmi) - case virtv2.AlwaysOnPolicy: - vmAction, err = h.handleAlwaysOnPolicy(ctx, s, kvvm, kvvmi, isConfigurationApplied, shutdownInfo) - if err != nil { - return err - } - case virtv2.AlwaysOnUnlessStoppedManually: - vmAction, err = h.handleAlwaysOnUnlessStoppedManuallyPolicy(ctx, s, kvvm, kvvmi, isConfigurationApplied, shutdownInfo) - if err != nil { - return err + if maintenance.Status != metav1.ConditionTrue { + switch runPolicy { + case virtv2.AlwaysOffPolicy: + vmAction = h.handleAlwaysOffPolicy(ctx, s, kvvmi) + case virtv2.AlwaysOnPolicy: + vmAction, err = h.handleAlwaysOnPolicy(ctx, s, kvvm, kvvmi, isConfigurationApplied, shutdownInfo) + if err != nil { + return err + } + case virtv2.AlwaysOnUnlessStoppedManually: + vmAction, err = h.handleAlwaysOnUnlessStoppedManuallyPolicy(ctx, s, kvvm, kvvmi, isConfigurationApplied, shutdownInfo) + if err != nil { + return err + } + case virtv2.ManualPolicy: + vmAction = h.handleManualPolicy(ctx, s, kvvm, kvvmi, isConfigurationApplied, shutdownInfo) } - case virtv2.ManualPolicy: - vmAction = h.handleManualPolicy(ctx, s, kvvm, kvvmi, isConfigurationApplied, shutdownInfo) + } else { + vmAction = Stop } switch vmAction { From 3cafbe20bca6156ab8653e6f516c26d3903261e1 Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Thu, 14 Aug 2025 16:33:58 +0300 Subject: [PATCH 18/39] fixes Signed-off-by: Daniil Loktev --- .../pkg/controller/vm/vm_reconciler.go | 108 +++++++++--------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go index b81dbdd778..79ce593c0e 100644 --- a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go +++ b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go @@ -129,60 +129,60 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco if maintenance.Status == metav1.ConditionFalse { conditions.RemoveCondition(vmcondition.TypeMaintenance, &changed.Status.Conditions) } - if maintenance.Status == metav1.ConditionTrue { - log.Info("Reconcile observe an VirtualMachine in maintenance mode") - - kvvmi, err := s.KVVMI(ctx) - if err != nil { - return reconcile.Result{}, fmt.Errorf("get KVVMI: %w", err) - } - - if kvvmi != nil && changed.Status.Phase != virtv2.MachineStopped { - log.Info("Stopping VM for maintenance mode") - - force := new(bool) - *force = false - if err := powerstate.StopVM(ctx, r.client, kvvmi, force); err != nil { - return reconcile.Result{}, fmt.Errorf("stop VM: %w", err) - } - } else { - log.Info("VM is stopped, cleaning up resources for maintenance mode") - - kvvm, err := s.KVVM(ctx) - if err != nil { - return reconcile.Result{}, fmt.Errorf("get KVVM: %w", err) - } - - if kvvm != nil { - log.Info("Deleting KVVM for maintenance mode") - err = object.CleanupObject(ctx, r.client, kvvm) - if err != nil { - return reconcile.Result{}, fmt.Errorf("delete KVVM: %w", err) - } - } - - if kvvmi != nil { - log.Info("Deleting KVVMI for maintenance mode") - err = object.CleanupObject(ctx, r.client, kvvmi) - if err != nil { - return reconcile.Result{}, fmt.Errorf("delete KVVMI: %w", err) - } - } - - pods, err := s.Pods(ctx) - if err != nil { - return reconcile.Result{}, fmt.Errorf("get pods: %w", err) - } - if pods != nil && len(pods.Items) > 0 { - log.Info("Deleting pods for maintenance mode") - for _, pod := range pods.Items { - object.CleanupObject(ctx, r.client, &pod) - } - } - - return reconcile.Result{}, nil - } - } + // if maintenance.Status == metav1.ConditionTrue { + // log.Info("Reconcile observe an VirtualMachine in maintenance mode") + // + // kvvmi, err := s.KVVMI(ctx) + // if err != nil { + // return reconcile.Result{}, fmt.Errorf("get KVVMI: %w", err) + // } + // + // if kvvmi != nil && changed.Status.Phase != virtv2.MachineStopped { + // log.Info("Stopping VM for maintenance mode") + // + // force := new(bool) + // *force = false + // if err := powerstate.StopVM(ctx, r.client, kvvmi, force); err != nil { + // return reconcile.Result{}, fmt.Errorf("stop VM: %w", err) + // } + // } else { + // log.Info("VM is stopped, cleaning up resources for maintenance mode") + // + // kvvm, err := s.KVVM(ctx) + // if err != nil { + // return reconcile.Result{}, fmt.Errorf("get KVVM: %w", err) + // } + // + // if kvvm != nil { + // log.Info("Deleting KVVM for maintenance mode") + // err = object.CleanupObject(ctx, r.client, kvvm) + // if err != nil { + // return reconcile.Result{}, fmt.Errorf("delete KVVM: %w", err) + // } + // } + // + // if kvvmi != nil { + // log.Info("Deleting KVVMI for maintenance mode") + // err = object.CleanupObject(ctx, r.client, kvvmi) + // if err != nil { + // return reconcile.Result{}, fmt.Errorf("delete KVVMI: %w", err) + // } + // } + // + // pods, err := s.Pods(ctx) + // if err != nil { + // return reconcile.Result{}, fmt.Errorf("get pods: %w", err) + // } + // if pods != nil && len(pods.Items) > 0 { + // log.Info("Deleting pods for maintenance mode") + // for _, pod := range pods.Items { + // object.CleanupObject(ctx, r.client, &pod) + // } + // } + // + // return reconcile.Result{}, nil + // } + // } rec := reconciler.NewBaseReconciler[Handler](r.handlers) rec.SetHandlerExecutor(func(ctx context.Context, h Handler) (reconcile.Result, error) { From f8ccf17d6f14ed3762380d95718669fd5a605410 Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Thu, 14 Aug 2025 16:40:39 +0300 Subject: [PATCH 19/39] fixes Signed-off-by: Daniil Loktev --- .../pkg/controller/vm/vm_reconciler.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go index 79ce593c0e..aa6d2d678c 100644 --- a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go +++ b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go @@ -30,9 +30,9 @@ import ( "sigs.k8s.io/controller-runtime/pkg/source" "github.com/deckhouse/virtualization-controller/pkg/common/annotations" - "github.com/deckhouse/virtualization-controller/pkg/common/object" + // "github.com/deckhouse/virtualization-controller/pkg/common/object" "github.com/deckhouse/virtualization-controller/pkg/controller/conditions" - "github.com/deckhouse/virtualization-controller/pkg/controller/powerstate" + // "github.com/deckhouse/virtualization-controller/pkg/controller/powerstate" "github.com/deckhouse/virtualization-controller/pkg/controller/reconciler" "github.com/deckhouse/virtualization-controller/pkg/controller/vm/internal/state" "github.com/deckhouse/virtualization-controller/pkg/controller/vm/internal/watcher" From 6f0dc0c686dbe4cd847fcc83b09cd2695489c2ae Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Thu, 14 Aug 2025 17:00:02 +0300 Subject: [PATCH 20/39] fixes Signed-off-by: Daniil Loktev --- .../pkg/controller/vm/vm_reconciler.go | 94 ++++++++----------- 1 file changed, 38 insertions(+), 56 deletions(-) diff --git a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go index aa6d2d678c..681163a8ec 100644 --- a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go +++ b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go @@ -30,9 +30,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/source" "github.com/deckhouse/virtualization-controller/pkg/common/annotations" - // "github.com/deckhouse/virtualization-controller/pkg/common/object" + "github.com/deckhouse/virtualization-controller/pkg/common/object" "github.com/deckhouse/virtualization-controller/pkg/controller/conditions" - // "github.com/deckhouse/virtualization-controller/pkg/controller/powerstate" "github.com/deckhouse/virtualization-controller/pkg/controller/reconciler" "github.com/deckhouse/virtualization-controller/pkg/controller/vm/internal/state" "github.com/deckhouse/virtualization-controller/pkg/controller/vm/internal/watcher" @@ -129,60 +128,43 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco if maintenance.Status == metav1.ConditionFalse { conditions.RemoveCondition(vmcondition.TypeMaintenance, &changed.Status.Conditions) } - // if maintenance.Status == metav1.ConditionTrue { - // log.Info("Reconcile observe an VirtualMachine in maintenance mode") - // - // kvvmi, err := s.KVVMI(ctx) - // if err != nil { - // return reconcile.Result{}, fmt.Errorf("get KVVMI: %w", err) - // } - // - // if kvvmi != nil && changed.Status.Phase != virtv2.MachineStopped { - // log.Info("Stopping VM for maintenance mode") - // - // force := new(bool) - // *force = false - // if err := powerstate.StopVM(ctx, r.client, kvvmi, force); err != nil { - // return reconcile.Result{}, fmt.Errorf("stop VM: %w", err) - // } - // } else { - // log.Info("VM is stopped, cleaning up resources for maintenance mode") - // - // kvvm, err := s.KVVM(ctx) - // if err != nil { - // return reconcile.Result{}, fmt.Errorf("get KVVM: %w", err) - // } - // - // if kvvm != nil { - // log.Info("Deleting KVVM for maintenance mode") - // err = object.CleanupObject(ctx, r.client, kvvm) - // if err != nil { - // return reconcile.Result{}, fmt.Errorf("delete KVVM: %w", err) - // } - // } - // - // if kvvmi != nil { - // log.Info("Deleting KVVMI for maintenance mode") - // err = object.CleanupObject(ctx, r.client, kvvmi) - // if err != nil { - // return reconcile.Result{}, fmt.Errorf("delete KVVMI: %w", err) - // } - // } - // - // pods, err := s.Pods(ctx) - // if err != nil { - // return reconcile.Result{}, fmt.Errorf("get pods: %w", err) - // } - // if pods != nil && len(pods.Items) > 0 { - // log.Info("Deleting pods for maintenance mode") - // for _, pod := range pods.Items { - // object.CleanupObject(ctx, r.client, &pod) - // } - // } - // - // return reconcile.Result{}, nil - // } - // } + if maintenance.Status == metav1.ConditionTrue { + log.Info("Reconcile observe an VirtualMachine in maintenance mode") + + kvvmi, err := s.KVVMI(ctx) + if err != nil { + return reconcile.Result{}, fmt.Errorf("get KVVMI: %w", err) + } + + if kvvmi == nil { + log.Info("VM is stopped, cleaning up resources for maintenance mode") + + kvvm, err := s.KVVM(ctx) + if err != nil { + return reconcile.Result{}, fmt.Errorf("get KVVM: %w", err) + } + if kvvm != nil { + log.Info("Deleting KVVM for maintenance mode") + err = object.CleanupObject(ctx, r.client, kvvm) + if err != nil { + return reconcile.Result{}, fmt.Errorf("delete KVVM: %w", err) + } + } + + pods, err := s.Pods(ctx) + if err != nil { + return reconcile.Result{}, fmt.Errorf("get pods: %w", err) + } + if pods != nil && len(pods.Items) > 0 { + log.Info("Deleting pods for maintenance mode") + for _, pod := range pods.Items { + object.CleanupObject(ctx, r.client, &pod) + } + } + + return reconcile.Result{}, nil + } + } rec := reconciler.NewBaseReconciler[Handler](r.handlers) rec.SetHandlerExecutor(func(ctx context.Context, h Handler) (reconcile.Result, error) { From 31e11db690eed888207a382b85f84f1a1e46df03 Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Thu, 14 Aug 2025 17:16:23 +0300 Subject: [PATCH 21/39] fixes Signed-off-by: Daniil Loktev --- .../pkg/common/annotations/annotations.go | 2 -- .../pkg/controller/vm/vm_reconciler.go | 21 +------------------ 2 files changed, 1 insertion(+), 22 deletions(-) diff --git a/images/virtualization-artifact/pkg/common/annotations/annotations.go b/images/virtualization-artifact/pkg/common/annotations/annotations.go index 9c16aabc15..d684959455 100644 --- a/images/virtualization-artifact/pkg/common/annotations/annotations.go +++ b/images/virtualization-artifact/pkg/common/annotations/annotations.go @@ -93,8 +93,6 @@ const ( AnnVMRestore = AnnAPIGroupV + "/vmrestore" // AnnVMOPEvacuation is an annotation on vmop that represents a vmop created by evacuation controller AnnVMOPEvacuation = AnnAPIGroupV + "/evacuation" - // DELETE ME - AnnVMMaintenance = AnnAPIGroupV + "/maintenance" // LabelsPrefix is a prefix for virtualization-controller labels. LabelsPrefix = "virtualization.deckhouse.io" diff --git a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go index 681163a8ec..df07ef9d3f 100644 --- a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go +++ b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go @@ -103,27 +103,8 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco } s := state.New(r.client, vm) - - current := s.VirtualMachine().Current() changed := s.VirtualMachine().Changed() - // DELETE ME - if current.Annotations[annotations.AnnVMMaintenance] == "true" { - cb := conditions.NewConditionBuilder(vmcondition.TypeMaintenance). - Generation(current.GetGeneration()). - Status(metav1.ConditionTrue). - Reason(vmcondition.ReasonMaintenanceRestore). - Message("VM is in maintenance mode") - conditions.SetCondition(cb, &changed.Status.Conditions) - } else if current.Annotations[annotations.AnnVMMaintenance] == "false" { - cb := conditions.NewConditionBuilder(vmcondition.TypeMaintenance). - Generation(current.GetGeneration()). - Status(metav1.ConditionFalse). - Reason(vmcondition.ReasonMaintenanceRestore). - Message("VM maintenance mode disabled") - conditions.SetCondition(cb, &changed.Status.Conditions) - } - maintenance, _ := conditions.GetCondition(vmcondition.TypeMaintenance, changed.Status.Conditions) if maintenance.Status == metav1.ConditionFalse { conditions.RemoveCondition(vmcondition.TypeMaintenance, &changed.Status.Conditions) @@ -137,7 +118,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco } if kvvmi == nil { - log.Info("VM is stopped, cleaning up resources for maintenance mode") + log.Info("VM is stopped, cleaning up resources if any for maintenance mode") kvvm, err := s.KVVM(ctx) if err != nil { From c3b7abb0a577247ce39b6d0df493136573a2d1ab Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Thu, 14 Aug 2025 17:27:15 +0300 Subject: [PATCH 22/39] fixes Signed-off-by: Daniil Loktev --- .../virtualization-artifact/pkg/controller/vm/vm_reconciler.go | 1 - 1 file changed, 1 deletion(-) diff --git a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go index df07ef9d3f..8c61187e1f 100644 --- a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go +++ b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go @@ -29,7 +29,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/source" - "github.com/deckhouse/virtualization-controller/pkg/common/annotations" "github.com/deckhouse/virtualization-controller/pkg/common/object" "github.com/deckhouse/virtualization-controller/pkg/controller/conditions" "github.com/deckhouse/virtualization-controller/pkg/controller/reconciler" From 6d6bcf7f281a0f856bc745d139307e82084a042d Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Thu, 14 Aug 2025 17:30:23 +0300 Subject: [PATCH 23/39] fix linter errors Signed-off-by: Daniil Loktev --- .../pkg/controller/vm/vm_reconciler.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go index 8c61187e1f..abd314c1cb 100644 --- a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go +++ b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go @@ -138,7 +138,10 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco if pods != nil && len(pods.Items) > 0 { log.Info("Deleting pods for maintenance mode") for _, pod := range pods.Items { - object.CleanupObject(ctx, r.client, &pod) + err = object.CleanupObject(ctx, r.client, &pod) + if err != nil { + return reconcile.Result{}, fmt.Errorf("delete pod: %w", err) + } } } From f6ef9f3e542da551b807118f3e7b51e63591ae08 Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Mon, 25 Aug 2025 12:47:01 +0300 Subject: [PATCH 24/39] fixes Signed-off-by: Daniil Loktev --- .../pkg/controller/reconciler/reconciler.go | 10 +- .../pkg/controller/vm/internal/maintenance.go | 122 ++++++++++++++++++ .../validators/maintenance_validator.go | 55 ++++++++ .../pkg/controller/vm/vm_controller.go | 1 + .../pkg/controller/vm/vm_reconciler.go | 50 ------- .../pkg/controller/vm/vm_webhook.go | 1 + 6 files changed, 187 insertions(+), 52 deletions(-) create mode 100644 images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go create mode 100644 images/virtualization-artifact/pkg/controller/vm/internal/validators/maintenance_validator.go diff --git a/images/virtualization-artifact/pkg/controller/reconciler/reconciler.go b/images/virtualization-artifact/pkg/controller/reconciler/reconciler.go index 01636127d4..5d22f0847d 100644 --- a/images/virtualization-artifact/pkg/controller/reconciler/reconciler.go +++ b/images/virtualization-artifact/pkg/controller/reconciler/reconciler.go @@ -1,5 +1,4 @@ -/* -Copyright 2025 Flant JSC +/* 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. @@ -28,6 +27,8 @@ import ( "github.com/deckhouse/virtualization-controller/pkg/logger" ) +var ErrStopHandlerChain = errors.New("stop handler chain execution") + type ResourceUpdater func(ctx context.Context) error type HandlerExecutor[H any] func(ctx context.Context, h H) (reconcile.Result, error) @@ -66,12 +67,17 @@ func (r *BaseReconciler[H]) Reconcile(ctx context.Context) (reconcile.Result, er var result reconcile.Result var errs error +handlersLoop: for _, h := range r.handlers { log := logger.FromContext(ctx).With(logger.SlogHandler(reflect.TypeOf(h).Elem().Name())) res, err := r.execute(ctx, h) switch { case err == nil: // OK. + case errors.Is(err, ErrStopHandlerChain): + log.Debug("Handler chain execution stopped") + result = MergeResults(result, res) + break handlersLoop case k8serrors.IsConflict(err): log.Debug("Conflict occurred during handler execution", logger.SlogErr(err)) result.RequeueAfter = 100 * time.Microsecond diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go b/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go new file mode 100644 index 0000000000..51bf9f9899 --- /dev/null +++ b/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go @@ -0,0 +1,122 @@ +/* +Copyright 2024 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" + "fmt" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + "github.com/deckhouse/virtualization-controller/pkg/common/object" + "github.com/deckhouse/virtualization-controller/pkg/controller/conditions" + "github.com/deckhouse/virtualization-controller/pkg/controller/reconciler" + "github.com/deckhouse/virtualization-controller/pkg/controller/vm/internal/state" + "github.com/deckhouse/virtualization-controller/pkg/logger" + "github.com/deckhouse/virtualization/api/core/v1alpha2/vmcondition" +) + +const nameMaintenanceHandler = "MaintenanceHandler" + +type MaintenanceHandler struct { + client client.Client +} + +func NewMaintenanceHandler(client client.Client) *MaintenanceHandler { + return &MaintenanceHandler{ + client: client, + } +} + +func (h *MaintenanceHandler) Handle(ctx context.Context, s state.VirtualMachineState) (reconcile.Result, error) { + log := logger.FromContext(ctx) + + if s.VirtualMachine().IsEmpty() { + return reconcile.Result{}, nil + } + changed := s.VirtualMachine().Changed() + maintenance, _ := conditions.GetCondition(vmcondition.TypeMaintenance, changed.Status.Conditions) + + if maintenance.Status == metav1.ConditionFalse { + conditions.RemoveCondition(vmcondition.TypeMaintenance, &changed.Status.Conditions) + return reconcile.Result{}, nil + } + + if maintenance.Status != metav1.ConditionTrue { + return reconcile.Result{}, nil + } + + log.Info("Reconcile observe a VirtualMachine in maintenance mode") + + // Hide all other conditions when in maintenance mode + if changed.Status.Conditions != nil { + var newConditions []metav1.Condition + for _, cond := range changed.Status.Conditions { + if vmcondition.Type(cond.Type) == vmcondition.TypeMaintenance { + newConditions = append(newConditions, cond) + } + } + changed.Status.Conditions = newConditions + } + + kvvmi, err := s.KVVMI(ctx) + if err != nil { + return reconcile.Result{}, fmt.Errorf("get KVVMI: %w", err) + } + + // If VM is not stopped yet, wait for it to stop (SyncPowerStateHandler will handle stopping) + if kvvmi != nil { + log.Info("VM is still running, waiting for shutdown in maintenance mode") + return reconcile.Result{}, nil + } + + log.Info("VM is stopped, cleaning up resources if any for maintenance mode") + + kvvm, err := s.KVVM(ctx) + if err != nil { + return reconcile.Result{}, fmt.Errorf("get KVVM: %w", err) + } + if kvvm != nil { + log.Info("Deleting KVVM for maintenance mode") + err = object.CleanupObject(ctx, h.client, kvvm) + if err != nil { + return reconcile.Result{}, fmt.Errorf("delete KVVM: %w", err) + } + } + + pods, err := s.Pods(ctx) + if err != nil { + return reconcile.Result{}, fmt.Errorf("get pods: %w", err) + } + if pods != nil && len(pods.Items) > 0 { + log.Info("Deleting pods for maintenance mode") + for i := range pods.Items { + err = object.CleanupObject(ctx, h.client, &pods.Items[i]) + if err != nil { + return reconcile.Result{}, fmt.Errorf("delete pod: %w", err) + } + } + } + + return reconcile.Result{}, reconciler.ErrStopHandlerChain +} + +func (h *MaintenanceHandler) Name() string { + return nameMaintenanceHandler +} diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/validators/maintenance_validator.go b/images/virtualization-artifact/pkg/controller/vm/internal/validators/maintenance_validator.go new file mode 100644 index 0000000000..951ccdbd4c --- /dev/null +++ b/images/virtualization-artifact/pkg/controller/vm/internal/validators/maintenance_validator.go @@ -0,0 +1,55 @@ +/* +Copyright 2024 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 validators + +import ( + "context" + "fmt" + "reflect" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + + "github.com/deckhouse/virtualization-controller/pkg/controller/conditions" + virtv2 "github.com/deckhouse/virtualization/api/core/v1alpha2" + "github.com/deckhouse/virtualization/api/core/v1alpha2/vmcondition" +) + +type MaintenanceValidator struct{} + +func NewMaintenanceValidator() *MaintenanceValidator { + return &MaintenanceValidator{} +} + +func (v *MaintenanceValidator) ValidateCreate(_ context.Context, _ *virtv2.VirtualMachine) (admission.Warnings, error) { + return nil, nil +} + +func (v *MaintenanceValidator) ValidateUpdate(_ context.Context, oldVM, newVM *virtv2.VirtualMachine) (admission.Warnings, error) { + maintenance, _ := conditions.GetCondition(vmcondition.TypeMaintenance, oldVM.Status.Conditions) + + if maintenance.Status != metav1.ConditionTrue { + return nil, nil + } + + if !reflect.DeepEqual(oldVM.Spec, newVM.Spec) { + return nil, fmt.Errorf("spec changes are not allowed while VirtualMachine is in maintenance mode") + } + + return nil, nil +} + diff --git a/images/virtualization-artifact/pkg/controller/vm/vm_controller.go b/images/virtualization-artifact/pkg/controller/vm/vm_controller.go index b9e04910a9..f99a4718a2 100644 --- a/images/virtualization-artifact/pkg/controller/vm/vm_controller.go +++ b/images/virtualization-artifact/pkg/controller/vm/vm_controller.go @@ -55,6 +55,7 @@ func SetupController( client := mgr.GetClient() blockDeviceService := service.NewBlockDeviceService(client) handlers := []Handler{ + internal.NewMaintenanceHandler(client), internal.NewDeletionHandler(client), internal.NewClassHandler(client, recorder), internal.NewIPAMHandler(ipam.New(), client, recorder), diff --git a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go index abd314c1cb..cc0212d79f 100644 --- a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go +++ b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go @@ -21,7 +21,6 @@ import ( "fmt" "reflect" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/handler" @@ -29,14 +28,11 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/source" - "github.com/deckhouse/virtualization-controller/pkg/common/object" - "github.com/deckhouse/virtualization-controller/pkg/controller/conditions" "github.com/deckhouse/virtualization-controller/pkg/controller/reconciler" "github.com/deckhouse/virtualization-controller/pkg/controller/vm/internal/state" "github.com/deckhouse/virtualization-controller/pkg/controller/vm/internal/watcher" "github.com/deckhouse/virtualization-controller/pkg/logger" virtv2 "github.com/deckhouse/virtualization/api/core/v1alpha2" - "github.com/deckhouse/virtualization/api/core/v1alpha2/vmcondition" ) type Handler interface { @@ -102,52 +98,6 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco } s := state.New(r.client, vm) - changed := s.VirtualMachine().Changed() - - maintenance, _ := conditions.GetCondition(vmcondition.TypeMaintenance, changed.Status.Conditions) - if maintenance.Status == metav1.ConditionFalse { - conditions.RemoveCondition(vmcondition.TypeMaintenance, &changed.Status.Conditions) - } - if maintenance.Status == metav1.ConditionTrue { - log.Info("Reconcile observe an VirtualMachine in maintenance mode") - - kvvmi, err := s.KVVMI(ctx) - if err != nil { - return reconcile.Result{}, fmt.Errorf("get KVVMI: %w", err) - } - - if kvvmi == nil { - log.Info("VM is stopped, cleaning up resources if any for maintenance mode") - - kvvm, err := s.KVVM(ctx) - if err != nil { - return reconcile.Result{}, fmt.Errorf("get KVVM: %w", err) - } - if kvvm != nil { - log.Info("Deleting KVVM for maintenance mode") - err = object.CleanupObject(ctx, r.client, kvvm) - if err != nil { - return reconcile.Result{}, fmt.Errorf("delete KVVM: %w", err) - } - } - - pods, err := s.Pods(ctx) - if err != nil { - return reconcile.Result{}, fmt.Errorf("get pods: %w", err) - } - if pods != nil && len(pods.Items) > 0 { - log.Info("Deleting pods for maintenance mode") - for _, pod := range pods.Items { - err = object.CleanupObject(ctx, r.client, &pod) - if err != nil { - return reconcile.Result{}, fmt.Errorf("delete pod: %w", err) - } - } - } - - return reconcile.Result{}, nil - } - } rec := reconciler.NewBaseReconciler[Handler](r.handlers) rec.SetHandlerExecutor(func(ctx context.Context, h Handler) (reconcile.Result, error) { diff --git a/images/virtualization-artifact/pkg/controller/vm/vm_webhook.go b/images/virtualization-artifact/pkg/controller/vm/vm_webhook.go index e7bbf4bf6b..8a52b45557 100644 --- a/images/virtualization-artifact/pkg/controller/vm/vm_webhook.go +++ b/images/virtualization-artifact/pkg/controller/vm/vm_webhook.go @@ -44,6 +44,7 @@ type Validator struct { func NewValidator(ipam internal.IPAM, client client.Client, service *service.BlockDeviceService, log *log.Logger) *Validator { return &Validator{ validators: []VirtualMachineValidator{ + validators.NewMaintenanceValidator(), validators.NewMetaValidator(client), validators.NewIPAMValidator(client), validators.NewBlockDeviceSpecRefsValidator(), From 074a805a0299755902b1af8a68b28479a0a8de11 Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Mon, 25 Aug 2025 12:54:19 +0300 Subject: [PATCH 25/39] fixes Signed-off-by: Daniil Loktev --- .../pkg/controller/reconciler/reconciler.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/images/virtualization-artifact/pkg/controller/reconciler/reconciler.go b/images/virtualization-artifact/pkg/controller/reconciler/reconciler.go index 5d22f0847d..7e8999dee9 100644 --- a/images/virtualization-artifact/pkg/controller/reconciler/reconciler.go +++ b/images/virtualization-artifact/pkg/controller/reconciler/reconciler.go @@ -1,4 +1,5 @@ -/* Copyright 2025 Flant JSC +/* +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. From b2917b6946a029044cc9cdbbe0479b8e72d3895d Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Mon, 25 Aug 2025 13:27:51 +0300 Subject: [PATCH 26/39] add comments Signed-off-by: Daniil Loktev --- api/core/v1alpha2/vmcondition/condition.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/api/core/v1alpha2/vmcondition/condition.go b/api/core/v1alpha2/vmcondition/condition.go index 6fb32d774c..28385de273 100644 --- a/api/core/v1alpha2/vmcondition/condition.go +++ b/api/core/v1alpha2/vmcondition/condition.go @@ -46,6 +46,8 @@ const ( TypeNetworkReady Type = "NetworkReady" + // TypeMaintenance indicates that the VirtualMachine is in maintenance mode. + // During this condition, the VM remains stopped and no changes are allowed. TypeMaintenance Type = "Maintenance" ) @@ -125,5 +127,6 @@ const ( ReasonNetworkNotReady Reason = "NetworkNotReady" ReasonSDNModuleDisable Reason = "SDNModuleDisable" + // ReasonMaintenanceRestore indicates that the VirtualMachine is in maintenance mode for restore operation. ReasonMaintenanceRestore Reason = "RestoreInProgress" ) From 8256de6c616dd515e04019c9b5a4d4a0358232f2 Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Mon, 25 Aug 2025 14:55:07 +0300 Subject: [PATCH 27/39] add annotation for testing Signed-off-by: Daniil Loktev --- .../pkg/common/annotations/annotations.go | 3 +++ .../pkg/controller/vm/internal/maintenance.go | 23 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/images/virtualization-artifact/pkg/common/annotations/annotations.go b/images/virtualization-artifact/pkg/common/annotations/annotations.go index d684959455..6a45b93c44 100644 --- a/images/virtualization-artifact/pkg/common/annotations/annotations.go +++ b/images/virtualization-artifact/pkg/common/annotations/annotations.go @@ -85,6 +85,9 @@ const ( // AnnVMRestartRequested is an annotation on KVVM that represents a request to restart a virtual machine. AnnVMRestartRequested = AnnAPIGroupV + "/vm-restart-requested" + // DELETE ME + AnnVMMaintenance = AnnAPIGroupV + "/maintenance" + // AnnVMOPWorkloadUpdate is an annotation on vmop that represents a vmop created by workload-updater controller. AnnVMOPWorkloadUpdate = AnnAPIGroupV + "/workload-update" AnnVMOPWorkloadUpdateImage = AnnAPIGroupV + "/workload-update-image" diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go b/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go index 51bf9f9899..efcf1897d9 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go @@ -24,6 +24,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/reconcile" + "github.com/deckhouse/virtualization-controller/pkg/common/annotations" "github.com/deckhouse/virtualization-controller/pkg/common/object" "github.com/deckhouse/virtualization-controller/pkg/controller/conditions" "github.com/deckhouse/virtualization-controller/pkg/controller/reconciler" @@ -51,6 +52,28 @@ func (h *MaintenanceHandler) Handle(ctx context.Context, s state.VirtualMachineS return reconcile.Result{}, nil } changed := s.VirtualMachine().Changed() + + // DELETE ME + current := s.VirtualMachine().Current() + switch changed.Annotations[annotations.AnnVMMaintenance] { + case "true": + // Set maintenance condition if annotation is present + cb := conditions.NewConditionBuilder(vmcondition.TypeMaintenance). + Generation(current.GetGeneration()). + Status(metav1.ConditionTrue). + Reason(vmcondition.ReasonMaintenanceRestore). + Message("VM is in maintenance mode") + conditions.SetCondition(cb, &changed.Status.Conditions) + case "false": + // Explicitly set maintenance to false if annotation is "false" + cb := conditions.NewConditionBuilder(vmcondition.TypeMaintenance). + Generation(current.GetGeneration()). + Status(metav1.ConditionFalse). + Reason(vmcondition.ReasonMaintenanceRestore). + Message("VM maintenance mode disabled") + conditions.SetCondition(cb, &changed.Status.Conditions) + } + maintenance, _ := conditions.GetCondition(vmcondition.TypeMaintenance, changed.Status.Conditions) if maintenance.Status == metav1.ConditionFalse { From eb1de7dd2ce2104d33511bae39216f4cd2075efd Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Mon, 25 Aug 2025 15:33:10 +0300 Subject: [PATCH 28/39] fixes Signed-off-by: Daniil Loktev --- .../pkg/controller/vm/internal/maintenance.go | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go b/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go index efcf1897d9..c1f860ac58 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go @@ -87,17 +87,6 @@ func (h *MaintenanceHandler) Handle(ctx context.Context, s state.VirtualMachineS log.Info("Reconcile observe a VirtualMachine in maintenance mode") - // Hide all other conditions when in maintenance mode - if changed.Status.Conditions != nil { - var newConditions []metav1.Condition - for _, cond := range changed.Status.Conditions { - if vmcondition.Type(cond.Type) == vmcondition.TypeMaintenance { - newConditions = append(newConditions, cond) - } - } - changed.Status.Conditions = newConditions - } - kvvmi, err := s.KVVMI(ctx) if err != nil { return reconcile.Result{}, fmt.Errorf("get KVVMI: %w", err) @@ -109,6 +98,17 @@ func (h *MaintenanceHandler) Handle(ctx context.Context, s state.VirtualMachineS return reconcile.Result{}, nil } + // Hide all other conditions when in maintenance mode + if changed.Status.Conditions != nil { + var newConditions []metav1.Condition + for _, cond := range changed.Status.Conditions { + if vmcondition.Type(cond.Type) == vmcondition.TypeMaintenance { + newConditions = append(newConditions, cond) + } + } + changed.Status.Conditions = newConditions + } + log.Info("VM is stopped, cleaning up resources if any for maintenance mode") kvvm, err := s.KVVM(ctx) From 6ed669452d09c2a08a470a16aa227ee181ef2955 Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Mon, 25 Aug 2025 15:48:22 +0300 Subject: [PATCH 29/39] fixes Signed-off-by: Daniil Loktev --- .../pkg/controller/vm/internal/maintenance.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go b/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go index c1f860ac58..f3dd66833e 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go @@ -30,6 +30,7 @@ import ( "github.com/deckhouse/virtualization-controller/pkg/controller/reconciler" "github.com/deckhouse/virtualization-controller/pkg/controller/vm/internal/state" "github.com/deckhouse/virtualization-controller/pkg/logger" + virtv2 "github.com/deckhouse/virtualization/api/core/v1alpha2" "github.com/deckhouse/virtualization/api/core/v1alpha2/vmcondition" ) @@ -93,7 +94,7 @@ func (h *MaintenanceHandler) Handle(ctx context.Context, s state.VirtualMachineS } // If VM is not stopped yet, wait for it to stop (SyncPowerStateHandler will handle stopping) - if kvvmi != nil { + if kvvmi != nil || changed.Status.Phase != virtv2.MachineStopped { log.Info("VM is still running, waiting for shutdown in maintenance mode") return reconcile.Result{}, nil } From ad1f841efeccd750c346b9486de10f60bcc921cd Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Mon, 25 Aug 2025 16:07:49 +0300 Subject: [PATCH 30/39] fixes Signed-off-by: Daniil Loktev --- .../pkg/controller/vm/internal/maintenance.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go b/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go index f3dd66833e..1abb2a86eb 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go @@ -94,13 +94,13 @@ func (h *MaintenanceHandler) Handle(ctx context.Context, s state.VirtualMachineS } // If VM is not stopped yet, wait for it to stop (SyncPowerStateHandler will handle stopping) - if kvvmi != nil || changed.Status.Phase != virtv2.MachineStopped { + if kvvmi != nil { log.Info("VM is still running, waiting for shutdown in maintenance mode") return reconcile.Result{}, nil } // Hide all other conditions when in maintenance mode - if changed.Status.Conditions != nil { + if changed.Status.Conditions != nil && changed.Status.Phase == virtv2.MachineStopped { var newConditions []metav1.Condition for _, cond := range changed.Status.Conditions { if vmcondition.Type(cond.Type) == vmcondition.TypeMaintenance { From 6c3d8a1738faaf8221ec545148d6c09ed44bf54f Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Mon, 25 Aug 2025 16:36:37 +0300 Subject: [PATCH 31/39] fixes Signed-off-by: Daniil Loktev --- .../pkg/controller/vm/internal/maintenance.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go b/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go index 1abb2a86eb..f7de6b7831 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go @@ -100,7 +100,7 @@ func (h *MaintenanceHandler) Handle(ctx context.Context, s state.VirtualMachineS } // Hide all other conditions when in maintenance mode - if changed.Status.Conditions != nil && changed.Status.Phase == virtv2.MachineStopped { + if changed.Status.Conditions != nil { var newConditions []metav1.Condition for _, cond := range changed.Status.Conditions { if vmcondition.Type(cond.Type) == vmcondition.TypeMaintenance { @@ -114,26 +114,26 @@ func (h *MaintenanceHandler) Handle(ctx context.Context, s state.VirtualMachineS kvvm, err := s.KVVM(ctx) if err != nil { - return reconcile.Result{}, fmt.Errorf("get KVVM: %w", err) + return reconcile.Result{}, fmt.Errorf("%w: get KVVM: %v", reconciler.ErrStopHandlerChain, err) } if kvvm != nil { log.Info("Deleting KVVM for maintenance mode") err = object.CleanupObject(ctx, h.client, kvvm) if err != nil { - return reconcile.Result{}, fmt.Errorf("delete KVVM: %w", err) + return reconcile.Result{}, fmt.Errorf("%w: delete KVVM: %v", reconciler.ErrStopHandlerChain, err) } } pods, err := s.Pods(ctx) if err != nil { - return reconcile.Result{}, fmt.Errorf("get pods: %w", err) + return reconcile.Result{}, fmt.Errorf("%w: get pods: %v", reconciler.ErrStopHandlerChain, err) } if pods != nil && len(pods.Items) > 0 { log.Info("Deleting pods for maintenance mode") for i := range pods.Items { err = object.CleanupObject(ctx, h.client, &pods.Items[i]) if err != nil { - return reconcile.Result{}, fmt.Errorf("delete pod: %w", err) + return reconcile.Result{}, fmt.Errorf("%w: delete pod: %v", reconciler.ErrStopHandlerChain, err) } } } From 013bceb01608e949a807f2f53bf3581bf6e8ba66 Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Mon, 25 Aug 2025 16:46:14 +0300 Subject: [PATCH 32/39] fixes Signed-off-by: Daniil Loktev --- .../pkg/controller/vm/internal/maintenance.go | 1 - 1 file changed, 1 deletion(-) diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go b/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go index f7de6b7831..bc4a33b5c3 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go @@ -30,7 +30,6 @@ import ( "github.com/deckhouse/virtualization-controller/pkg/controller/reconciler" "github.com/deckhouse/virtualization-controller/pkg/controller/vm/internal/state" "github.com/deckhouse/virtualization-controller/pkg/logger" - virtv2 "github.com/deckhouse/virtualization/api/core/v1alpha2" "github.com/deckhouse/virtualization/api/core/v1alpha2/vmcondition" ) From 888f4c63223b2c71c464c77402c03e7930f1ea82 Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Mon, 25 Aug 2025 17:01:54 +0300 Subject: [PATCH 33/39] remove annotation Signed-off-by: Daniil Loktev --- .../pkg/common/annotations/annotations.go | 3 --- .../pkg/controller/vm/internal/maintenance.go | 21 ------------------- 2 files changed, 24 deletions(-) diff --git a/images/virtualization-artifact/pkg/common/annotations/annotations.go b/images/virtualization-artifact/pkg/common/annotations/annotations.go index 88b9757ebb..98271bd7b4 100644 --- a/images/virtualization-artifact/pkg/common/annotations/annotations.go +++ b/images/virtualization-artifact/pkg/common/annotations/annotations.go @@ -88,9 +88,6 @@ const ( // AnnVMRestartRequested is an annotation on KVVM that represents a request to restart a virtual machine. AnnVMRestartRequested = AnnAPIGroupV + "/vm-restart-requested" - // DELETE ME - AnnVMMaintenance = AnnAPIGroupV + "/maintenance" - // AnnVMOPWorkloadUpdate is an annotation on vmop that represents a vmop created by workload-updater controller. AnnVMOPWorkloadUpdate = AnnAPIGroupV + "/workload-update" AnnVMOPWorkloadUpdateImage = AnnAPIGroupV + "/workload-update-image" diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go b/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go index bc4a33b5c3..b8319267e9 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go @@ -53,27 +53,6 @@ func (h *MaintenanceHandler) Handle(ctx context.Context, s state.VirtualMachineS } changed := s.VirtualMachine().Changed() - // DELETE ME - current := s.VirtualMachine().Current() - switch changed.Annotations[annotations.AnnVMMaintenance] { - case "true": - // Set maintenance condition if annotation is present - cb := conditions.NewConditionBuilder(vmcondition.TypeMaintenance). - Generation(current.GetGeneration()). - Status(metav1.ConditionTrue). - Reason(vmcondition.ReasonMaintenanceRestore). - Message("VM is in maintenance mode") - conditions.SetCondition(cb, &changed.Status.Conditions) - case "false": - // Explicitly set maintenance to false if annotation is "false" - cb := conditions.NewConditionBuilder(vmcondition.TypeMaintenance). - Generation(current.GetGeneration()). - Status(metav1.ConditionFalse). - Reason(vmcondition.ReasonMaintenanceRestore). - Message("VM maintenance mode disabled") - conditions.SetCondition(cb, &changed.Status.Conditions) - } - maintenance, _ := conditions.GetCondition(vmcondition.TypeMaintenance, changed.Status.Conditions) if maintenance.Status == metav1.ConditionFalse { From 8eda7c54e27850c5d6a587666b43a232e63df85e Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Mon, 25 Aug 2025 21:03:53 +0300 Subject: [PATCH 34/39] fixes Signed-off-by: Daniil Loktev --- .../pkg/controller/vm/internal/maintenance.go | 9 ++++----- .../vm/internal/validators/maintenance_validator.go | 1 - 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go b/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go index b8319267e9..a0b957e36a 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go @@ -24,7 +24,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/reconcile" - "github.com/deckhouse/virtualization-controller/pkg/common/annotations" "github.com/deckhouse/virtualization-controller/pkg/common/object" "github.com/deckhouse/virtualization-controller/pkg/controller/conditions" "github.com/deckhouse/virtualization-controller/pkg/controller/reconciler" @@ -92,26 +91,26 @@ func (h *MaintenanceHandler) Handle(ctx context.Context, s state.VirtualMachineS kvvm, err := s.KVVM(ctx) if err != nil { - return reconcile.Result{}, fmt.Errorf("%w: get KVVM: %v", reconciler.ErrStopHandlerChain, err) + return reconcile.Result{}, fmt.Errorf("%w: get KVVM: %v", reconciler.ErrStopHandlerChain, err.Error()) } if kvvm != nil { log.Info("Deleting KVVM for maintenance mode") err = object.CleanupObject(ctx, h.client, kvvm) if err != nil { - return reconcile.Result{}, fmt.Errorf("%w: delete KVVM: %v", reconciler.ErrStopHandlerChain, err) + return reconcile.Result{}, fmt.Errorf("%w: delete KVVM: %v", reconciler.ErrStopHandlerChain, err.Error()) } } pods, err := s.Pods(ctx) if err != nil { - return reconcile.Result{}, fmt.Errorf("%w: get pods: %v", reconciler.ErrStopHandlerChain, err) + return reconcile.Result{}, fmt.Errorf("%w: get pods: %v", reconciler.ErrStopHandlerChain, err.Error()) } if pods != nil && len(pods.Items) > 0 { log.Info("Deleting pods for maintenance mode") for i := range pods.Items { err = object.CleanupObject(ctx, h.client, &pods.Items[i]) if err != nil { - return reconcile.Result{}, fmt.Errorf("%w: delete pod: %v", reconciler.ErrStopHandlerChain, err) + return reconcile.Result{}, fmt.Errorf("%w: delete pod: %v", reconciler.ErrStopHandlerChain, err.Error()) } } } diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/validators/maintenance_validator.go b/images/virtualization-artifact/pkg/controller/vm/internal/validators/maintenance_validator.go index 951ccdbd4c..33d5d8281d 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/validators/maintenance_validator.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/validators/maintenance_validator.go @@ -52,4 +52,3 @@ func (v *MaintenanceValidator) ValidateUpdate(_ context.Context, oldVM, newVM *v return nil, nil } - From a2cd4779639407ef7247cfe034e69a9fe3a3f296 Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Mon, 25 Aug 2025 21:10:16 +0300 Subject: [PATCH 35/39] fixes Signed-off-by: Daniil Loktev --- .../pkg/common/annotations/annotations.go | 3 ++ .../pkg/controller/vm/internal/maintenance.go | 30 ++++++++++++++++--- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/images/virtualization-artifact/pkg/common/annotations/annotations.go b/images/virtualization-artifact/pkg/common/annotations/annotations.go index 98271bd7b4..88b9757ebb 100644 --- a/images/virtualization-artifact/pkg/common/annotations/annotations.go +++ b/images/virtualization-artifact/pkg/common/annotations/annotations.go @@ -88,6 +88,9 @@ const ( // AnnVMRestartRequested is an annotation on KVVM that represents a request to restart a virtual machine. AnnVMRestartRequested = AnnAPIGroupV + "/vm-restart-requested" + // DELETE ME + AnnVMMaintenance = AnnAPIGroupV + "/maintenance" + // AnnVMOPWorkloadUpdate is an annotation on vmop that represents a vmop created by workload-updater controller. AnnVMOPWorkloadUpdate = AnnAPIGroupV + "/workload-update" AnnVMOPWorkloadUpdateImage = AnnAPIGroupV + "/workload-update-image" diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go b/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go index a0b957e36a..23492bbc39 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go @@ -24,6 +24,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/reconcile" + "github.com/deckhouse/virtualization-controller/pkg/common/annotations" "github.com/deckhouse/virtualization-controller/pkg/common/object" "github.com/deckhouse/virtualization-controller/pkg/controller/conditions" "github.com/deckhouse/virtualization-controller/pkg/controller/reconciler" @@ -52,6 +53,27 @@ func (h *MaintenanceHandler) Handle(ctx context.Context, s state.VirtualMachineS } changed := s.VirtualMachine().Changed() + // DELETE ME + current := s.VirtualMachine().Current() + switch changed.Annotations[annotations.AnnVMMaintenance] { + case "true": + // Set maintenance condition if annotation is present + cb := conditions.NewConditionBuilder(vmcondition.TypeMaintenance). + Generation(current.GetGeneration()). + Status(metav1.ConditionTrue). + Reason(vmcondition.ReasonMaintenanceRestore). + Message("VM is in maintenance mode") + conditions.SetCondition(cb, &changed.Status.Conditions) + case "false": + // Explicitly set maintenance to false if annotation is "false" + cb := conditions.NewConditionBuilder(vmcondition.TypeMaintenance). + Generation(current.GetGeneration()). + Status(metav1.ConditionFalse). + Reason(vmcondition.ReasonMaintenanceRestore). + Message("VM maintenance mode disabled") + conditions.SetCondition(cb, &changed.Status.Conditions) + } + maintenance, _ := conditions.GetCondition(vmcondition.TypeMaintenance, changed.Status.Conditions) if maintenance.Status == metav1.ConditionFalse { @@ -91,26 +113,26 @@ func (h *MaintenanceHandler) Handle(ctx context.Context, s state.VirtualMachineS kvvm, err := s.KVVM(ctx) if err != nil { - return reconcile.Result{}, fmt.Errorf("%w: get KVVM: %v", reconciler.ErrStopHandlerChain, err.Error()) + return reconcile.Result{}, fmt.Errorf("%w: get KVVM: %w", reconciler.ErrStopHandlerChain, err) } if kvvm != nil { log.Info("Deleting KVVM for maintenance mode") err = object.CleanupObject(ctx, h.client, kvvm) if err != nil { - return reconcile.Result{}, fmt.Errorf("%w: delete KVVM: %v", reconciler.ErrStopHandlerChain, err.Error()) + return reconcile.Result{}, fmt.Errorf("%w: delete KVVM: %w", reconciler.ErrStopHandlerChain, err) } } pods, err := s.Pods(ctx) if err != nil { - return reconcile.Result{}, fmt.Errorf("%w: get pods: %v", reconciler.ErrStopHandlerChain, err.Error()) + return reconcile.Result{}, fmt.Errorf("%w: get pods: %w", reconciler.ErrStopHandlerChain, err) } if pods != nil && len(pods.Items) > 0 { log.Info("Deleting pods for maintenance mode") for i := range pods.Items { err = object.CleanupObject(ctx, h.client, &pods.Items[i]) if err != nil { - return reconcile.Result{}, fmt.Errorf("%w: delete pod: %v", reconciler.ErrStopHandlerChain, err.Error()) + return reconcile.Result{}, fmt.Errorf("%w: delete pod: %w", reconciler.ErrStopHandlerChain, err) } } } From ed14f1ee202320fdd829f729b03e67fbb01a15fe Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Mon, 25 Aug 2025 21:24:29 +0300 Subject: [PATCH 36/39] remove annotation Signed-off-by: Daniil Loktev --- .../pkg/common/annotations/annotations.go | 3 --- .../pkg/controller/vm/internal/maintenance.go | 21 ------------------- 2 files changed, 24 deletions(-) diff --git a/images/virtualization-artifact/pkg/common/annotations/annotations.go b/images/virtualization-artifact/pkg/common/annotations/annotations.go index 88b9757ebb..98271bd7b4 100644 --- a/images/virtualization-artifact/pkg/common/annotations/annotations.go +++ b/images/virtualization-artifact/pkg/common/annotations/annotations.go @@ -88,9 +88,6 @@ const ( // AnnVMRestartRequested is an annotation on KVVM that represents a request to restart a virtual machine. AnnVMRestartRequested = AnnAPIGroupV + "/vm-restart-requested" - // DELETE ME - AnnVMMaintenance = AnnAPIGroupV + "/maintenance" - // AnnVMOPWorkloadUpdate is an annotation on vmop that represents a vmop created by workload-updater controller. AnnVMOPWorkloadUpdate = AnnAPIGroupV + "/workload-update" AnnVMOPWorkloadUpdateImage = AnnAPIGroupV + "/workload-update-image" diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go b/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go index 23492bbc39..88eedd6d4c 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go @@ -53,27 +53,6 @@ func (h *MaintenanceHandler) Handle(ctx context.Context, s state.VirtualMachineS } changed := s.VirtualMachine().Changed() - // DELETE ME - current := s.VirtualMachine().Current() - switch changed.Annotations[annotations.AnnVMMaintenance] { - case "true": - // Set maintenance condition if annotation is present - cb := conditions.NewConditionBuilder(vmcondition.TypeMaintenance). - Generation(current.GetGeneration()). - Status(metav1.ConditionTrue). - Reason(vmcondition.ReasonMaintenanceRestore). - Message("VM is in maintenance mode") - conditions.SetCondition(cb, &changed.Status.Conditions) - case "false": - // Explicitly set maintenance to false if annotation is "false" - cb := conditions.NewConditionBuilder(vmcondition.TypeMaintenance). - Generation(current.GetGeneration()). - Status(metav1.ConditionFalse). - Reason(vmcondition.ReasonMaintenanceRestore). - Message("VM maintenance mode disabled") - conditions.SetCondition(cb, &changed.Status.Conditions) - } - maintenance, _ := conditions.GetCondition(vmcondition.TypeMaintenance, changed.Status.Conditions) if maintenance.Status == metav1.ConditionFalse { From a9993c0da0caf5b86e3724f3ac76661521b9534f Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Mon, 25 Aug 2025 21:25:23 +0300 Subject: [PATCH 37/39] fixes Signed-off-by: Daniil Loktev --- .../pkg/controller/vm/internal/maintenance.go | 1 - 1 file changed, 1 deletion(-) diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go b/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go index 88eedd6d4c..8d235959fb 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go @@ -24,7 +24,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/reconcile" - "github.com/deckhouse/virtualization-controller/pkg/common/annotations" "github.com/deckhouse/virtualization-controller/pkg/common/object" "github.com/deckhouse/virtualization-controller/pkg/controller/conditions" "github.com/deckhouse/virtualization-controller/pkg/controller/reconciler" From 251798ac724262634994e5c62bf36157d599ef6c Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Tue, 26 Aug 2025 15:24:22 +0300 Subject: [PATCH 38/39] fixes Signed-off-by: Daniil Loktev --- .../pkg/controller/vm/internal/maintenance.go | 12 ++---------- .../vm/internal/validators/maintenance_validator.go | 2 +- .../pkg/controller/vm/vm_controller.go | 2 +- .../pkg/controller/vm/vm_webhook.go | 2 +- 4 files changed, 5 insertions(+), 13 deletions(-) diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go b/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go index 8d235959fb..ab1a7d4d85 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/maintenance.go @@ -1,5 +1,5 @@ /* -Copyright 2024 Flant JSC +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. @@ -77,15 +77,7 @@ func (h *MaintenanceHandler) Handle(ctx context.Context, s state.VirtualMachineS } // Hide all other conditions when in maintenance mode - if changed.Status.Conditions != nil { - var newConditions []metav1.Condition - for _, cond := range changed.Status.Conditions { - if vmcondition.Type(cond.Type) == vmcondition.TypeMaintenance { - newConditions = append(newConditions, cond) - } - } - changed.Status.Conditions = newConditions - } + changed.Status.Conditions = []metav1.Condition{maintenance} log.Info("VM is stopped, cleaning up resources if any for maintenance mode") diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/validators/maintenance_validator.go b/images/virtualization-artifact/pkg/controller/vm/internal/validators/maintenance_validator.go index 33d5d8281d..b5b9f0a067 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/validators/maintenance_validator.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/validators/maintenance_validator.go @@ -1,5 +1,5 @@ /* -Copyright 2024 Flant JSC +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. diff --git a/images/virtualization-artifact/pkg/controller/vm/vm_controller.go b/images/virtualization-artifact/pkg/controller/vm/vm_controller.go index 7c948ae826..cb89f8451d 100644 --- a/images/virtualization-artifact/pkg/controller/vm/vm_controller.go +++ b/images/virtualization-artifact/pkg/controller/vm/vm_controller.go @@ -95,7 +95,7 @@ func SetupController( if err = builder.WebhookManagedBy(mgr). For(&v1alpha2.VirtualMachine{}). - WithValidator(NewValidator(ipam.New(), client, blockDeviceService, log)). + WithValidator(NewValidator(client, blockDeviceService, log)). WithDefaulter(NewDefaulter(client, vmClassService, log)). Complete(); err != nil { return err diff --git a/images/virtualization-artifact/pkg/controller/vm/vm_webhook.go b/images/virtualization-artifact/pkg/controller/vm/vm_webhook.go index c36832b83a..339c83b0ef 100644 --- a/images/virtualization-artifact/pkg/controller/vm/vm_webhook.go +++ b/images/virtualization-artifact/pkg/controller/vm/vm_webhook.go @@ -42,7 +42,7 @@ type Validator struct { log *log.Logger } -func NewValidator(ipam internal.IPAM, client client.Client, service *service.BlockDeviceService, log *log.Logger) *Validator { +func NewValidator(client client.Client, service *service.BlockDeviceService, log *log.Logger) *Validator { return &Validator{ validators: []VirtualMachineValidator{ validators.NewMaintenanceValidator(), From 8c9adc2532e744233938d1f2fdb3577829967603 Mon Sep 17 00:00:00 2001 From: Daniil Loktev Date: Tue, 26 Aug 2025 15:28:55 +0300 Subject: [PATCH 39/39] fixes Signed-off-by: Daniil Loktev --- images/virtualization-artifact/pkg/controller/vm/vm_webhook.go | 1 - 1 file changed, 1 deletion(-) diff --git a/images/virtualization-artifact/pkg/controller/vm/vm_webhook.go b/images/virtualization-artifact/pkg/controller/vm/vm_webhook.go index 339c83b0ef..049aa42055 100644 --- a/images/virtualization-artifact/pkg/controller/vm/vm_webhook.go +++ b/images/virtualization-artifact/pkg/controller/vm/vm_webhook.go @@ -26,7 +26,6 @@ import ( "github.com/deckhouse/deckhouse/pkg/log" "github.com/deckhouse/virtualization-controller/pkg/controller/service" - "github.com/deckhouse/virtualization-controller/pkg/controller/vm/internal" "github.com/deckhouse/virtualization-controller/pkg/controller/vm/internal/defaulter" "github.com/deckhouse/virtualization-controller/pkg/controller/vm/internal/validators" virtv2 "github.com/deckhouse/virtualization/api/core/v1alpha2"