diff --git a/images/virtualization-artifact/pkg/common/backoff/backgoff.go b/images/virtualization-artifact/pkg/common/backoff/backgoff.go deleted file mode 100644 index f3a3fe31ba..0000000000 --- a/images/virtualization-artifact/pkg/common/backoff/backgoff.go +++ /dev/null @@ -1,39 +0,0 @@ -/* -Copyright 2026 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 backoff - -import ( - "time" - - "k8s.io/apimachinery/pkg/util/wait" -) - -func CalculateBackOff(failedCount int) time.Duration { - if failedCount == 0 { - return 0 - } - - evacuationBackoff := wait.Backoff{ - Duration: 2 * time.Second, - Factor: 2.0, - Jitter: 0, - Cap: 5 * time.Minute, - Steps: failedCount, - } - - return evacuationBackoff.Step() -} diff --git a/images/virtualization-artifact/pkg/controller/evacuation/internal/handler/evacuation.go b/images/virtualization-artifact/pkg/controller/evacuation/internal/handler/evacuation.go index 0565ad4560..568285a0a1 100644 --- a/images/virtualization-artifact/pkg/controller/evacuation/internal/handler/evacuation.go +++ b/images/virtualization-artifact/pkg/controller/evacuation/internal/handler/evacuation.go @@ -30,9 +30,9 @@ import ( vmopbuilder "github.com/deckhouse/virtualization-controller/pkg/builder/vmop" "github.com/deckhouse/virtualization-controller/pkg/common/annotations" - "github.com/deckhouse/virtualization-controller/pkg/common/backoff" commonvmop "github.com/deckhouse/virtualization-controller/pkg/common/vmop" "github.com/deckhouse/virtualization-controller/pkg/controller/conditions" + "github.com/deckhouse/virtualization-controller/pkg/controller/service" "github.com/deckhouse/virtualization-controller/pkg/logger" "github.com/deckhouse/virtualization/api/core/v1alpha2" "github.com/deckhouse/virtualization/api/core/v1alpha2/vmcondition" @@ -44,6 +44,7 @@ func NewEvacuationHandler(client client.Client, evacuateCanceler EvacuateCancele return &EvacuationHandler{ client: client, evacuateCanceler: evacuateCanceler, + backoffSvc: service.NewBackoffService(), } } @@ -55,6 +56,7 @@ type EvacuateCanceler interface { type EvacuationHandler struct { client client.Client evacuateCanceler EvacuateCanceler + backoffSvc *service.BackoffService } func (h *EvacuationHandler) Handle(ctx context.Context, vm *v1alpha2.VirtualMachine) (reconcile.Result, error) { @@ -98,9 +100,9 @@ func (h *EvacuationHandler) Handle(ctx context.Context, vm *v1alpha2.VirtualMach } } - backoff := backoff.CalculateBackOff(failedCount) - if backoff > 0 { - return reconcile.Result{RequeueAfter: backoff}, nil + delay := h.backoffSvc.CalculateBackoff(failedCount) + if delay > 0 { + return reconcile.Result{RequeueAfter: delay}, nil } log.Info("Create evacuation vmop") diff --git a/images/virtualization-artifact/pkg/controller/service/backoff_service.go b/images/virtualization-artifact/pkg/controller/service/backoff_service.go new file mode 100644 index 0000000000..bccd9c008b --- /dev/null +++ b/images/virtualization-artifact/pkg/controller/service/backoff_service.go @@ -0,0 +1,176 @@ +/* +Copyright 2026 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 service + +import ( + "reflect" + "sync" + "time" + + "k8s.io/apimachinery/pkg/util/wait" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +const ( + defaultBaseDelay = 2 * time.Second + defaultFactor = 2.0 + defaultMaxDelay = 5 * time.Minute +) + +// BackoffService tracks per-object-type failure counts and calculates exponential backoff durations. +// It is safe for concurrent use. All methods are nil-safe: if the object is nil, +// the service returns the base delay instead of an error. +type BackoffService struct { + mu sync.RWMutex + // stores maps object-type key -> (object UID string -> failed attempts count). + stores map[string]map[string]int + baseDelay time.Duration + factor float64 + maxDelay time.Duration +} + +type BackoffOption func(*BackoffService) + +func WithBaseDelay(d time.Duration) BackoffOption { + return func(s *BackoffService) { s.baseDelay = d } +} + +func WithFactor(f float64) BackoffOption { + return func(s *BackoffService) { s.factor = f } +} + +func WithMaxDelay(d time.Duration) BackoffOption { + return func(s *BackoffService) { s.maxDelay = d } +} + +func NewBackoffService(opts ...BackoffOption) *BackoffService { + s := &BackoffService{ + stores: make(map[string]map[string]int), + baseDelay: defaultBaseDelay, + factor: defaultFactor, + maxDelay: defaultMaxDelay, + } + for _, opt := range opts { + opt(s) + } + return s +} + +// RegisterFailure increments the failure counter for the given object and returns the new count. +// If obj is nil, returns 1 (treated as a single failure). +func (s *BackoffService) RegisterFailure(obj client.Object) int { + typeKey, objKey := objectKeys(obj) + if typeKey == "" { + return 1 + } + + s.mu.Lock() + defer s.mu.Unlock() + + store := s.stores[typeKey] + if store == nil { + store = make(map[string]int) + s.stores[typeKey] = store + } + store[objKey]++ + return store[objKey] +} + +// ResetFailures resets the failure counter for the given object. +// If obj is nil, this is a no-op. +func (s *BackoffService) ResetFailures(obj client.Object) { + typeKey, objKey := objectKeys(obj) + if typeKey == "" { + return + } + + s.mu.Lock() + defer s.mu.Unlock() + + if store, ok := s.stores[typeKey]; ok { + delete(store, objKey) + } +} + +// GetFailures returns the current failure count for the given object. +// If obj is nil, returns 0. +func (s *BackoffService) GetFailures(obj client.Object) int { + typeKey, objKey := objectKeys(obj) + if typeKey == "" { + return 0 + } + + s.mu.RLock() + defer s.mu.RUnlock() + + if store, ok := s.stores[typeKey]; ok { + return store[objKey] + } + return 0 +} + +// Backoff calculates the backoff duration for the given object based on its failure count. +// If obj is nil, returns the base delay. +func (s *BackoffService) Backoff(obj client.Object) time.Duration { + return s.calculateBackoff(s.GetFailures(obj)) +} + +// RegisterFailureAndBackoff increments the failure counter and returns the backoff duration. +// If obj is nil, returns the base delay. +func (s *BackoffService) RegisterFailureAndBackoff(obj client.Object) time.Duration { + return s.calculateBackoff(s.RegisterFailure(obj)) +} + +// CalculateBackoff computes the backoff duration for the given failure count without modifying any state. +func (s *BackoffService) CalculateBackoff(failedCount int) time.Duration { + return s.calculateBackoff(failedCount) +} + +func (s *BackoffService) calculateBackoff(failedCount int) time.Duration { + if failedCount == 0 { + return 0 + } + + b := wait.Backoff{ + Duration: s.baseDelay, + Factor: s.factor, + Jitter: 0, + Cap: s.maxDelay, + Steps: failedCount, + } + + var d time.Duration + for range failedCount { + d = b.Step() + } + return d +} + +func objectKeys(obj client.Object) (typeKey, objKey string) { + if obj == nil { + return "", "" + } + t := reflect.TypeOf(obj) + if t == nil { + return "", "" + } + uid := string(obj.GetUID()) + if uid == "" { + uid = obj.GetNamespace() + "/" + obj.GetName() + } + return t.String(), uid +} diff --git a/images/virtualization-artifact/pkg/controller/service/backoff_service_test.go b/images/virtualization-artifact/pkg/controller/service/backoff_service_test.go new file mode 100644 index 0000000000..1d31ce2ec8 --- /dev/null +++ b/images/virtualization-artifact/pkg/controller/service/backoff_service_test.go @@ -0,0 +1,153 @@ +/* +Copyright 2026 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 service + +import ( + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + + "github.com/deckhouse/virtualization/api/core/v1alpha2" +) + +var _ = Describe("BackoffService", func() { + var svc *BackoffService + + BeforeEach(func() { + svc = NewBackoffService() + }) + + newVM := func(name string) *v1alpha2.VirtualMachine { + return &v1alpha2.VirtualMachine{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: "default", + UID: types.UID("default/" + name), + }, + } + } + + newVD := func(name string) *v1alpha2.VirtualDisk { + return &v1alpha2.VirtualDisk{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: "default", + UID: types.UID("default/" + name), + }, + } + } + + It("should return 0 failures and 0 backoff for new object", func() { + vm := newVM("test-vm") + Expect(svc.GetFailures(vm)).To(Equal(0)) + Expect(svc.Backoff(vm)).To(Equal(time.Duration(0))) + }) + + It("should increment failure count", func() { + vm := newVM("test-vm") + + Expect(svc.RegisterFailure(vm)).To(Equal(1)) + Expect(svc.RegisterFailure(vm)).To(Equal(2)) + Expect(svc.GetFailures(vm)).To(Equal(2)) + }) + + It("should reset failures", func() { + vm := newVM("test-vm") + + svc.RegisterFailure(vm) + svc.RegisterFailure(vm) + svc.ResetFailures(vm) + + Expect(svc.GetFailures(vm)).To(Equal(0)) + }) + + It("should track different objects of the same type independently", func() { + vm1 := newVM("vm-1") + vm2 := newVM("vm-2") + + svc.RegisterFailure(vm1) + svc.RegisterFailure(vm1) + svc.RegisterFailure(vm2) + + Expect(svc.GetFailures(vm1)).To(Equal(2)) + Expect(svc.GetFailures(vm2)).To(Equal(1)) + }) + + It("should track different object types independently", func() { + vm := newVM("obj-1") + vd := newVD("obj-1") + + svc.RegisterFailure(vm) + svc.RegisterFailure(vm) + svc.RegisterFailure(vm) + svc.RegisterFailure(vd) + + Expect(svc.GetFailures(vm)).To(Equal(3)) + Expect(svc.GetFailures(vd)).To(Equal(1)) + }) + + It("should calculate exponential backoff", func() { + vm := newVM("test-vm") + + // 1st failure: baseDelay * factor^0 = 2s + Expect(svc.RegisterFailureAndBackoff(vm)).To(Equal(2 * time.Second)) + + // 2nd failure: should be > 2s + Expect(svc.RegisterFailureAndBackoff(vm)).To(BeNumerically(">", 2*time.Second)) + }) + + It("should cap backoff at max delay", func() { + vm := newVM("test-vm") + + for range 30 { + svc.RegisterFailure(vm) + } + + Expect(svc.Backoff(vm)).To(Equal(defaultMaxDelay)) + }) + + It("should respect custom options", func() { + custom := NewBackoffService( + WithBaseDelay(5*time.Second), + WithFactor(3.0), + WithMaxDelay(1*time.Minute), + ) + + vm := newVM("test-vm") + Expect(custom.RegisterFailureAndBackoff(vm)).To(Equal(5 * time.Second)) + + for range 20 { + custom.RegisterFailure(vm) + } + + Expect(custom.Backoff(vm)).To(Equal(1 * time.Minute)) + }) + + It("should return base delay for nil object", func() { + Expect(svc.RegisterFailure(nil)).To(Equal(1)) + Expect(svc.GetFailures(nil)).To(Equal(0)) + Expect(svc.Backoff(nil)).To(Equal(time.Duration(0))) + Expect(svc.RegisterFailureAndBackoff(nil)).To(Equal(defaultBaseDelay)) + }) + + It("should be no-op reset for nil object", func() { + Expect(func() { svc.ResetFailures(nil) }).NotTo(Panic()) + }) +}) diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/usb_device_attach_handler.go b/images/virtualization-artifact/pkg/controller/vm/internal/usb_device_attach_handler.go index 1075afa870..457a571ef4 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/usb_device_attach_handler.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/usb_device_attach_handler.go @@ -20,7 +20,6 @@ import ( "context" "fmt" "strings" - "time" apierrors "k8s.io/apimachinery/pkg/api/errors" virtv1 "kubevirt.io/api/core/v1" @@ -28,6 +27,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" "github.com/deckhouse/virtualization-controller/pkg/controller/conditions" + "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" "github.com/deckhouse/virtualization/api/core/v1alpha2" @@ -42,11 +42,13 @@ func NewUSBDeviceAttachHandler(cl client.Client, virtClient VirtClient) *USBDevi client: cl, virtClient: virtClient, }, + backoffSvc: service.NewBackoffService(), } } type USBDeviceAttachHandler struct { usbDeviceHandlerBase + backoffSvc *service.BackoffService } func (h *USBDeviceAttachHandler) Name() string { @@ -163,7 +165,8 @@ func (h *USBDeviceAttachHandler) Handle(ctx context.Context, s state.VirtualMach requestName := h.getResourceClaimRequestName(deviceName) err := h.attachUSBDevice(ctx, vm, deviceName, templateName, requestName) if err != nil && !apierrors.IsAlreadyExists(err) && !strings.Contains(err.Error(), "already exists") { - return reconcile.Result{RequeueAfter: 5 * time.Second}, fmt.Errorf("failed to attach USB device %s: %w", deviceName, err) + log.Error("failed to attach USB device", "error", err, "usbDevice", deviceName) + return reconcile.Result{RequeueAfter: h.backoffSvc.RegisterFailureAndBackoff(vm)}, nil } nextStatusRefs = append(nextStatusRefs, h.buildDetachedStatus(existingStatus, deviceName, isReady)) @@ -171,6 +174,8 @@ func (h *USBDeviceAttachHandler) Handle(ctx context.Context, s state.VirtualMach changed.Status.USBDevices = nextStatusRefs + h.backoffSvc.ResetFailures(vm) + return reconcile.Result{}, nil } diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/usb_device_detach_handler.go b/images/virtualization-artifact/pkg/controller/vm/internal/usb_device_detach_handler.go index fcd1618f4f..872329acf4 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/usb_device_detach_handler.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/usb_device_detach_handler.go @@ -25,6 +25,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/reconcile" + "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" "github.com/deckhouse/virtualization/api/core/v1alpha2" @@ -38,11 +39,13 @@ func NewUSBDeviceDetachHandler(cl client.Client, virtClient VirtClient) *USBDevi client: cl, virtClient: virtClient, }, + backoffSvc: service.NewBackoffService(), } } type USBDeviceDetachHandler struct { usbDeviceHandlerBase + backoffSvc *service.BackoffService } func (h *USBDeviceDetachHandler) Name() string { @@ -80,7 +83,7 @@ func (h *USBDeviceDetachHandler) Handle(ctx context.Context, s state.VirtualMach err := h.detachUSBDevice(ctx, vm, existingStatus.Name) if err != nil && !apierrors.IsNotFound(err) && !strings.Contains(err.Error(), "it does not exist") { log.Error("failed to detach USB device", "error", err, "usbDevice", existingStatus.Name) - return reconcile.Result{}, fmt.Errorf("failed to detach USB device %s: %w", existingStatus.Name, err) + return reconcile.Result{RequeueAfter: h.backoffSvc.RegisterFailureAndBackoff(vm)}, nil } } } @@ -96,7 +99,7 @@ func (h *USBDeviceDetachHandler) Handle(ctx context.Context, s state.VirtualMach err := h.detachUSBDevice(ctx, vm, usbDeviceRef.Name) if err != nil && !apierrors.IsNotFound(err) && !strings.Contains(err.Error(), "it does not exist") { log.Error("failed to detach USB device (device not found)", "error", err, "usbDevice", usbDeviceRef.Name) - return reconcile.Result{}, fmt.Errorf("failed to detach USB device %s (device not found): %w", usbDeviceRef.Name, err) + return reconcile.Result{RequeueAfter: h.backoffSvc.RegisterFailureAndBackoff(vm)}, nil } continue } @@ -105,7 +108,7 @@ func (h *USBDeviceDetachHandler) Handle(ctx context.Context, s state.VirtualMach err := h.detachUSBDevice(ctx, vm, usbDeviceRef.Name) if err != nil && !apierrors.IsNotFound(err) && !strings.Contains(err.Error(), "it does not exist") { log.Error("failed to detach USB device (device deleting)", "error", err, "usbDevice", usbDeviceRef.Name) - return reconcile.Result{}, fmt.Errorf("failed to detach USB device %s (device deleting): %w", usbDeviceRef.Name, err) + return reconcile.Result{RequeueAfter: h.backoffSvc.RegisterFailureAndBackoff(vm)}, nil } continue } @@ -114,10 +117,12 @@ func (h *USBDeviceDetachHandler) Handle(ctx context.Context, s state.VirtualMach err := h.detachUSBDevice(ctx, vm, usbDeviceRef.Name) if err != nil && !apierrors.IsNotFound(err) && !strings.Contains(err.Error(), "it does not exist") { log.Error("failed to detach USB device (absent on device)", "error", err, "usbDevice", usbDeviceRef.Name) - return reconcile.Result{}, fmt.Errorf("failed to detach USB device %s (device not ready): %w", usbDeviceRef.Name, err) + return reconcile.Result{RequeueAfter: h.backoffSvc.RegisterFailureAndBackoff(vm)}, nil } } } + h.backoffSvc.ResetFailures(vm) + return reconcile.Result{}, nil } diff --git a/images/virtualization-artifact/pkg/controller/volumemigration/internal/handler/migration.go b/images/virtualization-artifact/pkg/controller/volumemigration/internal/handler/migration.go index aa84dea95c..8eff735b7d 100644 --- a/images/virtualization-artifact/pkg/controller/volumemigration/internal/handler/migration.go +++ b/images/virtualization-artifact/pkg/controller/volumemigration/internal/handler/migration.go @@ -26,7 +26,6 @@ import ( corev1 "k8s.io/api/core/v1" k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/reconcile" @@ -35,6 +34,7 @@ import ( commonvd "github.com/deckhouse/virtualization-controller/pkg/common/vd" commonvmop "github.com/deckhouse/virtualization-controller/pkg/common/vmop" "github.com/deckhouse/virtualization-controller/pkg/controller/conditions" + "github.com/deckhouse/virtualization-controller/pkg/controller/service" "github.com/deckhouse/virtualization-controller/pkg/eventrecord" "github.com/deckhouse/virtualization-controller/pkg/logger" "github.com/deckhouse/virtualization/api/core/v1alpha2" @@ -46,19 +46,16 @@ const ( ) type MigrationHandler struct { - client client.Client - recorder eventrecord.EventRecorderLogger - - backoff map[types.UID]time.Duration - nextTime map[types.UID]time.Time + client client.Client + recorder eventrecord.EventRecorderLogger + backoffSvc *service.BackoffService } func NewMigrationHandler(client client.Client, recorder eventrecord.EventRecorderLogger) *MigrationHandler { return &MigrationHandler{ - client: client, - recorder: recorder, - backoff: make(map[types.UID]time.Duration), - nextTime: make(map[types.UID]time.Time), + client: client, + recorder: recorder, + backoffSvc: service.NewBackoffService(), } } @@ -104,19 +101,10 @@ func (h *MigrationHandler) Handle(ctx context.Context, vd *v1alpha2.VirtualDisk) return reconcile.Result{}, nil } - setBackoff := h.backoff[vm.UID] - calculatedBackoff := h.calculateBackoff(finishedVMOPs, vm.GetCreationTimestamp()) - if calculatedBackoff > setBackoff { - h.backoff[vm.UID] = calculatedBackoff - h.nextTime[vm.UID] = time.Now().Add(calculatedBackoff) - } - - backoff := h.backoff[vm.UID] - nextTime := h.nextTime[vm.UID] - - if nextTime.After(time.Now()) { - h.recorder.Eventf(vd, corev1.EventTypeNormal, v1alpha2.ReasonVolumeMigrationCannotBeProcessed, "VMOP will be created after the backoff. backoff: %q", backoff.String()) - return reconcile.Result{RequeueAfter: backoff}, nil + delay := h.calculateBackoff(finishedVMOPs, vm.GetCreationTimestamp()) + if delay > 0 { + h.recorder.Eventf(vd, corev1.EventTypeNormal, v1alpha2.ReasonVolumeMigrationCannotBeProcessed, "VMOP will be created after the backoff. backoff: %q", delay.String()) + return reconcile.Result{RequeueAfter: delay}, nil } vmop := newVolumeMigrationVMOP(vm.Name, vm.Namespace) @@ -127,9 +115,6 @@ func (h *MigrationHandler) Handle(ctx context.Context, vd *v1alpha2.VirtualDisk) h.recorder.Eventf(vd, corev1.EventTypeNormal, v1alpha2.ReasonVMOPStarted, "Volume migration is started. vmop.name: %q, vmop.namespace: %q", vmop.Name, vmop.Namespace) - delete(h.backoff, vm.UID) - delete(h.nextTime, vm.UID) - return reconcile.Result{}, nil } @@ -186,20 +171,7 @@ func (h *MigrationHandler) calculateBackoff(finishedVMOPs []*v1alpha2.VirtualMac break } - if failedCount == 0 { - return 0 - } - - baseDelay := 5 * time.Second - maxDelay := 5 * time.Minute - - // exponential backoff formula = baseDelay * 2^(failedCount - 1) - backoff := baseDelay * time.Duration(1<<(failedCount-1)) - if backoff > maxDelay { - backoff = maxDelay - } - - return backoff + return h.backoffSvc.CalculateBackoff(failedCount) } func newVolumeMigrationVMOP(vmName, namespace string) *v1alpha2.VirtualMachineOperation { diff --git a/images/virtualization-artifact/pkg/controller/volumemigration/internal/handler/migration_test.go b/images/virtualization-artifact/pkg/controller/volumemigration/internal/handler/migration_test.go index 91afa16bab..dd6d11d9fa 100644 --- a/images/virtualization-artifact/pkg/controller/volumemigration/internal/handler/migration_test.go +++ b/images/virtualization-artifact/pkg/controller/volumemigration/internal/handler/migration_test.go @@ -187,7 +187,7 @@ var _ = Describe("TestMigrationHandler", func() { Expect(err).NotTo(HaveOccurred()) //nolint:staticcheck // check requeue is not used Expect(result.Requeue).To(BeFalse()) - Expect(result.RequeueAfter).To(Equal(5 * time.Second)) + Expect(result.RequeueAfter).To(Equal(2 * time.Second)) // Check that no new VMOP was created vmopList := &v1alpha2.VirtualMachineOperationList{} @@ -219,7 +219,7 @@ var _ = Describe("TestMigrationHandler", func() { Expect(err).NotTo(HaveOccurred()) //nolint:staticcheck // check requeue is not used Expect(result.Requeue).To(BeFalse()) - Expect(result.RequeueAfter).To(Equal(10 * time.Second)) + Expect(result.RequeueAfter).To(Equal(4 * time.Second)) // Check that no new VMOP was created vmopList := &v1alpha2.VirtualMachineOperationList{} @@ -272,7 +272,7 @@ var _ = Describe("TestMigrationHandler", func() { } withCreationTime(secondTime, vmops...) backoff := handler.calculateBackoff(vmops, firstTime) - Expect(backoff).To(Equal(40 * time.Second)) + Expect(backoff).To(Equal(16 * time.Second)) }) It("should cap backoff at maximum delay", func() {