diff --git a/ci/ci-test.sh b/ci/ci-test.sh index 4f6bef003..d518c1662 100755 --- a/ci/ci-test.sh +++ b/ci/ci-test.sh @@ -107,7 +107,7 @@ runTestSuite() { echo "running ginkgo test case with coverage ${coverageFile}" - if ! ginkgo -v -coverprofile="${coverageFile}" --label-filter="${labelFilter}" -covermode=atomic; then + if ! ginkgo -p -v -coverprofile="${coverageFile}" --label-filter="${labelFilter}" -covermode=atomic; then sudo zpool status diff --git a/pkg/driver/controller.go b/pkg/driver/controller.go index 990eee458..3abefba5d 100644 --- a/pkg/driver/controller.go +++ b/pkg/driver/controller.go @@ -502,6 +502,18 @@ func (cs *controller) DeleteVolume( unlock := cs.volumeLock.LockVolume(volumeID) defer unlock() + // Fetch the list of snapshot for the given volume + snapList, err := zfs.GetSnapshotForVolume(volumeID) + if err != nil { + return nil, status.Errorf( + codes.NotFound, + "failed to handle delete volume request for {%s}, "+ + "validation failed checking for snapshots. Error: %s", + req.VolumeId, + err.Error(), + ) + } + // verify if the volume has already been deleted vol, err := zfs.GetVolume(volumeID) if vol != nil && vol.DeletionTimestamp != nil { @@ -524,14 +536,17 @@ func (cs *controller) DeleteVolume( return nil, status.Error(codes.Internal, "can not delete, volume creation is in progress") } - // Delete the corresponding ZV CR - err = zfs.DeleteVolume(volumeID) - if err != nil { - return nil, errors.Wrapf( - err, - "failed to handle delete volume request for {%s}", - volumeID, - ) + // Delete the corresponding ZV CR only if there are no snapshots present for the volume + + if len(snapList.Items) == 0 { + err = zfs.DeleteVolume(volumeID) + if err != nil { + return nil, errors.Wrapf( + err, + "failed to handle delete volume request for {%s}", + volumeID, + ) + } } sendEventOrIgnore("", volumeID, vol.Spec.Capacity, analytics.VolumeDeprovision) diff --git a/pkg/zfs/volume.go b/pkg/zfs/volume.go index 446e4a667..b4da2cd2d 100644 --- a/pkg/zfs/volume.go +++ b/pkg/zfs/volume.go @@ -441,3 +441,12 @@ func IsVolumeReady(vol *apis.ZFSVolume) bool { return false } + +// GetSnapshotForVolume fetches all the snapshots for the given volume +func GetSnapshotForVolume(volumeID string) (*apis.ZFSSnapshotList, error) { + listOptions := metav1.ListOptions{ + LabelSelector: ZFSVolKey + "=" + volumeID, + } + snapList, err := snapbuilder.NewKubeclient().WithNamespace(OpenEBSNamespace).List(listOptions) + return snapList, err +} diff --git a/tests/provision_test.go b/tests/provision_test.go index 35246080a..c1750f3ad 100644 --- a/tests/provision_test.go +++ b/tests/provision_test.go @@ -24,6 +24,7 @@ var _ = Describe("[zfspv] TEST VOLUME PROVISIONING", func() { Context("App is deployed with zfs driver", func() { It("Running zfs volume Creation Test", volumeCreationTest) It("Running zfs volume Creation Test with custom node id", Label("custom-node-id"), volumeCreationTest) + It("Running zfs volume Deletion Test", volumeDeletionTest) }) }) @@ -41,7 +42,7 @@ func exhaustiveVolumeTests(parameters map[string]string) { snapshotAndCloneCreate() // btrfs does not support online resize if fstype != "btrfs" { - By("Resizing the PVC", resizeAndVerifyPVC) + By("Resizing the PVC", func() { resizeAndVerifyPVC(pvcNameFS) }) } snapshotAndCloneCleanUp() cleanUp() @@ -51,8 +52,8 @@ func exhaustiveVolumeTests(parameters map[string]string) { func create(parameters map[string]string) { By("####### Creating the storage class : " + parameters["fstype"] + " #######") createFstypeStorageClass(parameters) - By("creating and verifying PVC bound status", createAndVerifyPVC) - By("Creating and deploying app pod", createDeployVerifyApp) + By("creating and verifying PVC bound status", func() { createAndVerifyPVC(pvcNameFS) }) + By("Creating and deploying app pod", func() { createDeployVerifyApp(appNameFS, pvcNameFS) }) By("verifying ZFSVolume object", VerifyZFSVolume) By("verifying storage class parameters") VerifyStorageClassParams(parameters) @@ -60,48 +61,78 @@ func create(parameters map[string]string) { // Creates the snapshot/clone resources func snapshotAndCloneCreate() { - createSnapshot(pvcName, snapName) - verifySnapshotCreated(snapName) - createClone(clonePvcName, snapName, scObj.Name) - By("Creating and deploying clone app pod", createDeployVerifyCloneApp) + createSnapshot(pvcNameFS, snapNameFS) + verifySnapshotCreated(snapNameFS) + createClone(clonePvcNameFS, snapNameFS, scObj.Name) + By("Creating and deploying clone app pod", func() { createDeployVerifyCloneApp(cloneAppNameFS, clonePvcNameFS) }) } // Removes the snapshot/clone resources func snapshotAndCloneCleanUp() { - deleteAppDeployment(cloneAppName) - deletePVC(clonePvcName) - deleteSnapshot(pvcName, snapName) + deleteAppDeployment(cloneAppNameFS) + deletePVC(clonePvcNameFS) + deleteSnapshot(pvcNameFS, snapNameFS) } // Removes the resources func cleanUp() { - deleteAppDeployment(appName) - deletePVC(pvcName) + deleteAppDeployment(appNameFS) + deletePVC(pvcNameFS) By("Deleting storage class", deleteStorageClass) } func blockVolCreationTest() { By("Creating default storage class", createStorageClass) - By("creating and verifying PVC bound status", createAndVerifyBlockPVC) + By("creating and verifying PVC bound status", func() { createAndVerifyPVC(pvcNameBlock) }) - By("Creating and deploying app pod", createDeployVerifyBlockApp) + By("Creating and deploying app pod", func() { createDeployVerifyApp(appNameBlock, pvcNameBlock) }) By("verifying ZFSVolume object", VerifyZFSVolume) By("verifying ZFSVolume property change", VerifyZFSVolumePropEdit) - By("Deleting application deployment") - createSnapshot(pvcName, snapName) - verifySnapshotCreated(snapName) - createClone(clonePvcName, snapName, scObj.Name) - By("Creating and deploying clone app pod", createDeployVerifyCloneApp) + createSnapshot(pvcNameBlock, snapNameBlock) + verifySnapshotCreated(snapNameBlock) + createClone(clonePvcNameBlock, snapNameBlock, scObj.Name) + By("Creating and deploying clone app pod", func() { createDeployVerifyCloneApp(cloneAppNameBlock, clonePvcNameBlock) }) By("Deleting clone and main application deployment") - deleteAppDeployment(cloneAppName) - deleteAppDeployment(appName) + deleteAppDeployment(cloneAppNameBlock) + deleteAppDeployment(appNameBlock) By("Deleting snapshot, main pvc and clone pvc") - deletePVC(clonePvcName) - deleteSnapshot(pvcName, snapName) - deletePVC(pvcName) + deletePVC(clonePvcNameBlock) + deleteSnapshot(pvcNameBlock, snapNameBlock) + deletePVC(pvcNameBlock) + + By("Deleting storage class", deleteStorageClass) +} + +func blockVolDeletionTest() { + By("Creating default storage class", createStorageClass) + By("creating and verifying PVC bound status", func() { createAndVerifyPVC(pvcNameAplha) }) + + By("Creating and deploying app pod", func() { createDeployVerifyApp(appNameAlpha, pvcNameAplha) }) + By("verifying ZFSVolume object", VerifyZFSVolume) + + createSnapshot(pvcNameAplha, snapNameAlpha) + verifySnapshotCreated(snapNameAlpha) + + By("Deleting main application deployment") + deleteAppDeployment(appNameAlpha) + + By("Deleting main pvc") + deletePVC(pvcNameAplha) + + By("Verifying ZFSVolume object after pvc deletion when snapshot is present", VerifyZFSVolume) + + By("Creating clone from the snapshot") + createClone(clonePvcNameAlpha, snapNameAlpha, scObj.Name) + By("Creating and deploying clone app pod", func() { createDeployVerifyCloneApp(cloneAppNameAlpha, clonePvcNameAlpha) }) + + By("Deleting clone application deployment, clone pvc") + deleteAppDeployment(cloneAppNameAlpha) + + deletePVC(clonePvcNameAlpha) + deleteSnapshot(pvcNameAplha, snapNameAlpha) By("Deleting storage class", deleteStorageClass) } @@ -109,4 +140,9 @@ func blockVolCreationTest() { func volumeCreationTest() { By("Running volume creation test", fsVolCreationTest) By("Running block volume creation test", blockVolCreationTest) + +} + +func volumeDeletionTest() { + By("Running volume deletion test", blockVolDeletionTest) } diff --git a/tests/pts/pts.go b/tests/pts/pts.go index 37dbe6312..0ff8bd2f5 100644 --- a/tests/pts/pts.go +++ b/tests/pts/pts.go @@ -339,6 +339,14 @@ func (b *Builder) WithContainerBuilders( return b } +// WithTerminationGracePeriodSeconds adds the terminationGracePeriodSeconds +func (b *Builder) WithTerminationGracePeriodSeconds( + period int64, +) *Builder { + b.podtemplatespec.Object.Spec.TerminationGracePeriodSeconds = &period + return b +} + // WithVolumeBuilders builds the list of volumebuilders provided // and merges it to the volumes field of podtemplatespec. func (b *Builder) WithVolumeBuilders( diff --git a/tests/suite_test.go b/tests/suite_test.go index b20d46b57..8954d6475 100644 --- a/tests/suite_test.go +++ b/tests/suite_test.go @@ -48,11 +48,33 @@ var ( PodClient *pod.KubeClient scName = "zfspv-sc" ZFSProvisioner = "zfs.csi.openebs.io" - pvcName = "zfspv-pvc" - snapName = "zfspv-snap" - appName = "busybox-zfspv" - clonePvcName = "zfspv-pvc-clone" - cloneAppName = "busybox-zfspv-clone" + + pvcName = "zfspv-pvc-fs" + snapName = "zfspv-snap" + appName = "busybox-zfspv" + + clonePvcName = "zfspv-pvc-clone" + cloneAppName = "busybox-zfspv-clone" + + pvcNameFS = "zfspv-pvc-fs" + pvcNameBlock = "zfspv-pvc-block" + pvcNameAplha = "zfspv-pvc-alpha" + + appNameFS = "busybox-zfspv-fs" + appNameBlock = "busybox-zfspv-block" + appNameAlpha = "busybox-zfspv-alpha" + + snapNameFS = "zfspv-snap-fs" + snapNameBlock = "zfspv-snap-block" + snapNameAlpha = "zfspv-snap-alpha" + + clonePvcNameFS = "zfspv-pvc-clone-fs" + clonePvcNameBlock = "zfspv-pvc-clone-block" + clonePvcNameAlpha = "zfspv-pvc-clone-alpha" + + cloneAppNameFS = "busybox-zfspv-clone-fs" + cloneAppNameBlock = "busybox-zfspv-clone-block" + cloneAppNameAlpha = "busybox-zfspv-clone-alpha" scObj *storagev1.StorageClass deployObj *appsv1.Deployment @@ -71,7 +93,8 @@ func init() { OpenEBSNamespace = os.Getenv("OPENEBS_NAMESPACE") if OpenEBSNamespace == "" { - os.Setenv("OPENEBS_NAMESPACE", "openebs") + OpenEBSNamespace = "openebs" + os.Setenv("OPENEBS_NAMESPACE", OpenEBSNamespace) } SCClient = sc.NewKubeClient(sc.WithKubeConfigPath(KubeConfigPath)) PVCClient = pvc.NewKubeClient(pvc.WithKubeConfigPath(KubeConfigPath)) diff --git a/tests/utils.go b/tests/utils.go index a8102a0bb..d12c0ecb5 100644 --- a/tests/utils.go +++ b/tests/utils.go @@ -18,10 +18,11 @@ package tests import ( "fmt" - "k8s.io/klog/v2" "os/exec" "time" + "k8s.io/klog/v2" + "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" @@ -349,11 +350,11 @@ func deleteStorageClass() { "while deleting zfs storageclass {%s}", scObj.Name) } -func createAndVerifyPVC() { +func createAndVerifyPVC(pvcName string) { var ( - err error - pvcName = "zfspv-pvc" + err error ) + ginkgo.By("building a pvc") pvcObj, err = pvc.NewBuilder(). WithName(pvcName). @@ -361,53 +362,12 @@ func createAndVerifyPVC() { WithStorageClass(scObj.Name). WithAccessModes(accessModes). WithCapacity(capacity).Build() - gomega.Expect(err).ShouldNot( - gomega.HaveOccurred(), - "while building pvc {%s} in namespace {%s}", - pvcName, - OpenEBSNamespace, - ) - - ginkgo.By("creating above pvc") - pvcObj, err = PVCClient.WithNamespace(OpenEBSNamespace).Create(pvcObj) - gomega.Expect(err).To( - gomega.BeNil(), - "while creating pvc {%s} in namespace {%s}", - pvcName, - OpenEBSNamespace, - ) - - ginkgo.By("verifying pvc status as bound") - - status := IsPVCBoundEventually(pvcName) - gomega.Expect(status).To(gomega.Equal(true), - "while checking status equal to bound") - - pvcObj, err = PVCClient.WithNamespace(OpenEBSNamespace).Get(pvcObj.Name, metav1.GetOptions{}) - gomega.Expect(err).To( - gomega.BeNil(), - "while retrieving pvc {%s} in namespace {%s}", - pvcName, - OpenEBSNamespace, - ) -} -func createAndVerifyBlockPVC() { - var ( - err error - pvcName = "zfspv-pvc" - ) - - volmode := corev1.PersistentVolumeBlock + if pvcName == "zfspv-pvc-block" { + volmode := corev1.PersistentVolumeBlock + pvcObj.Spec.VolumeMode = &volmode + } - ginkgo.By("building a pvc") - pvcObj, err = pvc.NewBuilder(). - WithName(pvcName). - WithNamespace(OpenEBSNamespace). - WithStorageClass(scObj.Name). - WithAccessModes(accessModes). - WithVolumeMode(&volmode). - WithCapacity(capacity).Build() gomega.Expect(err).ShouldNot( gomega.HaveOccurred(), "while building pvc {%s} in namespace {%s}", @@ -439,10 +399,9 @@ func createAndVerifyBlockPVC() { ) } -func resizeAndVerifyPVC() { +func resizeAndVerifyPVC(pvcName string) { var ( - err error - pvcName = "zfspv-pvc" + err error ) ginkgo.By("updating the pvc with new size") pvcObj, err = PVCClient.WithNamespace(OpenEBSNamespace).Get(pvcObj.Name, metav1.GetOptions{}) @@ -476,43 +435,39 @@ func resizeAndVerifyPVC() { OpenEBSNamespace, ) } -func createDeployVerifyApp() { +func createDeployVerifyApp(appName, pvcName string) { ginkgo.By("creating and deploying app pod") - createAndDeployAppPod(appName) + if pvcName == "zfspv-pvc-block" || pvcName == "pvc-name-for-del-test" { + createAndDeployBlockAppPod(appName, pvcName) + } else { + createAndDeployAppPod(appName, pvcName) + } + time.Sleep(30 * time.Second) - ginkgo.By("verifying app pod is running", verifyAppPodRunning) + ginkgo.By("verifying app pod is running", func() { verifyAppPodRunning(appName) }) } -func createDeployVerifyCloneApp() { +func createDeployVerifyCloneApp(cloneAppName, clonePvcName string) { ginkgo.By("creating and deploying app pod") - createAndDeployAppPod(cloneAppName) - time.Sleep(30 * time.Second) - ginkgo.By("verifying app pod is running", verifyAppPodRunning) + createAndDeployAppPod(cloneAppName, clonePvcName) + ginkgo.By("verifying app pod is running", func() { verifyAppPodRunning(cloneAppName) }) } -func createAndDeployAppPod(appname string) { +func createAndDeployAppPod(appName string, pvcname string) { var err error + labels := map[string]string{ + "app": "busybox", + "appName": appName, + } ginkgo.By("building a busybox app pod deployment using above zfs volume") deployObj, err = deploy.NewBuilder(). - WithName(appname). + WithName(appName). WithNamespace(OpenEBSNamespace). - WithLabelsNew( - map[string]string{ - "app": "busybox", - }, - ). - WithSelectorMatchLabelsNew( - map[string]string{ - "app": "busybox", - }, - ). + WithLabelsNew(labels). + WithSelectorMatchLabelsNew(labels). WithPodTemplateSpecBuilder( pts.NewBuilder(). - WithLabelsNew( - map[string]string{ - "app": "busybox", - }, - ). + WithLabelsNew(labels). WithContainerBuilders( container.NewBuilder(). WithImage("busybox"). @@ -537,8 +492,9 @@ func createAndDeployAppPod(appname string) { WithVolumeBuilders( k8svolume.NewBuilder(). WithName("datavol1"). - WithPVCSource(pvcObj.Name), - ), + WithPVCSource(pvcname), + ). + WithTerminationGracePeriodSeconds(5), ). Build() @@ -553,29 +509,21 @@ func createAndDeployAppPod(appname string) { ) } -func createAndDeployBlockAppPod() { +func createAndDeployBlockAppPod(appName, pvcName string) { var err error + labels := map[string]string{ + "app": "busybox", + "appName": appName, + } ginkgo.By("building a busybox app pod deployment using above zfs volume") deployObj, err = deploy.NewBuilder(). WithName(appName). WithNamespace(OpenEBSNamespace). - WithLabelsNew( - map[string]string{ - "app": "busybox", - }, - ). - WithSelectorMatchLabelsNew( - map[string]string{ - "app": "busybox", - }, - ). + WithLabelsNew(labels). + WithSelectorMatchLabelsNew(labels). WithPodTemplateSpecBuilder( pts.NewBuilder(). - WithLabelsNew( - map[string]string{ - "app": "busybox", - }, - ). + WithLabelsNew(labels). WithContainerBuilders( container.NewBuilder(). WithImage("busybox"). @@ -601,7 +549,8 @@ func createAndDeployBlockAppPod() { k8svolume.NewBuilder(). WithName("datavol1"). WithPVCSource(pvcObj.Name), - ), + ). + WithTerminationGracePeriodSeconds(5), ). Build() @@ -616,20 +565,18 @@ func createAndDeployBlockAppPod() { ) } -func createDeployVerifyBlockApp() { - ginkgo.By("creating and deploying app pod", createAndDeployBlockAppPod) - time.Sleep(30 * time.Second) - ginkgo.By("verifying app pod is running", verifyAppPodRunning) -} - -func verifyAppPodRunning() { +func verifyAppPodRunning(appname string) { var err error - appPod, err = PodClient.WithNamespace(OpenEBSNamespace). - List(metav1.ListOptions{ - LabelSelector: "app=busybox", - }, - ) - gomega.Expect(err).ShouldNot(gomega.HaveOccurred(), "while verifying application pod") + gomega.Eventually(func() bool { + appPod, err = PodClient.WithNamespace(OpenEBSNamespace). + List(metav1.ListOptions{ + LabelSelector: "app=busybox,appName=" + appname, + }) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred(), "while verifying application pod") + return len(appPod.Items) == 1 + }, + 60, 5). + Should(gomega.BeTrue()) status := IsPodRunningEventually(OpenEBSNamespace, appPod.Items[0].Name) gomega.Expect(status).To(gomega.Equal(true), "while checking status of pod {%s}", appPod.Items[0].Name)