From 74065bb82452575aa527f2a00d759e50c85ab03f Mon Sep 17 00:00:00 2001 From: Maksim Fedotov Date: Fri, 24 Oct 2025 18:11:55 +0300 Subject: [PATCH 01/10] 15k vm test: do not merge Signed-off-by: Maksim Fedotov --- .keep | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 .keep diff --git a/.keep b/.keep new file mode 100644 index 0000000000..e69de29bb2 From f135a665e9c57d97c9156b4f53620644170c17ca Mon Sep 17 00:00:00 2001 From: Maksim Fedotov Date: Mon, 27 Oct 2025 13:38:42 +0300 Subject: [PATCH 02/10] enable PPROF port Signed-off-by: Maksim Fedotov --- templates/virtualization-controller/_helpers.tpl | 2 -- 1 file changed, 2 deletions(-) diff --git a/templates/virtualization-controller/_helpers.tpl b/templates/virtualization-controller/_helpers.tpl index 764e0d2df8..a4594f35d4 100644 --- a/templates/virtualization-controller/_helpers.tpl +++ b/templates/virtualization-controller/_helpers.tpl @@ -94,10 +94,8 @@ true {{- end }} - name: METRICS_BIND_ADDRESS value: "127.0.0.1:8080" -{{- if eq (include "moduleLogLevel" .) "debug" }} - name: PPROF_BIND_ADDRESS value: ":8081" -{{- end }} - name: FIRMWARE_IMAGE value: {{ include "helm_lib_module_image" (list . "virtLauncher") }} - name: CLUSTER_UUID From 8de8437f194e00ef7e2534f5f448df0893f489c2 Mon Sep 17 00:00:00 2001 From: Maksim Fedotov Date: Mon, 27 Oct 2025 19:32:21 +0300 Subject: [PATCH 03/10] set KIND_ROUTE_WATCHER to ebpf Signed-off-by: Maksim Fedotov --- templates/vm-route-forge/daemonset.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/templates/vm-route-forge/daemonset.yaml b/templates/vm-route-forge/daemonset.yaml index 8b0c20cf53..547fe4a5be 100644 --- a/templates/vm-route-forge/daemonset.yaml +++ b/templates/vm-route-forge/daemonset.yaml @@ -83,6 +83,8 @@ spec: {{- end }} - name: HEALTH_PROBE_BIND_ADDRESS value: ":8118" + - name: KIND_ROUTE_WATCHER + value: "ebpf" resources: requests: {{- include "helm_lib_module_ephemeral_storage_only_logs" . | nindent 14 }} From d4daf52489cc5036e434c6285e4af1913cd939ed Mon Sep 17 00:00:00 2001 From: Maksim Fedotov Date: Mon, 27 Oct 2025 19:34:00 +0300 Subject: [PATCH 04/10] set replicas to 1 for virtualization-controller Signed-off-by: Maksim Fedotov --- templates/virtualization-controller/deployment.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/virtualization-controller/deployment.yaml b/templates/virtualization-controller/deployment.yaml index d0cf32075b..3d87681c17 100644 --- a/templates/virtualization-controller/deployment.yaml +++ b/templates/virtualization-controller/deployment.yaml @@ -54,7 +54,7 @@ metadata: namespace: d8-{{ .Chart.Name }} {{- include "helm_lib_module_labels" (list . (dict "app" "virtualization-controller")) | nindent 2 }} spec: - replicas: {{ include "helm_lib_is_ha_to_value" (list . 3 1) }} + replicas: 1 {{- if (include "helm_lib_ha_enabled" .) }} strategy: type: RollingUpdate From 65f2f429ff87d61eb3596c3e63e57fc2bf17e28c Mon Sep 17 00:00:00 2001 From: Isteb4k Date: Thu, 23 Oct 2025 18:17:43 +0200 Subject: [PATCH 05/10] cherry-pick commits from #1619 for vi on volumesnapshot Signed-off-by: Isteb4k Signed-off-by: Maksim Fedotov feat(vd): create pvc using volume snapshot Signed-off-by: Isteb4k Signed-off-by: Maksim Fedotov --- .../pkg/common/annotations/annotations.go | 2 + .../pkg/controller/service/disk_service.go | 45 +++ .../vd/internal/source/object_ref_vi.go | 1 + .../source/step/create_dv_from_vi_step.go | 6 + .../source/step/create_pvc_from_vs_step.go | 258 ++++++++++++++++++ .../source/step/ensure_node_placement.go | 6 + .../internal/source/step/wait_for_dv_step.go | 6 + .../pkg/controller/vi/internal/source/http.go | 43 +++ 8 files changed, 367 insertions(+) create mode 100644 images/virtualization-artifact/pkg/controller/vd/internal/source/step/create_pvc_from_vs_step.go diff --git a/images/virtualization-artifact/pkg/common/annotations/annotations.go b/images/virtualization-artifact/pkg/common/annotations/annotations.go index ba7453b2a8..44218117ef 100644 --- a/images/virtualization-artifact/pkg/common/annotations/annotations.go +++ b/images/virtualization-artifact/pkg/common/annotations/annotations.go @@ -164,6 +164,8 @@ const ( AnnStorageProvisioner = "volume.kubernetes.io/storage-provisioner" AnnStorageProvisionerDeprecated = "volume.beta.kubernetes.io/storage-provisioner" + AnnUseVolumeSnapshot = AnnAPIGroupV + "/use-volume-snapshot" + // AppLabel is the app name label. AppLabel = "app" // CDILabelValue provides a constant for CDI Pod label values. diff --git a/images/virtualization-artifact/pkg/controller/service/disk_service.go b/images/virtualization-artifact/pkg/controller/service/disk_service.go index 4865cf173f..5a79e96856 100644 --- a/images/virtualization-artifact/pkg/controller/service/disk_service.go +++ b/images/virtualization-artifact/pkg/controller/service/disk_service.go @@ -238,6 +238,51 @@ func (s DiskService) CheckProvisioning(ctx context.Context, pvc *corev1.Persiste return nil } +func (s DiskService) CreateVolumeSnapshot(ctx context.Context, pvc *corev1.PersistentVolumeClaim) error { + if pvc == nil || pvc.Status.Phase != corev1.ClaimBound { + return errors.New("pvc not Bound") + } + + anno := make(map[string]string) + if pvc.Spec.StorageClassName != nil && *pvc.Spec.StorageClassName != "" { + anno[annotations.AnnStorageClassName] = *pvc.Spec.StorageClassName + } + + if pvc.Spec.VolumeMode != nil && *pvc.Spec.VolumeMode != "" { + anno[annotations.AnnVolumeMode] = string(*pvc.Spec.VolumeMode) + } + + accessModes := make([]string, 0, len(pvc.Status.AccessModes)) + for _, accessMode := range pvc.Status.AccessModes { + accessModes = append(accessModes, string(accessMode)) + } + + anno[annotations.AnnAccessModes] = strings.Join(accessModes, ",") + + vs := &vsv1.VolumeSnapshot{ + ObjectMeta: metav1.ObjectMeta{ + Name: pvc.Name, + Namespace: pvc.Namespace, + Annotations: anno, + OwnerReferences: []metav1.OwnerReference{ + MakeOwnerReference(pvc), + }, + }, + Spec: vsv1.VolumeSnapshotSpec{ + Source: vsv1.VolumeSnapshotSource{ + PersistentVolumeClaimName: &pvc.Name, + }, + }, + } + + err := s.client.Create(ctx, vs) + if err != nil && !k8serrors.IsAlreadyExists(err) { + return fmt.Errorf("create vs: %w", err) + } + + return nil +} + func (s DiskService) CreatePersistentVolumeClaim(ctx context.Context, pvc *corev1.PersistentVolumeClaim) error { err := s.client.Create(ctx, pvc) if err != nil && !k8serrors.IsAlreadyExists(err) { diff --git a/images/virtualization-artifact/pkg/controller/vd/internal/source/object_ref_vi.go b/images/virtualization-artifact/pkg/controller/vd/internal/source/object_ref_vi.go index ecf5891a25..571c42cb70 100644 --- a/images/virtualization-artifact/pkg/controller/vd/internal/source/object_ref_vi.go +++ b/images/virtualization-artifact/pkg/controller/vd/internal/source/object_ref_vi.go @@ -74,6 +74,7 @@ func (ds ObjectRefVirtualImage) Sync(ctx context.Context, vd *v1alpha2.VirtualDi return steptaker.NewStepTakers[*v1alpha2.VirtualDisk]( step.NewReadyStep(ds.diskService, pvc, cb), step.NewTerminatingStep(pvc), + step.NewCreatePVCFromVSStep(pvc, ds.client, cb), step.NewCreateDataVolumeFromVirtualImageStep(pvc, dv, ds.diskService, ds.client, cb), step.NewEnsureNodePlacementStep(pvc, dv, ds.diskService, ds.client, cb), step.NewWaitForDVStep(pvc, dv, ds.diskService, ds.client, cb), diff --git a/images/virtualization-artifact/pkg/controller/vd/internal/source/step/create_dv_from_vi_step.go b/images/virtualization-artifact/pkg/controller/vd/internal/source/step/create_dv_from_vi_step.go index 959f65ecfd..3ee1e7cc71 100644 --- a/images/virtualization-artifact/pkg/controller/vd/internal/source/step/create_dv_from_vi_step.go +++ b/images/virtualization-artifact/pkg/controller/vd/internal/source/step/create_dv_from_vi_step.go @@ -31,6 +31,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" "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/object" "github.com/deckhouse/virtualization-controller/pkg/controller/conditions" @@ -69,6 +70,11 @@ func (s CreateDataVolumeFromVirtualImageStep) Take(ctx context.Context, vd *v1al return nil, nil } + _, exists := vd.Annotations[annotations.AnnUseVolumeSnapshot] + if exists { + return nil, nil + } + viRefKey := types.NamespacedName{Name: vd.Spec.DataSource.ObjectRef.Name, Namespace: vd.Namespace} viRef, err := object.FetchObject(ctx, viRefKey, s.client, &v1alpha2.VirtualImage{}) if err != nil { diff --git a/images/virtualization-artifact/pkg/controller/vd/internal/source/step/create_pvc_from_vs_step.go b/images/virtualization-artifact/pkg/controller/vd/internal/source/step/create_pvc_from_vs_step.go new file mode 100644 index 0000000000..e5ab0085b4 --- /dev/null +++ b/images/virtualization-artifact/pkg/controller/vd/internal/source/step/create_pvc_from_vs_step.go @@ -0,0 +1,258 @@ +/* +Copyright 2025 Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package step + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "strings" + + vsv1 "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1" + corev1 "k8s.io/api/core/v1" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/ptr" + "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/common/pointer" + "github.com/deckhouse/virtualization-controller/pkg/controller/conditions" + "github.com/deckhouse/virtualization-controller/pkg/controller/service" + vdsupplements "github.com/deckhouse/virtualization-controller/pkg/controller/vd/internal/supplements" + "github.com/deckhouse/virtualization/api/core/v1alpha2" + "github.com/deckhouse/virtualization/api/core/v1alpha2/vdcondition" +) + +type CreatePVCFromVSStep struct { + pvc *corev1.PersistentVolumeClaim + client client.Client + cb *conditions.ConditionBuilder +} + +func NewCreatePVCFromVSStep( + pvc *corev1.PersistentVolumeClaim, + client client.Client, + cb *conditions.ConditionBuilder, +) *CreatePVCFromVSStep { + return &CreatePVCFromVSStep{ + pvc: pvc, + client: client, + cb: cb, + } +} + +func (s CreatePVCFromVSStep) Take(ctx context.Context, vd *v1alpha2.VirtualDisk) (*reconcile.Result, error) { + if s.pvc != nil { + return nil, nil + } + + _, exists := vd.Annotations[annotations.AnnUseVolumeSnapshot] + if !exists { + return nil, nil + } + + vi, err := object.FetchObject(ctx, types.NamespacedName{ + Namespace: vd.Namespace, + Name: vd.Spec.DataSource.ObjectRef.Name, + }, s.client, &v1alpha2.VirtualImage{}) + if err != nil { + vd.Status.Phase = v1alpha2.DiskFailed + s.cb. + Status(metav1.ConditionFalse). + Reason(vdcondition.ProvisioningFailed). + Message("The VirtualImage not found") + return &reconcile.Result{}, nil + } + + if vi.Status.Target.PersistentVolumeClaim == "" { + vd.Status.Phase = v1alpha2.DiskFailed + s.cb. + Status(metav1.ConditionFalse). + Reason(vdcondition.ProvisioningFailed). + Message("The VirtualImage does not have the target pvc") + return &reconcile.Result{}, nil + } + + vs, err := object.FetchObject(ctx, types.NamespacedName{ + Namespace: vi.Namespace, + Name: vi.Status.Target.PersistentVolumeClaim, + }, s.client, &vsv1.VolumeSnapshot{}) + if err != nil { + vd.Status.Phase = v1alpha2.DiskFailed + s.cb. + Status(metav1.ConditionFalse). + Reason(vdcondition.ProvisioningFailed). + Message("The VolumeSnapshot not found") + return &reconcile.Result{}, nil + } + + if vs.Status == nil || !(*vs.Status.ReadyToUse) { + vd.Status.Phase = v1alpha2.DiskFailed + s.cb. + Status(metav1.ConditionFalse). + Reason(vdcondition.ProvisioningFailed). + Message("The VolumeSnapshot is not ready to use") + return &reconcile.Result{}, nil + } + + pvc, err := s.buildPVC(vd, vs, vi) + if err != nil { + return nil, fmt.Errorf("failed to build pvc: %w", err) + } + + err = s.client.Create(ctx, pvc) + if err != nil && !k8serrors.IsAlreadyExists(err) { + return nil, fmt.Errorf("create pvc: %w", err) + } + + vd.Status.Phase = v1alpha2.DiskProvisioning + s.cb. + Status(metav1.ConditionFalse). + Reason(vdcondition.Provisioning). + Message("The PersistentVolumeClaim has been created: waiting for it to be Bound.") + + vd.Status.Progress = "0%" + vd.Status.SourceUID = pointer.GetPointer(vi.UID) + vdsupplements.SetPVCName(vd, pvc.Name) + + s.addOriginalMetadata(vd, vs) + return nil, nil +} + +func (s CreatePVCFromVSStep) buildPVC(vd *v1alpha2.VirtualDisk, vs *vsv1.VolumeSnapshot, vi *v1alpha2.VirtualImage) (*corev1.PersistentVolumeClaim, error) { + var storageClassName string + if vd.Spec.PersistentVolumeClaim.StorageClass != nil && *vd.Spec.PersistentVolumeClaim.StorageClass != "" { + storageClassName = *vd.Spec.PersistentVolumeClaim.StorageClass + } else { + storageClassName = vs.Annotations[annotations.AnnStorageClassName] + if storageClassName == "" { + storageClassName = vs.Annotations[annotations.AnnStorageClassNameDeprecated] + } + } + + volumeMode := vs.Annotations[annotations.AnnVolumeMode] + if volumeMode == "" { + volumeMode = vs.Annotations[annotations.AnnVolumeModeDeprecated] + } + accessModesRaw := vs.Annotations[annotations.AnnAccessModes] + if accessModesRaw == "" { + accessModesRaw = vs.Annotations[annotations.AnnAccessModesDeprecated] + } + + accessModesStr := strings.Split(accessModesRaw, ",") + accessModes := make([]corev1.PersistentVolumeAccessMode, 0, len(accessModesStr)) + for _, accessModeStr := range accessModesStr { + accessModes = append(accessModes, corev1.PersistentVolumeAccessMode(accessModeStr)) + } + + spec := corev1.PersistentVolumeClaimSpec{ + AccessModes: accessModes, + DataSource: &corev1.TypedLocalObjectReference{ + APIGroup: ptr.To(vs.GroupVersionKind().Group), + Kind: vs.Kind, + Name: vi.Status.Target.PersistentVolumeClaim, + }, + } + + if storageClassName != "" { + spec.StorageClassName = &storageClassName + vd.Status.StorageClassName = storageClassName + } + + if volumeMode != "" { + spec.VolumeMode = ptr.To(corev1.PersistentVolumeMode(volumeMode)) + } + + pvcSize, err := s.getPVCSize(vd, vs) + if err != nil { + return nil, err + } + + spec.Resources = corev1.VolumeResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceStorage: pvcSize, + }, + } + + pvcKey := vdsupplements.NewGenerator(vd).PersistentVolumeClaim() + + return &corev1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: pvcKey.Name, + Namespace: pvcKey.Namespace, + OwnerReferences: []metav1.OwnerReference{ + service.MakeOwnerReference(vd), + }, + }, + Spec: spec, + }, nil +} + +func (s CreatePVCFromVSStep) getPVCSize(vd *v1alpha2.VirtualDisk, vs *vsv1.VolumeSnapshot) (resource.Quantity, error) { + if vs.Status == nil || vs.Status.RestoreSize == nil || vs.Status.RestoreSize.IsZero() { + return resource.Quantity{}, errors.New("vs has zero size") + } + + if vd.Spec.PersistentVolumeClaim.Size == nil || vd.Spec.PersistentVolumeClaim.Size.IsZero() { + return *vs.Status.RestoreSize, nil + } + + if vd.Spec.PersistentVolumeClaim.Size.Cmp(*vs.Status.RestoreSize) == 1 { + return *vd.Spec.PersistentVolumeClaim.Size, nil + } + + return *vs.Status.RestoreSize, nil +} + +// AddOriginalMetadata adds original annotations and labels from VolumeSnapshot to VirtualDisk, +// without overwriting existing values +func (s CreatePVCFromVSStep) addOriginalMetadata(vd *v1alpha2.VirtualDisk, vs *vsv1.VolumeSnapshot) { + if vd.Annotations == nil { + vd.Annotations = make(map[string]string) + } + if vd.Labels == nil { + vd.Labels = make(map[string]string) + } + + if annotationsJSON := vs.Annotations[annotations.AnnVirtualDiskOriginalAnnotations]; annotationsJSON != "" { + var originalAnnotations map[string]string + if err := json.Unmarshal([]byte(annotationsJSON), &originalAnnotations); err == nil { + for key, value := range originalAnnotations { + if _, exists := vd.Annotations[key]; !exists { + vd.Annotations[key] = value + } + } + } + } + + if labelsJSON := vs.Annotations[annotations.AnnVirtualDiskOriginalLabels]; labelsJSON != "" { + var originalLabels map[string]string + if err := json.Unmarshal([]byte(labelsJSON), &originalLabels); err == nil { + for key, value := range originalLabels { + if _, exists := vd.Labels[key]; !exists { + vd.Labels[key] = value + } + } + } + } +} diff --git a/images/virtualization-artifact/pkg/controller/vd/internal/source/step/ensure_node_placement.go b/images/virtualization-artifact/pkg/controller/vd/internal/source/step/ensure_node_placement.go index 120505af0f..7be72e5091 100644 --- a/images/virtualization-artifact/pkg/controller/vd/internal/source/step/ensure_node_placement.go +++ b/images/virtualization-artifact/pkg/controller/vd/internal/source/step/ensure_node_placement.go @@ -27,6 +27,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/provisioner" "github.com/deckhouse/virtualization-controller/pkg/controller/conditions" "github.com/deckhouse/virtualization-controller/pkg/controller/service" @@ -71,6 +72,11 @@ func (s EnsureNodePlacementStep) Take(ctx context.Context, vd *v1alpha2.VirtualD return nil, nil } + _, exists := vd.Annotations[annotations.AnnUseVolumeSnapshot] + if exists { + return nil, nil + } + err := s.disk.CheckProvisioning(ctx, s.pvc) switch { case err == nil: diff --git a/images/virtualization-artifact/pkg/controller/vd/internal/source/step/wait_for_dv_step.go b/images/virtualization-artifact/pkg/controller/vd/internal/source/step/wait_for_dv_step.go index 656d60dd72..f03f954917 100644 --- a/images/virtualization-artifact/pkg/controller/vd/internal/source/step/wait_for_dv_step.go +++ b/images/virtualization-artifact/pkg/controller/vd/internal/source/step/wait_for_dv_step.go @@ -29,6 +29,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/reconcile" + "github.com/deckhouse/virtualization-controller/pkg/common/annotations" dvutil "github.com/deckhouse/virtualization-controller/pkg/common/datavolume" "github.com/deckhouse/virtualization-controller/pkg/common/object" "github.com/deckhouse/virtualization-controller/pkg/controller/conditions" @@ -67,6 +68,11 @@ func NewWaitForDVStep( } func (s WaitForDVStep) Take(ctx context.Context, vd *v1alpha2.VirtualDisk) (*reconcile.Result, error) { + _, exists := vd.Annotations[annotations.AnnUseVolumeSnapshot] + if exists { + return nil, nil + } + if s.dv == nil { vd.Status.Phase = v1alpha2.DiskProvisioning s.cb. diff --git a/images/virtualization-artifact/pkg/controller/vi/internal/source/http.go b/images/virtualization-artifact/pkg/controller/vi/internal/source/http.go index 68e16610a5..d9a39be59d 100644 --- a/images/virtualization-artifact/pkg/controller/vi/internal/source/http.go +++ b/images/virtualization-artifact/pkg/controller/vi/internal/source/http.go @@ -22,6 +22,7 @@ import ( "fmt" "time" + vsv1 "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1" corev1 "k8s.io/api/core/v1" storagev1 "k8s.io/api/storage/v1" "k8s.io/apimachinery/pkg/api/resource" @@ -379,6 +380,48 @@ func (ds HTTPDataSource) StoreToPVC(ctx context.Context, vi *v1alpha2.VirtualIma "The HTTP DataSource import has completed", ) + _, exists := vi.Annotations[annotations.AnnUseVolumeSnapshot] + if exists { + var vs *vsv1.VolumeSnapshot + vs, err = ds.diskService.GetVolumeSnapshot(ctx, pvc.Name, pvc.Namespace) + if err != nil { + vi.Status.Phase = v1alpha2.ImageFailed + cb. + Status(metav1.ConditionFalse). + Reason(vicondition.ProvisioningFailed). + Message(err.Error()) + return reconcile.Result{}, nil + } + + if vs == nil { + err = ds.diskService.CreateVolumeSnapshot(ctx, pvc) + if err != nil { + vi.Status.Phase = v1alpha2.ImageFailed + cb. + Status(metav1.ConditionFalse). + Reason(vicondition.ProvisioningFailed). + Message(err.Error()) + return reconcile.Result{}, nil + } + + vi.Status.Phase = v1alpha2.ImageProvisioning + cb. + Status(metav1.ConditionFalse). + Reason(vicondition.Provisioning). + Message("The VolumeSnapshot has been created.") + return reconcile.Result{RequeueAfter: time.Second}, nil + } + + if vs.Status.ReadyToUse == nil || !(*vs.Status.ReadyToUse) { + vi.Status.Phase = v1alpha2.ImageProvisioning + cb. + Status(metav1.ConditionFalse). + Reason(vicondition.Provisioning). + Message("Waiting for the VolumeSnapshot to be ready to use.") + return reconcile.Result{RequeueAfter: time.Second}, nil + } + } + vi.Status.Phase = v1alpha2.ImageReady cb. Status(metav1.ConditionTrue). From a010e4bd0dfdbfc8be40bddf3dc05bfc18b0f1e1 Mon Sep 17 00:00:00 2001 From: Isteb4k Date: Tue, 28 Oct 2025 17:52:05 +0100 Subject: [PATCH 06/10] fix(vm): disable vm watchers Signed-off-by: Isteb4k --- .../pkg/controller/vm/vm_reconciler.go | 35 ++++++++++++------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go index 92d0baaa20..999a6e71e2 100644 --- a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go +++ b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go @@ -20,6 +20,7 @@ import ( "context" "fmt" "reflect" + "time" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" @@ -30,7 +31,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/controller/vm/internal/watcher" "github.com/deckhouse/virtualization-controller/pkg/logger" "github.com/deckhouse/virtualization/api/core/v1alpha2" ) @@ -62,17 +62,17 @@ func (r *Reconciler) SetupController(_ context.Context, mgr manager.Manager, ctr } for _, w := range []Watcher{ - watcher.NewKVVMWatcher(), - watcher.NewKVVMIWatcher(), - watcher.NewPodWatcher(), - watcher.NewVirtualImageWatcher(mgr.GetClient()), - watcher.NewClusterVirtualImageWatcher(mgr.GetClient()), - watcher.NewVirtualDiskWatcher(mgr.GetClient()), - watcher.NewVMIPWatcher(), - watcher.NewVirtualMachineClassWatcher(), - watcher.NewVirtualMachineSnapshotWatcher(), - watcher.NewVMOPWatcher(), - watcher.NewVMMACWatcher(), + // watcher.NewKVVMWatcher(), + // watcher.NewKVVMIWatcher(), + // watcher.NewPodWatcher(), + // watcher.NewVirtualImageWatcher(mgr.GetClient()), + // watcher.NewClusterVirtualImageWatcher(mgr.GetClient()), + // watcher.NewVirtualDiskWatcher(mgr.GetClient()), + // watcher.NewVMIPWatcher(), + // watcher.NewVirtualMachineClassWatcher(), + // watcher.NewVirtualMachineSnapshotWatcher(), + // watcher.NewVMOPWatcher(), + // watcher.NewVMMACWatcher(), } { err := w.Watch(mgr, ctr) if err != nil { @@ -108,7 +108,16 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco return vm.Update(ctx) }) - return rec.Reconcile(ctx) + res, err := rec.Reconcile(ctx) + if err != nil { + return reconcile.Result{}, err + } + + if vm.Changed().Status.Phase != v1alpha2.MachineRunning { + res.RequeueAfter = time.Second * 30 + } + + return res, nil } func (r *Reconciler) factory() *v1alpha2.VirtualMachine { From 992dde95be77c651d2bb40e891898af03d2cfecf Mon Sep 17 00:00:00 2001 From: Daniil Antoshin Date: Tue, 28 Oct 2025 19:06:21 +0200 Subject: [PATCH 07/10] test indexer Signed-off-by: Daniil Antoshin --- .../pkg/controller/vd/internal/inuse.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/images/virtualization-artifact/pkg/controller/vd/internal/inuse.go b/images/virtualization-artifact/pkg/controller/vd/internal/inuse.go index e581028475..d3af6aa454 100644 --- a/images/virtualization-artifact/pkg/controller/vd/internal/inuse.go +++ b/images/virtualization-artifact/pkg/controller/vd/internal/inuse.go @@ -33,6 +33,7 @@ import ( "github.com/deckhouse/virtualization-controller/pkg/common/object" commonvd "github.com/deckhouse/virtualization-controller/pkg/common/vd" "github.com/deckhouse/virtualization-controller/pkg/controller/conditions" + "github.com/deckhouse/virtualization-controller/pkg/controller/indexer" "github.com/deckhouse/virtualization/api/core/v1alpha2" "github.com/deckhouse/virtualization/api/core/v1alpha2/vdcondition" ) @@ -152,8 +153,8 @@ func (h InUseHandler) checkImageUsage(ctx context.Context, vd *v1alpha2.VirtualD func (h InUseHandler) updateAttachedVirtualMachines(ctx context.Context, vd *v1alpha2.VirtualDisk) error { var vms v1alpha2.VirtualMachineList - err := h.client.List(ctx, &vms, &client.ListOptions{ - Namespace: vd.GetNamespace(), + err := h.client.List(ctx, &vms, &client.MatchingFields{ + indexer.IndexFieldVMByVD: vd.Name, }) if err != nil { return fmt.Errorf("error getting virtual machines: %w", err) From 49ac95fff1c89970b1380f7c8057a91d1d9aa5f2 Mon Sep 17 00:00:00 2001 From: Daniil Antoshin Date: Wed, 29 Oct 2025 12:07:44 +0200 Subject: [PATCH 08/10] add vmip indexer Signed-off-by: Daniil Antoshin --- .../pkg/controller/indexer/indexer.go | 12 ++++++++++++ .../pkg/controller/vmip/internal/attached_handler.go | 5 ++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/images/virtualization-artifact/pkg/controller/indexer/indexer.go b/images/virtualization-artifact/pkg/controller/indexer/indexer.go index 146d3f75ca..d8289ccb1e 100644 --- a/images/virtualization-artifact/pkg/controller/indexer/indexer.go +++ b/images/virtualization-artifact/pkg/controller/indexer/indexer.go @@ -34,6 +34,7 @@ const ( IndexFieldVMByVD = "spec.blockDeviceRefs.VirtualDisk" IndexFieldVMByVI = "spec.blockDeviceRefs.VirtualImage" IndexFieldVMByCVI = "spec.blockDeviceRefs.ClusterVirtualImage" + IndexFieldVMByIP = "status.ipAddress" IndexFieldVMByNode = "status.node" IndexFieldVDByVDSnapshot = "vd,spec.DataSource.ObjectRef.Name,.Kind=VirtualDiskSnapshot" @@ -64,6 +65,7 @@ var IndexGetters = []IndexGetter{ IndexVMByVD, IndexVMByVI, IndexVMByCVI, + IndexVMByIP, IndexVMByNode, IndexVMSnapshotByVM, IndexVMSnapshotByVDSnapshot, @@ -121,6 +123,16 @@ func IndexVMByCVI() (obj client.Object, field string, extractValue client.Indexe } } +func IndexVMByIP() (obj client.Object, field string, extractValue client.IndexerFunc) { + return &v1alpha2.VirtualMachine{}, IndexFieldVMByIP, func(object client.Object) []string { + vm, ok := object.(*v1alpha2.VirtualMachine) + if !ok || vm == nil || vm.Status.IPAddress == "" { + return nil + } + return []string{vm.Status.IPAddress} + } +} + func IndexVMByNode() (obj client.Object, field string, extractValue client.IndexerFunc) { return &v1alpha2.VirtualMachine{}, IndexFieldVMByNode, func(object client.Object) []string { vm, ok := object.(*v1alpha2.VirtualMachine) diff --git a/images/virtualization-artifact/pkg/controller/vmip/internal/attached_handler.go b/images/virtualization-artifact/pkg/controller/vmip/internal/attached_handler.go index 9bf4a651b0..299b72f142 100644 --- a/images/virtualization-artifact/pkg/controller/vmip/internal/attached_handler.go +++ b/images/virtualization-artifact/pkg/controller/vmip/internal/attached_handler.go @@ -29,6 +29,7 @@ import ( "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/indexer" "github.com/deckhouse/virtualization-controller/pkg/eventrecord" "github.com/deckhouse/virtualization/api/core/v1alpha2" "github.com/deckhouse/virtualization/api/core/v1alpha2/vmipcondition" @@ -83,7 +84,9 @@ func (h *AttachedHandler) Handle(ctx context.Context, vmip *v1alpha2.VirtualMach func (h *AttachedHandler) getAttachedVirtualMachine(ctx context.Context, vmip *v1alpha2.VirtualMachineIPAddress) (*v1alpha2.VirtualMachine, error) { var vms v1alpha2.VirtualMachineList - err := h.client.List(ctx, &vms, &client.ListOptions{Namespace: vmip.Namespace}) + err := h.client.List(ctx, &vms, &client.MatchingFields{ + indexer.IndexFieldVMByIP: vmip.Status.Address, + }) if err != nil { return nil, fmt.Errorf("list vms: %w", err) } From dc361207fab5fcd2cc4b74dba43e23e0e98cdc6c Mon Sep 17 00:00:00 2001 From: Daniil Antoshin Date: Thu, 30 Oct 2025 15:55:16 +0200 Subject: [PATCH 09/10] Revert "fix(vm): disable vm watchers" This reverts commit 3df5e340a268f532e103cf269f18c5c8a690f8b6. --- .../pkg/controller/vm/vm_reconciler.go | 35 +++++++------------ 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go b/images/virtualization-artifact/pkg/controller/vm/vm_reconciler.go index 999a6e71e2..92d0baaa20 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" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" @@ -31,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/controller/vm/internal/watcher" "github.com/deckhouse/virtualization-controller/pkg/logger" "github.com/deckhouse/virtualization/api/core/v1alpha2" ) @@ -62,17 +62,17 @@ func (r *Reconciler) SetupController(_ context.Context, mgr manager.Manager, ctr } for _, w := range []Watcher{ - // watcher.NewKVVMWatcher(), - // watcher.NewKVVMIWatcher(), - // watcher.NewPodWatcher(), - // watcher.NewVirtualImageWatcher(mgr.GetClient()), - // watcher.NewClusterVirtualImageWatcher(mgr.GetClient()), - // watcher.NewVirtualDiskWatcher(mgr.GetClient()), - // watcher.NewVMIPWatcher(), - // watcher.NewVirtualMachineClassWatcher(), - // watcher.NewVirtualMachineSnapshotWatcher(), - // watcher.NewVMOPWatcher(), - // watcher.NewVMMACWatcher(), + watcher.NewKVVMWatcher(), + watcher.NewKVVMIWatcher(), + watcher.NewPodWatcher(), + watcher.NewVirtualImageWatcher(mgr.GetClient()), + watcher.NewClusterVirtualImageWatcher(mgr.GetClient()), + watcher.NewVirtualDiskWatcher(mgr.GetClient()), + watcher.NewVMIPWatcher(), + watcher.NewVirtualMachineClassWatcher(), + watcher.NewVirtualMachineSnapshotWatcher(), + watcher.NewVMOPWatcher(), + watcher.NewVMMACWatcher(), } { err := w.Watch(mgr, ctr) if err != nil { @@ -108,16 +108,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco return vm.Update(ctx) }) - res, err := rec.Reconcile(ctx) - if err != nil { - return reconcile.Result{}, err - } - - if vm.Changed().Status.Phase != v1alpha2.MachineRunning { - res.RequeueAfter = time.Second * 30 - } - - return res, nil + return rec.Reconcile(ctx) } func (r *Reconciler) factory() *v1alpha2.VirtualMachine { From a71c901525e5cb884950f63248453acd577ab9a8 Mon Sep 17 00:00:00 2001 From: Daniil Antoshin Date: Thu, 30 Oct 2025 17:40:55 +0200 Subject: [PATCH 10/10] enable UsePriorityQueue Signed-off-by: Daniil Antoshin --- .../virtualization-artifact/pkg/controller/vm/vm_controller.go | 1 + 1 file changed, 1 insertion(+) diff --git a/images/virtualization-artifact/pkg/controller/vm/vm_controller.go b/images/virtualization-artifact/pkg/controller/vm/vm_controller.go index 29d683f0f5..1cd2ad4433 100644 --- a/images/virtualization-artifact/pkg/controller/vm/vm_controller.go +++ b/images/virtualization-artifact/pkg/controller/vm/vm_controller.go @@ -88,6 +88,7 @@ func SetupController( RecoverPanic: ptr.To(true), LogConstructor: logger.NewConstructor(log), CacheSyncTimeout: 10 * time.Minute, + UsePriorityQueue: ptr.To(true), }) if err != nil { return err