diff --git a/pkg/controller/plan/BUILD.bazel b/pkg/controller/plan/BUILD.bazel index 8a9d635a1..363f98ac6 100644 --- a/pkg/controller/plan/BUILD.bazel +++ b/pkg/controller/plan/BUILD.bazel @@ -77,6 +77,7 @@ go_library( go_test( name = "plan_test", srcs = [ + "kubevirt_test.go", "plan_suite_test.go", "validation_test.go", "vm_name_handler_test.go", @@ -85,7 +86,9 @@ go_test( deps = [ "//pkg/apis/forklift/v1beta1", "//pkg/apis/forklift/v1beta1/provider", + "//pkg/apis/forklift/v1beta1/ref", "//pkg/controller/base", + "//pkg/controller/plan/context", "//pkg/lib/condition", "//pkg/lib/logging", "//vendor/github.com/onsi/ginkgo/v2:ginkgo", diff --git a/pkg/controller/plan/adapter/openstack/BUILD.bazel b/pkg/controller/plan/adapter/openstack/BUILD.bazel index 7530a494b..c901f0d26 100644 --- a/pkg/controller/plan/adapter/openstack/BUILD.bazel +++ b/pkg/controller/plan/adapter/openstack/BUILD.bazel @@ -47,10 +47,20 @@ go_test( srcs = [ "adapter_suite_test.go", "builder_test.go", + "destionationclient_test.go", ], embed = [":openstack"], deps = [ + "//pkg/apis/forklift/v1beta1", + "//pkg/apis/forklift/v1beta1/plan", + "//pkg/controller/plan/context", + "//pkg/lib/logging", "//vendor/github.com/onsi/ginkgo/v2:ginkgo", "//vendor/github.com/onsi/gomega", + "//vendor/k8s.io/api/core/v1:core", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:meta", + "//vendor/k8s.io/apimachinery/pkg/runtime", + "//vendor/sigs.k8s.io/controller-runtime/pkg/client", + "//vendor/sigs.k8s.io/controller-runtime/pkg/client/fake", ], ) diff --git a/pkg/controller/plan/adapter/openstack/adapter_suite_test.go b/pkg/controller/plan/adapter/openstack/adapter_suite_test.go index d3544b743..7273d404e 100644 --- a/pkg/controller/plan/adapter/openstack/adapter_suite_test.go +++ b/pkg/controller/plan/adapter/openstack/adapter_suite_test.go @@ -18,5 +18,5 @@ func forkliftFailHandler(message string, callerSkip ...int) { func TestTests(t *testing.T) { defer GinkgoRecover() RegisterFailHandler(forkliftFailHandler) - RunSpecs(t, "openstack builder") + RunSpecs(t, "OpenStack Suite") } diff --git a/pkg/controller/plan/adapter/openstack/builder.go b/pkg/controller/plan/adapter/openstack/builder.go index da1046490..a76536b55 100644 --- a/pkg/controller/plan/adapter/openstack/builder.go +++ b/pkg/controller/plan/adapter/openstack/builder.go @@ -1163,6 +1163,7 @@ func (r *Builder) getVolumePopulatorCR(imageID string) (populatorCr api.Openstac }), }) if err != nil { + err = liberr.Wrap(err) return } if len(populatorCrList.Items) == 0 { @@ -1171,6 +1172,7 @@ func (r *Builder) getVolumePopulatorCR(imageID string) (populatorCr api.Openstac } if len(populatorCrList.Items) > 1 { err = liberr.New("multiple OpenstackVolumePopulator CRs found for image", "imageID", imageID) + return } populatorCr = populatorCrList.Items[0] @@ -1188,6 +1190,7 @@ func (r *Builder) getVolumePopulatorPVC(imageID string) (populatorPvc *core.Pers }), }) if err != nil { + err = liberr.Wrap(err) return } @@ -1197,6 +1200,7 @@ func (r *Builder) getVolumePopulatorPVC(imageID string) (populatorPvc *core.Pers } if len(populatorPvcList.Items) > 1 { err = liberr.New("multiple PersistentVolumeClaims found for image", "imageID", imageID) + return } populatorPvc = &populatorPvcList.Items[0] @@ -1272,10 +1276,12 @@ func (r *Builder) persistentVolumeClaimWithSourceRef(image model.Image, func (r *Builder) PopulatorTransferredBytes(persistentVolumeClaim *core.PersistentVolumeClaim) (transferredBytes int64, err error) { image, err := r.getImageFromPVC(persistentVolumeClaim) if err != nil { + err = liberr.Wrap(err) return } populatorCr, err := r.getVolumePopulatorCR(image.ID) if err != nil { + err = liberr.Wrap(err) return } progressPercentage, err := strconv.ParseInt(populatorCr.Status.Progress, 10, 64) @@ -1306,6 +1312,7 @@ func (r *Builder) SetPopulatorDataSourceLabels(vmRef ref.Ref, pvcs []*core.Persi workload := &model.Workload{} err = r.Source.Inventory.Find(workload, vmRef) if err != nil { + err = liberr.Wrap(err) return } var images []*model.Image @@ -1313,6 +1320,7 @@ func (r *Builder) SetPopulatorDataSourceLabels(vmRef ref.Ref, pvcs []*core.Persi lookupName := getImageFromVolumeName(r.Context, vmRef.ID, volume.ID) image, err := r.getImageByName(lookupName) if err != nil { + r.Log.Error(err, "Couldn't find the image from the volume.", "volume", volume.ID, "vmRef", vmRef) continue } images = append(images, image) diff --git a/pkg/controller/plan/adapter/openstack/destinationclient.go b/pkg/controller/plan/adapter/openstack/destinationclient.go index b26b3e378..f14f50dcb 100644 --- a/pkg/controller/plan/adapter/openstack/destinationclient.go +++ b/pkg/controller/plan/adapter/openstack/destinationclient.go @@ -119,6 +119,7 @@ func (r *DestinationClient) findPVCByCR(cr *v1beta1.OpenstackVolumePopulator) (p } if len(pvcList.Items) > 1 { err = liberr.New("Multiple PVCs found", "imageID", cr.Spec.ImageID) + return } pvc = &pvcList.Items[0] diff --git a/pkg/controller/plan/adapter/openstack/destionationclient_test.go b/pkg/controller/plan/adapter/openstack/destionationclient_test.go new file mode 100644 index 000000000..5034fd3fc --- /dev/null +++ b/pkg/controller/plan/adapter/openstack/destionationclient_test.go @@ -0,0 +1,143 @@ +//nolint:errcheck +package openstack + +import ( + "context" + + v1beta1 "github.com/konveyor/forklift-controller/pkg/apis/forklift/v1beta1" + "github.com/konveyor/forklift-controller/pkg/apis/forklift/v1beta1/plan" + plancontext "github.com/konveyor/forklift-controller/pkg/controller/plan/context" + "github.com/konveyor/forklift-controller/pkg/lib/logging" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" +) + +var destinationClientLog = logging.WithName("openstack-destinationclient-test") + +var _ = Describe("openstack destinationclient tests", func() { + destinationClient := createDestinationClient() + openstackVolPopCr := &v1beta1.OpenstackVolumePopulator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test", + Labels: map[string]string{ + "migration": "migration1", + "imageID": "image1", + }, + }, + Spec: v1beta1.OpenstackVolumePopulatorSpec{ + ImageID: "image1", + }, + } + + pvc1 := &v1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: "testPVC", + Namespace: "test", + Labels: map[string]string{ + "migration": "migration1", + "imageID": "image1", + }, + }, + } + + Describe("findPvcByCr", func() { + It("should return an error when PVC is not found", func() { + pvc, err := destinationClient.findPVCByCR(openstackVolPopCr) + Expect(pvc).To(BeNil()) + Expect(err).To(MatchError("PVC not found")) + }) + + It("should return the PVC when it is found", func() { + destinationClient = createDestinationClient(pvc1) + pvc, err := destinationClient.findPVCByCR(openstackVolPopCr) + Expect(pvc).NotTo(BeNil()) + Expect(pvc.Name).To(Equal("testPVC")) + Expect(err).ToNot(HaveOccurred()) + }) + + It("should return an error when multiple PVCs are found", func() { + pvc2 := &v1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: "testPVC2", + Namespace: "test", + Labels: map[string]string{ + "migration": "migration1", + "imageID": "image1", + }, + }, + } + destinationClient = createDestinationClient(pvc1, pvc2) + pvc, err := destinationClient.findPVCByCR(openstackVolPopCr) + Expect(pvc).To(BeNil()) + Expect(err).To(MatchError("Multiple PVCs found")) + }) + }) + + Describe("SetPopulatorCrOwnership", func() { + It("should set the owner reference for the populator CR", func() { + destinationClient = createDestinationClient(openstackVolPopCr, pvc1) + destinationClient.SetPopulatorCrOwnership() + + patchedOpenstackVolPopCr := &v1beta1.OpenstackVolumePopulator{} + err := destinationClient.Client.Get(context.TODO(), client.ObjectKey{ + Name: "test", + Namespace: "test", + }, patchedOpenstackVolPopCr) + Expect(err).ToNot(HaveOccurred()) + Expect(patchedOpenstackVolPopCr.GetOwnerReferences()).To(HaveLen(1)) + Expect(patchedOpenstackVolPopCr.GetOwnerReferences()[0].Kind).To(Equal("PersistentVolumeClaim")) + Expect(patchedOpenstackVolPopCr.GetOwnerReferences()[0].Name).To(Equal("testPVC")) + }) + }) +}) + +func createDestinationClient(objs ...runtime.Object) *DestinationClient { + scheme := runtime.NewScheme() + _ = v1.AddToScheme(scheme) + v1beta1.SchemeBuilder.AddToScheme(scheme) + client := fake.NewClientBuilder(). + WithScheme(scheme). + WithRuntimeObjects(objs...). + Build() + return &DestinationClient{ + Context: &plancontext.Context{ + Destination: plancontext.Destination{ + Client: client, + }, + Plan: createPlan(), + Log: destinationClientLog, + + // To make sure r.Scheme is not nil + Client: client, + }, + } +} + +func createPlan() *v1beta1.Plan { + return &v1beta1.Plan{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test", + }, + Spec: v1beta1.PlanSpec{ + TargetNamespace: "test", + }, + Status: v1beta1.PlanStatus{ + Migration: plan.MigrationStatus{ + History: []plan.Snapshot{ + { + Migration: plan.SnapshotRef{ + UID: "migration1", + }, + }, + }, + }, + }, + } +} diff --git a/pkg/controller/plan/adapter/ovirt/BUILD.bazel b/pkg/controller/plan/adapter/ovirt/BUILD.bazel index 539a508c0..1be7f18d0 100644 --- a/pkg/controller/plan/adapter/ovirt/BUILD.bazel +++ b/pkg/controller/plan/adapter/ovirt/BUILD.bazel @@ -1,4 +1,4 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "ovirt", @@ -40,3 +40,25 @@ go_library( "//vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllerutil", ], ) + +go_test( + name = "ovirt_test", + srcs = [ + "destinationclient_test.go", + "ovirt_suite_test.go", + ], + embed = [":ovirt"], + deps = [ + "//pkg/apis/forklift/v1beta1", + "//pkg/apis/forklift/v1beta1/plan", + "//pkg/controller/plan/context", + "//pkg/lib/logging", + "//vendor/github.com/onsi/ginkgo/v2:ginkgo", + "//vendor/github.com/onsi/gomega", + "//vendor/k8s.io/api/core/v1:core", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:meta", + "//vendor/k8s.io/apimachinery/pkg/runtime", + "//vendor/sigs.k8s.io/controller-runtime/pkg/client", + "//vendor/sigs.k8s.io/controller-runtime/pkg/client/fake", + ], +) diff --git a/pkg/controller/plan/adapter/ovirt/destinationclient.go b/pkg/controller/plan/adapter/ovirt/destinationclient.go index bbba1daf2..823fd8d3d 100644 --- a/pkg/controller/plan/adapter/ovirt/destinationclient.go +++ b/pkg/controller/plan/adapter/ovirt/destinationclient.go @@ -108,7 +108,6 @@ func (r *DestinationClient) findPVCByCR(cr *v1beta1.OvirtVolumePopulator) (pvc * "diskID": cr.Spec.DiskID, }), }) - if err != nil { err = liberr.Wrap(err) return @@ -116,9 +115,12 @@ func (r *DestinationClient) findPVCByCR(cr *v1beta1.OvirtVolumePopulator) (pvc * if len(pvcList.Items) == 0 { err = liberr.New("PVC not found", "diskID", cr.Spec.DiskID) + return } + if len(pvcList.Items) > 1 { err = liberr.New("Multiple PVCs found", "diskID", cr.Spec.DiskID) + return } pvc = &pvcList.Items[0] diff --git a/pkg/controller/plan/adapter/ovirt/destinationclient_test.go b/pkg/controller/plan/adapter/ovirt/destinationclient_test.go new file mode 100644 index 000000000..4a59e24a2 --- /dev/null +++ b/pkg/controller/plan/adapter/ovirt/destinationclient_test.go @@ -0,0 +1,142 @@ +//nolint:errcheck +package ovirt + +import ( + "context" + + v1beta1 "github.com/konveyor/forklift-controller/pkg/apis/forklift/v1beta1" + "github.com/konveyor/forklift-controller/pkg/apis/forklift/v1beta1/plan" + plancontext "github.com/konveyor/forklift-controller/pkg/controller/plan/context" + "github.com/konveyor/forklift-controller/pkg/lib/logging" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" +) + +var destinationClientLog = logging.WithName("ovirt-destinationclient-test") + +var _ = Describe("ovirt destinationclient tests", func() { + destinationClient := createDestinationClient() + ovirtVolPopCr := &v1beta1.OvirtVolumePopulator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test", + Labels: map[string]string{ + "migration": "migration1", + "diskID": "disk1", + }, + }, + Spec: v1beta1.OvirtVolumePopulatorSpec{ + DiskID: "disk1", + }, + } + + pvc1 := &v1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: "testPVC", + Namespace: "test", + Labels: map[string]string{ + "migration": "migration1", + "diskID": "disk1", + }, + }, + } + + Describe("findPvcByCr", func() { + It("should return an error when PVC is not found", func() { + pvc, err := destinationClient.findPVCByCR(ovirtVolPopCr) + Expect(pvc).To(BeNil()) + Expect(err).To(MatchError("PVC not found")) + }) + + It("should return the PVC when it is found", func() { + destinationClient = createDestinationClient(pvc1) + pvc, err := destinationClient.findPVCByCR(ovirtVolPopCr) + Expect(pvc.Name).To(Equal("testPVC")) + Expect(err).ToNot(HaveOccurred()) + }) + + It("should return an error when multiple PVCs are found", func() { + pvc2 := &v1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: "testPVC2", + Namespace: "test", + Labels: map[string]string{ + "migration": "migration1", + "diskID": "disk1", + }, + }, + } + destinationClient = createDestinationClient(pvc1, pvc2) + pvc, err := destinationClient.findPVCByCR(ovirtVolPopCr) + Expect(pvc).To(BeNil()) + Expect(err).To(MatchError("Multiple PVCs found")) + }) + }) + + Describe("SetPopulatorCrOwnership", func() { + It("should set the owner reference for the populator CR", func() { + destinationClient = createDestinationClient(ovirtVolPopCr, pvc1) + destinationClient.SetPopulatorCrOwnership() + + patchedOvirtVolPopCr := &v1beta1.OvirtVolumePopulator{} + err := destinationClient.Client.Get(context.TODO(), client.ObjectKey{ + Name: "test", + Namespace: "test", + }, patchedOvirtVolPopCr) + Expect(err).ToNot(HaveOccurred()) + Expect(patchedOvirtVolPopCr.GetOwnerReferences()).To(HaveLen(1)) + Expect(patchedOvirtVolPopCr.GetOwnerReferences()[0].Kind).To(Equal("PersistentVolumeClaim")) + Expect(patchedOvirtVolPopCr.GetOwnerReferences()[0].Name).To(Equal("testPVC")) + }) + }) +}) + +func createDestinationClient(objs ...runtime.Object) *DestinationClient { + scheme := runtime.NewScheme() + _ = v1.AddToScheme(scheme) + v1beta1.SchemeBuilder.AddToScheme(scheme) + client := fake.NewClientBuilder(). + WithScheme(scheme). + WithRuntimeObjects(objs...). + Build() + return &DestinationClient{ + Context: &plancontext.Context{ + Destination: plancontext.Destination{ + Client: client, + }, + Plan: createPlan(), + Log: destinationClientLog, + + // To make sure r.Scheme is not nil + Client: client, + }, + } +} + +func createPlan() *v1beta1.Plan { + return &v1beta1.Plan{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test", + }, + Spec: v1beta1.PlanSpec{ + TargetNamespace: "test", + }, + Status: v1beta1.PlanStatus{ + Migration: plan.MigrationStatus{ + History: []plan.Snapshot{ + { + Migration: plan.SnapshotRef{ + UID: "migration1", + }, + }, + }, + }, + }, + } +} diff --git a/pkg/controller/plan/adapter/ovirt/ovirt_suite_test.go b/pkg/controller/plan/adapter/ovirt/ovirt_suite_test.go new file mode 100644 index 000000000..4b9b583e6 --- /dev/null +++ b/pkg/controller/plan/adapter/ovirt/ovirt_suite_test.go @@ -0,0 +1,13 @@ +package ovirt_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestOvirt(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Ovirt Suite") +} diff --git a/pkg/controller/plan/kubevirt.go b/pkg/controller/plan/kubevirt.go index 3242bddbd..0ef3f977f 100644 --- a/pkg/controller/plan/kubevirt.go +++ b/pkg/controller/plan/kubevirt.go @@ -652,6 +652,7 @@ func (r *KubeVirt) getPVCs(vmRef ref.Ref) (pvcs []*core.PersistentVolumeClaim, e ) if err != nil { err = liberr.Wrap(err) + return } pvcs = make([]*core.PersistentVolumeClaim, len(pvcsList.Items)) diff --git a/pkg/controller/plan/kubevirt_test.go b/pkg/controller/plan/kubevirt_test.go new file mode 100644 index 000000000..7e0a6496c --- /dev/null +++ b/pkg/controller/plan/kubevirt_test.go @@ -0,0 +1,70 @@ +//nolint:errcheck +package plan + +import ( + v1beta1 "github.com/konveyor/forklift-controller/pkg/apis/forklift/v1beta1" + "github.com/konveyor/forklift-controller/pkg/apis/forklift/v1beta1/ref" + plancontext "github.com/konveyor/forklift-controller/pkg/controller/plan/context" + "github.com/konveyor/forklift-controller/pkg/lib/logging" + ginkgo "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/controller-runtime/pkg/client/fake" +) + +var KubeVirtLog = logging.WithName("kubevirt-test") + +var _ = ginkgo.Describe("kubevirt tests", func() { + ginkgo.Describe("getPVCs", func() { + pvc := &v1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-pvc", + Namespace: "test", + Labels: map[string]string{ + "migration": "test", + "vmID": "test", + }, + }, + } + + ginkgo.It("should return PVCs", func() { + kubevirt := createKubeVirt(pvc) + pvcs, err := kubevirt.getPVCs(ref.Ref{ID: "test"}) + Expect(err).ToNot(HaveOccurred()) + Expect(pvcs).To(HaveLen(1)) + }) + }) + +}) + +func createKubeVirt(objs ...runtime.Object) *KubeVirt { + scheme := runtime.NewScheme() + _ = v1.AddToScheme(scheme) + v1beta1.SchemeBuilder.AddToScheme(scheme) + client := fake.NewClientBuilder(). + WithScheme(scheme). + WithRuntimeObjects(objs...). + Build() + return &KubeVirt{ + Context: &plancontext.Context{ + Destination: plancontext.Destination{ + Client: client, + }, + Log: KubeVirtLog, + Migration: createMigration(), + Client: client, + }, + } +} + +func createMigration() *v1beta1.Migration { + return &v1beta1.Migration{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test", + UID: "test", + }, + } +}