diff --git a/test/e2e/storage/framework/testdriver.go b/test/e2e/storage/framework/testdriver.go index f3f2dab6159..ba7936874e6 100644 --- a/test/e2e/storage/framework/testdriver.go +++ b/test/e2e/storage/framework/testdriver.go @@ -185,7 +185,8 @@ const ( // capacity information for it. CapCapacity Capability = "capacity" - // Anti-capability for drivers that do not support filesystem resizing of PVCs that are cloned or restored from a snapshot. + // Anti-capability for drivers that do not support filesystem resizing of PVCs + // that are cloned or restored from a snapshot. CapFSResizeFromSourceNotSupported Capability = "FSResizeFromSourceNotSupported" // To support ReadWriteOncePod, the following CSI sidecars must be @@ -204,6 +205,9 @@ const ( // talk to Kubernetes API server in any way should keep this capability enabled, because // they will see the same NodeStage / NodePublish requests as if only one PV existed. CapMultiplePVsSameID Capability = "multiplePVsSameID" + + // The driver supports ReadOnlyMany (ROX) access mode + CapReadOnlyMany Capability = "capReadOnlyMany" ) // DriverInfo represents static information about a TestDriver. diff --git a/test/e2e/storage/testsuites/provisioning.go b/test/e2e/storage/testsuites/provisioning.go index 2bb409488d2..26ebe69f935 100644 --- a/test/e2e/storage/testsuites/provisioning.go +++ b/test/e2e/storage/testsuites/provisioning.go @@ -19,12 +19,13 @@ package testsuites import ( "context" "fmt" - e2ekubectl "k8s.io/kubernetes/test/e2e/framework/kubectl" "strconv" "strings" "sync" "time" + e2ekubectl "k8s.io/kubernetes/test/e2e/framework/kubectl" + "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" @@ -234,6 +235,48 @@ func (p *provisioningTestSuite) DefineTests(driver storageframework.TestDriver, l.testCase.TestDynamicProvisioning(ctx) }) + ginkgo.It("should provision storage with snapshot data source (ROX mode) [Feature:VolumeSnapshotDataSource]", func(ctx context.Context) { + if !dInfo.Capabilities[storageframework.CapSnapshotDataSource] { + e2eskipper.Skipf("Driver %q does not support populating data from snapshot - skipping", dInfo.Name) + } + if !dInfo.SupportedFsType.Has(pattern.FsType) { + e2eskipper.Skipf("Driver %q does not support %q fs type - skipping", dInfo.Name, pattern.FsType) + } + if !dInfo.Capabilities[storageframework.CapReadOnlyMany] { + e2eskipper.Skipf("Driver %q does not support ROX access mode - skipping", dInfo.Name) + } + + sDriver, ok := driver.(storageframework.SnapshottableTestDriver) + if !ok { + framework.Failf("Driver %q has CapSnapshotDataSource but does not implement SnapshottableTestDriver", dInfo.Name) + } + + init(ctx) + + dc := l.config.Framework.DynamicClient + testConfig := storageframework.ConvertTestConfig(l.config) + expectedContent := fmt.Sprintf("Hello from namespace %s", f.Namespace.Name) + dataSourceRef := prepareSnapshotDataSourceForProvisioning(ctx, f, testConfig, l.config, pattern, l.cs, dc, l.pvc, l.sc, sDriver, pattern.VolMode, expectedContent) + + l.pvc.Spec.DataSourceRef = dataSourceRef + l.pvc.Spec.AccessModes = []v1.PersistentVolumeAccessMode{ + v1.PersistentVolumeAccessMode(v1.ReadOnlyMany), + } + l.testCase.PvCheck = func(ctx context.Context, claim *v1.PersistentVolumeClaim) { + ginkgo.By("checking whether the created volume has the pre-populated data") + tests := []e2evolume.Test{ + { + Volume: *storageutils.CreateVolumeSource(claim.Name, false /* readOnly */), + Mode: pattern.VolMode, + File: "index.html", + ExpectedContent: expectedContent, + }, + } + e2evolume.TestVolumeClientSlow(ctx, f, testConfig, nil, "", tests) + } + l.testCase.TestDynamicProvisioning(ctx) + }) + ginkgo.It("should provision storage with any volume data source [Serial]", func(ctx context.Context) { if len(dInfo.InTreePluginName) != 0 { e2eskipper.Skipf("AnyVolumeDataSource feature only works with CSI drivers - skipping") @@ -527,6 +570,48 @@ func (p *provisioningTestSuite) DefineTests(driver storageframework.TestDriver, l.testCase.TestDynamicProvisioning(ctx) }) + ginkgo.It("should provision storage with pvc data source (ROX mode)", func(ctx context.Context) { + if !dInfo.Capabilities[storageframework.CapPVCDataSource] { + e2eskipper.Skipf("Driver %q does not support cloning - skipping", dInfo.Name) + } + if !dInfo.Capabilities[storageframework.CapReadOnlyMany] { + e2eskipper.Skipf("Driver %q does not support ROX access mode - skipping", dInfo.Name) + } + init(ctx) + + if l.config.ClientNodeSelection.Name == "" { + // Schedule all pods to the same topology segment (e.g. a cloud availability zone), some + // drivers don't support cloning across them. + if err := ensureTopologyRequirements(ctx, &l.config.ClientNodeSelection, l.cs, dInfo, 1); err != nil { + framework.Failf("Error setting topology requirements: %v", err) + } + } + testConfig := storageframework.ConvertTestConfig(l.config) + expectedContent := fmt.Sprintf("Hello from namespace %s", f.Namespace.Name) + dataSourceRef := preparePVCDataSourceForProvisioning(ctx, f, testConfig, l.cs, l.sourcePVC, l.sc, pattern.VolMode, expectedContent) + l.pvc.Spec.DataSourceRef = dataSourceRef + l.pvc.Spec.AccessModes = []v1.PersistentVolumeAccessMode{ + v1.PersistentVolumeAccessMode(v1.ReadOnlyMany), + } + l.testCase.NodeSelection = testConfig.ClientNodeSelection + l.testCase.PvCheck = func(ctx context.Context, claim *v1.PersistentVolumeClaim) { + ginkgo.By("checking whether the created volume has the pre-populated data") + tests := []e2evolume.Test{ + { + Volume: *storageutils.CreateVolumeSource(claim.Name, false /* readOnly */), + Mode: pattern.VolMode, + File: "index.html", + ExpectedContent: expectedContent, + }, + } + e2evolume.TestVolumeClientSlow(ctx, f, testConfig, nil, "", tests) + } + // Cloning fails if the source disk is still in the process of detaching, so we wait for the VolumeAttachment to be removed before cloning. + volumeAttachment := e2evolume.GetVolumeAttachmentName(ctx, f.ClientSet, testConfig, l.testCase.Provisioner, dataSourceRef.Name, l.sourcePVC.Namespace) + e2evolume.WaitForVolumeAttachmentTerminated(ctx, volumeAttachment, f.ClientSet, f.Timeouts.DataSourceProvision) + l.testCase.TestDynamicProvisioning(ctx) + }) + ginkgo.It("should provision storage with pvc data source in parallel [Slow]", func(ctx context.Context) { // Test cloning a single volume multiple times. if !dInfo.Capabilities[storageframework.CapPVCDataSource] {