From 8f57970fa5a2cda509578f4f9f9a96f31d0d7c8a Mon Sep 17 00:00:00 2001 From: Lukas Frank Date: Fri, 4 Aug 2023 17:13:27 +0200 Subject: [PATCH] Extended tests --- .../controllers/controllers_suite_test.go | 26 +- .../controllers/volume_controller_test.go | 5 +- .../controllers/volumepool_controller.go | 6 +- .../controllers/volumepool_controller_test.go | 238 +++++++++++++++--- 4 files changed, 229 insertions(+), 46 deletions(-) diff --git a/poollet/volumepoollet/controllers/controllers_suite_test.go b/poollet/volumepoollet/controllers/controllers_suite_test.go index 3861c767e..86edeb7ac 100644 --- a/poollet/volumepoollet/controllers/controllers_suite_test.go +++ b/poollet/volumepoollet/controllers/controllers_suite_test.go @@ -35,6 +35,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes/scheme" @@ -174,12 +175,12 @@ func SetupTest() (*corev1.Namespace, *storagev1alpha1.VolumePool, *storagev1alph *expandableVc = storagev1alpha1.VolumeClass{ ObjectMeta: metav1.ObjectMeta{ - GenerateName: "test-vc-", + GenerateName: "test-vc-expandable-", }, ResizePolicy: storagev1alpha1.ResizePolicyExpandOnly, Capabilities: corev1alpha1.ResourceList{ corev1alpha1.ResourceTPS: resource.MustParse("250Mi"), - corev1alpha1.ResourceIOPS: resource.MustParse("15000"), + corev1alpha1.ResourceIOPS: resource.MustParse("1000"), }, } Expect(k8sClient.Create(ctx, expandableVc)).To(Succeed(), "failed to create test volume class") @@ -198,7 +199,19 @@ func SetupTest() (*corev1.Namespace, *storagev1alpha1.VolumePool, *storagev1alph }, }, }, + { + VolumeClassStatus: ori.VolumeClassStatus{ + VolumeClass: &ori.VolumeClass{ + Name: expandableVc.Name, + Capabilities: &ori.VolumeClassCapabilities{ + Tps: 262144000, + Iops: 1000, + }, + }, + }, + }, }) + DeferCleanup(srv.SetVolumeClasses, []*volume.FakeVolumeClassStatus{}) k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{ Scheme: scheme.Scheme, @@ -230,13 +243,13 @@ func SetupTest() (*corev1.Namespace, *storagev1alpha1.VolumePool, *storagev1alph Client: k8sManager.GetClient(), VolumeRuntime: srv, VolumeClassMapper: volumeClassMapper, - VolumePoolName: TestVolumePool, + VolumePoolName: vp.Name, }).SetupWithManager(k8sManager)).To(Succeed()) Expect((&controllers.VolumePoolAnnotatorReconciler{ Client: k8sManager.GetClient(), VolumeClassMapper: volumeClassMapper, - VolumePoolName: TestVolumePool, + VolumePoolName: vp.Name, }).SetupWithManager(k8sManager)).To(Succeed()) go func() { @@ -247,3 +260,8 @@ func SetupTest() (*corev1.Namespace, *storagev1alpha1.VolumePool, *storagev1alph return ns, vp, vc, expandableVc, srv } + +func ExpectVolumeDeleted(ctx context.Context, volume *storagev1alpha1.Volume) { + Expect(k8sClient.Delete(ctx, volume)).Should(Succeed()) + Eventually(Get(volume)).Should(Satisfy(errors.IsNotFound)) +} diff --git a/poollet/volumepoollet/controllers/volume_controller_test.go b/poollet/volumepoollet/controllers/volume_controller_test.go index 050591572..b65d293ba 100644 --- a/poollet/volumepoollet/controllers/volume_controller_test.go +++ b/poollet/volumepoollet/controllers/volume_controller_test.go @@ -64,6 +64,7 @@ var _ = Describe("VolumeController", func() { }, } Expect(k8sClient.Create(ctx, volume)).To(Succeed()) + DeferCleanup(ExpectVolumeDeleted, volume) By("waiting for the runtime to report the volume") Eventually(srv).Should(SatisfyAll( @@ -149,6 +150,7 @@ var _ = Describe("VolumeController", func() { }, } Expect(k8sClient.Create(ctx, volume)).To(Succeed()) + DeferCleanup(ExpectVolumeDeleted, volume) By("waiting for the runtime to report the volume") Eventually(srv).Should(SatisfyAll( @@ -183,6 +185,7 @@ var _ = Describe("VolumeController", func() { }, } Expect(k8sClient.Create(ctx, volume)).To(Succeed()) + DeferCleanup(ExpectVolumeDeleted, volume) By("waiting for the runtime to report the volume") Eventually(srv).Should(SatisfyAll( @@ -192,7 +195,7 @@ var _ = Describe("VolumeController", func() { _, oriVolume := GetSingleMapEntry(srv.Volumes) Expect(oriVolume.Spec.Image).To(Equal("")) - Expect(oriVolume.Spec.Class).To(Equal(vc.Name)) + Expect(oriVolume.Spec.Class).To(Equal(expandableVc.Name)) Expect(oriVolume.Spec.Resources.StorageBytes).To(Equal(uint64(size.Value()))) By("update increasing the storage resource") diff --git a/poollet/volumepoollet/controllers/volumepool_controller.go b/poollet/volumepoollet/controllers/volumepool_controller.go index 2c59fc603..7c9a4ecdd 100644 --- a/poollet/volumepoollet/controllers/volumepool_controller.go +++ b/poollet/volumepoollet/controllers/volumepool_controller.go @@ -106,7 +106,7 @@ func (r *VolumePoolReconciler) calculateCapacity( } supported = append(supported, corev1.LocalObjectReference{Name: volumeClass.Name}) - capacity[corev1alpha1.ClassCountFor(corev1alpha1.ClassTypeVolumeClass, volumeClass.Name)] = *resource.NewQuantity(quantity, resource.DecimalSI) + capacity[corev1alpha1.ClassCountFor(corev1alpha1.ClassTypeVolumeClass, volumeClass.Name)] = *resource.NewQuantity(quantity, resource.BinarySI) } usedResources := corev1alpha1.ResourceList{} @@ -114,11 +114,11 @@ func (r *VolumePoolReconciler) calculateCapacity( className := volume.Spec.VolumeClassRef.Name res, ok := usedResources[corev1alpha1.ClassCountFor(corev1alpha1.ClassTypeVolumeClass, className)] if !ok { - usedResources[corev1alpha1.ClassCountFor(corev1alpha1.ClassTypeVolumeClass, className)] = *resource.NewQuantity(1, resource.DecimalSI) + usedResources[corev1alpha1.ClassCountFor(corev1alpha1.ClassTypeVolumeClass, className)] = *volume.Spec.Resources.Storage() continue } - res.Add(resource.MustParse("1")) + res.Add(*volume.Spec.Resources.Storage()) } return capacity, quota.SubtractWithNonNegativeResult(capacity, usedResources), supported, nil diff --git a/poollet/volumepoollet/controllers/volumepool_controller_test.go b/poollet/volumepoollet/controllers/volumepool_controller_test.go index f9a446168..a21146063 100644 --- a/poollet/volumepoollet/controllers/volumepool_controller_test.go +++ b/poollet/volumepoollet/controllers/volumepool_controller_test.go @@ -19,6 +19,7 @@ import ( storagev1alpha1 "github.com/onmetal/onmetal-api/api/storage/v1alpha1" ori "github.com/onmetal/onmetal-api/ori/apis/volume/v1alpha1" "github.com/onmetal/onmetal-api/ori/testing/volume" + "github.com/onmetal/onmetal-api/utils/quota" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" @@ -27,110 +28,271 @@ import ( . "sigs.k8s.io/controller-runtime/pkg/envtest/komega" ) -const ( - TestVolumePool = "test-volumepool" -) - var _ = Describe("VolumePoolController", func() { - _, _, _, _, srv := SetupTest() + ns, volumePool, volumeClass, expandableVolumeClass, srv := SetupTest() It("should add volume classes to pool", func(ctx SpecContext) { - srv.SetVolumeClasses([]*volume.FakeVolumeClassStatus{}) + By("checking if the default volume classes are present") + Eventually(Object(volumePool)).Should(SatisfyAll( + HaveField("Status.AvailableVolumeClasses", ContainElements([]corev1.LocalObjectReference{ + { + Name: volumeClass.Name, + }, + { + Name: expandableVolumeClass.Name, + }, + }))), + ) By("creating a volume class") - vc := &storagev1alpha1.VolumeClass{ + testVolumeClass := &storagev1alpha1.VolumeClass{ ObjectMeta: metav1.ObjectMeta{ GenerateName: "test-vc-1-", }, Capabilities: corev1alpha1.ResourceList{ corev1alpha1.ResourceTPS: resource.MustParse("250Mi"), - corev1alpha1.ResourceIOPS: resource.MustParse("1"), + corev1alpha1.ResourceIOPS: resource.MustParse("100"), }, } - Expect(k8sClient.Create(ctx, vc)).To(Succeed(), "failed to create test volume class") + Expect(k8sClient.Create(ctx, testVolumeClass)).To(Succeed(), "failed to create test volume class") + DeferCleanup(k8sClient.Delete, testVolumeClass) srv.SetVolumeClasses([]*volume.FakeVolumeClassStatus{ { VolumeClassStatus: ori.VolumeClassStatus{ VolumeClass: &ori.VolumeClass{ - Name: vc.Name, + Name: volumeClass.Name, + Capabilities: &ori.VolumeClassCapabilities{ + Tps: 262144000, + Iops: 15000, + }, + }, + }, + }, + { + VolumeClassStatus: ori.VolumeClassStatus{ + VolumeClass: &ori.VolumeClass{ + Name: expandableVolumeClass.Name, + Capabilities: &ori.VolumeClassCapabilities{ + Tps: 262144000, + Iops: 1000, + }, + }, + }, + }, + { + VolumeClassStatus: ori.VolumeClassStatus{ + VolumeClass: &ori.VolumeClass{ + Name: testVolumeClass.Name, Capabilities: &ori.VolumeClassCapabilities{ Tps: 262144000, - Iops: 1, + Iops: 100, }, }, }, }, }) - By("creating a volume pool") - volumePool := &storagev1alpha1.VolumePool{ - ObjectMeta: metav1.ObjectMeta{ - Name: TestVolumePool, + By("checking if the test volume class is present") + Eventually(Object(volumePool)).Should(SatisfyAll( + HaveField("Status.AvailableVolumeClasses", ContainElements([]corev1.LocalObjectReference{ + { + Name: volumeClass.Name, + }, + { + Name: expandableVolumeClass.Name, + }, + { + Name: testVolumeClass.Name, + }, + }))), + ) + }) + + It("should add volume classes to pool", func(ctx SpecContext) { + By("checking if the default volume classes are present") + Eventually(Object(volumePool)).Should(SatisfyAll( + HaveField("Status.AvailableVolumeClasses", ContainElements([]corev1.LocalObjectReference{ + { + Name: volumeClass.Name, + }, + { + Name: expandableVolumeClass.Name, + }, + }))), + ) + + By("creating a volume class") + srv.SetVolumeClasses([]*volume.FakeVolumeClassStatus{ + { + VolumeClassStatus: ori.VolumeClassStatus{ + VolumeClass: &ori.VolumeClass{ + Name: volumeClass.Name, + Capabilities: &ori.VolumeClassCapabilities{ + Tps: 262144000, + Iops: 15000, + }, + }, + }, }, - } - Expect(k8sClient.Create(ctx, volumePool)).To(Succeed(), "failed to create test volume pool") + { + VolumeClassStatus: ori.VolumeClassStatus{ + VolumeClass: &ori.VolumeClass{ + Name: expandableVolumeClass.Name, + Capabilities: &ori.VolumeClassCapabilities{ + Tps: 262144000, + Iops: 1000, + }, + }, + }, + }, + { + VolumeClassStatus: ori.VolumeClassStatus{ + VolumeClass: &ori.VolumeClass{ + Name: "testVolumeClass.Name", + Capabilities: &ori.VolumeClassCapabilities{ + Tps: 262144000, + Iops: 100, + }, + }, + }, + }, + }) - By("checking if the default volume class is present") Eventually(Object(volumePool)).Should(SatisfyAll( - HaveField("Status.AvailableVolumeClasses", Equal([]corev1.LocalObjectReference{ + HaveField("Status.AvailableVolumeClasses", ContainElements([]corev1.LocalObjectReference{ + { + Name: volumeClass.Name, + }, { - Name: vc.Name, + Name: expandableVolumeClass.Name, }, }))), ) - By("creating a second volume class") - vc2 := &storagev1alpha1.VolumeClass{ + testVolumeClass := &storagev1alpha1.VolumeClass{ ObjectMeta: metav1.ObjectMeta{ - GenerateName: "test-vc-2-", + GenerateName: "test-vc-1-", }, Capabilities: corev1alpha1.ResourceList{ corev1alpha1.ResourceTPS: resource.MustParse("250Mi"), - corev1alpha1.ResourceIOPS: resource.MustParse("2"), + corev1alpha1.ResourceIOPS: resource.MustParse("100"), }, } - Expect(k8sClient.Create(ctx, vc2)).To(Succeed(), "failed to create test volume class") + Expect(k8sClient.Create(ctx, testVolumeClass)).To(Succeed(), "failed to create test volume class") + DeferCleanup(k8sClient.Delete, testVolumeClass) + By("checking if the test volume class is present") Eventually(Object(volumePool)).Should(SatisfyAll( - HaveField("Status.AvailableVolumeClasses", HaveLen(1))), + HaveField("Status.AvailableVolumeClasses", ContainElements([]corev1.LocalObjectReference{ + { + Name: volumeClass.Name, + }, + { + Name: expandableVolumeClass.Name, + }, + { + Name: testVolumeClass.Name, + }, + }))), + ) + }) + + It("should calculate pool capacity", func(ctx SpecContext) { + var ( + volumeClassCapacity, expandableVolumeClassCapacity = resource.MustParse("12Gi"), resource.MustParse("50Gi") ) + By("announcing the capacity") srv.SetVolumeClasses([]*volume.FakeVolumeClassStatus{ { VolumeClassStatus: ori.VolumeClassStatus{ VolumeClass: &ori.VolumeClass{ - Name: vc.Name, + Name: volumeClass.Name, Capabilities: &ori.VolumeClassCapabilities{ Tps: 262144000, - Iops: 1, + Iops: 15000, }, }, + Quantity: volumeClassCapacity.AsDec().UnscaledBig().Int64(), }, }, { VolumeClassStatus: ori.VolumeClassStatus{ VolumeClass: &ori.VolumeClass{ - Name: vc2.Name, + Name: expandableVolumeClass.Name, Capabilities: &ori.VolumeClassCapabilities{ Tps: 262144000, - Iops: 2, + Iops: 1000, }, }, + Quantity: expandableVolumeClassCapacity.AsDec().UnscaledBig().Int64(), }, }, }) - By("checking if the second volume class is present") + By("checking if the capacity is correct") Eventually(Object(volumePool)).Should(SatisfyAll( - HaveField("Status.AvailableVolumeClasses", ConsistOf( - corev1.LocalObjectReference{ - Name: vc.Name, + HaveField("Status.Capacity", Satisfy(func(capacity corev1alpha1.ResourceList) bool { + return quota.Equals(capacity, corev1alpha1.ResourceList{ + corev1alpha1.ClassCountFor(corev1alpha1.ClassTypeVolumeClass, volumeClass.Name): volumeClassCapacity, + corev1alpha1.ClassCountFor(corev1alpha1.ClassTypeVolumeClass, expandableVolumeClass.Name): expandableVolumeClassCapacity, + }) + })), + )) + + By("creating a volume") + volume := &storagev1alpha1.Volume{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: ns.Name, + GenerateName: "volume-", + }, + Spec: storagev1alpha1.VolumeSpec{ + VolumeClassRef: &corev1.LocalObjectReference{Name: volumeClass.Name}, + VolumePoolRef: &corev1.LocalObjectReference{Name: volumePool.Name}, + Resources: corev1alpha1.ResourceList{ + corev1alpha1.ResourceStorage: resource.MustParse("10Gi"), }, - corev1.LocalObjectReference{ - Name: vc2.Name, + }, + } + Expect(k8sClient.Create(ctx, volume)).To(Succeed(), "failed to create volume") + DeferCleanup(ExpectVolumeDeleted, volume) + + By("checking if the allocatable resources are correct") + Eventually(Object(volumePool)).Should(SatisfyAll( + HaveField("Status.Allocatable", Satisfy(func(allocatable corev1alpha1.ResourceList) bool { + return quota.Equals(allocatable, corev1alpha1.ResourceList{ + corev1alpha1.ClassCountFor(corev1alpha1.ClassTypeVolumeClass, volumeClass.Name): resource.MustParse("2Gi"), + corev1alpha1.ClassCountFor(corev1alpha1.ClassTypeVolumeClass, expandableVolumeClass.Name): expandableVolumeClassCapacity, + }) + })), + )) + + By("creating a second volume") + secondVolume := &storagev1alpha1.Volume{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: ns.Name, + GenerateName: "volume-", + }, + Spec: storagev1alpha1.VolumeSpec{ + VolumeClassRef: &corev1.LocalObjectReference{Name: expandableVolumeClass.Name}, + VolumePoolRef: &corev1.LocalObjectReference{Name: volumePool.Name}, + Resources: corev1alpha1.ResourceList{ + corev1alpha1.ResourceStorage: resource.MustParse("10Gi"), }, - ))), - ) + }, + } + Expect(k8sClient.Create(ctx, secondVolume)).To(Succeed(), "failed to create second volume") + DeferCleanup(ExpectVolumeDeleted, secondVolume) + + By("checking if the allocatable resources are correct") + Eventually(Object(volumePool)).Should(SatisfyAll( + HaveField("Status.Allocatable", Satisfy(func(allocatable corev1alpha1.ResourceList) bool { + return quota.Equals(allocatable, corev1alpha1.ResourceList{ + corev1alpha1.ClassCountFor(corev1alpha1.ClassTypeVolumeClass, volumeClass.Name): resource.MustParse("2Gi"), + corev1alpha1.ClassCountFor(corev1alpha1.ClassTypeVolumeClass, expandableVolumeClass.Name): resource.MustParse("40Gi"), + }) + })), + )) }) })