From 1f9f2390cb57832f5fbd02e53d39023b81500cc2 Mon Sep 17 00:00:00 2001 From: Jan Safranek Date: Thu, 27 Feb 2020 09:56:15 +0100 Subject: [PATCH 1/2] Add block cloning tests Rework the tests to use InjectContent/TestVolumeClient when at it. --- test/e2e/storage/testsuites/provisioning.go | 111 +++++++++++++++++--- 1 file changed, 98 insertions(+), 13 deletions(-) diff --git a/test/e2e/storage/testsuites/provisioning.go b/test/e2e/storage/testsuites/provisioning.go index 2bb2b915bad..481dce871f5 100644 --- a/test/e2e/storage/testsuites/provisioning.go +++ b/test/e2e/storage/testsuites/provisioning.go @@ -19,6 +19,7 @@ package testsuites import ( "context" "fmt" + "sync" "time" "github.com/onsi/ginkgo" @@ -74,6 +75,7 @@ func InitProvisioningTestSuite() TestSuite { Name: "provisioning", TestPatterns: []testpatterns.TestPattern{ testpatterns.DefaultFsDynamicPV, + testpatterns.BlockVolModeDynamicPV, testpatterns.NtfsDynamicPV, }, SupportedSizeRange: volume.SizeRange{ @@ -115,6 +117,10 @@ func (p *provisioningTestSuite) DefineTests(driver TestDriver, pattern testpatte if pattern.VolType != testpatterns.DynamicPV { e2eskipper.Skipf("Suite %q does not support %v", p.tsInfo.Name, pattern.VolType) } + if pattern.VolMode == v1.PersistentVolumeBlock && !dInfo.Capabilities[CapBlock] { + e2eskipper.Skipf("Driver %q does not support block volumes - skipping", dInfo.Name) + } + ok := false dDriver, ok = driver.(DynamicPVTestDriver) if !ok { @@ -147,10 +153,12 @@ func (p *provisioningTestSuite) DefineTests(driver TestDriver, pattern testpatte l.pvc = e2epv.MakePersistentVolumeClaim(e2epv.PersistentVolumeClaimConfig{ ClaimSize: claimSize, StorageClassName: &(l.sc.Name), + VolumeMode: &pattern.VolMode, }, l.config.Framework.Namespace.Name) l.sourcePVC = e2epv.MakePersistentVolumeClaim(e2epv.PersistentVolumeClaimConfig{ ClaimSize: claimSize, StorageClassName: &(l.sc.Name), + VolumeMode: &pattern.VolMode, }, l.config.Framework.Namespace.Name) framework.Logf("In creating storage class object and pvc objects for driver - sc: %v, pvc: %v, src-pvc: %v", l.sc, l.pvc, l.sourcePVC) l.testCase = &StorageClassTest{ @@ -160,6 +168,7 @@ func (p *provisioningTestSuite) DefineTests(driver TestDriver, pattern testpatte Class: l.sc, ClaimSize: claimSize, ExpectedSize: claimSize, + VolumeMode: pattern.VolMode, } } @@ -175,6 +184,9 @@ func (p *provisioningTestSuite) DefineTests(driver TestDriver, pattern testpatte if dInfo.SupportedMountOption == nil { e2eskipper.Skipf("Driver %q does not define supported mount option - skipping", dInfo.Name) } + if pattern.VolMode == v1.PersistentVolumeBlock { + e2eskipper.Skipf("Block volumes do not support mount options - skipping") + } init() defer cleanup() @@ -212,26 +224,85 @@ func (p *provisioningTestSuite) DefineTests(driver TestDriver, pattern testpatte } l.testCase.TestDynamicProvisioning() }) + ginkgo.It("should provision storage with pvc data source", func() { if !dInfo.Capabilities[CapPVCDataSource] { e2eskipper.Skipf("Driver %q does not support cloning - skipping", dInfo.Name) } - init() defer cleanup() - dc := l.config.Framework.DynamicClient - dataSource, dataSourceCleanup := preparePVCDataSourceForProvisioning(l.config.ClientNodeSelection, l.cs, dc, l.sourcePVC, l.sc) + testConfig := convertTestConfig(l.config) + expectedContent := fmt.Sprintf("Hello from namespace %s", f.Namespace.Name) + dataSource, dataSourceCleanup := preparePVCDataSourceForProvisioning(f, testConfig, l.cs, l.sourcePVC, l.sc, pattern.VolMode, expectedContent) defer dataSourceCleanup() l.pvc.Spec.DataSource = dataSource l.testCase.PvCheck = func(claim *v1.PersistentVolumeClaim) { ginkgo.By("checking whether the created volume has the pre-populated data") - command := fmt.Sprintf("grep '%s' /mnt/test/initialData", claim.Namespace) - RunInPodWithVolume(l.cs, claim.Namespace, claim.Name, "pvc-datasource-tester", command, l.config.ClientNodeSelection) + tests := []volume.Test{ + { + Volume: *createVolumeSource(claim.Name, false /* readOnly */), + Mode: pattern.VolMode, + File: "index.html", + ExpectedContent: expectedContent, + }, + } + volume.TestVolumeClient(f, testConfig, nil, "", tests) } l.testCase.TestDynamicProvisioning() }) + + ginkgo.It("should provision storage with pvc data source in parallel [Slow]", func() { + // Test cloning a single volume multiple times. + if !dInfo.Capabilities[CapPVCDataSource] { + e2eskipper.Skipf("Driver %q does not support cloning - skipping", dInfo.Name) + } + if pattern.VolMode == v1.PersistentVolumeBlock && !dInfo.Capabilities[CapBlock] { + e2eskipper.Skipf("Driver %q does not support block volumes - skipping", dInfo.Name) + } + + init() + defer cleanup() + + testConfig := convertTestConfig(l.config) + expectedContent := fmt.Sprintf("Hello from namespace %s", f.Namespace.Name) + dataSource, dataSourceCleanup := preparePVCDataSourceForProvisioning(f, testConfig, l.cs, l.sourcePVC, l.sc, pattern.VolMode, expectedContent) + defer dataSourceCleanup() + l.pvc.Spec.DataSource = dataSource + + var wg sync.WaitGroup + for i := 0; i < 5; i++ { + wg.Add(1) + go func(i int) { + defer ginkgo.GinkgoRecover() + defer wg.Done() + ginkgo.By(fmt.Sprintf("Cloning volume nr. %d", i)) + // Each go routine must have its own pod prefix + myTestConfig := testConfig + myTestConfig.Prefix = fmt.Sprintf("%s-%d", myTestConfig.Prefix, i) + + // Each go routine must have its own testCase copy to store their claim + myTestCase := *l.testCase + myTestCase.Claim = myTestCase.Claim.DeepCopy() + myTestCase.Class = nil // Do not create/delete the storage class in TestDynamicProvisioning, it already exists. + myTestCase.PvCheck = func(claim *v1.PersistentVolumeClaim) { + ginkgo.By(fmt.Sprintf("checking whether the created volume %d has the pre-populated data", i)) + tests := []volume.Test{ + { + Volume: *createVolumeSource(claim.Name, false /* readOnly */), + Mode: pattern.VolMode, + File: "index.html", + ExpectedContent: expectedContent, + }, + } + volume.TestVolumeClient(f, myTestConfig, nil, "", tests) + } + myTestCase.TestDynamicProvisioning() + }(i) + } + wg.Wait() + }) } // TestDynamicProvisioning tests dynamic provisioning with specified StorageClassTest @@ -701,16 +772,18 @@ func prepareSnapshotDataSourceForProvisioning( } func preparePVCDataSourceForProvisioning( - node e2epod.NodeSelection, + f *framework.Framework, + config volume.TestConfig, client clientset.Interface, - dynamicClient dynamic.Interface, source *v1.PersistentVolumeClaim, class *storagev1.StorageClass, + mode v1.PersistentVolumeMode, + injectContent string, ) (*v1.TypedLocalObjectReference, func()) { var err error if class != nil { ginkgo.By("[Initialize dataSource]creating a StorageClass " + class.Name) - _, err = client.StorageV1().StorageClasses().Create(context.TODO(), class, metav1.CreateOptions{}) + class, err = client.StorageV1().StorageClasses().Create(context.TODO(), class, metav1.CreateOptions{}) framework.ExpectNoError(err) } @@ -718,10 +791,15 @@ func preparePVCDataSourceForProvisioning( sourcePVC, err := client.CoreV1().PersistentVolumeClaims(source.Namespace).Create(context.TODO(), source, metav1.CreateOptions{}) framework.ExpectNoError(err) - // write namespace to the /mnt/test (= the volume). - ginkgo.By("[Initialize dataSource]write data to volume") - command := fmt.Sprintf("echo '%s' > /mnt/test/initialData", sourcePVC.GetNamespace()) - RunInPodWithVolume(client, sourcePVC.Namespace, sourcePVC.Name, "pvc-datasource-writer", command, node) + tests := []volume.Test{ + { + Volume: *createVolumeSource(sourcePVC.Name, false /* readOnly */), + Mode: mode, + File: "index.html", + ExpectedContent: injectContent, + }, + } + volume.InjectContent(f, config, nil, "", tests) dataSourceRef := &v1.TypedLocalObjectReference{ Kind: "PersistentVolumeClaim", @@ -730,10 +808,17 @@ func preparePVCDataSourceForProvisioning( cleanupFunc := func() { framework.Logf("deleting source PVC %q/%q", sourcePVC.Namespace, sourcePVC.Name) - err = client.CoreV1().PersistentVolumeClaims(sourcePVC.Namespace).Delete(context.TODO(), sourcePVC.Name, nil) + err := client.CoreV1().PersistentVolumeClaims(sourcePVC.Namespace).Delete(context.TODO(), sourcePVC.Name, nil) if err != nil && !apierrors.IsNotFound(err) { framework.Failf("Error deleting source PVC %q. Error: %v", sourcePVC.Name, err) } + if class != nil { + framework.Logf("deleting class %q", class.Name) + err := client.StorageV1().StorageClasses().Delete(context.TODO(), class.Name, nil) + if err != nil && !apierrors.IsNotFound(err) { + framework.Failf("Error deleting storage class %q. Error: %v", class.Name, err) + } + } } return dataSourceRef, cleanupFunc From 347d8419488c3bc9f5e4e63ea774c0f8dcf41eee Mon Sep 17 00:00:00 2001 From: Jan Safranek Date: Thu, 27 Feb 2020 12:13:45 +0100 Subject: [PATCH 2/2] Bump CSI hostpath driver To get block cloning support --- .../storage-csi/hostpath/hostpath/csi-hostpath-plugin.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/testing-manifests/storage-csi/hostpath/hostpath/csi-hostpath-plugin.yaml b/test/e2e/testing-manifests/storage-csi/hostpath/hostpath/csi-hostpath-plugin.yaml index 35c53dda7eb..9906b45bb5a 100644 --- a/test/e2e/testing-manifests/storage-csi/hostpath/hostpath/csi-hostpath-plugin.yaml +++ b/test/e2e/testing-manifests/storage-csi/hostpath/hostpath/csi-hostpath-plugin.yaml @@ -65,7 +65,7 @@ spec: name: csi-data-dir - name: hostpath - image: quay.io/k8scsi/hostpathplugin:v1.3.0-rc1 + image: quay.io/k8scsi/hostpathplugin:v1.4.0-rc1 args: - "--drivername=hostpath.csi.k8s.io" - "--v=5"