From 7ceb475f9945e9172a2743b0a14485d9590d2a19 Mon Sep 17 00:00:00 2001 From: Valeriy Khorunzhin Date: Thu, 19 Feb 2026 09:11:47 +0300 Subject: [PATCH 1/5] fix Signed-off-by: Valeriy Khorunzhin --- .../vmbda/internal/block_device_ready.go | 179 +++++++------- .../vmbda/internal/block_device_ready_test.go | 198 ++++++++++++++++ .../controller/vmbda/internal/interfaces.go | 6 + .../pkg/controller/vmbda/internal/mock.go | 220 +++++++++++++++++- 4 files changed, 513 insertions(+), 90 deletions(-) create mode 100644 images/virtualization-artifact/pkg/controller/vmbda/internal/block_device_ready_test.go diff --git a/images/virtualization-artifact/pkg/controller/vmbda/internal/block_device_ready.go b/images/virtualization-artifact/pkg/controller/vmbda/internal/block_device_ready.go index 5ddb39a426..b93b5e9afe 100644 --- a/images/virtualization-artifact/pkg/controller/vmbda/internal/block_device_ready.go +++ b/images/virtualization-artifact/pkg/controller/vmbda/internal/block_device_ready.go @@ -33,10 +33,10 @@ import ( ) type BlockDeviceReadyHandler struct { - attachment *service.AttachmentService + attachment AttachmentService } -func NewBlockDeviceReadyHandler(attachment *service.AttachmentService) *BlockDeviceReadyHandler { +func NewBlockDeviceReadyHandler(attachment AttachmentService) *BlockDeviceReadyHandler { return &BlockDeviceReadyHandler{ attachment: attachment, } @@ -57,91 +57,7 @@ func (h BlockDeviceReadyHandler) Handle(ctx context.Context, vmbda *v1alpha2.Vir switch vmbda.Spec.BlockDeviceRef.Kind { case v1alpha2.VMBDAObjectRefKindVirtualDisk: - vdKey := types.NamespacedName{ - Name: vmbda.Spec.BlockDeviceRef.Name, - Namespace: vmbda.Namespace, - } - - vd, err := h.attachment.GetVirtualDisk(ctx, vdKey.Name, vdKey.Namespace) - if err != nil { - return reconcile.Result{}, err - } - - if vd == nil { - cb. - Status(metav1.ConditionFalse). - Reason(vmbdacondition.BlockDeviceNotReady). - Message(fmt.Sprintf("VirtualDisk %q not found.", vdKey.String())) - return reconcile.Result{}, nil - } - - if vd.GetDeletionTimestamp() != nil { - cb. - Status(metav1.ConditionFalse). - Reason(vmbdacondition.BlockDeviceNotReady). - Message(fmt.Sprintf("VirtualDisk %q is being deleted. Delete VirtualMachineBlockDeviceAttachment to detach disk from the VirtualMachine.", vdKey.String())) - return reconcile.Result{}, nil - } - - if vd.Generation != vd.Status.ObservedGeneration { - cb. - Status(metav1.ConditionFalse). - Reason(vmbdacondition.BlockDeviceNotReady). - Message(fmt.Sprintf("Waiting for the VirtualDisk %q to be observed in its latest state generation.", vdKey.String())) - return reconcile.Result{}, nil - } - - if vd.Status.Phase != v1alpha2.DiskReady && vd.Status.Phase != v1alpha2.DiskWaitForFirstConsumer { - cb. - Status(metav1.ConditionFalse). - Reason(vmbdacondition.BlockDeviceNotReady). - Message(fmt.Sprintf("VirtualDisk %q is not ready to be attached to the virtual machine: waiting for the VirtualDisk to be ready for attachment.", vdKey.String())) - return reconcile.Result{}, nil - } - - if vd.Status.Phase == v1alpha2.DiskReady { - diskReadyCondition, _ := conditions.GetCondition(vdcondition.ReadyType, vd.Status.Conditions) - if diskReadyCondition.Status != metav1.ConditionTrue { - cb. - Status(metav1.ConditionFalse). - Reason(vmbdacondition.BlockDeviceNotReady). - Message(fmt.Sprintf("VirtualDisk %q is not Ready: waiting for the VirtualDisk to be Ready.", vdKey.String())) - return reconcile.Result{}, nil - } - } - - if vd.Status.Target.PersistentVolumeClaim == "" { - cb. - Status(metav1.ConditionFalse). - Reason(vmbdacondition.BlockDeviceNotReady). - Message("Waiting until VirtualDisk has associated PersistentVolumeClaim name.") - return reconcile.Result{}, nil - } - - ad := service.NewAttachmentDiskFromVirtualDisk(vd) - pvc, err := h.attachment.GetPersistentVolumeClaim(ctx, ad) - if err != nil { - return reconcile.Result{}, err - } - - if pvc == nil { - cb. - Status(metav1.ConditionFalse). - Reason(vmbdacondition.BlockDeviceNotReady). - Message(fmt.Sprintf("Underlying PersistentVolumeClaim %q not found.", vd.Status.Target)) - return reconcile.Result{}, nil - } - - if vd.Status.Phase == v1alpha2.DiskReady && pvc.Status.Phase != corev1.ClaimBound { - cb. - Status(metav1.ConditionFalse). - Reason(vmbdacondition.BlockDeviceNotReady). - Message(fmt.Sprintf("Underlying PersistentVolumeClaim %q not bound.", vd.Status.Target)) - return reconcile.Result{}, nil - } - - cb.Status(metav1.ConditionTrue).Reason(vmbdacondition.BlockDeviceReady) - return reconcile.Result{}, nil + return reconcile.Result{}, h.ValidateVirtualDiskReady(ctx, vmbda, cb) case v1alpha2.VMBDAObjectRefKindVirtualImage: viKey := types.NamespacedName{ Name: vmbda.Spec.BlockDeviceRef.Name, @@ -285,3 +201,92 @@ func (h BlockDeviceReadyHandler) Handle(ctx context.Context, vmbda *v1alpha2.Vir return reconcile.Result{}, fmt.Errorf("unknown block device kind %s", vmbda.Spec.BlockDeviceRef.Kind) } } + +func (h BlockDeviceReadyHandler) ValidateVirtualDiskReady(ctx context.Context, vmbda *v1alpha2.VirtualMachineBlockDeviceAttachment, cb *conditions.ConditionBuilder) error { + vdKey := types.NamespacedName{ + Name: vmbda.Spec.BlockDeviceRef.Name, + Namespace: vmbda.Namespace, + } + + vd, err := h.attachment.GetVirtualDisk(ctx, vdKey.Name, vdKey.Namespace) + if err != nil { + return err + } + + if vd == nil { + cb. + Status(metav1.ConditionFalse). + Reason(vmbdacondition.BlockDeviceNotReady). + Message(fmt.Sprintf("VirtualDisk %q not found.", vdKey.String())) + return nil + } + + if vd.GetDeletionTimestamp() != nil { + cb. + Status(metav1.ConditionFalse). + Reason(vmbdacondition.BlockDeviceNotReady). + Message(fmt.Sprintf("VirtualDisk %q is being deleted. Delete VirtualMachineBlockDeviceAttachment to detach disk from the VirtualMachine.", vdKey.String())) + return nil + } + + if vd.Generation != vd.Status.ObservedGeneration { + cb. + Status(metav1.ConditionFalse). + Reason(vmbdacondition.BlockDeviceNotReady). + Message(fmt.Sprintf("Waiting for the VirtualDisk %q to be observed in its latest state generation.", vdKey.String())) + return nil + } + + diskPhaseReadyOrMigrating := vd.Status.Phase == v1alpha2.DiskReady || vd.Status.Phase == v1alpha2.DiskMigrating + if !diskPhaseReadyOrMigrating && vd.Status.Phase != v1alpha2.DiskWaitForFirstConsumer { + cb. + Status(metav1.ConditionFalse). + Reason(vmbdacondition.BlockDeviceNotReady). + Message(fmt.Sprintf("VirtualDisk %q is not ready to be attached to the virtual machine: waiting for the VirtualDisk to be ready for attachment.", vdKey.String())) + return nil + } + + if diskPhaseReadyOrMigrating { + diskReadyCondition, _ := conditions.GetCondition(vdcondition.ReadyType, vd.Status.Conditions) + if diskReadyCondition.Status != metav1.ConditionTrue { + cb. + Status(metav1.ConditionFalse). + Reason(vmbdacondition.BlockDeviceNotReady). + Message(fmt.Sprintf("VirtualDisk %q is not Ready: waiting for the VirtualDisk to be Ready.", vdKey.String())) + return nil + } + } + + if vd.Status.Target.PersistentVolumeClaim == "" { + cb. + Status(metav1.ConditionFalse). + Reason(vmbdacondition.BlockDeviceNotReady). + Message("Waiting until VirtualDisk has associated PersistentVolumeClaim name.") + return nil + } + + ad := service.NewAttachmentDiskFromVirtualDisk(vd) + pvc, err := h.attachment.GetPersistentVolumeClaim(ctx, ad) + if err != nil { + return err + } + + if pvc == nil { + cb. + Status(metav1.ConditionFalse). + Reason(vmbdacondition.BlockDeviceNotReady). + Message(fmt.Sprintf("Underlying PersistentVolumeClaim %q not found.", vd.Status.Target)) + return nil + } + + if pvc.Status.Phase != corev1.ClaimBound { + cb. + Status(metav1.ConditionFalse). + Reason(vmbdacondition.BlockDeviceNotReady). + Message(fmt.Sprintf("Underlying PersistentVolumeClaim %q not bound.", vd.Status.Target)) + return nil + } + + cb.Status(metav1.ConditionTrue).Reason(vmbdacondition.BlockDeviceReady) + return nil +} diff --git a/images/virtualization-artifact/pkg/controller/vmbda/internal/block_device_ready_test.go b/images/virtualization-artifact/pkg/controller/vmbda/internal/block_device_ready_test.go new file mode 100644 index 0000000000..bdb8317881 --- /dev/null +++ b/images/virtualization-artifact/pkg/controller/vmbda/internal/block_device_ready_test.go @@ -0,0 +1,198 @@ +/* +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 internal + +import ( + "context" + "errors" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + vmbdaBuilder "github.com/deckhouse/virtualization-controller/pkg/builder/vmbda" + "github.com/deckhouse/virtualization-controller/pkg/controller/conditions" + "github.com/deckhouse/virtualization-controller/pkg/controller/service" + "github.com/deckhouse/virtualization/api/core/v1alpha2" + "github.com/deckhouse/virtualization/api/core/v1alpha2/vdcondition" + "github.com/deckhouse/virtualization/api/core/v1alpha2/vmbdacondition" +) + +var _ = Describe("BlockDeviceReadyHandler ValidateVirtualDiskReady", func() { + var ( + vmbda *v1alpha2.VirtualMachineBlockDeviceAttachment + cb *conditions.ConditionBuilder + attachmentServiceMock AttachmentServiceMock + ctx context.Context + ) + + BeforeEach(func() { + vmbda = vmbdaBuilder.NewEmpty("vmbda", "default") + cb = conditions.NewConditionBuilder(vmbdacondition.BlockDeviceReadyType) + attachmentServiceMock = AttachmentServiceMock{ + GetVirtualDiskFunc: func(_ context.Context, _, _ string) (*v1alpha2.VirtualDisk, error) { + return nil, nil + }, + GetPersistentVolumeClaimFunc: func(_ context.Context, _ *service.AttachmentDisk) (*corev1.PersistentVolumeClaim, error) { + return nil, nil + }, + } + ctx = context.Background() + }) + + It("returns error when getting VirtualDisk fails", func() { + attachmentServiceMock.GetVirtualDiskFunc = func(_ context.Context, _, _ string) (*v1alpha2.VirtualDisk, error) { + return nil, errors.New("error") + } + err := NewBlockDeviceReadyHandler(&attachmentServiceMock).ValidateVirtualDiskReady(ctx, vmbda, cb) + Expect(err).To(HaveOccurred()) + }) + + It("sets condition to False when VirtualDisk is nil", func() { + err := NewBlockDeviceReadyHandler(&attachmentServiceMock).ValidateVirtualDiskReady(ctx, vmbda, cb) + Expect(err).NotTo(HaveOccurred()) + Expect(cb.Condition().Status).To(Equal(metav1.ConditionFalse)) + Expect(cb.Condition().Reason).To(Equal(string(vmbdacondition.BlockDeviceNotReady))) + }) + + It("sets condition to False when VirtualDisk has DeletionTimestamp", func() { + attachmentServiceMock.GetVirtualDiskFunc = func(_ context.Context, _, _ string) (*v1alpha2.VirtualDisk, error) { + return &v1alpha2.VirtualDisk{ + ObjectMeta: metav1.ObjectMeta{ + DeletionTimestamp: &metav1.Time{}, + }, + }, nil + } + err := NewBlockDeviceReadyHandler(&attachmentServiceMock).ValidateVirtualDiskReady(ctx, vmbda, cb) + Expect(err).NotTo(HaveOccurred()) + Expect(cb.Condition().Status).To(Equal(metav1.ConditionFalse)) + Expect(cb.Condition().Reason).To(Equal(string(vmbdacondition.BlockDeviceNotReady))) + }) + + It("returns error when getting PVC fails", func() { + attachmentServiceMock.GetVirtualDiskFunc = func(_ context.Context, _, _ string) (*v1alpha2.VirtualDisk, error) { + return generateVD(v1alpha2.DiskReady, metav1.ConditionTrue), nil + } + attachmentServiceMock.GetPersistentVolumeClaimFunc = func(_ context.Context, _ *service.AttachmentDisk) (*corev1.PersistentVolumeClaim, error) { + return nil, errors.New("error") + } + err := NewBlockDeviceReadyHandler(&attachmentServiceMock).ValidateVirtualDiskReady(ctx, vmbda, cb) + Expect(err).To(HaveOccurred()) + }) + + It("sets condition to False when PVC is nil", func() { + attachmentServiceMock.GetVirtualDiskFunc = func(_ context.Context, _, _ string) (*v1alpha2.VirtualDisk, error) { + return generateVD(v1alpha2.DiskReady, metav1.ConditionTrue), nil + } + attachmentServiceMock.GetPersistentVolumeClaimFunc = func(_ context.Context, _ *service.AttachmentDisk) (*corev1.PersistentVolumeClaim, error) { + return nil, nil + } + err := NewBlockDeviceReadyHandler(&attachmentServiceMock).ValidateVirtualDiskReady(ctx, vmbda, cb) + Expect(err).NotTo(HaveOccurred()) + Expect(cb.Condition().Status).To(Equal(metav1.ConditionFalse)) + Expect(cb.Condition().Reason).To(Equal(string(vmbdacondition.BlockDeviceNotReady))) + }) + + It("sets condition to False when VirtualDisk DiskReady condition is False", func() { + attachmentServiceMock.GetVirtualDiskFunc = func(_ context.Context, _, _ string) (*v1alpha2.VirtualDisk, error) { + return generateVD(v1alpha2.DiskReady, metav1.ConditionFalse), nil + } + attachmentServiceMock.GetPersistentVolumeClaimFunc = func(_ context.Context, _ *service.AttachmentDisk) (*corev1.PersistentVolumeClaim, error) { + return nil, nil + } + err := NewBlockDeviceReadyHandler(&attachmentServiceMock).ValidateVirtualDiskReady(ctx, vmbda, cb) + Expect(err).NotTo(HaveOccurred()) + Expect(cb.Condition().Status).To(Equal(metav1.ConditionFalse)) + Expect(cb.Condition().Reason).To(Equal(string(vmbdacondition.BlockDeviceNotReady))) + }) + + DescribeTable("sets condition status based on VirtualDisk phase", func(phase v1alpha2.DiskPhase, expectedStatus metav1.ConditionStatus) { + attachmentServiceMock.GetVirtualDiskFunc = func(_ context.Context, _, _ string) (*v1alpha2.VirtualDisk, error) { + return generateVD(phase, metav1.ConditionTrue), nil + } + attachmentServiceMock.GetPersistentVolumeClaimFunc = func(_ context.Context, _ *service.AttachmentDisk) (*corev1.PersistentVolumeClaim, error) { + return &corev1.PersistentVolumeClaim{ + Status: corev1.PersistentVolumeClaimStatus{ + Phase: corev1.ClaimBound, + }, + }, nil + } + err := NewBlockDeviceReadyHandler(&attachmentServiceMock).ValidateVirtualDiskReady(ctx, vmbda, cb) + Expect(err).NotTo(HaveOccurred()) + Expect(cb.Condition().Status).To(Equal(expectedStatus)) + if expectedStatus == metav1.ConditionTrue { + Expect(cb.Condition().Reason).To(Equal(vmbdacondition.BlockDeviceReady.String())) + } + }, + Entry("DiskReady", v1alpha2.DiskReady, metav1.ConditionTrue), + Entry("DiskMigrating", v1alpha2.DiskMigrating, metav1.ConditionTrue), + Entry("DiskWaitForFirstConsumer", v1alpha2.DiskWaitForFirstConsumer, metav1.ConditionTrue), + Entry("DiskPending", v1alpha2.DiskPending, metav1.ConditionFalse), + Entry("DiskProvisioning", v1alpha2.DiskProvisioning, metav1.ConditionFalse), + Entry("DiskWaitForUserUpload", v1alpha2.DiskWaitForUserUpload, metav1.ConditionFalse), + Entry("DiskResizing", v1alpha2.DiskResizing, metav1.ConditionFalse), + Entry("DiskFailed", v1alpha2.DiskFailed, metav1.ConditionFalse), + Entry("DiskLost", v1alpha2.DiskLost, metav1.ConditionFalse), + Entry("DiskExporting", v1alpha2.DiskExporting, metav1.ConditionFalse), + Entry("DiskTerminating", v1alpha2.DiskTerminating, metav1.ConditionFalse), + ) + + DescribeTable("sets condition status based on PVC phase", func(phase corev1.PersistentVolumeClaimPhase, expectedStatus metav1.ConditionStatus) { + attachmentServiceMock.GetVirtualDiskFunc = func(_ context.Context, _, _ string) (*v1alpha2.VirtualDisk, error) { + return generateVD(v1alpha2.DiskReady, metav1.ConditionTrue), nil + } + attachmentServiceMock.GetPersistentVolumeClaimFunc = func(_ context.Context, _ *service.AttachmentDisk) (*corev1.PersistentVolumeClaim, error) { + return &corev1.PersistentVolumeClaim{ + Status: corev1.PersistentVolumeClaimStatus{ + Phase: phase, + }, + }, nil + } + err := NewBlockDeviceReadyHandler(&attachmentServiceMock).ValidateVirtualDiskReady(ctx, vmbda, cb) + Expect(err).NotTo(HaveOccurred()) + Expect(cb.Condition().Status).To(Equal(expectedStatus)) + if expectedStatus == metav1.ConditionTrue { + Expect(cb.Condition().Reason).To(Equal(vmbdacondition.BlockDeviceReady.String())) + } + }, + Entry("ClaimBound", corev1.ClaimBound, metav1.ConditionTrue), + Entry("ClaimPending", corev1.ClaimPending, metav1.ConditionFalse), + Entry("ClaimLost", corev1.ClaimLost, metav1.ConditionFalse), + ) +}) + +func generateVD(phase v1alpha2.DiskPhase, readyConditionStatus metav1.ConditionStatus) *v1alpha2.VirtualDisk { + return &v1alpha2.VirtualDisk{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vd", + Namespace: "namespace", + }, + Status: v1alpha2.VirtualDiskStatus{ + Phase: phase, + Conditions: []metav1.Condition{ + { + Type: string(vdcondition.ReadyType), + Status: readyConditionStatus, + }, + }, + Target: v1alpha2.DiskTarget{ + PersistentVolumeClaim: "pvc", + }, + }, + } +} diff --git a/images/virtualization-artifact/pkg/controller/vmbda/internal/interfaces.go b/images/virtualization-artifact/pkg/controller/vmbda/internal/interfaces.go index dc7b5adb3e..5fcbf16099 100644 --- a/images/virtualization-artifact/pkg/controller/vmbda/internal/interfaces.go +++ b/images/virtualization-artifact/pkg/controller/vmbda/internal/interfaces.go @@ -19,8 +19,10 @@ package internal import ( "context" + corev1 "k8s.io/api/core/v1" virtv1 "kubevirt.io/api/core/v1" + "github.com/deckhouse/virtualization-controller/pkg/controller/service" "github.com/deckhouse/virtualization/api/core/v1alpha2" ) @@ -30,4 +32,8 @@ type AttachmentService interface { GetVirtualMachine(ctx context.Context, name, namespace string) (*v1alpha2.VirtualMachine, error) GetKVVMI(ctx context.Context, vm *v1alpha2.VirtualMachine) (*virtv1.VirtualMachineInstance, error) GetKVVM(ctx context.Context, vm *v1alpha2.VirtualMachine) (*virtv1.VirtualMachine, error) + GetVirtualDisk(ctx context.Context, name, namespace string) (*v1alpha2.VirtualDisk, error) + GetVirtualImage(ctx context.Context, name, namespace string) (*v1alpha2.VirtualImage, error) + GetClusterVirtualImage(ctx context.Context, name string) (*v1alpha2.ClusterVirtualImage, error) + GetPersistentVolumeClaim(ctx context.Context, ad *service.AttachmentDisk) (*corev1.PersistentVolumeClaim, error) } diff --git a/images/virtualization-artifact/pkg/controller/vmbda/internal/mock.go b/images/virtualization-artifact/pkg/controller/vmbda/internal/mock.go index 8f0e6440c4..34b4f669ef 100644 --- a/images/virtualization-artifact/pkg/controller/vmbda/internal/mock.go +++ b/images/virtualization-artifact/pkg/controller/vmbda/internal/mock.go @@ -5,7 +5,9 @@ package internal import ( "context" + "github.com/deckhouse/virtualization-controller/pkg/controller/service" "github.com/deckhouse/virtualization/api/core/v1alpha2" + corev1 "k8s.io/api/core/v1" virtv1 "kubevirt.io/api/core/v1" "sync" ) @@ -20,12 +22,24 @@ var _ AttachmentService = &AttachmentServiceMock{} // // // make and configure a mocked AttachmentService // mockedAttachmentService := &AttachmentServiceMock{ +// GetClusterVirtualImageFunc: func(ctx context.Context, name string) (*v1alpha2.ClusterVirtualImage, error) { +// panic("mock out the GetClusterVirtualImage method") +// }, // GetKVVMFunc: func(ctx context.Context, vm *v1alpha2.VirtualMachine) (*virtv1.VirtualMachine, error) { // panic("mock out the GetKVVM method") // }, // GetKVVMIFunc: func(ctx context.Context, vm *v1alpha2.VirtualMachine) (*virtv1.VirtualMachineInstance, error) { // panic("mock out the GetKVVMI method") // }, +// GetPersistentVolumeClaimFunc: func(ctx context.Context, ad *service.AttachmentDisk) (*corev1.PersistentVolumeClaim, error) { +// panic("mock out the GetPersistentVolumeClaim method") +// }, +// GetVirtualDiskFunc: func(ctx context.Context, name string, namespace string) (*v1alpha2.VirtualDisk, error) { +// panic("mock out the GetVirtualDisk method") +// }, +// GetVirtualImageFunc: func(ctx context.Context, name string, namespace string) (*v1alpha2.VirtualImage, error) { +// panic("mock out the GetVirtualImage method") +// }, // GetVirtualMachineFunc: func(ctx context.Context, name string, namespace string) (*v1alpha2.VirtualMachine, error) { // panic("mock out the GetVirtualMachine method") // }, @@ -36,17 +50,36 @@ var _ AttachmentService = &AttachmentServiceMock{} // // } type AttachmentServiceMock struct { + // GetClusterVirtualImageFunc mocks the GetClusterVirtualImage method. + GetClusterVirtualImageFunc func(ctx context.Context, name string) (*v1alpha2.ClusterVirtualImage, error) + // GetKVVMFunc mocks the GetKVVM method. GetKVVMFunc func(ctx context.Context, vm *v1alpha2.VirtualMachine) (*virtv1.VirtualMachine, error) // GetKVVMIFunc mocks the GetKVVMI method. GetKVVMIFunc func(ctx context.Context, vm *v1alpha2.VirtualMachine) (*virtv1.VirtualMachineInstance, error) + // GetPersistentVolumeClaimFunc mocks the GetPersistentVolumeClaim method. + GetPersistentVolumeClaimFunc func(ctx context.Context, ad *service.AttachmentDisk) (*corev1.PersistentVolumeClaim, error) + + // GetVirtualDiskFunc mocks the GetVirtualDisk method. + GetVirtualDiskFunc func(ctx context.Context, name string, namespace string) (*v1alpha2.VirtualDisk, error) + + // GetVirtualImageFunc mocks the GetVirtualImage method. + GetVirtualImageFunc func(ctx context.Context, name string, namespace string) (*v1alpha2.VirtualImage, error) + // GetVirtualMachineFunc mocks the GetVirtualMachine method. GetVirtualMachineFunc func(ctx context.Context, name string, namespace string) (*v1alpha2.VirtualMachine, error) // calls tracks calls to the methods. calls struct { + // GetClusterVirtualImage holds details about calls to the GetClusterVirtualImage method. + GetClusterVirtualImage []struct { + // Ctx is the ctx argument value. + Ctx context.Context + // Name is the name argument value. + Name string + } // GetKVVM holds details about calls to the GetKVVM method. GetKVVM []struct { // Ctx is the ctx argument value. @@ -61,6 +94,31 @@ type AttachmentServiceMock struct { // VM is the vm argument value. VM *v1alpha2.VirtualMachine } + // GetPersistentVolumeClaim holds details about calls to the GetPersistentVolumeClaim method. + GetPersistentVolumeClaim []struct { + // Ctx is the ctx argument value. + Ctx context.Context + // Ad is the ad argument value. + Ad *service.AttachmentDisk + } + // GetVirtualDisk holds details about calls to the GetVirtualDisk method. + GetVirtualDisk []struct { + // Ctx is the ctx argument value. + Ctx context.Context + // Name is the name argument value. + Name string + // Namespace is the namespace argument value. + Namespace string + } + // GetVirtualImage holds details about calls to the GetVirtualImage method. + GetVirtualImage []struct { + // Ctx is the ctx argument value. + Ctx context.Context + // Name is the name argument value. + Name string + // Namespace is the namespace argument value. + Namespace string + } // GetVirtualMachine holds details about calls to the GetVirtualMachine method. GetVirtualMachine []struct { // Ctx is the ctx argument value. @@ -71,9 +129,49 @@ type AttachmentServiceMock struct { Namespace string } } - lockGetKVVM sync.RWMutex - lockGetKVVMI sync.RWMutex - lockGetVirtualMachine sync.RWMutex + lockGetClusterVirtualImage sync.RWMutex + lockGetKVVM sync.RWMutex + lockGetKVVMI sync.RWMutex + lockGetPersistentVolumeClaim sync.RWMutex + lockGetVirtualDisk sync.RWMutex + lockGetVirtualImage sync.RWMutex + lockGetVirtualMachine sync.RWMutex +} + +// GetClusterVirtualImage calls GetClusterVirtualImageFunc. +func (mock *AttachmentServiceMock) GetClusterVirtualImage(ctx context.Context, name string) (*v1alpha2.ClusterVirtualImage, error) { + if mock.GetClusterVirtualImageFunc == nil { + panic("AttachmentServiceMock.GetClusterVirtualImageFunc: method is nil but AttachmentService.GetClusterVirtualImage was just called") + } + callInfo := struct { + Ctx context.Context + Name string + }{ + Ctx: ctx, + Name: name, + } + mock.lockGetClusterVirtualImage.Lock() + mock.calls.GetClusterVirtualImage = append(mock.calls.GetClusterVirtualImage, callInfo) + mock.lockGetClusterVirtualImage.Unlock() + return mock.GetClusterVirtualImageFunc(ctx, name) +} + +// GetClusterVirtualImageCalls gets all the calls that were made to GetClusterVirtualImage. +// Check the length with: +// +// len(mockedAttachmentService.GetClusterVirtualImageCalls()) +func (mock *AttachmentServiceMock) GetClusterVirtualImageCalls() []struct { + Ctx context.Context + Name string +} { + var calls []struct { + Ctx context.Context + Name string + } + mock.lockGetClusterVirtualImage.RLock() + calls = mock.calls.GetClusterVirtualImage + mock.lockGetClusterVirtualImage.RUnlock() + return calls } // GetKVVM calls GetKVVMFunc. @@ -148,6 +246,122 @@ func (mock *AttachmentServiceMock) GetKVVMICalls() []struct { return calls } +// GetPersistentVolumeClaim calls GetPersistentVolumeClaimFunc. +func (mock *AttachmentServiceMock) GetPersistentVolumeClaim(ctx context.Context, ad *service.AttachmentDisk) (*corev1.PersistentVolumeClaim, error) { + if mock.GetPersistentVolumeClaimFunc == nil { + panic("AttachmentServiceMock.GetPersistentVolumeClaimFunc: method is nil but AttachmentService.GetPersistentVolumeClaim was just called") + } + callInfo := struct { + Ctx context.Context + Ad *service.AttachmentDisk + }{ + Ctx: ctx, + Ad: ad, + } + mock.lockGetPersistentVolumeClaim.Lock() + mock.calls.GetPersistentVolumeClaim = append(mock.calls.GetPersistentVolumeClaim, callInfo) + mock.lockGetPersistentVolumeClaim.Unlock() + return mock.GetPersistentVolumeClaimFunc(ctx, ad) +} + +// GetPersistentVolumeClaimCalls gets all the calls that were made to GetPersistentVolumeClaim. +// Check the length with: +// +// len(mockedAttachmentService.GetPersistentVolumeClaimCalls()) +func (mock *AttachmentServiceMock) GetPersistentVolumeClaimCalls() []struct { + Ctx context.Context + Ad *service.AttachmentDisk +} { + var calls []struct { + Ctx context.Context + Ad *service.AttachmentDisk + } + mock.lockGetPersistentVolumeClaim.RLock() + calls = mock.calls.GetPersistentVolumeClaim + mock.lockGetPersistentVolumeClaim.RUnlock() + return calls +} + +// GetVirtualDisk calls GetVirtualDiskFunc. +func (mock *AttachmentServiceMock) GetVirtualDisk(ctx context.Context, name string, namespace string) (*v1alpha2.VirtualDisk, error) { + if mock.GetVirtualDiskFunc == nil { + panic("AttachmentServiceMock.GetVirtualDiskFunc: method is nil but AttachmentService.GetVirtualDisk was just called") + } + callInfo := struct { + Ctx context.Context + Name string + Namespace string + }{ + Ctx: ctx, + Name: name, + Namespace: namespace, + } + mock.lockGetVirtualDisk.Lock() + mock.calls.GetVirtualDisk = append(mock.calls.GetVirtualDisk, callInfo) + mock.lockGetVirtualDisk.Unlock() + return mock.GetVirtualDiskFunc(ctx, name, namespace) +} + +// GetVirtualDiskCalls gets all the calls that were made to GetVirtualDisk. +// Check the length with: +// +// len(mockedAttachmentService.GetVirtualDiskCalls()) +func (mock *AttachmentServiceMock) GetVirtualDiskCalls() []struct { + Ctx context.Context + Name string + Namespace string +} { + var calls []struct { + Ctx context.Context + Name string + Namespace string + } + mock.lockGetVirtualDisk.RLock() + calls = mock.calls.GetVirtualDisk + mock.lockGetVirtualDisk.RUnlock() + return calls +} + +// GetVirtualImage calls GetVirtualImageFunc. +func (mock *AttachmentServiceMock) GetVirtualImage(ctx context.Context, name string, namespace string) (*v1alpha2.VirtualImage, error) { + if mock.GetVirtualImageFunc == nil { + panic("AttachmentServiceMock.GetVirtualImageFunc: method is nil but AttachmentService.GetVirtualImage was just called") + } + callInfo := struct { + Ctx context.Context + Name string + Namespace string + }{ + Ctx: ctx, + Name: name, + Namespace: namespace, + } + mock.lockGetVirtualImage.Lock() + mock.calls.GetVirtualImage = append(mock.calls.GetVirtualImage, callInfo) + mock.lockGetVirtualImage.Unlock() + return mock.GetVirtualImageFunc(ctx, name, namespace) +} + +// GetVirtualImageCalls gets all the calls that were made to GetVirtualImage. +// Check the length with: +// +// len(mockedAttachmentService.GetVirtualImageCalls()) +func (mock *AttachmentServiceMock) GetVirtualImageCalls() []struct { + Ctx context.Context + Name string + Namespace string +} { + var calls []struct { + Ctx context.Context + Name string + Namespace string + } + mock.lockGetVirtualImage.RLock() + calls = mock.calls.GetVirtualImage + mock.lockGetVirtualImage.RUnlock() + return calls +} + // GetVirtualMachine calls GetVirtualMachineFunc. func (mock *AttachmentServiceMock) GetVirtualMachine(ctx context.Context, name string, namespace string) (*v1alpha2.VirtualMachine, error) { if mock.GetVirtualMachineFunc == nil { From 697e9daa7e401e2a746fe82063fb81db9a0de09d Mon Sep 17 00:00:00 2001 From: Valeriy Khorunzhin Date: Thu, 19 Feb 2026 23:41:02 +0300 Subject: [PATCH 2/5] fix Signed-off-by: Valeriy Khorunzhin --- .../vmbda/internal/block_device_ready.go | 2 +- .../vmbda/internal/block_device_ready_test.go | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/images/virtualization-artifact/pkg/controller/vmbda/internal/block_device_ready.go b/images/virtualization-artifact/pkg/controller/vmbda/internal/block_device_ready.go index b93b5e9afe..97e4c247f4 100644 --- a/images/virtualization-artifact/pkg/controller/vmbda/internal/block_device_ready.go +++ b/images/virtualization-artifact/pkg/controller/vmbda/internal/block_device_ready.go @@ -279,7 +279,7 @@ func (h BlockDeviceReadyHandler) ValidateVirtualDiskReady(ctx context.Context, v return nil } - if pvc.Status.Phase != corev1.ClaimBound { + if diskPhaseReadyOrMigrating && pvc.Status.Phase != corev1.ClaimBound { cb. Status(metav1.ConditionFalse). Reason(vmbdacondition.BlockDeviceNotReady). diff --git a/images/virtualization-artifact/pkg/controller/vmbda/internal/block_device_ready_test.go b/images/virtualization-artifact/pkg/controller/vmbda/internal/block_device_ready_test.go index bdb8317881..79d34f6ede 100644 --- a/images/virtualization-artifact/pkg/controller/vmbda/internal/block_device_ready_test.go +++ b/images/virtualization-artifact/pkg/controller/vmbda/internal/block_device_ready_test.go @@ -152,14 +152,14 @@ var _ = Describe("BlockDeviceReadyHandler ValidateVirtualDiskReady", func() { Entry("DiskTerminating", v1alpha2.DiskTerminating, metav1.ConditionFalse), ) - DescribeTable("sets condition status based on PVC phase", func(phase corev1.PersistentVolumeClaimPhase, expectedStatus metav1.ConditionStatus) { + DescribeTable("sets condition status based on VD and PVC phase", func(vdPhase v1alpha2.DiskPhase, pvcPhase corev1.PersistentVolumeClaimPhase, expectedStatus metav1.ConditionStatus) { attachmentServiceMock.GetVirtualDiskFunc = func(_ context.Context, _, _ string) (*v1alpha2.VirtualDisk, error) { - return generateVD(v1alpha2.DiskReady, metav1.ConditionTrue), nil + return generateVD(vdPhase, metav1.ConditionTrue), nil } attachmentServiceMock.GetPersistentVolumeClaimFunc = func(_ context.Context, _ *service.AttachmentDisk) (*corev1.PersistentVolumeClaim, error) { return &corev1.PersistentVolumeClaim{ Status: corev1.PersistentVolumeClaimStatus{ - Phase: phase, + Phase: pvcPhase, }, }, nil } @@ -170,9 +170,11 @@ var _ = Describe("BlockDeviceReadyHandler ValidateVirtualDiskReady", func() { Expect(cb.Condition().Reason).To(Equal(vmbdacondition.BlockDeviceReady.String())) } }, - Entry("ClaimBound", corev1.ClaimBound, metav1.ConditionTrue), - Entry("ClaimPending", corev1.ClaimPending, metav1.ConditionFalse), - Entry("ClaimLost", corev1.ClaimLost, metav1.ConditionFalse), + Entry("VirtualDisk Ready and PVC ClaimBound", v1alpha2.DiskReady, corev1.ClaimBound, metav1.ConditionTrue), + Entry("VirtualDisk Ready and PVC ClaimPending", v1alpha2.DiskReady, corev1.ClaimPending, metav1.ConditionFalse), + Entry("VirtualDisk Ready and PVC ClaimLost", v1alpha2.DiskReady, corev1.ClaimLost, metav1.ConditionFalse), + Entry("VirtualDisk Migrating and PVC ClaimLost", v1alpha2.DiskMigrating, corev1.ClaimLost, metav1.ConditionFalse), + Entry("VirtualDisk WaitForFirstConsumer and PVC ClaimLost", v1alpha2.DiskWaitForFirstConsumer, corev1.ClaimLost, metav1.ConditionTrue), ) }) From c4cb4323b55391c0528d2e7e5623ecad78e90a38 Mon Sep 17 00:00:00 2001 From: Valeriy Khorunzhin Date: Fri, 20 Feb 2026 16:39:39 +0300 Subject: [PATCH 3/5] svc Signed-off-by: Valeriy Khorunzhin --- .../vmbda/internal/block_device_ready.go | 6 +++--- .../vmbda/internal/block_device_ready_test.go | 14 +++++++------- .../controller/vmbda/internal/interfaces.go | 4 ++-- .../controller/vmbda/internal/life_cycle.go | 19 ++++++++++--------- .../pkg/controller/vmbda/internal/mock.go | 16 ++++++++-------- .../internal}/service/attachment_service.go | 5 +++-- .../service/attachment_service_test.go | 5 +++-- .../attachment_conflict_validator.go | 6 +++--- .../pkg/controller/vmbda/vmbda_controller.go | 3 ++- .../pkg/controller/vmbda/vmbda_webhook.go | 3 ++- 10 files changed, 43 insertions(+), 38 deletions(-) rename images/virtualization-artifact/pkg/controller/{ => vmbda/internal}/service/attachment_service.go (98%) rename images/virtualization-artifact/pkg/controller/{ => vmbda/internal}/service/attachment_service_test.go (97%) diff --git a/images/virtualization-artifact/pkg/controller/vmbda/internal/block_device_ready.go b/images/virtualization-artifact/pkg/controller/vmbda/internal/block_device_ready.go index 97e4c247f4..d23b3b490d 100644 --- a/images/virtualization-artifact/pkg/controller/vmbda/internal/block_device_ready.go +++ b/images/virtualization-artifact/pkg/controller/vmbda/internal/block_device_ready.go @@ -26,7 +26,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" + intsvc "github.com/deckhouse/virtualization-controller/pkg/controller/vmbda/internal/service" "github.com/deckhouse/virtualization/api/core/v1alpha2" "github.com/deckhouse/virtualization/api/core/v1alpha2/vdcondition" "github.com/deckhouse/virtualization/api/core/v1alpha2/vmbdacondition" @@ -109,7 +109,7 @@ func (h BlockDeviceReadyHandler) Handle(ctx context.Context, vmbda *v1alpha2.Vir Message("Waiting until VirtualImage has associated PersistentVolumeClaim name.") return reconcile.Result{}, nil } - ad := service.NewAttachmentDiskFromVirtualImage(vi) + ad := intsvc.NewAttachmentDiskFromVirtualImage(vi) pvc, err := h.attachment.GetPersistentVolumeClaim(ctx, ad) if err != nil { return reconcile.Result{}, err @@ -265,7 +265,7 @@ func (h BlockDeviceReadyHandler) ValidateVirtualDiskReady(ctx context.Context, v return nil } - ad := service.NewAttachmentDiskFromVirtualDisk(vd) + ad := intsvc.NewAttachmentDiskFromVirtualDisk(vd) pvc, err := h.attachment.GetPersistentVolumeClaim(ctx, ad) if err != nil { return err diff --git a/images/virtualization-artifact/pkg/controller/vmbda/internal/block_device_ready_test.go b/images/virtualization-artifact/pkg/controller/vmbda/internal/block_device_ready_test.go index 79d34f6ede..334bc017d9 100644 --- a/images/virtualization-artifact/pkg/controller/vmbda/internal/block_device_ready_test.go +++ b/images/virtualization-artifact/pkg/controller/vmbda/internal/block_device_ready_test.go @@ -27,7 +27,7 @@ import ( vmbdaBuilder "github.com/deckhouse/virtualization-controller/pkg/builder/vmbda" "github.com/deckhouse/virtualization-controller/pkg/controller/conditions" - "github.com/deckhouse/virtualization-controller/pkg/controller/service" + intsvc "github.com/deckhouse/virtualization-controller/pkg/controller/vmbda/internal/service" "github.com/deckhouse/virtualization/api/core/v1alpha2" "github.com/deckhouse/virtualization/api/core/v1alpha2/vdcondition" "github.com/deckhouse/virtualization/api/core/v1alpha2/vmbdacondition" @@ -48,7 +48,7 @@ var _ = Describe("BlockDeviceReadyHandler ValidateVirtualDiskReady", func() { GetVirtualDiskFunc: func(_ context.Context, _, _ string) (*v1alpha2.VirtualDisk, error) { return nil, nil }, - GetPersistentVolumeClaimFunc: func(_ context.Context, _ *service.AttachmentDisk) (*corev1.PersistentVolumeClaim, error) { + GetPersistentVolumeClaimFunc: func(_ context.Context, _ *intsvc.AttachmentDisk) (*corev1.PersistentVolumeClaim, error) { return nil, nil }, } @@ -88,7 +88,7 @@ var _ = Describe("BlockDeviceReadyHandler ValidateVirtualDiskReady", func() { attachmentServiceMock.GetVirtualDiskFunc = func(_ context.Context, _, _ string) (*v1alpha2.VirtualDisk, error) { return generateVD(v1alpha2.DiskReady, metav1.ConditionTrue), nil } - attachmentServiceMock.GetPersistentVolumeClaimFunc = func(_ context.Context, _ *service.AttachmentDisk) (*corev1.PersistentVolumeClaim, error) { + attachmentServiceMock.GetPersistentVolumeClaimFunc = func(_ context.Context, _ *intsvc.AttachmentDisk) (*corev1.PersistentVolumeClaim, error) { return nil, errors.New("error") } err := NewBlockDeviceReadyHandler(&attachmentServiceMock).ValidateVirtualDiskReady(ctx, vmbda, cb) @@ -99,7 +99,7 @@ var _ = Describe("BlockDeviceReadyHandler ValidateVirtualDiskReady", func() { attachmentServiceMock.GetVirtualDiskFunc = func(_ context.Context, _, _ string) (*v1alpha2.VirtualDisk, error) { return generateVD(v1alpha2.DiskReady, metav1.ConditionTrue), nil } - attachmentServiceMock.GetPersistentVolumeClaimFunc = func(_ context.Context, _ *service.AttachmentDisk) (*corev1.PersistentVolumeClaim, error) { + attachmentServiceMock.GetPersistentVolumeClaimFunc = func(_ context.Context, _ *intsvc.AttachmentDisk) (*corev1.PersistentVolumeClaim, error) { return nil, nil } err := NewBlockDeviceReadyHandler(&attachmentServiceMock).ValidateVirtualDiskReady(ctx, vmbda, cb) @@ -112,7 +112,7 @@ var _ = Describe("BlockDeviceReadyHandler ValidateVirtualDiskReady", func() { attachmentServiceMock.GetVirtualDiskFunc = func(_ context.Context, _, _ string) (*v1alpha2.VirtualDisk, error) { return generateVD(v1alpha2.DiskReady, metav1.ConditionFalse), nil } - attachmentServiceMock.GetPersistentVolumeClaimFunc = func(_ context.Context, _ *service.AttachmentDisk) (*corev1.PersistentVolumeClaim, error) { + attachmentServiceMock.GetPersistentVolumeClaimFunc = func(_ context.Context, _ *intsvc.AttachmentDisk) (*corev1.PersistentVolumeClaim, error) { return nil, nil } err := NewBlockDeviceReadyHandler(&attachmentServiceMock).ValidateVirtualDiskReady(ctx, vmbda, cb) @@ -125,7 +125,7 @@ var _ = Describe("BlockDeviceReadyHandler ValidateVirtualDiskReady", func() { attachmentServiceMock.GetVirtualDiskFunc = func(_ context.Context, _, _ string) (*v1alpha2.VirtualDisk, error) { return generateVD(phase, metav1.ConditionTrue), nil } - attachmentServiceMock.GetPersistentVolumeClaimFunc = func(_ context.Context, _ *service.AttachmentDisk) (*corev1.PersistentVolumeClaim, error) { + attachmentServiceMock.GetPersistentVolumeClaimFunc = func(_ context.Context, _ *intsvc.AttachmentDisk) (*corev1.PersistentVolumeClaim, error) { return &corev1.PersistentVolumeClaim{ Status: corev1.PersistentVolumeClaimStatus{ Phase: corev1.ClaimBound, @@ -156,7 +156,7 @@ var _ = Describe("BlockDeviceReadyHandler ValidateVirtualDiskReady", func() { attachmentServiceMock.GetVirtualDiskFunc = func(_ context.Context, _, _ string) (*v1alpha2.VirtualDisk, error) { return generateVD(vdPhase, metav1.ConditionTrue), nil } - attachmentServiceMock.GetPersistentVolumeClaimFunc = func(_ context.Context, _ *service.AttachmentDisk) (*corev1.PersistentVolumeClaim, error) { + attachmentServiceMock.GetPersistentVolumeClaimFunc = func(_ context.Context, _ *intsvc.AttachmentDisk) (*corev1.PersistentVolumeClaim, error) { return &corev1.PersistentVolumeClaim{ Status: corev1.PersistentVolumeClaimStatus{ Phase: pvcPhase, diff --git a/images/virtualization-artifact/pkg/controller/vmbda/internal/interfaces.go b/images/virtualization-artifact/pkg/controller/vmbda/internal/interfaces.go index 5fcbf16099..38222b31e8 100644 --- a/images/virtualization-artifact/pkg/controller/vmbda/internal/interfaces.go +++ b/images/virtualization-artifact/pkg/controller/vmbda/internal/interfaces.go @@ -22,7 +22,7 @@ import ( corev1 "k8s.io/api/core/v1" virtv1 "kubevirt.io/api/core/v1" - "github.com/deckhouse/virtualization-controller/pkg/controller/service" + intsvc "github.com/deckhouse/virtualization-controller/pkg/controller/vmbda/internal/service" "github.com/deckhouse/virtualization/api/core/v1alpha2" ) @@ -35,5 +35,5 @@ type AttachmentService interface { GetVirtualDisk(ctx context.Context, name, namespace string) (*v1alpha2.VirtualDisk, error) GetVirtualImage(ctx context.Context, name, namespace string) (*v1alpha2.VirtualImage, error) GetClusterVirtualImage(ctx context.Context, name string) (*v1alpha2.ClusterVirtualImage, error) - GetPersistentVolumeClaim(ctx context.Context, ad *service.AttachmentDisk) (*corev1.PersistentVolumeClaim, error) + GetPersistentVolumeClaim(ctx context.Context, ad *intsvc.AttachmentDisk) (*corev1.PersistentVolumeClaim, error) } diff --git a/images/virtualization-artifact/pkg/controller/vmbda/internal/life_cycle.go b/images/virtualization-artifact/pkg/controller/vmbda/internal/life_cycle.go index 52b3c92f7b..d12c94f99d 100644 --- a/images/virtualization-artifact/pkg/controller/vmbda/internal/life_cycle.go +++ b/images/virtualization-artifact/pkg/controller/vmbda/internal/life_cycle.go @@ -28,16 +28,17 @@ import ( "github.com/deckhouse/virtualization-controller/pkg/controller/conditions" "github.com/deckhouse/virtualization-controller/pkg/controller/service" + intsvc "github.com/deckhouse/virtualization-controller/pkg/controller/vmbda/internal/service" "github.com/deckhouse/virtualization-controller/pkg/logger" "github.com/deckhouse/virtualization/api/core/v1alpha2" "github.com/deckhouse/virtualization/api/core/v1alpha2/vmbdacondition" ) type LifeCycleHandler struct { - attacher *service.AttachmentService + attacher *intsvc.AttachmentService } -func NewLifeCycleHandler(attacher *service.AttachmentService) *LifeCycleHandler { +func NewLifeCycleHandler(attacher *intsvc.AttachmentService) *LifeCycleHandler { return &LifeCycleHandler{ attacher: attacher, } @@ -55,7 +56,7 @@ func (h LifeCycleHandler) Handle(ctx context.Context, vmbda *v1alpha2.VirtualMac cb.Status(metav1.ConditionUnknown).Reason(conditions.ReasonUnknown) } - var ad *service.AttachmentDisk + var ad *intsvc.AttachmentDisk switch vmbda.Spec.BlockDeviceRef.Kind { case v1alpha2.VMBDAObjectRefKindVirtualDisk: vd, err := h.attacher.GetVirtualDisk(ctx, vmbda.Spec.BlockDeviceRef.Name, vmbda.Namespace) @@ -63,7 +64,7 @@ func (h LifeCycleHandler) Handle(ctx context.Context, vmbda *v1alpha2.VirtualMac return reconcile.Result{}, err } if vd != nil { - ad = service.NewAttachmentDiskFromVirtualDisk(vd) + ad = intsvc.NewAttachmentDiskFromVirtualDisk(vd) } case v1alpha2.VMBDAObjectRefKindVirtualImage: vi, err := h.attacher.GetVirtualImage(ctx, vmbda.Spec.BlockDeviceRef.Name, vmbda.Namespace) @@ -71,7 +72,7 @@ func (h LifeCycleHandler) Handle(ctx context.Context, vmbda *v1alpha2.VirtualMac return reconcile.Result{}, err } if vi != nil { - ad = service.NewAttachmentDiskFromVirtualImage(vi) + ad = intsvc.NewAttachmentDiskFromVirtualImage(vi) } case v1alpha2.VMBDAObjectRefKindClusterVirtualImage: cvi, err := h.attacher.GetClusterVirtualImage(ctx, vmbda.Spec.BlockDeviceRef.Name) @@ -79,7 +80,7 @@ func (h LifeCycleHandler) Handle(ctx context.Context, vmbda *v1alpha2.VirtualMac return reconcile.Result{}, err } if cvi != nil { - ad = service.NewAttachmentDiskFromClusterVirtualImage(cvi) + ad = intsvc.NewAttachmentDiskFromClusterVirtualImage(cvi) } } @@ -196,7 +197,7 @@ func (h LifeCycleHandler) Handle(ctx context.Context, vmbda *v1alpha2.VirtualMac isHotPlugged, err := h.attacher.IsHotPlugged(ad, vm, kvvmi) if err != nil { - if errors.Is(err, service.ErrVolumeStatusNotReady) { + if errors.Is(err, intsvc.ErrVolumeStatusNotReady) { vmbda.Status.Phase = v1alpha2.BlockDeviceAttachmentPhaseInProgress cb. Status(metav1.ConditionFalse). @@ -277,7 +278,7 @@ func (h LifeCycleHandler) Handle(ctx context.Context, vmbda *v1alpha2.VirtualMac Reason(vmbdacondition.AttachmentRequestSent). Message("Attachment request has sent: attachment is in progress.") return reconcile.Result{}, nil - case errors.Is(err, service.ErrBlockDeviceIsSpecAttached): + case errors.Is(err, intsvc.ErrBlockDeviceIsSpecAttached): log.Info("VirtualDisk is already attached to the virtual machine spec") vmbda.Status.Phase = v1alpha2.BlockDeviceAttachmentPhaseFailed @@ -286,7 +287,7 @@ func (h LifeCycleHandler) Handle(ctx context.Context, vmbda *v1alpha2.VirtualMac Reason(vmbdacondition.Conflict). Message(service.CapitalizeFirstLetter(err.Error())) return reconcile.Result{}, nil - case errors.Is(err, service.ErrHotPlugRequestAlreadySent): + case errors.Is(err, intsvc.ErrHotPlugRequestAlreadySent): log.Info("Attachment request sent: attachment is in progress.") vmbda.Status.Phase = v1alpha2.BlockDeviceAttachmentPhaseInProgress diff --git a/images/virtualization-artifact/pkg/controller/vmbda/internal/mock.go b/images/virtualization-artifact/pkg/controller/vmbda/internal/mock.go index 34b4f669ef..522cf3ee5c 100644 --- a/images/virtualization-artifact/pkg/controller/vmbda/internal/mock.go +++ b/images/virtualization-artifact/pkg/controller/vmbda/internal/mock.go @@ -5,7 +5,7 @@ package internal import ( "context" - "github.com/deckhouse/virtualization-controller/pkg/controller/service" + intsvc "github.com/deckhouse/virtualization-controller/pkg/controller/vmbda/internal/service" "github.com/deckhouse/virtualization/api/core/v1alpha2" corev1 "k8s.io/api/core/v1" virtv1 "kubevirt.io/api/core/v1" @@ -31,7 +31,7 @@ var _ AttachmentService = &AttachmentServiceMock{} // GetKVVMIFunc: func(ctx context.Context, vm *v1alpha2.VirtualMachine) (*virtv1.VirtualMachineInstance, error) { // panic("mock out the GetKVVMI method") // }, -// GetPersistentVolumeClaimFunc: func(ctx context.Context, ad *service.AttachmentDisk) (*corev1.PersistentVolumeClaim, error) { +// GetPersistentVolumeClaimFunc: func(ctx context.Context, ad *intsvc.AttachmentDisk) (*corev1.PersistentVolumeClaim, error) { // panic("mock out the GetPersistentVolumeClaim method") // }, // GetVirtualDiskFunc: func(ctx context.Context, name string, namespace string) (*v1alpha2.VirtualDisk, error) { @@ -60,7 +60,7 @@ type AttachmentServiceMock struct { GetKVVMIFunc func(ctx context.Context, vm *v1alpha2.VirtualMachine) (*virtv1.VirtualMachineInstance, error) // GetPersistentVolumeClaimFunc mocks the GetPersistentVolumeClaim method. - GetPersistentVolumeClaimFunc func(ctx context.Context, ad *service.AttachmentDisk) (*corev1.PersistentVolumeClaim, error) + GetPersistentVolumeClaimFunc func(ctx context.Context, ad *intsvc.AttachmentDisk) (*corev1.PersistentVolumeClaim, error) // GetVirtualDiskFunc mocks the GetVirtualDisk method. GetVirtualDiskFunc func(ctx context.Context, name string, namespace string) (*v1alpha2.VirtualDisk, error) @@ -99,7 +99,7 @@ type AttachmentServiceMock struct { // Ctx is the ctx argument value. Ctx context.Context // Ad is the ad argument value. - Ad *service.AttachmentDisk + Ad *intsvc.AttachmentDisk } // GetVirtualDisk holds details about calls to the GetVirtualDisk method. GetVirtualDisk []struct { @@ -247,13 +247,13 @@ func (mock *AttachmentServiceMock) GetKVVMICalls() []struct { } // GetPersistentVolumeClaim calls GetPersistentVolumeClaimFunc. -func (mock *AttachmentServiceMock) GetPersistentVolumeClaim(ctx context.Context, ad *service.AttachmentDisk) (*corev1.PersistentVolumeClaim, error) { +func (mock *AttachmentServiceMock) GetPersistentVolumeClaim(ctx context.Context, ad *intsvc.AttachmentDisk) (*corev1.PersistentVolumeClaim, error) { if mock.GetPersistentVolumeClaimFunc == nil { panic("AttachmentServiceMock.GetPersistentVolumeClaimFunc: method is nil but AttachmentService.GetPersistentVolumeClaim was just called") } callInfo := struct { Ctx context.Context - Ad *service.AttachmentDisk + Ad *intsvc.AttachmentDisk }{ Ctx: ctx, Ad: ad, @@ -270,11 +270,11 @@ func (mock *AttachmentServiceMock) GetPersistentVolumeClaim(ctx context.Context, // len(mockedAttachmentService.GetPersistentVolumeClaimCalls()) func (mock *AttachmentServiceMock) GetPersistentVolumeClaimCalls() []struct { Ctx context.Context - Ad *service.AttachmentDisk + Ad *intsvc.AttachmentDisk } { var calls []struct { Ctx context.Context - Ad *service.AttachmentDisk + Ad *intsvc.AttachmentDisk } mock.lockGetPersistentVolumeClaim.RLock() calls = mock.calls.GetPersistentVolumeClaim diff --git a/images/virtualization-artifact/pkg/controller/service/attachment_service.go b/images/virtualization-artifact/pkg/controller/vmbda/internal/service/attachment_service.go similarity index 98% rename from images/virtualization-artifact/pkg/controller/service/attachment_service.go rename to images/virtualization-artifact/pkg/controller/vmbda/internal/service/attachment_service.go index 3fc9890555..39cef02fa9 100644 --- a/images/virtualization-artifact/pkg/controller/service/attachment_service.go +++ b/images/virtualization-artifact/pkg/controller/vmbda/internal/service/attachment_service.go @@ -30,18 +30,19 @@ import ( "github.com/deckhouse/virtualization-controller/pkg/common/object" "github.com/deckhouse/virtualization-controller/pkg/controller/kvbuilder" + "github.com/deckhouse/virtualization-controller/pkg/controller/service" "github.com/deckhouse/virtualization/api/client/kubeclient" "github.com/deckhouse/virtualization/api/core/v1alpha2" subv1alpha2 "github.com/deckhouse/virtualization/api/subresources/v1alpha2" ) type AttachmentService struct { - client Client + client service.Client virtClient kubeclient.Client controllerNamespace string } -func NewAttachmentService(client Client, virtClient kubeclient.Client, controllerNamespace string) *AttachmentService { +func NewAttachmentService(client service.Client, virtClient kubeclient.Client, controllerNamespace string) *AttachmentService { return &AttachmentService{ client: client, virtClient: virtClient, diff --git a/images/virtualization-artifact/pkg/controller/service/attachment_service_test.go b/images/virtualization-artifact/pkg/controller/vmbda/internal/service/attachment_service_test.go similarity index 97% rename from images/virtualization-artifact/pkg/controller/service/attachment_service_test.go rename to images/virtualization-artifact/pkg/controller/vmbda/internal/service/attachment_service_test.go index 3643c0ef2a..d3dc88993f 100644 --- a/images/virtualization-artifact/pkg/controller/service/attachment_service_test.go +++ b/images/virtualization-artifact/pkg/controller/vmbda/internal/service/attachment_service_test.go @@ -25,11 +25,12 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" + service "github.com/deckhouse/virtualization-controller/pkg/controller/service" "github.com/deckhouse/virtualization/api/core/v1alpha2" ) var _ = Describe("AttachmentService method IsConflictedAttachment", func() { - var clientMock *ClientMock + var clientMock *service.ClientMock var vmbdaAlpha *v1alpha2.VirtualMachineBlockDeviceAttachment var vmbdaBeta *v1alpha2.VirtualMachineBlockDeviceAttachment @@ -60,7 +61,7 @@ var _ = Describe("AttachmentService method IsConflictedAttachment", func() { Spec: spec, } - clientMock = &ClientMock{} + clientMock = &service.ClientMock{} }) // T1: -->VMBDA A Should be Conflicted diff --git a/images/virtualization-artifact/pkg/controller/vmbda/internal/validators/attachment_conflict_validator.go b/images/virtualization-artifact/pkg/controller/vmbda/internal/validators/attachment_conflict_validator.go index 82d5147299..eeef8ca152 100644 --- a/images/virtualization-artifact/pkg/controller/vmbda/internal/validators/attachment_conflict_validator.go +++ b/images/virtualization-artifact/pkg/controller/vmbda/internal/validators/attachment_conflict_validator.go @@ -23,16 +23,16 @@ import ( "sigs.k8s.io/controller-runtime/pkg/webhook/admission" "github.com/deckhouse/deckhouse/pkg/log" - "github.com/deckhouse/virtualization-controller/pkg/controller/service" + intsvc "github.com/deckhouse/virtualization-controller/pkg/controller/vmbda/internal/service" "github.com/deckhouse/virtualization/api/core/v1alpha2" ) type AttachmentConflictValidator struct { log *log.Logger - service *service.AttachmentService + service *intsvc.AttachmentService } -func NewAttachmentConflictValidator(service *service.AttachmentService, log *log.Logger) *AttachmentConflictValidator { +func NewAttachmentConflictValidator(service *intsvc.AttachmentService, log *log.Logger) *AttachmentConflictValidator { return &AttachmentConflictValidator{ log: log, service: service, diff --git a/images/virtualization-artifact/pkg/controller/vmbda/vmbda_controller.go b/images/virtualization-artifact/pkg/controller/vmbda/vmbda_controller.go index 99cff3e675..7dc7d00ed7 100644 --- a/images/virtualization-artifact/pkg/controller/vmbda/vmbda_controller.go +++ b/images/virtualization-artifact/pkg/controller/vmbda/vmbda_controller.go @@ -29,6 +29,7 @@ import ( "github.com/deckhouse/deckhouse/pkg/log" "github.com/deckhouse/virtualization-controller/pkg/controller/service" "github.com/deckhouse/virtualization-controller/pkg/controller/vmbda/internal" + intsvc "github.com/deckhouse/virtualization-controller/pkg/controller/vmbda/internal/service" "github.com/deckhouse/virtualization-controller/pkg/logger" vmbdametrics "github.com/deckhouse/virtualization-controller/pkg/monitoring/metrics/vmbda" "github.com/deckhouse/virtualization/api/client/kubeclient" @@ -44,7 +45,7 @@ func NewController( lg *log.Logger, ns string, ) (controller.Controller, error) { - attacher := service.NewAttachmentService(mgr.GetClient(), virtClient, ns) + attacher := intsvc.NewAttachmentService(mgr.GetClient(), virtClient, ns) blockDeviceService := service.NewBlockDeviceService(mgr.GetClient()) reconciler := NewReconciler( diff --git a/images/virtualization-artifact/pkg/controller/vmbda/vmbda_webhook.go b/images/virtualization-artifact/pkg/controller/vmbda/vmbda_webhook.go index b355b03710..3dab83b997 100644 --- a/images/virtualization-artifact/pkg/controller/vmbda/vmbda_webhook.go +++ b/images/virtualization-artifact/pkg/controller/vmbda/vmbda_webhook.go @@ -25,6 +25,7 @@ import ( "github.com/deckhouse/deckhouse/pkg/log" "github.com/deckhouse/virtualization-controller/pkg/controller/service" + intsvc "github.com/deckhouse/virtualization-controller/pkg/controller/vmbda/internal/service" "github.com/deckhouse/virtualization-controller/pkg/controller/vmbda/internal/validators" "github.com/deckhouse/virtualization/api/core/v1alpha2" ) @@ -39,7 +40,7 @@ type Validator struct { log *log.Logger } -func NewValidator(attachmentService *service.AttachmentService, service *service.BlockDeviceService, log *log.Logger) *Validator { +func NewValidator(attachmentService *intsvc.AttachmentService, service *service.BlockDeviceService, log *log.Logger) *Validator { return &Validator{ log: log.With("webhook", "validation"), validators: []VirtualMachineBlockDeviceAttachmentValidator{ From 710a2f861dc418544567082c37c81492430d31bb Mon Sep 17 00:00:00 2001 From: Valeriy Khorunzhin Date: Fri, 27 Feb 2026 10:20:38 +0300 Subject: [PATCH 4/5] remove service.Client Signed-off-by: Valeriy Khorunzhin --- .../controller/vmbda/internal/service/attachment_service.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/images/virtualization-artifact/pkg/controller/vmbda/internal/service/attachment_service.go b/images/virtualization-artifact/pkg/controller/vmbda/internal/service/attachment_service.go index 39cef02fa9..31440c3a3a 100644 --- a/images/virtualization-artifact/pkg/controller/vmbda/internal/service/attachment_service.go +++ b/images/virtualization-artifact/pkg/controller/vmbda/internal/service/attachment_service.go @@ -30,19 +30,18 @@ import ( "github.com/deckhouse/virtualization-controller/pkg/common/object" "github.com/deckhouse/virtualization-controller/pkg/controller/kvbuilder" - "github.com/deckhouse/virtualization-controller/pkg/controller/service" "github.com/deckhouse/virtualization/api/client/kubeclient" "github.com/deckhouse/virtualization/api/core/v1alpha2" subv1alpha2 "github.com/deckhouse/virtualization/api/subresources/v1alpha2" ) type AttachmentService struct { - client service.Client + client client.Client virtClient kubeclient.Client controllerNamespace string } -func NewAttachmentService(client service.Client, virtClient kubeclient.Client, controllerNamespace string) *AttachmentService { +func NewAttachmentService(client client.Client, virtClient kubeclient.Client, controllerNamespace string) *AttachmentService { return &AttachmentService{ client: client, virtClient: virtClient, From 3dc5b39f306c5f3ceefb18aa72dd2b7f6f919fa0 Mon Sep 17 00:00:00 2001 From: Valeriy Khorunzhin Date: Fri, 27 Feb 2026 14:00:54 +0300 Subject: [PATCH 5/5] resolve Signed-off-by: Valeriy Khorunzhin --- .../service/attachment_service_test.go | 5 +- .../vmbda/internal/service/interfaces.go | 23 + .../controller/vmbda/internal/service/mock.go | 682 ++++++++++++++++++ 3 files changed, 707 insertions(+), 3 deletions(-) create mode 100644 images/virtualization-artifact/pkg/controller/vmbda/internal/service/interfaces.go create mode 100644 images/virtualization-artifact/pkg/controller/vmbda/internal/service/mock.go diff --git a/images/virtualization-artifact/pkg/controller/vmbda/internal/service/attachment_service_test.go b/images/virtualization-artifact/pkg/controller/vmbda/internal/service/attachment_service_test.go index d3dc88993f..3643c0ef2a 100644 --- a/images/virtualization-artifact/pkg/controller/vmbda/internal/service/attachment_service_test.go +++ b/images/virtualization-artifact/pkg/controller/vmbda/internal/service/attachment_service_test.go @@ -25,12 +25,11 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" - service "github.com/deckhouse/virtualization-controller/pkg/controller/service" "github.com/deckhouse/virtualization/api/core/v1alpha2" ) var _ = Describe("AttachmentService method IsConflictedAttachment", func() { - var clientMock *service.ClientMock + var clientMock *ClientMock var vmbdaAlpha *v1alpha2.VirtualMachineBlockDeviceAttachment var vmbdaBeta *v1alpha2.VirtualMachineBlockDeviceAttachment @@ -61,7 +60,7 @@ var _ = Describe("AttachmentService method IsConflictedAttachment", func() { Spec: spec, } - clientMock = &service.ClientMock{} + clientMock = &ClientMock{} }) // T1: -->VMBDA A Should be Conflicted diff --git a/images/virtualization-artifact/pkg/controller/vmbda/internal/service/interfaces.go b/images/virtualization-artifact/pkg/controller/vmbda/internal/service/interfaces.go new file mode 100644 index 0000000000..1550f019c8 --- /dev/null +++ b/images/virtualization-artifact/pkg/controller/vmbda/internal/service/interfaces.go @@ -0,0 +1,23 @@ +/* +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 "sigs.k8s.io/controller-runtime/pkg/client" + +//go:generate go tool moq -rm -out mock.go . Client + +type Client = client.Client diff --git a/images/virtualization-artifact/pkg/controller/vmbda/internal/service/mock.go b/images/virtualization-artifact/pkg/controller/vmbda/internal/service/mock.go new file mode 100644 index 0000000000..32f3b129e8 --- /dev/null +++ b/images/virtualization-artifact/pkg/controller/vmbda/internal/service/mock.go @@ -0,0 +1,682 @@ +// Code generated by moq; DO NOT EDIT. +// github.com/matryer/moq + +package service + +import ( + "context" + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/client" + "sync" +) + +// Ensure, that ClientMock does implement Client. +// If this is not the case, regenerate this file with moq. +var _ Client = &ClientMock{} + +// ClientMock is a mock implementation of Client. +// +// func TestSomethingThatUsesClient(t *testing.T) { +// +// // make and configure a mocked Client +// mockedClient := &ClientMock{ +// CreateFunc: func(ctx context.Context, obj client.Object, opts ...client.CreateOption) error { +// panic("mock out the Create method") +// }, +// DeleteFunc: func(ctx context.Context, obj client.Object, opts ...client.DeleteOption) error { +// panic("mock out the Delete method") +// }, +// DeleteAllOfFunc: func(ctx context.Context, obj client.Object, opts ...client.DeleteAllOfOption) error { +// panic("mock out the DeleteAllOf method") +// }, +// GetFunc: func(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error { +// panic("mock out the Get method") +// }, +// GroupVersionKindForFunc: func(obj runtime.Object) (schema.GroupVersionKind, error) { +// panic("mock out the GroupVersionKindFor method") +// }, +// IsObjectNamespacedFunc: func(obj runtime.Object) (bool, error) { +// panic("mock out the IsObjectNamespaced method") +// }, +// ListFunc: func(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error { +// panic("mock out the List method") +// }, +// PatchFunc: func(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.PatchOption) error { +// panic("mock out the Patch method") +// }, +// RESTMapperFunc: func() meta.RESTMapper { +// panic("mock out the RESTMapper method") +// }, +// SchemeFunc: func() *runtime.Scheme { +// panic("mock out the Scheme method") +// }, +// StatusFunc: func() client.SubResourceWriter { +// panic("mock out the Status method") +// }, +// SubResourceFunc: func(subResource string) client.SubResourceClient { +// panic("mock out the SubResource method") +// }, +// UpdateFunc: func(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { +// panic("mock out the Update method") +// }, +// } +// +// // use mockedClient in code that requires Client +// // and then make assertions. +// +// } +type ClientMock struct { + // CreateFunc mocks the Create method. + CreateFunc func(ctx context.Context, obj client.Object, opts ...client.CreateOption) error + + // DeleteFunc mocks the Delete method. + DeleteFunc func(ctx context.Context, obj client.Object, opts ...client.DeleteOption) error + + // DeleteAllOfFunc mocks the DeleteAllOf method. + DeleteAllOfFunc func(ctx context.Context, obj client.Object, opts ...client.DeleteAllOfOption) error + + // GetFunc mocks the Get method. + GetFunc func(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error + + // GroupVersionKindForFunc mocks the GroupVersionKindFor method. + GroupVersionKindForFunc func(obj runtime.Object) (schema.GroupVersionKind, error) + + // IsObjectNamespacedFunc mocks the IsObjectNamespaced method. + IsObjectNamespacedFunc func(obj runtime.Object) (bool, error) + + // ListFunc mocks the List method. + ListFunc func(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error + + // PatchFunc mocks the Patch method. + PatchFunc func(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.PatchOption) error + + // RESTMapperFunc mocks the RESTMapper method. + RESTMapperFunc func() meta.RESTMapper + + // SchemeFunc mocks the Scheme method. + SchemeFunc func() *runtime.Scheme + + // StatusFunc mocks the Status method. + StatusFunc func() client.SubResourceWriter + + // SubResourceFunc mocks the SubResource method. + SubResourceFunc func(subResource string) client.SubResourceClient + + // UpdateFunc mocks the Update method. + UpdateFunc func(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error + + // calls tracks calls to the methods. + calls struct { + // Create holds details about calls to the Create method. + Create []struct { + // Ctx is the ctx argument value. + Ctx context.Context + // Obj is the obj argument value. + Obj client.Object + // Opts is the opts argument value. + Opts []client.CreateOption + } + // Delete holds details about calls to the Delete method. + Delete []struct { + // Ctx is the ctx argument value. + Ctx context.Context + // Obj is the obj argument value. + Obj client.Object + // Opts is the opts argument value. + Opts []client.DeleteOption + } + // DeleteAllOf holds details about calls to the DeleteAllOf method. + DeleteAllOf []struct { + // Ctx is the ctx argument value. + Ctx context.Context + // Obj is the obj argument value. + Obj client.Object + // Opts is the opts argument value. + Opts []client.DeleteAllOfOption + } + // Get holds details about calls to the Get method. + Get []struct { + // Ctx is the ctx argument value. + Ctx context.Context + // Key is the key argument value. + Key client.ObjectKey + // Obj is the obj argument value. + Obj client.Object + // Opts is the opts argument value. + Opts []client.GetOption + } + // GroupVersionKindFor holds details about calls to the GroupVersionKindFor method. + GroupVersionKindFor []struct { + // Obj is the obj argument value. + Obj runtime.Object + } + // IsObjectNamespaced holds details about calls to the IsObjectNamespaced method. + IsObjectNamespaced []struct { + // Obj is the obj argument value. + Obj runtime.Object + } + // List holds details about calls to the List method. + List []struct { + // Ctx is the ctx argument value. + Ctx context.Context + // List is the list argument value. + List client.ObjectList + // Opts is the opts argument value. + Opts []client.ListOption + } + // Patch holds details about calls to the Patch method. + Patch []struct { + // Ctx is the ctx argument value. + Ctx context.Context + // Obj is the obj argument value. + Obj client.Object + // Patch is the patch argument value. + Patch client.Patch + // Opts is the opts argument value. + Opts []client.PatchOption + } + // RESTMapper holds details about calls to the RESTMapper method. + RESTMapper []struct { + } + // Scheme holds details about calls to the Scheme method. + Scheme []struct { + } + // Status holds details about calls to the Status method. + Status []struct { + } + // SubResource holds details about calls to the SubResource method. + SubResource []struct { + // SubResource is the subResource argument value. + SubResource string + } + // Update holds details about calls to the Update method. + Update []struct { + // Ctx is the ctx argument value. + Ctx context.Context + // Obj is the obj argument value. + Obj client.Object + // Opts is the opts argument value. + Opts []client.UpdateOption + } + } + lockCreate sync.RWMutex + lockDelete sync.RWMutex + lockDeleteAllOf sync.RWMutex + lockGet sync.RWMutex + lockGroupVersionKindFor sync.RWMutex + lockIsObjectNamespaced sync.RWMutex + lockList sync.RWMutex + lockPatch sync.RWMutex + lockRESTMapper sync.RWMutex + lockScheme sync.RWMutex + lockStatus sync.RWMutex + lockSubResource sync.RWMutex + lockUpdate sync.RWMutex +} + +// Create calls CreateFunc. +func (mock *ClientMock) Create(ctx context.Context, obj client.Object, opts ...client.CreateOption) error { + if mock.CreateFunc == nil { + panic("ClientMock.CreateFunc: method is nil but Client.Create was just called") + } + callInfo := struct { + Ctx context.Context + Obj client.Object + Opts []client.CreateOption + }{ + Ctx: ctx, + Obj: obj, + Opts: opts, + } + mock.lockCreate.Lock() + mock.calls.Create = append(mock.calls.Create, callInfo) + mock.lockCreate.Unlock() + return mock.CreateFunc(ctx, obj, opts...) +} + +// CreateCalls gets all the calls that were made to Create. +// Check the length with: +// +// len(mockedClient.CreateCalls()) +func (mock *ClientMock) CreateCalls() []struct { + Ctx context.Context + Obj client.Object + Opts []client.CreateOption +} { + var calls []struct { + Ctx context.Context + Obj client.Object + Opts []client.CreateOption + } + mock.lockCreate.RLock() + calls = mock.calls.Create + mock.lockCreate.RUnlock() + return calls +} + +// Delete calls DeleteFunc. +func (mock *ClientMock) Delete(ctx context.Context, obj client.Object, opts ...client.DeleteOption) error { + if mock.DeleteFunc == nil { + panic("ClientMock.DeleteFunc: method is nil but Client.Delete was just called") + } + callInfo := struct { + Ctx context.Context + Obj client.Object + Opts []client.DeleteOption + }{ + Ctx: ctx, + Obj: obj, + Opts: opts, + } + mock.lockDelete.Lock() + mock.calls.Delete = append(mock.calls.Delete, callInfo) + mock.lockDelete.Unlock() + return mock.DeleteFunc(ctx, obj, opts...) +} + +// DeleteCalls gets all the calls that were made to Delete. +// Check the length with: +// +// len(mockedClient.DeleteCalls()) +func (mock *ClientMock) DeleteCalls() []struct { + Ctx context.Context + Obj client.Object + Opts []client.DeleteOption +} { + var calls []struct { + Ctx context.Context + Obj client.Object + Opts []client.DeleteOption + } + mock.lockDelete.RLock() + calls = mock.calls.Delete + mock.lockDelete.RUnlock() + return calls +} + +// DeleteAllOf calls DeleteAllOfFunc. +func (mock *ClientMock) DeleteAllOf(ctx context.Context, obj client.Object, opts ...client.DeleteAllOfOption) error { + if mock.DeleteAllOfFunc == nil { + panic("ClientMock.DeleteAllOfFunc: method is nil but Client.DeleteAllOf was just called") + } + callInfo := struct { + Ctx context.Context + Obj client.Object + Opts []client.DeleteAllOfOption + }{ + Ctx: ctx, + Obj: obj, + Opts: opts, + } + mock.lockDeleteAllOf.Lock() + mock.calls.DeleteAllOf = append(mock.calls.DeleteAllOf, callInfo) + mock.lockDeleteAllOf.Unlock() + return mock.DeleteAllOfFunc(ctx, obj, opts...) +} + +// DeleteAllOfCalls gets all the calls that were made to DeleteAllOf. +// Check the length with: +// +// len(mockedClient.DeleteAllOfCalls()) +func (mock *ClientMock) DeleteAllOfCalls() []struct { + Ctx context.Context + Obj client.Object + Opts []client.DeleteAllOfOption +} { + var calls []struct { + Ctx context.Context + Obj client.Object + Opts []client.DeleteAllOfOption + } + mock.lockDeleteAllOf.RLock() + calls = mock.calls.DeleteAllOf + mock.lockDeleteAllOf.RUnlock() + return calls +} + +// Get calls GetFunc. +func (mock *ClientMock) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error { + if mock.GetFunc == nil { + panic("ClientMock.GetFunc: method is nil but Client.Get was just called") + } + callInfo := struct { + Ctx context.Context + Key client.ObjectKey + Obj client.Object + Opts []client.GetOption + }{ + Ctx: ctx, + Key: key, + Obj: obj, + Opts: opts, + } + mock.lockGet.Lock() + mock.calls.Get = append(mock.calls.Get, callInfo) + mock.lockGet.Unlock() + return mock.GetFunc(ctx, key, obj, opts...) +} + +// GetCalls gets all the calls that were made to Get. +// Check the length with: +// +// len(mockedClient.GetCalls()) +func (mock *ClientMock) GetCalls() []struct { + Ctx context.Context + Key client.ObjectKey + Obj client.Object + Opts []client.GetOption +} { + var calls []struct { + Ctx context.Context + Key client.ObjectKey + Obj client.Object + Opts []client.GetOption + } + mock.lockGet.RLock() + calls = mock.calls.Get + mock.lockGet.RUnlock() + return calls +} + +// GroupVersionKindFor calls GroupVersionKindForFunc. +func (mock *ClientMock) GroupVersionKindFor(obj runtime.Object) (schema.GroupVersionKind, error) { + if mock.GroupVersionKindForFunc == nil { + panic("ClientMock.GroupVersionKindForFunc: method is nil but Client.GroupVersionKindFor was just called") + } + callInfo := struct { + Obj runtime.Object + }{ + Obj: obj, + } + mock.lockGroupVersionKindFor.Lock() + mock.calls.GroupVersionKindFor = append(mock.calls.GroupVersionKindFor, callInfo) + mock.lockGroupVersionKindFor.Unlock() + return mock.GroupVersionKindForFunc(obj) +} + +// GroupVersionKindForCalls gets all the calls that were made to GroupVersionKindFor. +// Check the length with: +// +// len(mockedClient.GroupVersionKindForCalls()) +func (mock *ClientMock) GroupVersionKindForCalls() []struct { + Obj runtime.Object +} { + var calls []struct { + Obj runtime.Object + } + mock.lockGroupVersionKindFor.RLock() + calls = mock.calls.GroupVersionKindFor + mock.lockGroupVersionKindFor.RUnlock() + return calls +} + +// IsObjectNamespaced calls IsObjectNamespacedFunc. +func (mock *ClientMock) IsObjectNamespaced(obj runtime.Object) (bool, error) { + if mock.IsObjectNamespacedFunc == nil { + panic("ClientMock.IsObjectNamespacedFunc: method is nil but Client.IsObjectNamespaced was just called") + } + callInfo := struct { + Obj runtime.Object + }{ + Obj: obj, + } + mock.lockIsObjectNamespaced.Lock() + mock.calls.IsObjectNamespaced = append(mock.calls.IsObjectNamespaced, callInfo) + mock.lockIsObjectNamespaced.Unlock() + return mock.IsObjectNamespacedFunc(obj) +} + +// IsObjectNamespacedCalls gets all the calls that were made to IsObjectNamespaced. +// Check the length with: +// +// len(mockedClient.IsObjectNamespacedCalls()) +func (mock *ClientMock) IsObjectNamespacedCalls() []struct { + Obj runtime.Object +} { + var calls []struct { + Obj runtime.Object + } + mock.lockIsObjectNamespaced.RLock() + calls = mock.calls.IsObjectNamespaced + mock.lockIsObjectNamespaced.RUnlock() + return calls +} + +// List calls ListFunc. +func (mock *ClientMock) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error { + if mock.ListFunc == nil { + panic("ClientMock.ListFunc: method is nil but Client.List was just called") + } + callInfo := struct { + Ctx context.Context + List client.ObjectList + Opts []client.ListOption + }{ + Ctx: ctx, + List: list, + Opts: opts, + } + mock.lockList.Lock() + mock.calls.List = append(mock.calls.List, callInfo) + mock.lockList.Unlock() + return mock.ListFunc(ctx, list, opts...) +} + +// ListCalls gets all the calls that were made to List. +// Check the length with: +// +// len(mockedClient.ListCalls()) +func (mock *ClientMock) ListCalls() []struct { + Ctx context.Context + List client.ObjectList + Opts []client.ListOption +} { + var calls []struct { + Ctx context.Context + List client.ObjectList + Opts []client.ListOption + } + mock.lockList.RLock() + calls = mock.calls.List + mock.lockList.RUnlock() + return calls +} + +// Patch calls PatchFunc. +func (mock *ClientMock) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.PatchOption) error { + if mock.PatchFunc == nil { + panic("ClientMock.PatchFunc: method is nil but Client.Patch was just called") + } + callInfo := struct { + Ctx context.Context + Obj client.Object + Patch client.Patch + Opts []client.PatchOption + }{ + Ctx: ctx, + Obj: obj, + Patch: patch, + Opts: opts, + } + mock.lockPatch.Lock() + mock.calls.Patch = append(mock.calls.Patch, callInfo) + mock.lockPatch.Unlock() + return mock.PatchFunc(ctx, obj, patch, opts...) +} + +// PatchCalls gets all the calls that were made to Patch. +// Check the length with: +// +// len(mockedClient.PatchCalls()) +func (mock *ClientMock) PatchCalls() []struct { + Ctx context.Context + Obj client.Object + Patch client.Patch + Opts []client.PatchOption +} { + var calls []struct { + Ctx context.Context + Obj client.Object + Patch client.Patch + Opts []client.PatchOption + } + mock.lockPatch.RLock() + calls = mock.calls.Patch + mock.lockPatch.RUnlock() + return calls +} + +// RESTMapper calls RESTMapperFunc. +func (mock *ClientMock) RESTMapper() meta.RESTMapper { + if mock.RESTMapperFunc == nil { + panic("ClientMock.RESTMapperFunc: method is nil but Client.RESTMapper was just called") + } + callInfo := struct { + }{} + mock.lockRESTMapper.Lock() + mock.calls.RESTMapper = append(mock.calls.RESTMapper, callInfo) + mock.lockRESTMapper.Unlock() + return mock.RESTMapperFunc() +} + +// RESTMapperCalls gets all the calls that were made to RESTMapper. +// Check the length with: +// +// len(mockedClient.RESTMapperCalls()) +func (mock *ClientMock) RESTMapperCalls() []struct { +} { + var calls []struct { + } + mock.lockRESTMapper.RLock() + calls = mock.calls.RESTMapper + mock.lockRESTMapper.RUnlock() + return calls +} + +// Scheme calls SchemeFunc. +func (mock *ClientMock) Scheme() *runtime.Scheme { + if mock.SchemeFunc == nil { + panic("ClientMock.SchemeFunc: method is nil but Client.Scheme was just called") + } + callInfo := struct { + }{} + mock.lockScheme.Lock() + mock.calls.Scheme = append(mock.calls.Scheme, callInfo) + mock.lockScheme.Unlock() + return mock.SchemeFunc() +} + +// SchemeCalls gets all the calls that were made to Scheme. +// Check the length with: +// +// len(mockedClient.SchemeCalls()) +func (mock *ClientMock) SchemeCalls() []struct { +} { + var calls []struct { + } + mock.lockScheme.RLock() + calls = mock.calls.Scheme + mock.lockScheme.RUnlock() + return calls +} + +// Status calls StatusFunc. +func (mock *ClientMock) Status() client.SubResourceWriter { + if mock.StatusFunc == nil { + panic("ClientMock.StatusFunc: method is nil but Client.Status was just called") + } + callInfo := struct { + }{} + mock.lockStatus.Lock() + mock.calls.Status = append(mock.calls.Status, callInfo) + mock.lockStatus.Unlock() + return mock.StatusFunc() +} + +// StatusCalls gets all the calls that were made to Status. +// Check the length with: +// +// len(mockedClient.StatusCalls()) +func (mock *ClientMock) StatusCalls() []struct { +} { + var calls []struct { + } + mock.lockStatus.RLock() + calls = mock.calls.Status + mock.lockStatus.RUnlock() + return calls +} + +// SubResource calls SubResourceFunc. +func (mock *ClientMock) SubResource(subResource string) client.SubResourceClient { + if mock.SubResourceFunc == nil { + panic("ClientMock.SubResourceFunc: method is nil but Client.SubResource was just called") + } + callInfo := struct { + SubResource string + }{ + SubResource: subResource, + } + mock.lockSubResource.Lock() + mock.calls.SubResource = append(mock.calls.SubResource, callInfo) + mock.lockSubResource.Unlock() + return mock.SubResourceFunc(subResource) +} + +// SubResourceCalls gets all the calls that were made to SubResource. +// Check the length with: +// +// len(mockedClient.SubResourceCalls()) +func (mock *ClientMock) SubResourceCalls() []struct { + SubResource string +} { + var calls []struct { + SubResource string + } + mock.lockSubResource.RLock() + calls = mock.calls.SubResource + mock.lockSubResource.RUnlock() + return calls +} + +// Update calls UpdateFunc. +func (mock *ClientMock) Update(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { + if mock.UpdateFunc == nil { + panic("ClientMock.UpdateFunc: method is nil but Client.Update was just called") + } + callInfo := struct { + Ctx context.Context + Obj client.Object + Opts []client.UpdateOption + }{ + Ctx: ctx, + Obj: obj, + Opts: opts, + } + mock.lockUpdate.Lock() + mock.calls.Update = append(mock.calls.Update, callInfo) + mock.lockUpdate.Unlock() + return mock.UpdateFunc(ctx, obj, opts...) +} + +// UpdateCalls gets all the calls that were made to Update. +// Check the length with: +// +// len(mockedClient.UpdateCalls()) +func (mock *ClientMock) UpdateCalls() []struct { + Ctx context.Context + Obj client.Object + Opts []client.UpdateOption +} { + var calls []struct { + Ctx context.Context + Obj client.Object + Opts []client.UpdateOption + } + mock.lockUpdate.RLock() + calls = mock.calls.Update + mock.lockUpdate.RUnlock() + return calls +}