mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-22 11:21:47 +00:00
Merge pull request #88242 from jsafrane/cloning-test
Add block cloning test
This commit is contained in:
commit
0046b4cefb
@ -19,6 +19,7 @@ package testsuites
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/onsi/ginkgo"
|
"github.com/onsi/ginkgo"
|
||||||
@ -74,6 +75,7 @@ func InitProvisioningTestSuite() TestSuite {
|
|||||||
Name: "provisioning",
|
Name: "provisioning",
|
||||||
TestPatterns: []testpatterns.TestPattern{
|
TestPatterns: []testpatterns.TestPattern{
|
||||||
testpatterns.DefaultFsDynamicPV,
|
testpatterns.DefaultFsDynamicPV,
|
||||||
|
testpatterns.BlockVolModeDynamicPV,
|
||||||
testpatterns.NtfsDynamicPV,
|
testpatterns.NtfsDynamicPV,
|
||||||
},
|
},
|
||||||
SupportedSizeRange: volume.SizeRange{
|
SupportedSizeRange: volume.SizeRange{
|
||||||
@ -115,6 +117,10 @@ func (p *provisioningTestSuite) DefineTests(driver TestDriver, pattern testpatte
|
|||||||
if pattern.VolType != testpatterns.DynamicPV {
|
if pattern.VolType != testpatterns.DynamicPV {
|
||||||
e2eskipper.Skipf("Suite %q does not support %v", p.tsInfo.Name, pattern.VolType)
|
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
|
ok := false
|
||||||
dDriver, ok = driver.(DynamicPVTestDriver)
|
dDriver, ok = driver.(DynamicPVTestDriver)
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -147,10 +153,12 @@ func (p *provisioningTestSuite) DefineTests(driver TestDriver, pattern testpatte
|
|||||||
l.pvc = e2epv.MakePersistentVolumeClaim(e2epv.PersistentVolumeClaimConfig{
|
l.pvc = e2epv.MakePersistentVolumeClaim(e2epv.PersistentVolumeClaimConfig{
|
||||||
ClaimSize: claimSize,
|
ClaimSize: claimSize,
|
||||||
StorageClassName: &(l.sc.Name),
|
StorageClassName: &(l.sc.Name),
|
||||||
|
VolumeMode: &pattern.VolMode,
|
||||||
}, l.config.Framework.Namespace.Name)
|
}, l.config.Framework.Namespace.Name)
|
||||||
l.sourcePVC = e2epv.MakePersistentVolumeClaim(e2epv.PersistentVolumeClaimConfig{
|
l.sourcePVC = e2epv.MakePersistentVolumeClaim(e2epv.PersistentVolumeClaimConfig{
|
||||||
ClaimSize: claimSize,
|
ClaimSize: claimSize,
|
||||||
StorageClassName: &(l.sc.Name),
|
StorageClassName: &(l.sc.Name),
|
||||||
|
VolumeMode: &pattern.VolMode,
|
||||||
}, l.config.Framework.Namespace.Name)
|
}, 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)
|
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{
|
l.testCase = &StorageClassTest{
|
||||||
@ -160,6 +168,7 @@ func (p *provisioningTestSuite) DefineTests(driver TestDriver, pattern testpatte
|
|||||||
Class: l.sc,
|
Class: l.sc,
|
||||||
ClaimSize: claimSize,
|
ClaimSize: claimSize,
|
||||||
ExpectedSize: claimSize,
|
ExpectedSize: claimSize,
|
||||||
|
VolumeMode: pattern.VolMode,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,6 +184,9 @@ func (p *provisioningTestSuite) DefineTests(driver TestDriver, pattern testpatte
|
|||||||
if dInfo.SupportedMountOption == nil {
|
if dInfo.SupportedMountOption == nil {
|
||||||
e2eskipper.Skipf("Driver %q does not define supported mount option - skipping", dInfo.Name)
|
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()
|
init()
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
@ -212,26 +224,85 @@ func (p *provisioningTestSuite) DefineTests(driver TestDriver, pattern testpatte
|
|||||||
}
|
}
|
||||||
l.testCase.TestDynamicProvisioning()
|
l.testCase.TestDynamicProvisioning()
|
||||||
})
|
})
|
||||||
|
|
||||||
ginkgo.It("should provision storage with pvc data source", func() {
|
ginkgo.It("should provision storage with pvc data source", func() {
|
||||||
if !dInfo.Capabilities[CapPVCDataSource] {
|
if !dInfo.Capabilities[CapPVCDataSource] {
|
||||||
e2eskipper.Skipf("Driver %q does not support cloning - skipping", dInfo.Name)
|
e2eskipper.Skipf("Driver %q does not support cloning - skipping", dInfo.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
init()
|
init()
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
dc := l.config.Framework.DynamicClient
|
testConfig := convertTestConfig(l.config)
|
||||||
dataSource, dataSourceCleanup := preparePVCDataSourceForProvisioning(l.config.ClientNodeSelection, l.cs, dc, l.sourcePVC, l.sc)
|
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()
|
defer dataSourceCleanup()
|
||||||
|
|
||||||
l.pvc.Spec.DataSource = dataSource
|
l.pvc.Spec.DataSource = dataSource
|
||||||
l.testCase.PvCheck = func(claim *v1.PersistentVolumeClaim) {
|
l.testCase.PvCheck = func(claim *v1.PersistentVolumeClaim) {
|
||||||
ginkgo.By("checking whether the created volume has the pre-populated data")
|
ginkgo.By("checking whether the created volume has the pre-populated data")
|
||||||
command := fmt.Sprintf("grep '%s' /mnt/test/initialData", claim.Namespace)
|
tests := []volume.Test{
|
||||||
RunInPodWithVolume(l.cs, claim.Namespace, claim.Name, "pvc-datasource-tester", command, l.config.ClientNodeSelection)
|
{
|
||||||
|
Volume: *createVolumeSource(claim.Name, false /* readOnly */),
|
||||||
|
Mode: pattern.VolMode,
|
||||||
|
File: "index.html",
|
||||||
|
ExpectedContent: expectedContent,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
volume.TestVolumeClient(f, testConfig, nil, "", tests)
|
||||||
}
|
}
|
||||||
l.testCase.TestDynamicProvisioning()
|
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
|
// TestDynamicProvisioning tests dynamic provisioning with specified StorageClassTest
|
||||||
@ -701,16 +772,18 @@ func prepareSnapshotDataSourceForProvisioning(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func preparePVCDataSourceForProvisioning(
|
func preparePVCDataSourceForProvisioning(
|
||||||
node e2epod.NodeSelection,
|
f *framework.Framework,
|
||||||
|
config volume.TestConfig,
|
||||||
client clientset.Interface,
|
client clientset.Interface,
|
||||||
dynamicClient dynamic.Interface,
|
|
||||||
source *v1.PersistentVolumeClaim,
|
source *v1.PersistentVolumeClaim,
|
||||||
class *storagev1.StorageClass,
|
class *storagev1.StorageClass,
|
||||||
|
mode v1.PersistentVolumeMode,
|
||||||
|
injectContent string,
|
||||||
) (*v1.TypedLocalObjectReference, func()) {
|
) (*v1.TypedLocalObjectReference, func()) {
|
||||||
var err error
|
var err error
|
||||||
if class != nil {
|
if class != nil {
|
||||||
ginkgo.By("[Initialize dataSource]creating a StorageClass " + class.Name)
|
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)
|
framework.ExpectNoError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -718,10 +791,15 @@ func preparePVCDataSourceForProvisioning(
|
|||||||
sourcePVC, err := client.CoreV1().PersistentVolumeClaims(source.Namespace).Create(context.TODO(), source, metav1.CreateOptions{})
|
sourcePVC, err := client.CoreV1().PersistentVolumeClaims(source.Namespace).Create(context.TODO(), source, metav1.CreateOptions{})
|
||||||
framework.ExpectNoError(err)
|
framework.ExpectNoError(err)
|
||||||
|
|
||||||
// write namespace to the /mnt/test (= the volume).
|
tests := []volume.Test{
|
||||||
ginkgo.By("[Initialize dataSource]write data to volume")
|
{
|
||||||
command := fmt.Sprintf("echo '%s' > /mnt/test/initialData", sourcePVC.GetNamespace())
|
Volume: *createVolumeSource(sourcePVC.Name, false /* readOnly */),
|
||||||
RunInPodWithVolume(client, sourcePVC.Namespace, sourcePVC.Name, "pvc-datasource-writer", command, node)
|
Mode: mode,
|
||||||
|
File: "index.html",
|
||||||
|
ExpectedContent: injectContent,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
volume.InjectContent(f, config, nil, "", tests)
|
||||||
|
|
||||||
dataSourceRef := &v1.TypedLocalObjectReference{
|
dataSourceRef := &v1.TypedLocalObjectReference{
|
||||||
Kind: "PersistentVolumeClaim",
|
Kind: "PersistentVolumeClaim",
|
||||||
@ -730,10 +808,17 @@ func preparePVCDataSourceForProvisioning(
|
|||||||
|
|
||||||
cleanupFunc := func() {
|
cleanupFunc := func() {
|
||||||
framework.Logf("deleting source PVC %q/%q", sourcePVC.Namespace, sourcePVC.Name)
|
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) {
|
if err != nil && !apierrors.IsNotFound(err) {
|
||||||
framework.Failf("Error deleting source PVC %q. Error: %v", sourcePVC.Name, 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
|
return dataSourceRef, cleanupFunc
|
||||||
|
@ -65,7 +65,7 @@ spec:
|
|||||||
name: csi-data-dir
|
name: csi-data-dir
|
||||||
|
|
||||||
- name: hostpath
|
- name: hostpath
|
||||||
image: quay.io/k8scsi/hostpathplugin:v1.3.0-rc1
|
image: quay.io/k8scsi/hostpathplugin:v1.4.0-rc1
|
||||||
args:
|
args:
|
||||||
- "--drivername=hostpath.csi.k8s.io"
|
- "--drivername=hostpath.csi.k8s.io"
|
||||||
- "--v=5"
|
- "--v=5"
|
||||||
|
Loading…
Reference in New Issue
Block a user