diff --git a/api/core/v1alpha2/vmcondition/condition.go b/api/core/v1alpha2/vmcondition/condition.go index e99b6416a..025aa982a 100644 --- a/api/core/v1alpha2/vmcondition/condition.go +++ b/api/core/v1alpha2/vmcondition/condition.go @@ -23,17 +23,23 @@ func (t Type) String() string { } const ( - TypeIPAddressReady Type = "VirtualMachineIPAddressReady" - TypeClassReady Type = "VirtualMachineClassReady" - TypeBlockDevicesReady Type = "BlockDevicesReady" - TypeRunning Type = "Running" - TypeMigrating Type = "Migrating" - TypeMigratable Type = "Migratable" - TypePodStarted Type = "PodStarted" - TypeProvisioningReady Type = "ProvisioningReady" - TypeAgentReady Type = "AgentReady" - TypeAgentVersionNotSupported Type = "AgentVersionNotSupported" - TypeConfigurationApplied Type = "ConfigurationApplied" + TypeIPAddressReady Type = "VirtualMachineIPAddressReady" + TypeClassReady Type = "VirtualMachineClassReady" + TypeBlockDevicesReady Type = "BlockDevicesReady" + TypeRunning Type = "Running" + TypeMigrating Type = "Migrating" + TypeMigratable Type = "Migratable" + TypePodStarted Type = "PodStarted" + TypeProvisioningReady Type = "ProvisioningReady" + TypeAgentReady Type = "AgentReady" + TypeAgentVersionNotSupported Type = "AgentVersionNotSupported" + + // TypeConfigurationApplied indicates status to the synchronization of the KVVM configuration. + TypeConfigurationApplied Type = "ConfigurationApplied" + + // TypePowerConfigurationApplied indicates status to the synchronization of the power state. + TypePowerConfigurationApplied Type = "PowerConfigurationApplied" + TypeAwaitingRestartToApplyConfiguration Type = "AwaitingRestartToApplyConfiguration" TypeFilesystemReady Type = "FilesystemReady" TypeSizingPolicyMatched Type = "SizingPolicyMatched" diff --git a/images/virtualization-artifact/pkg/common/annotations/annotations.go b/images/virtualization-artifact/pkg/common/annotations/annotations.go index a7221cd17..f62928121 100644 --- a/images/virtualization-artifact/pkg/common/annotations/annotations.go +++ b/images/virtualization-artifact/pkg/common/annotations/annotations.go @@ -70,6 +70,8 @@ const ( AppKubernetesManagedByLabel = "app.kubernetes.io/managed-by" // AppKubernetesComponentLabel is the Kubernetes recommended component label AppKubernetesComponentLabel = "app.kubernetes.io/component" + + NeedStartVMAnnotation = AnnAPIGroup + "/need-start-vm" ) // AddAnnotation adds an annotation to an object diff --git a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go index 3795ecf07..f788ead9a 100644 --- a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go +++ b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go @@ -126,26 +126,8 @@ func (b *KVVM) SetCPUModel(class *virtv2.VirtualMachineClass) error { return nil } -func (b *KVVM) SetRunPolicy(runPolicy virtv2.RunPolicy) error { - switch runPolicy { - case virtv2.AlwaysOnPolicy: - b.Resource.Spec.RunStrategy = pointer.GetPointer(virtv1.RunStrategyAlways) - case virtv2.AlwaysOffPolicy: - b.Resource.Spec.RunStrategy = pointer.GetPointer(virtv1.RunStrategyHalted) - case virtv2.ManualPolicy: - if !b.ResourceExists { - // initialize only - b.Resource.Spec.RunStrategy = pointer.GetPointer(virtv1.RunStrategyManual) - } - case virtv2.AlwaysOnUnlessStoppedManually: - if !b.ResourceExists { - // initialize only - b.Resource.Spec.RunStrategy = pointer.GetPointer(virtv1.RunStrategyAlways) - } - default: - return fmt.Errorf("unexpected runPolicy %s. %w", runPolicy, common.ErrUnknownValue) - } - return nil +func (b *KVVM) SetRunPolicy() { + b.Resource.Spec.RunStrategy = pointer.GetPointer(virtv1.RunStrategyManual) } func (b *KVVM) SetNodeSelector(vmNodeSelector, classNodeSelector map[string]string) { diff --git a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_utils.go b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_utils.go index 9b963dc8c..b5670e714 100644 --- a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_utils.go +++ b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_utils.go @@ -24,6 +24,7 @@ import ( virtv1 "kubevirt.io/api/core/v1" "github.com/deckhouse/virtualization-controller/pkg/common" + "github.com/deckhouse/virtualization-controller/pkg/common/annotations" "github.com/deckhouse/virtualization-controller/pkg/common/imageformat" "github.com/deckhouse/virtualization-controller/pkg/common/pointer" "github.com/deckhouse/virtualization-controller/pkg/controller/ipam" @@ -71,9 +72,6 @@ func ApplyVirtualMachineSpec( class *virtv2.VirtualMachineClass, ipAddress string, ) error { - if err := kvvm.SetRunPolicy(vm.Spec.RunPolicy); err != nil { - return err - } if err := kvvm.SetOsType(vm.Spec.OsType); err != nil { return err } @@ -84,6 +82,11 @@ func ApplyVirtualMachineSpec( return err } + kvvm.SetRunPolicy() + if vm.Spec.RunPolicy == virtv2.AlwaysOnUnlessStoppedManually { + kvvm.SetKVVMIAnnotation(annotations.NeedStartVMAnnotation, "True") + } + kvvm.SetNetworkInterface(NetworkInterfaceName) kvvm.SetTablet("default-0") kvvm.SetNodeSelector(vm.Spec.NodeSelector, class.Spec.NodeSelector.MatchLabels) 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 99d9fbacd..4884b31c0 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 @@ -20,36 +20,28 @@ import ( "context" "fmt" - corev1 "k8s.io/api/core/v1" k8serrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/tools/record" virtv1 "kubevirt.io/api/core/v1" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/reconcile" - kvvmutil "github.com/deckhouse/virtualization-controller/pkg/common/kvvm" - "github.com/deckhouse/virtualization-controller/pkg/controller/conditions" + "github.com/deckhouse/virtualization-controller/pkg/common/annotations" "github.com/deckhouse/virtualization-controller/pkg/controller/powerstate" - "github.com/deckhouse/virtualization-controller/pkg/controller/service" "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" ) const nameSyncPowerStateHandler = "SyncPowerStateHandler" -func NewSyncPowerStateHandler(client client.Client, recorder record.EventRecorder) *SyncPowerStateHandler { +func NewSyncPowerStateHandler(client client.Client) *SyncPowerStateHandler { return &SyncPowerStateHandler{ - client: client, - recorder: recorder, + client: client, } } type SyncPowerStateHandler struct { - client client.Client - recorder record.EventRecorder + client client.Client } func (h *SyncPowerStateHandler) Handle(ctx context.Context, s state.VirtualMachineState) (reconcile.Result, error) { @@ -57,46 +49,27 @@ func (h *SyncPowerStateHandler) Handle(ctx context.Context, s state.VirtualMachi return reconcile.Result{}, nil } - current := s.VirtualMachine().Current() changed := s.VirtualMachine().Changed() - cbConfApplied := conditions.NewConditionBuilder(vmcondition.TypeConfigurationApplied). - Generation(current.GetGeneration()). - Status(metav1.ConditionUnknown). - Reason(conditions.ReasonUnknown) - - defer func() { - conditions.SetCondition(cbConfApplied, &changed.Status.Conditions) - }() - kvvm, err := s.KVVM(ctx) if err != nil { - cbConfApplied. - Status(metav1.ConditionFalse). - Reason(vmcondition.ReasonConfigurationNotApplied). - Message(service.CapitalizeFirstLetter(err.Error()) + ".") return reconcile.Result{}, err } - err = h.syncPowerState(ctx, s, kvvm, &changed.Spec) + err = h.syncPowerState(ctx, s, kvvm, changed.Spec.RunPolicy) if err != nil { err = fmt.Errorf("failed to sync powerstate: %w", err) - h.recorder.Event(current, corev1.EventTypeWarning, virtv2.ReasonErrVmNotSynced, err.Error()) - cbConfApplied. - Status(metav1.ConditionFalse). - Reason(vmcondition.ReasonConfigurationNotApplied). - Message(service.CapitalizeFirstLetter(err.Error()) + ".") } return reconcile.Result{}, err } // syncPowerState enforces runPolicy on the underlying KVVM. -func (h *SyncPowerStateHandler) syncPowerState(ctx context.Context, s state.VirtualMachineState, kvvm *virtv1.VirtualMachine, effectiveSpec *virtv2.VirtualMachineSpec) error { +func (h *SyncPowerStateHandler) syncPowerState(ctx context.Context, s state.VirtualMachineState, kvvm *virtv1.VirtualMachine, vmRunPolicy virtv2.RunPolicy) error { log := logger.FromContext(ctx) if kvvm == nil { - return nil + return fmt.Errorf("kvvm is nil") } kvvmi, err := s.KVVMI(ctx) @@ -104,7 +77,6 @@ func (h *SyncPowerStateHandler) syncPowerState(ctx context.Context, s state.Virt return fmt.Errorf("find the internal virtual machine instance: %w", err) } - vmRunPolicy := effectiveSpec.RunPolicy var shutdownInfo powerstate.ShutdownInfo s.Shared(func(s *state.Shared) { shutdownInfo = s.ShutdownInfo @@ -119,10 +91,8 @@ func (h *SyncPowerStateHandler) syncPowerState(ctx context.Context, s state.Virt return fmt.Errorf("force AlwaysOff: delete KVVMI: %w", err) } } - err = h.ensureRunStrategy(ctx, kvvm, virtv1.RunStrategyHalted) case virtv2.AlwaysOnPolicy: - strategy, _ := kvvm.RunStrategy() - if strategy == virtv1.RunStrategyAlways && kvvmi == nil { + if kvvmi == nil { if err = powerstate.StartVM(ctx, h.client, kvvm); err != nil { return fmt.Errorf("failed to start VM: %w", err) } @@ -145,13 +115,14 @@ func (h *SyncPowerStateHandler) syncPowerState(ctx context.Context, s state.Virt } } } - - err = h.ensureRunStrategy(ctx, kvvm, virtv1.RunStrategyManual) case virtv2.AlwaysOnUnlessStoppedManually: - strategy, _ := kvvm.RunStrategy() - if strategy == virtv1.RunStrategyAlways && kvvmi == nil { - if err = powerstate.StartVM(ctx, h.client, kvvm); err != nil { - return fmt.Errorf("failed to start VM: %w", err) + if kvvmi == nil { + if kvvm.Annotations[annotations.NeedStartVMAnnotation] != "" { + if err = powerstate.StartVM(ctx, h.client, kvvm); err != nil { + return fmt.Errorf("failed to start VM: %w", err) + } + + annotations.AddAnnotation(kvvm, annotations.NeedStartVMAnnotation, "") } } if kvvmi != nil && kvvmi.DeletionTimestamp == nil { @@ -183,8 +154,6 @@ func (h *SyncPowerStateHandler) syncPowerState(ctx context.Context, s state.Virt } } } - - err = h.ensureRunStrategy(ctx, kvvm, virtv1.RunStrategyManual) case virtv2.ManualPolicy: // Manual policy requires to handle only guest-reset event. // All types of shutdown are a final state. @@ -207,30 +176,6 @@ func (h *SyncPowerStateHandler) syncPowerState(ctx context.Context, s state.Virt } } } - - err = h.ensureRunStrategy(ctx, kvvm, virtv1.RunStrategyManual) - } - - if err != nil { - return fmt.Errorf("enforce runPolicy %s: %w", vmRunPolicy, err) - } - - return nil -} - -func (h *SyncPowerStateHandler) ensureRunStrategy(ctx context.Context, kvvm *virtv1.VirtualMachine, desiredRunStrategy virtv1.VirtualMachineRunStrategy) error { - if kvvm == nil { - return nil - } - kvvmRunStrategy := kvvmutil.GetRunStrategy(kvvm) - - if kvvmRunStrategy == desiredRunStrategy { - return nil - } - patch := kvvmutil.PatchRunStrategy(desiredRunStrategy) - err := h.client.Patch(ctx, kvvm, patch) - if err != nil { - return fmt.Errorf("patch KVVM with runStrategy %s: %w", desiredRunStrategy, err) } return nil diff --git a/images/virtualization-artifact/pkg/controller/vm/vm_controller.go b/images/virtualization-artifact/pkg/controller/vm/vm_controller.go index 95f104e5c..6859ce4cd 100644 --- a/images/virtualization-artifact/pkg/controller/vm/vm_controller.go +++ b/images/virtualization-artifact/pkg/controller/vm/vm_controller.go @@ -27,6 +27,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/metrics" "github.com/deckhouse/deckhouse/pkg/log" + "github.com/deckhouse/virtualization-controller/pkg/controller/ipam" "github.com/deckhouse/virtualization-controller/pkg/controller/service" "github.com/deckhouse/virtualization-controller/pkg/controller/vm/internal" @@ -63,7 +64,7 @@ func SetupController( internal.NewPodHandler(client), internal.NewSizePolicyHandler(), internal.NewSyncKvvmHandler(dvcrSettings, client, recorder), - internal.NewSyncPowerStateHandler(client, recorder), + internal.NewSyncPowerStateHandler(client), internal.NewSyncMetadataHandler(client), internal.NewLifeCycleHandler(client, recorder), internal.NewStatisticHandler(client),