mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-14 22:33:34 +00:00
Move minimum set of dynamic provisioning e2e test to testsuites
This commit is contained in:
parent
7cbb999518
commit
4fcb36e205
@ -31,6 +31,7 @@ import (
|
|||||||
csiv1alpha1 "k8s.io/csi-api/pkg/apis/csi/v1alpha1"
|
csiv1alpha1 "k8s.io/csi-api/pkg/apis/csi/v1alpha1"
|
||||||
csiclient "k8s.io/csi-api/pkg/client/clientset/versioned"
|
csiclient "k8s.io/csi-api/pkg/client/clientset/versioned"
|
||||||
"k8s.io/kubernetes/test/e2e/framework"
|
"k8s.io/kubernetes/test/e2e/framework"
|
||||||
|
"k8s.io/kubernetes/test/e2e/storage/testsuites"
|
||||||
"k8s.io/kubernetes/test/e2e/storage/utils"
|
"k8s.io/kubernetes/test/e2e/storage/utils"
|
||||||
imageutils "k8s.io/kubernetes/test/utils/image"
|
imageutils "k8s.io/kubernetes/test/utils/image"
|
||||||
|
|
||||||
@ -49,7 +50,7 @@ const (
|
|||||||
type csiTestDriver interface {
|
type csiTestDriver interface {
|
||||||
createCSIDriver()
|
createCSIDriver()
|
||||||
cleanupCSIDriver()
|
cleanupCSIDriver()
|
||||||
createStorageClassTest(node v1.Node) storageClassTest
|
createStorageClassTest(node v1.Node) testsuites.StorageClassTest
|
||||||
}
|
}
|
||||||
|
|
||||||
var csiTestDrivers = map[string]func(f *framework.Framework, config framework.VolumeTestConfig) csiTestDriver{
|
var csiTestDrivers = map[string]func(f *framework.Framework, config framework.VolumeTestConfig) csiTestDriver{
|
||||||
@ -110,7 +111,7 @@ var _ = utils.SIGDescribe("CSI Volumes", func() {
|
|||||||
claim := newClaim(t, ns.GetName(), "")
|
claim := newClaim(t, ns.GetName(), "")
|
||||||
class := newStorageClass(t, ns.GetName(), "")
|
class := newStorageClass(t, ns.GetName(), "")
|
||||||
claim.Spec.StorageClassName = &class.ObjectMeta.Name
|
claim.Spec.StorageClassName = &class.ObjectMeta.Name
|
||||||
testDynamicProvisioning(t, cs, claim, class)
|
testsuites.TestDynamicProvisioning(t, cs, claim, class)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -187,7 +188,7 @@ var _ = utils.SIGDescribe("CSI Volumes", func() {
|
|||||||
By("Checking if VolumeAttachment was created for the pod")
|
By("Checking if VolumeAttachment was created for the pod")
|
||||||
// Check that VolumeAttachment does not exist
|
// Check that VolumeAttachment does not exist
|
||||||
handle := getVolumeHandle(cs, claim)
|
handle := getVolumeHandle(cs, claim)
|
||||||
attachmentHash := sha256.Sum256([]byte(fmt.Sprintf("%s%s%s", handle, t.provisioner, node.Name)))
|
attachmentHash := sha256.Sum256([]byte(fmt.Sprintf("%s%s%s", handle, t.Provisioner, node.Name)))
|
||||||
attachmentName := fmt.Sprintf("csi-%x", attachmentHash)
|
attachmentName := fmt.Sprintf("csi-%x", attachmentHash)
|
||||||
_, err = cs.StorageV1beta1().VolumeAttachments().Get(attachmentName, metav1.GetOptions{})
|
_, err = cs.StorageV1beta1().VolumeAttachments().Get(attachmentName, metav1.GetOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -242,7 +243,7 @@ func getVolumeHandle(cs clientset.Interface, claim *v1.PersistentVolumeClaim) st
|
|||||||
return pv.Spec.CSI.VolumeHandle
|
return pv.Spec.CSI.VolumeHandle
|
||||||
}
|
}
|
||||||
|
|
||||||
func startPausePod(cs clientset.Interface, t storageClassTest, ns string) (*storagev1.StorageClass, *v1.PersistentVolumeClaim, *v1.Pod) {
|
func startPausePod(cs clientset.Interface, t testsuites.StorageClassTest, ns string) (*storagev1.StorageClass, *v1.PersistentVolumeClaim, *v1.Pod) {
|
||||||
class := newStorageClass(t, ns, "")
|
class := newStorageClass(t, ns, "")
|
||||||
class, err := cs.StorageV1().StorageClasses().Create(class)
|
class, err := cs.StorageV1().StorageClasses().Create(class)
|
||||||
framework.ExpectNoError(err, "Failed to create class : %v", err)
|
framework.ExpectNoError(err, "Failed to create class : %v", err)
|
||||||
@ -283,8 +284,8 @@ func startPausePod(cs clientset.Interface, t storageClassTest, ns string) (*stor
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(t.nodeName) != 0 {
|
if len(t.NodeName) != 0 {
|
||||||
pod.Spec.NodeName = t.nodeName
|
pod.Spec.NodeName = t.NodeName
|
||||||
}
|
}
|
||||||
pod, err = cs.CoreV1().Pods(ns).Create(pod)
|
pod, err = cs.CoreV1().Pods(ns).Create(pod)
|
||||||
framework.ExpectNoError(err, "Failed to create pod: %v", err)
|
framework.ExpectNoError(err, "Failed to create pod: %v", err)
|
||||||
@ -311,14 +312,14 @@ func initCSIHostpath(f *framework.Framework, config framework.VolumeTestConfig)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *hostpathCSIDriver) createStorageClassTest(node v1.Node) storageClassTest {
|
func (h *hostpathCSIDriver) createStorageClassTest(node v1.Node) testsuites.StorageClassTest {
|
||||||
return storageClassTest{
|
return testsuites.StorageClassTest{
|
||||||
name: "csi-hostpath",
|
Name: "csi-hostpath",
|
||||||
provisioner: "csi-hostpath",
|
Provisioner: "csi-hostpath",
|
||||||
parameters: map[string]string{},
|
Parameters: map[string]string{},
|
||||||
claimSize: "1Gi",
|
ClaimSize: "1Gi",
|
||||||
expectedSize: "1Gi",
|
ExpectedSize: "1Gi",
|
||||||
nodeName: node.Name,
|
NodeName: node.Name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -379,14 +380,14 @@ func initCSIgcePD(f *framework.Framework, config framework.VolumeTestConfig) csi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *gcePDCSIDriver) createStorageClassTest(node v1.Node) storageClassTest {
|
func (g *gcePDCSIDriver) createStorageClassTest(node v1.Node) testsuites.StorageClassTest {
|
||||||
return storageClassTest{
|
return testsuites.StorageClassTest{
|
||||||
name: "com.google.csi.gcepd",
|
Name: "com.google.csi.gcepd",
|
||||||
provisioner: "com.google.csi.gcepd",
|
Provisioner: "com.google.csi.gcepd",
|
||||||
parameters: map[string]string{"type": "pd-standard"},
|
Parameters: map[string]string{"type": "pd-standard"},
|
||||||
claimSize: "5Gi",
|
ClaimSize: "5Gi",
|
||||||
expectedSize: "5Gi",
|
ExpectedSize: "5Gi",
|
||||||
nodeName: node.Name,
|
NodeName: node.Name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,11 +81,13 @@ type DriverInfo struct {
|
|||||||
Name string // Name of the driver
|
Name string // Name of the driver
|
||||||
FeatureTag string // FeatureTag for the driver
|
FeatureTag string // FeatureTag for the driver
|
||||||
|
|
||||||
MaxFileSize int64 // Max file size to be tested for this driver
|
MaxFileSize int64 // Max file size to be tested for this driver
|
||||||
SupportedFsType sets.String // Map of string for supported fs type
|
SupportedFsType sets.String // Map of string for supported fs type
|
||||||
IsPersistent bool // Flag to represent whether it provides persistency
|
SupportedMountOption sets.String // Map of string for supported mount option
|
||||||
IsFsGroupSupported bool // Flag to represent whether it supports fsGroup
|
RequiredMountOption sets.String // Map of string for required mount option (Optional)
|
||||||
IsBlockSupported bool // Flag to represent whether it supports Block Volume
|
IsPersistent bool // Flag to represent whether it provides persistency
|
||||||
|
IsFsGroupSupported bool // Flag to represent whether it supports fsGroup
|
||||||
|
IsBlockSupported bool // Flag to represent whether it supports Block Volume
|
||||||
|
|
||||||
// Parameters below will be set inside test loop by using SetCommonDriverParameters.
|
// Parameters below will be set inside test loop by using SetCommonDriverParameters.
|
||||||
// Drivers that implement TestDriver is required to set all the above parameters
|
// Drivers that implement TestDriver is required to set all the above parameters
|
||||||
@ -104,8 +106,8 @@ func GetDriverNameWithFeatureTags(driver TestDriver) string {
|
|||||||
return fmt.Sprintf("[Driver: %s]%s", dInfo.Name, dInfo.FeatureTag)
|
return fmt.Sprintf("[Driver: %s]%s", dInfo.Name, dInfo.FeatureTag)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateVolume creates volume for test unless dynamicPV test
|
||||||
func CreateVolume(driver TestDriver, volType testpatterns.TestVolType) interface{} {
|
func CreateVolume(driver TestDriver, volType testpatterns.TestVolType) interface{} {
|
||||||
// Create Volume for test unless dynamicPV test
|
|
||||||
switch volType {
|
switch volType {
|
||||||
case testpatterns.InlineVolume:
|
case testpatterns.InlineVolume:
|
||||||
fallthrough
|
fallthrough
|
||||||
@ -121,8 +123,8 @@ func CreateVolume(driver TestDriver, volType testpatterns.TestVolType) interface
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteVolume deletes volume for test unless dynamicPV test
|
||||||
func DeleteVolume(driver TestDriver, volType testpatterns.TestVolType, testResource interface{}) {
|
func DeleteVolume(driver TestDriver, volType testpatterns.TestVolType, testResource interface{}) {
|
||||||
// Delete Volume for test unless dynamicPV test
|
|
||||||
switch volType {
|
switch volType {
|
||||||
case testpatterns.InlineVolume:
|
case testpatterns.InlineVolume:
|
||||||
fallthrough
|
fallthrough
|
||||||
|
@ -88,9 +88,11 @@ func InitNFSDriver() TestDriver {
|
|||||||
SupportedFsType: sets.NewString(
|
SupportedFsType: sets.NewString(
|
||||||
"", // Default fsType
|
"", // Default fsType
|
||||||
),
|
),
|
||||||
IsPersistent: true,
|
SupportedMountOption: sets.NewString("proto=tcp", "nosuid"),
|
||||||
IsFsGroupSupported: false,
|
RequiredMountOption: sets.NewString("vers=4.1"),
|
||||||
IsBlockSupported: false,
|
IsPersistent: true,
|
||||||
|
IsFsGroupSupported: false,
|
||||||
|
IsBlockSupported: false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -673,7 +675,7 @@ var _ TestDriver = &hostPathDriver{}
|
|||||||
var _ PreprovisionedVolumeTestDriver = &hostPathDriver{}
|
var _ PreprovisionedVolumeTestDriver = &hostPathDriver{}
|
||||||
var _ InlineVolumeTestDriver = &hostPathDriver{}
|
var _ InlineVolumeTestDriver = &hostPathDriver{}
|
||||||
|
|
||||||
// InitHostpathDriver returns hostPathDriver that implements TestDriver interface
|
// InitHostPathDriver returns hostPathDriver that implements TestDriver interface
|
||||||
func InitHostPathDriver() TestDriver {
|
func InitHostPathDriver() TestDriver {
|
||||||
return &hostPathDriver{
|
return &hostPathDriver{
|
||||||
driverInfo: DriverInfo{
|
driverInfo: DriverInfo{
|
||||||
@ -1118,9 +1120,10 @@ func InitGcePdDriver() TestDriver {
|
|||||||
"ext4",
|
"ext4",
|
||||||
"xfs",
|
"xfs",
|
||||||
),
|
),
|
||||||
IsPersistent: true,
|
SupportedMountOption: sets.NewString("debug", "nouid32"),
|
||||||
IsFsGroupSupported: true,
|
IsPersistent: true,
|
||||||
IsBlockSupported: true,
|
IsFsGroupSupported: true,
|
||||||
|
IsBlockSupported: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1460,9 +1463,10 @@ func InitAwsDriver() TestDriver {
|
|||||||
"", // Default fsType
|
"", // Default fsType
|
||||||
"ext3",
|
"ext3",
|
||||||
),
|
),
|
||||||
IsPersistent: true,
|
SupportedMountOption: sets.NewString("debug", "nouid32"),
|
||||||
IsFsGroupSupported: true,
|
IsPersistent: true,
|
||||||
IsBlockSupported: true,
|
IsFsGroupSupported: true,
|
||||||
|
IsBlockSupported: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ import (
|
|||||||
"k8s.io/api/core/v1"
|
"k8s.io/api/core/v1"
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/kubernetes/test/e2e/framework"
|
"k8s.io/kubernetes/test/e2e/framework"
|
||||||
|
"k8s.io/kubernetes/test/e2e/storage/testsuites"
|
||||||
"k8s.io/kubernetes/test/e2e/storage/utils"
|
"k8s.io/kubernetes/test/e2e/storage/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -85,9 +86,9 @@ var _ = utils.SIGDescribe("GenericPersistentVolume[Disruptive]", func() {
|
|||||||
|
|
||||||
func createPodPVCFromSC(f *framework.Framework, c clientset.Interface, ns string) (*v1.Pod, *v1.PersistentVolumeClaim, *v1.PersistentVolume) {
|
func createPodPVCFromSC(f *framework.Framework, c clientset.Interface, ns string) (*v1.Pod, *v1.PersistentVolumeClaim, *v1.PersistentVolume) {
|
||||||
var err error
|
var err error
|
||||||
test := storageClassTest{
|
test := testsuites.StorageClassTest{
|
||||||
name: "default",
|
Name: "default",
|
||||||
claimSize: "2Gi",
|
ClaimSize: "2Gi",
|
||||||
}
|
}
|
||||||
pvc := newClaim(test, ns, "default")
|
pvc := newClaim(test, ns, "default")
|
||||||
pvc, err = c.CoreV1().PersistentVolumeClaims(pvc.Namespace).Create(pvc)
|
pvc, err = c.CoreV1().PersistentVolumeClaims(pvc.Namespace).Create(pvc)
|
||||||
|
@ -50,6 +50,7 @@ var testSuites = []func() testsuites.TestSuite{
|
|||||||
testsuites.InitVolumeIOTestSuite,
|
testsuites.InitVolumeIOTestSuite,
|
||||||
testsuites.InitVolumeModeTestSuite,
|
testsuites.InitVolumeModeTestSuite,
|
||||||
testsuites.InitSubPathTestSuite,
|
testsuites.InitSubPathTestSuite,
|
||||||
|
testsuites.InitProvisioningTestSuite,
|
||||||
}
|
}
|
||||||
|
|
||||||
// This executes testSuites for in-tree volumes.
|
// This executes testSuites for in-tree volumes.
|
||||||
|
@ -31,6 +31,7 @@ import (
|
|||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/kubernetes/pkg/client/conditions"
|
"k8s.io/kubernetes/pkg/client/conditions"
|
||||||
"k8s.io/kubernetes/test/e2e/framework"
|
"k8s.io/kubernetes/test/e2e/framework"
|
||||||
|
"k8s.io/kubernetes/test/e2e/storage/testsuites"
|
||||||
"k8s.io/kubernetes/test/e2e/storage/utils"
|
"k8s.io/kubernetes/test/e2e/storage/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -72,9 +73,9 @@ var _ = utils.SIGDescribe("Mounted volume expand[Slow]", func() {
|
|||||||
isNodeLabeled = true
|
isNodeLabeled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
test := storageClassTest{
|
test := testsuites.StorageClassTest{
|
||||||
name: "default",
|
Name: "default",
|
||||||
claimSize: "2Gi",
|
ClaimSize: "2Gi",
|
||||||
}
|
}
|
||||||
resizableSc, err = createResizableStorageClass(test, ns, "resizing", c)
|
resizableSc, err = createResizableStorageClass(test, ns, "resizing", c)
|
||||||
Expect(err).NotTo(HaveOccurred(), "Error creating resizable storage class")
|
Expect(err).NotTo(HaveOccurred(), "Error creating resizable storage class")
|
||||||
|
@ -26,6 +26,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/util/slice"
|
"k8s.io/kubernetes/pkg/util/slice"
|
||||||
volumeutil "k8s.io/kubernetes/pkg/volume/util"
|
volumeutil "k8s.io/kubernetes/pkg/volume/util"
|
||||||
"k8s.io/kubernetes/test/e2e/framework"
|
"k8s.io/kubernetes/test/e2e/framework"
|
||||||
|
"k8s.io/kubernetes/test/e2e/storage/testsuites"
|
||||||
"k8s.io/kubernetes/test/e2e/storage/utils"
|
"k8s.io/kubernetes/test/e2e/storage/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -47,8 +48,8 @@ var _ = utils.SIGDescribe("PVC Protection", func() {
|
|||||||
By("Creating a PVC")
|
By("Creating a PVC")
|
||||||
suffix := "pvc-protection"
|
suffix := "pvc-protection"
|
||||||
defaultSC := getDefaultStorageClassName(client)
|
defaultSC := getDefaultStorageClassName(client)
|
||||||
testStorageClass := storageClassTest{
|
testStorageClass := testsuites.StorageClassTest{
|
||||||
claimSize: "1Gi",
|
ClaimSize: "1Gi",
|
||||||
}
|
}
|
||||||
pvc = newClaim(testStorageClass, nameSpace, suffix)
|
pvc = newClaim(testStorageClass, nameSpace, suffix)
|
||||||
pvc.Spec.StorageClassName = &defaultSC
|
pvc.Spec.StorageClassName = &defaultSC
|
||||||
|
@ -38,6 +38,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/volume/util"
|
"k8s.io/kubernetes/pkg/volume/util"
|
||||||
"k8s.io/kubernetes/test/e2e/framework"
|
"k8s.io/kubernetes/test/e2e/framework"
|
||||||
"k8s.io/kubernetes/test/e2e/framework/providers/gce"
|
"k8s.io/kubernetes/test/e2e/framework/providers/gce"
|
||||||
|
"k8s.io/kubernetes/test/e2e/storage/testsuites"
|
||||||
"k8s.io/kubernetes/test/e2e/storage/utils"
|
"k8s.io/kubernetes/test/e2e/storage/utils"
|
||||||
imageutils "k8s.io/kubernetes/test/utils/image"
|
imageutils "k8s.io/kubernetes/test/utils/image"
|
||||||
)
|
)
|
||||||
@ -90,19 +91,19 @@ func testVolumeProvisioning(c clientset.Interface, ns string) {
|
|||||||
|
|
||||||
// This test checks that dynamic provisioning can provision a volume
|
// This test checks that dynamic provisioning can provision a volume
|
||||||
// that can be used to persist data among pods.
|
// that can be used to persist data among pods.
|
||||||
tests := []storageClassTest{
|
tests := []testsuites.StorageClassTest{
|
||||||
{
|
{
|
||||||
name: "HDD Regional PD on GCE/GKE",
|
Name: "HDD Regional PD on GCE/GKE",
|
||||||
cloudProviders: []string{"gce", "gke"},
|
CloudProviders: []string{"gce", "gke"},
|
||||||
provisioner: "kubernetes.io/gce-pd",
|
Provisioner: "kubernetes.io/gce-pd",
|
||||||
parameters: map[string]string{
|
Parameters: map[string]string{
|
||||||
"type": "pd-standard",
|
"type": "pd-standard",
|
||||||
"zones": strings.Join(cloudZones, ","),
|
"zones": strings.Join(cloudZones, ","),
|
||||||
"replication-type": "regional-pd",
|
"replication-type": "regional-pd",
|
||||||
},
|
},
|
||||||
claimSize: "1.5Gi",
|
ClaimSize: "1.5Gi",
|
||||||
expectedSize: "2Gi",
|
ExpectedSize: "2Gi",
|
||||||
pvCheck: func(volume *v1.PersistentVolume) error {
|
PvCheck: func(volume *v1.PersistentVolume) error {
|
||||||
err := checkGCEPD(volume, "pd-standard")
|
err := checkGCEPD(volume, "pd-standard")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -111,16 +112,16 @@ func testVolumeProvisioning(c clientset.Interface, ns string) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "HDD Regional PD with auto zone selection on GCE/GKE",
|
Name: "HDD Regional PD with auto zone selection on GCE/GKE",
|
||||||
cloudProviders: []string{"gce", "gke"},
|
CloudProviders: []string{"gce", "gke"},
|
||||||
provisioner: "kubernetes.io/gce-pd",
|
Provisioner: "kubernetes.io/gce-pd",
|
||||||
parameters: map[string]string{
|
Parameters: map[string]string{
|
||||||
"type": "pd-standard",
|
"type": "pd-standard",
|
||||||
"replication-type": "regional-pd",
|
"replication-type": "regional-pd",
|
||||||
},
|
},
|
||||||
claimSize: "1.5Gi",
|
ClaimSize: "1.5Gi",
|
||||||
expectedSize: "2Gi",
|
ExpectedSize: "2Gi",
|
||||||
pvCheck: func(volume *v1.PersistentVolume) error {
|
PvCheck: func(volume *v1.PersistentVolume) error {
|
||||||
err := checkGCEPD(volume, "pd-standard")
|
err := checkGCEPD(volume, "pd-standard")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -138,7 +139,7 @@ func testVolumeProvisioning(c clientset.Interface, ns string) {
|
|||||||
class := newStorageClass(test, ns, "" /* suffix */)
|
class := newStorageClass(test, ns, "" /* suffix */)
|
||||||
claim := newClaim(test, ns, "" /* suffix */)
|
claim := newClaim(test, ns, "" /* suffix */)
|
||||||
claim.Spec.StorageClassName = &class.Name
|
claim.Spec.StorageClassName = &class.Name
|
||||||
testDynamicProvisioning(test, c, claim, class)
|
testsuites.TestDynamicProvisioning(test, c, claim, class)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -278,15 +279,15 @@ func testZonalFailover(c clientset.Interface, ns string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testRegionalDelayedBinding(c clientset.Interface, ns string) {
|
func testRegionalDelayedBinding(c clientset.Interface, ns string) {
|
||||||
test := storageClassTest{
|
test := testsuites.StorageClassTest{
|
||||||
name: "Regional PD storage class with waitForFirstConsumer test on GCE",
|
Name: "Regional PD storage class with waitForFirstConsumer test on GCE",
|
||||||
provisioner: "kubernetes.io/gce-pd",
|
Provisioner: "kubernetes.io/gce-pd",
|
||||||
parameters: map[string]string{
|
Parameters: map[string]string{
|
||||||
"type": "pd-standard",
|
"type": "pd-standard",
|
||||||
"replication-type": "regional-pd",
|
"replication-type": "regional-pd",
|
||||||
},
|
},
|
||||||
claimSize: "2Gi",
|
ClaimSize: "2Gi",
|
||||||
delayBinding: true,
|
DelayBinding: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
suffix := "delayed-regional"
|
suffix := "delayed-regional"
|
||||||
@ -305,15 +306,15 @@ func testRegionalDelayedBinding(c clientset.Interface, ns string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testRegionalAllowedTopologies(c clientset.Interface, ns string) {
|
func testRegionalAllowedTopologies(c clientset.Interface, ns string) {
|
||||||
test := storageClassTest{
|
test := testsuites.StorageClassTest{
|
||||||
name: "Regional PD storage class with allowedTopologies test on GCE",
|
Name: "Regional PD storage class with allowedTopologies test on GCE",
|
||||||
provisioner: "kubernetes.io/gce-pd",
|
Provisioner: "kubernetes.io/gce-pd",
|
||||||
parameters: map[string]string{
|
Parameters: map[string]string{
|
||||||
"type": "pd-standard",
|
"type": "pd-standard",
|
||||||
"replication-type": "regional-pd",
|
"replication-type": "regional-pd",
|
||||||
},
|
},
|
||||||
claimSize: "2Gi",
|
ClaimSize: "2Gi",
|
||||||
expectedSize: "2Gi",
|
ExpectedSize: "2Gi",
|
||||||
}
|
}
|
||||||
|
|
||||||
suffix := "topo-regional"
|
suffix := "topo-regional"
|
||||||
@ -322,20 +323,20 @@ func testRegionalAllowedTopologies(c clientset.Interface, ns string) {
|
|||||||
addAllowedTopologiesToStorageClass(c, class, zones)
|
addAllowedTopologiesToStorageClass(c, class, zones)
|
||||||
claim := newClaim(test, ns, suffix)
|
claim := newClaim(test, ns, suffix)
|
||||||
claim.Spec.StorageClassName = &class.Name
|
claim.Spec.StorageClassName = &class.Name
|
||||||
pv := testDynamicProvisioning(test, c, claim, class)
|
pv := testsuites.TestDynamicProvisioning(test, c, claim, class)
|
||||||
checkZonesFromLabelAndAffinity(pv, sets.NewString(zones...), true)
|
checkZonesFromLabelAndAffinity(pv, sets.NewString(zones...), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testRegionalAllowedTopologiesWithDelayedBinding(c clientset.Interface, ns string) {
|
func testRegionalAllowedTopologiesWithDelayedBinding(c clientset.Interface, ns string) {
|
||||||
test := storageClassTest{
|
test := testsuites.StorageClassTest{
|
||||||
name: "Regional PD storage class with allowedTopologies and waitForFirstConsumer test on GCE",
|
Name: "Regional PD storage class with allowedTopologies and waitForFirstConsumer test on GCE",
|
||||||
provisioner: "kubernetes.io/gce-pd",
|
Provisioner: "kubernetes.io/gce-pd",
|
||||||
parameters: map[string]string{
|
Parameters: map[string]string{
|
||||||
"type": "pd-standard",
|
"type": "pd-standard",
|
||||||
"replication-type": "regional-pd",
|
"replication-type": "regional-pd",
|
||||||
},
|
},
|
||||||
claimSize: "2Gi",
|
ClaimSize: "2Gi",
|
||||||
delayBinding: true,
|
DelayBinding: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
suffix := "topo-delayed-regional"
|
suffix := "topo-delayed-regional"
|
||||||
|
@ -4,6 +4,7 @@ go_library(
|
|||||||
name = "go_default_library",
|
name = "go_default_library",
|
||||||
srcs = [
|
srcs = [
|
||||||
"base.go",
|
"base.go",
|
||||||
|
"provisioning.go",
|
||||||
"subpath.go",
|
"subpath.go",
|
||||||
"volume_io.go",
|
"volume_io.go",
|
||||||
"volumemode.go",
|
"volumemode.go",
|
||||||
|
@ -34,7 +34,7 @@ import (
|
|||||||
"k8s.io/kubernetes/test/e2e/storage/testpatterns"
|
"k8s.io/kubernetes/test/e2e/storage/testpatterns"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestSuite represents an interface for a set of tests whchi works with TestDriver
|
// TestSuite represents an interface for a set of tests which works with TestDriver
|
||||||
type TestSuite interface {
|
type TestSuite interface {
|
||||||
// getTestSuiteInfo returns the TestSuiteInfo for this TestSuite
|
// getTestSuiteInfo returns the TestSuiteInfo for this TestSuite
|
||||||
getTestSuiteInfo() TestSuiteInfo
|
getTestSuiteInfo() TestSuiteInfo
|
||||||
@ -44,6 +44,7 @@ type TestSuite interface {
|
|||||||
execTest(drivers.TestDriver, testpatterns.TestPattern)
|
execTest(drivers.TestDriver, testpatterns.TestPattern)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestSuiteInfo represents a set of parameters for TestSuite
|
||||||
type TestSuiteInfo struct {
|
type TestSuiteInfo struct {
|
||||||
name string // name of the TestSuite
|
name string // name of the TestSuite
|
||||||
featureTag string // featureTag for the TestSuite
|
featureTag string // featureTag for the TestSuite
|
||||||
@ -132,7 +133,7 @@ type genericVolumeTestResource struct {
|
|||||||
|
|
||||||
var _ TestResource = &genericVolumeTestResource{}
|
var _ TestResource = &genericVolumeTestResource{}
|
||||||
|
|
||||||
// SetupResource sets up genericVolumeTestResource
|
// setupResource sets up genericVolumeTestResource
|
||||||
func (r *genericVolumeTestResource) setupResource(driver drivers.TestDriver, pattern testpatterns.TestPattern) {
|
func (r *genericVolumeTestResource) setupResource(driver drivers.TestDriver, pattern testpatterns.TestPattern) {
|
||||||
r.driver = driver
|
r.driver = driver
|
||||||
dInfo := driver.GetDriverInfo()
|
dInfo := driver.GetDriverInfo()
|
||||||
@ -186,7 +187,7 @@ func (r *genericVolumeTestResource) setupResource(driver drivers.TestDriver, pat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CleanupResource clean up genericVolumeTestResource
|
// cleanupResource cleans up genericVolumeTestResource
|
||||||
func (r *genericVolumeTestResource) cleanupResource(driver drivers.TestDriver, pattern testpatterns.TestPattern) {
|
func (r *genericVolumeTestResource) cleanupResource(driver drivers.TestDriver, pattern testpatterns.TestPattern) {
|
||||||
dInfo := driver.GetDriverInfo()
|
dInfo := driver.GetDriverInfo()
|
||||||
f := dInfo.Framework
|
f := dInfo.Framework
|
||||||
|
367
test/e2e/storage/testsuites/provisioning.go
Normal file
367
test/e2e/storage/testsuites/provisioning.go
Normal file
@ -0,0 +1,367 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2018 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package testsuites
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
|
||||||
|
apierrs "k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
|
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
|
storage "k8s.io/api/storage/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
|
"k8s.io/kubernetes/test/e2e/framework"
|
||||||
|
"k8s.io/kubernetes/test/e2e/storage/drivers"
|
||||||
|
"k8s.io/kubernetes/test/e2e/storage/testpatterns"
|
||||||
|
imageutils "k8s.io/kubernetes/test/utils/image"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StorageClassTest represents parameters to be used by provisioning tests
|
||||||
|
type StorageClassTest struct {
|
||||||
|
Name string
|
||||||
|
CloudProviders []string
|
||||||
|
Provisioner string
|
||||||
|
Parameters map[string]string
|
||||||
|
DelayBinding bool
|
||||||
|
ClaimSize string
|
||||||
|
ExpectedSize string
|
||||||
|
PvCheck func(volume *v1.PersistentVolume) error
|
||||||
|
NodeName string
|
||||||
|
SkipWriteReadCheck bool
|
||||||
|
VolumeMode *v1.PersistentVolumeMode
|
||||||
|
}
|
||||||
|
|
||||||
|
type provisioningTestSuite struct {
|
||||||
|
tsInfo TestSuiteInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ TestSuite = &provisioningTestSuite{}
|
||||||
|
|
||||||
|
// InitProvisioningTestSuite returns provisioningTestSuite that implements TestSuite interface
|
||||||
|
func InitProvisioningTestSuite() TestSuite {
|
||||||
|
return &provisioningTestSuite{
|
||||||
|
tsInfo: TestSuiteInfo{
|
||||||
|
name: "provisioning",
|
||||||
|
testPatterns: []testpatterns.TestPattern{
|
||||||
|
testpatterns.DefaultFsDynamicPV,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *provisioningTestSuite) getTestSuiteInfo() TestSuiteInfo {
|
||||||
|
return p.tsInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *provisioningTestSuite) skipUnsupportedTest(pattern testpatterns.TestPattern, driver drivers.TestDriver) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func createProvisioningTestInput(driver drivers.TestDriver, pattern testpatterns.TestPattern) (provisioningTestResource, provisioningTestInput) {
|
||||||
|
// Setup test resource for driver and testpattern
|
||||||
|
resource := provisioningTestResource{}
|
||||||
|
resource.setupResource(driver, pattern)
|
||||||
|
|
||||||
|
input := provisioningTestInput{
|
||||||
|
testCase: StorageClassTest{
|
||||||
|
ClaimSize: resource.claimSize,
|
||||||
|
ExpectedSize: resource.claimSize,
|
||||||
|
},
|
||||||
|
cs: driver.GetDriverInfo().Framework.ClientSet,
|
||||||
|
pvc: resource.pvc,
|
||||||
|
sc: resource.sc,
|
||||||
|
dInfo: driver.GetDriverInfo(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if driver.GetDriverInfo().Config.ClientNodeName != "" {
|
||||||
|
input.testCase.NodeName = driver.GetDriverInfo().Config.ClientNodeName
|
||||||
|
}
|
||||||
|
|
||||||
|
return resource, input
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *provisioningTestSuite) execTest(driver drivers.TestDriver, pattern testpatterns.TestPattern) {
|
||||||
|
Context(getTestNameStr(p, pattern), func() {
|
||||||
|
var (
|
||||||
|
resource provisioningTestResource
|
||||||
|
input provisioningTestInput
|
||||||
|
needsCleanup bool
|
||||||
|
)
|
||||||
|
|
||||||
|
BeforeEach(func() {
|
||||||
|
needsCleanup = false
|
||||||
|
// Skip unsupported tests to avoid unnecessary resource initialization
|
||||||
|
skipUnsupportedTest(p, driver, pattern)
|
||||||
|
needsCleanup = true
|
||||||
|
|
||||||
|
// Create test input
|
||||||
|
resource, input = createProvisioningTestInput(driver, pattern)
|
||||||
|
})
|
||||||
|
|
||||||
|
AfterEach(func() {
|
||||||
|
if needsCleanup {
|
||||||
|
resource.cleanupResource(driver, pattern)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Ginkgo's "Global Shared Behaviors" require arguments for a shared function
|
||||||
|
// to be a single struct and to be passed as a pointer.
|
||||||
|
// Please see https://onsi.github.io/ginkgo/#global-shared-behaviors for details.
|
||||||
|
testProvisioning(&input)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type provisioningTestResource struct {
|
||||||
|
driver drivers.TestDriver
|
||||||
|
|
||||||
|
claimSize string
|
||||||
|
sc *storage.StorageClass
|
||||||
|
pvc *v1.PersistentVolumeClaim
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ TestResource = &provisioningTestResource{}
|
||||||
|
|
||||||
|
func (p *provisioningTestResource) setupResource(driver drivers.TestDriver, pattern testpatterns.TestPattern) {
|
||||||
|
// Setup provisioningTest resource
|
||||||
|
switch pattern.VolType {
|
||||||
|
case testpatterns.DynamicPV:
|
||||||
|
if dDriver, ok := driver.(drivers.DynamicPVTestDriver); ok {
|
||||||
|
p.sc = dDriver.GetDynamicProvisionStorageClass("")
|
||||||
|
if p.sc == nil {
|
||||||
|
framework.Skipf("Driver %q does not define Dynamic Provision StorageClass - skipping", driver.GetDriverInfo().Name)
|
||||||
|
}
|
||||||
|
p.driver = driver
|
||||||
|
p.claimSize = "2Gi"
|
||||||
|
p.pvc = getClaim(p.claimSize, driver.GetDriverInfo().Framework.Namespace.Name)
|
||||||
|
p.pvc.Spec.StorageClassName = &p.sc.Name
|
||||||
|
framework.Logf("In creating storage class object and pvc object for driver - sc: %v, pvc: %v", p.sc, p.pvc)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
framework.Failf("Dynamic Provision test doesn't support: %s", pattern.VolType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *provisioningTestResource) cleanupResource(driver drivers.TestDriver, pattern testpatterns.TestPattern) {
|
||||||
|
}
|
||||||
|
|
||||||
|
type provisioningTestInput struct {
|
||||||
|
testCase StorageClassTest
|
||||||
|
cs clientset.Interface
|
||||||
|
pvc *v1.PersistentVolumeClaim
|
||||||
|
sc *storage.StorageClass
|
||||||
|
dInfo *drivers.DriverInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func testProvisioning(input *provisioningTestInput) {
|
||||||
|
It("should provision storage", func() {
|
||||||
|
TestDynamicProvisioning(input.testCase, input.cs, input.pvc, input.sc)
|
||||||
|
})
|
||||||
|
|
||||||
|
It("should provision storage with mount options", func() {
|
||||||
|
if input.dInfo.SupportedMountOption == nil {
|
||||||
|
framework.Skipf("Driver %q does not define supported mount option - skipping", input.dInfo.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
input.sc.MountOptions = input.dInfo.SupportedMountOption.Union(input.dInfo.RequiredMountOption).List()
|
||||||
|
TestDynamicProvisioning(input.testCase, input.cs, input.pvc, input.sc)
|
||||||
|
})
|
||||||
|
|
||||||
|
It("should provision storage with non-default reclaim policy Retain", func() {
|
||||||
|
retain := v1.PersistentVolumeReclaimRetain
|
||||||
|
input.sc.ReclaimPolicy = &retain
|
||||||
|
pv := TestDynamicProvisioning(input.testCase, input.cs, input.pvc, input.sc)
|
||||||
|
|
||||||
|
By(fmt.Sprintf("waiting for the provisioned PV %q to enter phase %s", pv.Name, v1.VolumeReleased))
|
||||||
|
framework.ExpectNoError(framework.WaitForPersistentVolumePhase(v1.VolumeReleased, input.cs, pv.Name, 1*time.Second, 30*time.Second))
|
||||||
|
|
||||||
|
By(fmt.Sprintf("deleting the PV %q", pv.Name))
|
||||||
|
framework.ExpectNoError(framework.DeletePersistentVolume(input.cs, pv.Name), "Failed to delete PV ", pv.Name)
|
||||||
|
framework.ExpectNoError(framework.WaitForPersistentVolumeDeleted(input.cs, pv.Name, 1*time.Second, 30*time.Second))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("should create and delete block persistent volumes [Feature:BlockVolume]", func() {
|
||||||
|
if !input.dInfo.IsBlockSupported {
|
||||||
|
framework.Skipf("Driver %q does not support BlockVolume - skipping", input.dInfo.Name)
|
||||||
|
}
|
||||||
|
block := v1.PersistentVolumeBlock
|
||||||
|
input.testCase.VolumeMode = &block
|
||||||
|
input.testCase.SkipWriteReadCheck = true
|
||||||
|
input.pvc.Spec.VolumeMode = &block
|
||||||
|
TestDynamicProvisioning(input.testCase, input.cs, input.pvc, input.sc)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestDynamicProvisioning tests dynamic provisioning with specified StorageClassTest and storageClass
|
||||||
|
func TestDynamicProvisioning(t StorageClassTest, client clientset.Interface, claim *v1.PersistentVolumeClaim, class *storage.StorageClass) *v1.PersistentVolume {
|
||||||
|
var err error
|
||||||
|
if class != nil {
|
||||||
|
By("creating a StorageClass " + class.Name)
|
||||||
|
class, err = client.StorageV1().StorageClasses().Create(class)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
defer func() {
|
||||||
|
framework.Logf("deleting storage class %s", class.Name)
|
||||||
|
framework.ExpectNoError(client.StorageV1().StorageClasses().Delete(class.Name, nil))
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
By("creating a claim")
|
||||||
|
claim, err = client.CoreV1().PersistentVolumeClaims(claim.Namespace).Create(claim)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
defer func() {
|
||||||
|
framework.Logf("deleting claim %q/%q", claim.Namespace, claim.Name)
|
||||||
|
// typically this claim has already been deleted
|
||||||
|
err = client.CoreV1().PersistentVolumeClaims(claim.Namespace).Delete(claim.Name, nil)
|
||||||
|
if err != nil && !apierrs.IsNotFound(err) {
|
||||||
|
framework.Failf("Error deleting claim %q. Error: %v", claim.Name, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
err = framework.WaitForPersistentVolumeClaimPhase(v1.ClaimBound, client, claim.Namespace, claim.Name, framework.Poll, framework.ClaimProvisionTimeout)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
By("checking the claim")
|
||||||
|
// Get new copy of the claim
|
||||||
|
claim, err = client.CoreV1().PersistentVolumeClaims(claim.Namespace).Get(claim.Name, metav1.GetOptions{})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
// Get the bound PV
|
||||||
|
pv, err := client.CoreV1().PersistentVolumes().Get(claim.Spec.VolumeName, metav1.GetOptions{})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
// Check sizes
|
||||||
|
expectedCapacity := resource.MustParse(t.ExpectedSize)
|
||||||
|
pvCapacity := pv.Spec.Capacity[v1.ResourceName(v1.ResourceStorage)]
|
||||||
|
Expect(pvCapacity.Value()).To(Equal(expectedCapacity.Value()), "pvCapacity is not equal to expectedCapacity")
|
||||||
|
|
||||||
|
requestedCapacity := resource.MustParse(t.ClaimSize)
|
||||||
|
claimCapacity := claim.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)]
|
||||||
|
Expect(claimCapacity.Value()).To(Equal(requestedCapacity.Value()), "claimCapacity is not equal to requestedCapacity")
|
||||||
|
|
||||||
|
// Check PV properties
|
||||||
|
By("checking the PV")
|
||||||
|
expectedAccessModes := []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}
|
||||||
|
Expect(pv.Spec.AccessModes).To(Equal(expectedAccessModes))
|
||||||
|
Expect(pv.Spec.ClaimRef.Name).To(Equal(claim.ObjectMeta.Name))
|
||||||
|
Expect(pv.Spec.ClaimRef.Namespace).To(Equal(claim.ObjectMeta.Namespace))
|
||||||
|
if class == nil {
|
||||||
|
Expect(pv.Spec.PersistentVolumeReclaimPolicy).To(Equal(v1.PersistentVolumeReclaimDelete))
|
||||||
|
} else {
|
||||||
|
Expect(pv.Spec.PersistentVolumeReclaimPolicy).To(Equal(*class.ReclaimPolicy))
|
||||||
|
Expect(pv.Spec.MountOptions).To(Equal(class.MountOptions))
|
||||||
|
}
|
||||||
|
if t.VolumeMode != nil {
|
||||||
|
Expect(pv.Spec.VolumeMode).NotTo(BeNil())
|
||||||
|
Expect(*pv.Spec.VolumeMode).To(Equal(*t.VolumeMode))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the checker
|
||||||
|
if t.PvCheck != nil {
|
||||||
|
err = t.PvCheck(pv)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
}
|
||||||
|
|
||||||
|
if !t.SkipWriteReadCheck {
|
||||||
|
// We start two pods:
|
||||||
|
// - The first writes 'hello word' to the /mnt/test (= the volume).
|
||||||
|
// - The second one runs grep 'hello world' on /mnt/test.
|
||||||
|
// If both succeed, Kubernetes actually allocated something that is
|
||||||
|
// persistent across pods.
|
||||||
|
By("checking the created volume is writable and has the PV's mount options")
|
||||||
|
command := "echo 'hello world' > /mnt/test/data"
|
||||||
|
// We give the first pod the secondary responsibility of checking the volume has
|
||||||
|
// been mounted with the PV's mount options, if the PV was provisioned with any
|
||||||
|
for _, option := range pv.Spec.MountOptions {
|
||||||
|
// Get entry, get mount options at 6th word, replace brackets with commas
|
||||||
|
command += fmt.Sprintf(" && ( mount | grep 'on /mnt/test' | awk '{print $6}' | sed 's/^(/,/; s/)$/,/' | grep -q ,%s, )", option)
|
||||||
|
}
|
||||||
|
runInPodWithVolume(client, claim.Namespace, claim.Name, t.NodeName, command)
|
||||||
|
|
||||||
|
By("checking the created volume is readable and retains data")
|
||||||
|
runInPodWithVolume(client, claim.Namespace, claim.Name, t.NodeName, "grep 'hello world' /mnt/test/data")
|
||||||
|
}
|
||||||
|
By(fmt.Sprintf("deleting claim %q/%q", claim.Namespace, claim.Name))
|
||||||
|
framework.ExpectNoError(client.CoreV1().PersistentVolumeClaims(claim.Namespace).Delete(claim.Name, nil))
|
||||||
|
|
||||||
|
// Wait for the PV to get deleted if reclaim policy is Delete. (If it's
|
||||||
|
// Retain, there's no use waiting because the PV won't be auto-deleted and
|
||||||
|
// it's expected for the caller to do it.) Technically, the first few delete
|
||||||
|
// attempts may fail, as the volume is still attached to a node because
|
||||||
|
// kubelet is slowly cleaning up the previous pod, however it should succeed
|
||||||
|
// in a couple of minutes. Wait 20 minutes to recover from random cloud
|
||||||
|
// hiccups.
|
||||||
|
if pv.Spec.PersistentVolumeReclaimPolicy == v1.PersistentVolumeReclaimDelete {
|
||||||
|
By(fmt.Sprintf("deleting the claim's PV %q", pv.Name))
|
||||||
|
framework.ExpectNoError(framework.WaitForPersistentVolumeDeleted(client, pv.Name, 5*time.Second, 20*time.Minute))
|
||||||
|
}
|
||||||
|
|
||||||
|
return pv
|
||||||
|
}
|
||||||
|
|
||||||
|
// runInPodWithVolume runs a command in a pod with given claim mounted to /mnt directory.
|
||||||
|
func runInPodWithVolume(c clientset.Interface, ns, claimName, nodeName, command string) {
|
||||||
|
pod := &v1.Pod{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
Kind: "Pod",
|
||||||
|
APIVersion: "v1",
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
GenerateName: "pvc-volume-tester-",
|
||||||
|
},
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "volume-tester",
|
||||||
|
Image: imageutils.GetE2EImage(imageutils.BusyBox),
|
||||||
|
Command: []string{"/bin/sh"},
|
||||||
|
Args: []string{"-c", command},
|
||||||
|
VolumeMounts: []v1.VolumeMount{
|
||||||
|
{
|
||||||
|
Name: "my-volume",
|
||||||
|
MountPath: "/mnt/test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RestartPolicy: v1.RestartPolicyNever,
|
||||||
|
Volumes: []v1.Volume{
|
||||||
|
{
|
||||||
|
Name: "my-volume",
|
||||||
|
VolumeSource: v1.VolumeSource{
|
||||||
|
PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
|
||||||
|
ClaimName: claimName,
|
||||||
|
ReadOnly: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(nodeName) != 0 {
|
||||||
|
pod.Spec.NodeName = nodeName
|
||||||
|
}
|
||||||
|
pod, err := c.CoreV1().Pods(ns).Create(pod)
|
||||||
|
framework.ExpectNoError(err, "Failed to create pod: %v", err)
|
||||||
|
defer func() {
|
||||||
|
framework.DeletePodOrFail(c, ns, pod.Name)
|
||||||
|
}()
|
||||||
|
framework.ExpectNoError(framework.WaitForPodSuccessInNamespaceSlow(c, pod.Name, pod.Namespace))
|
||||||
|
}
|
@ -338,7 +338,7 @@ func StartExternalProvisioner(c clientset.Interface, ns string, externalPluginNa
|
|||||||
Containers: []v1.Container{
|
Containers: []v1.Container{
|
||||||
{
|
{
|
||||||
Name: "nfs-provisioner",
|
Name: "nfs-provisioner",
|
||||||
Image: "quay.io/kubernetes_incubator/nfs-provisioner:v2.1.0-k8s1.11",
|
Image: "quay.io/kubernetes_incubator/nfs-provisioner:v2.2.0-k8s1.12",
|
||||||
SecurityContext: &v1.SecurityContext{
|
SecurityContext: &v1.SecurityContext{
|
||||||
Capabilities: &v1.Capabilities{
|
Capabilities: &v1.Capabilities{
|
||||||
Add: []v1.Capability{"DAC_READ_SEARCH"},
|
Add: []v1.Capability{"DAC_READ_SEARCH"},
|
||||||
|
@ -30,6 +30,7 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/kubernetes/test/e2e/framework"
|
"k8s.io/kubernetes/test/e2e/framework"
|
||||||
|
"k8s.io/kubernetes/test/e2e/storage/testsuites"
|
||||||
"k8s.io/kubernetes/test/e2e/storage/utils"
|
"k8s.io/kubernetes/test/e2e/storage/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -54,9 +55,9 @@ var _ = utils.SIGDescribe("Volume expand [Slow]", func() {
|
|||||||
c = f.ClientSet
|
c = f.ClientSet
|
||||||
ns = f.Namespace.Name
|
ns = f.Namespace.Name
|
||||||
framework.ExpectNoError(framework.WaitForAllNodesSchedulable(c, framework.TestContext.NodeSchedulableTimeout))
|
framework.ExpectNoError(framework.WaitForAllNodesSchedulable(c, framework.TestContext.NodeSchedulableTimeout))
|
||||||
test := storageClassTest{
|
test := testsuites.StorageClassTest{
|
||||||
name: "default",
|
Name: "default",
|
||||||
claimSize: "2Gi",
|
ClaimSize: "2Gi",
|
||||||
}
|
}
|
||||||
resizableSc, err = createResizableStorageClass(test, ns, "resizing", c)
|
resizableSc, err = createResizableStorageClass(test, ns, "resizing", c)
|
||||||
Expect(err).NotTo(HaveOccurred(), "Error creating resizable storage class")
|
Expect(err).NotTo(HaveOccurred(), "Error creating resizable storage class")
|
||||||
@ -133,7 +134,7 @@ var _ = utils.SIGDescribe("Volume expand [Slow]", func() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
func createResizableStorageClass(t storageClassTest, ns string, suffix string, c clientset.Interface) (*storage.StorageClass, error) {
|
func createResizableStorageClass(t testsuites.StorageClassTest, ns string, suffix string, c clientset.Interface) (*storage.StorageClass, error) {
|
||||||
stKlass := newStorageClass(t, ns, suffix)
|
stKlass := newStorageClass(t, ns, suffix)
|
||||||
allowExpansion := true
|
allowExpansion := true
|
||||||
stKlass.AllowVolumeExpansion = &allowExpansion
|
stKlass.AllowVolumeExpansion = &allowExpansion
|
||||||
|
@ -31,6 +31,7 @@ import (
|
|||||||
kubeletmetrics "k8s.io/kubernetes/pkg/kubelet/metrics"
|
kubeletmetrics "k8s.io/kubernetes/pkg/kubelet/metrics"
|
||||||
"k8s.io/kubernetes/test/e2e/framework"
|
"k8s.io/kubernetes/test/e2e/framework"
|
||||||
"k8s.io/kubernetes/test/e2e/framework/metrics"
|
"k8s.io/kubernetes/test/e2e/framework/metrics"
|
||||||
|
"k8s.io/kubernetes/test/e2e/storage/testsuites"
|
||||||
"k8s.io/kubernetes/test/e2e/storage/utils"
|
"k8s.io/kubernetes/test/e2e/storage/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -52,9 +53,9 @@ var _ = utils.SIGDescribe("[Serial] Volume metrics", func() {
|
|||||||
defaultScName := getDefaultStorageClassName(c)
|
defaultScName := getDefaultStorageClassName(c)
|
||||||
verifyDefaultStorageClass(c, defaultScName, true)
|
verifyDefaultStorageClass(c, defaultScName, true)
|
||||||
|
|
||||||
test := storageClassTest{
|
test := testsuites.StorageClassTest{
|
||||||
name: "default",
|
Name: "default",
|
||||||
claimSize: "2Gi",
|
ClaimSize: "2Gi",
|
||||||
}
|
}
|
||||||
|
|
||||||
pvc = newClaim(test, ns, "default")
|
pvc = newClaim(test, ns, "default")
|
||||||
|
@ -47,133 +47,15 @@ import (
|
|||||||
volumeutil "k8s.io/kubernetes/pkg/volume/util"
|
volumeutil "k8s.io/kubernetes/pkg/volume/util"
|
||||||
"k8s.io/kubernetes/test/e2e/framework"
|
"k8s.io/kubernetes/test/e2e/framework"
|
||||||
"k8s.io/kubernetes/test/e2e/framework/providers/gce"
|
"k8s.io/kubernetes/test/e2e/framework/providers/gce"
|
||||||
|
"k8s.io/kubernetes/test/e2e/storage/testsuites"
|
||||||
"k8s.io/kubernetes/test/e2e/storage/utils"
|
"k8s.io/kubernetes/test/e2e/storage/utils"
|
||||||
imageutils "k8s.io/kubernetes/test/utils/image"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type storageClassTest struct {
|
|
||||||
name string
|
|
||||||
cloudProviders []string
|
|
||||||
provisioner string
|
|
||||||
parameters map[string]string
|
|
||||||
delayBinding bool
|
|
||||||
claimSize string
|
|
||||||
expectedSize string
|
|
||||||
pvCheck func(volume *v1.PersistentVolume) error
|
|
||||||
nodeName string
|
|
||||||
skipWriteReadCheck bool
|
|
||||||
volumeMode *v1.PersistentVolumeMode
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Plugin name of the external provisioner
|
// Plugin name of the external provisioner
|
||||||
externalPluginName = "example.com/nfs"
|
externalPluginName = "example.com/nfs"
|
||||||
)
|
)
|
||||||
|
|
||||||
func testDynamicProvisioning(t storageClassTest, client clientset.Interface, claim *v1.PersistentVolumeClaim, class *storage.StorageClass) *v1.PersistentVolume {
|
|
||||||
var err error
|
|
||||||
if class != nil {
|
|
||||||
By("creating a StorageClass " + class.Name)
|
|
||||||
class, err = client.StorageV1().StorageClasses().Create(class)
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
defer func() {
|
|
||||||
framework.Logf("deleting storage class %s", class.Name)
|
|
||||||
framework.ExpectNoError(client.StorageV1().StorageClasses().Delete(class.Name, nil))
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
By("creating a claim")
|
|
||||||
claim, err = client.CoreV1().PersistentVolumeClaims(claim.Namespace).Create(claim)
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
defer func() {
|
|
||||||
framework.Logf("deleting claim %q/%q", claim.Namespace, claim.Name)
|
|
||||||
// typically this claim has already been deleted
|
|
||||||
err = client.CoreV1().PersistentVolumeClaims(claim.Namespace).Delete(claim.Name, nil)
|
|
||||||
if err != nil && !apierrs.IsNotFound(err) {
|
|
||||||
framework.Failf("Error deleting claim %q. Error: %v", claim.Name, err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
err = framework.WaitForPersistentVolumeClaimPhase(v1.ClaimBound, client, claim.Namespace, claim.Name, framework.Poll, framework.ClaimProvisionTimeout)
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
|
|
||||||
By("checking the claim")
|
|
||||||
// Get new copy of the claim
|
|
||||||
claim, err = client.CoreV1().PersistentVolumeClaims(claim.Namespace).Get(claim.Name, metav1.GetOptions{})
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
|
|
||||||
// Get the bound PV
|
|
||||||
pv, err := client.CoreV1().PersistentVolumes().Get(claim.Spec.VolumeName, metav1.GetOptions{})
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
|
|
||||||
// Check sizes
|
|
||||||
expectedCapacity := resource.MustParse(t.expectedSize)
|
|
||||||
pvCapacity := pv.Spec.Capacity[v1.ResourceName(v1.ResourceStorage)]
|
|
||||||
Expect(pvCapacity.Value()).To(Equal(expectedCapacity.Value()), "pvCapacity is not equal to expectedCapacity")
|
|
||||||
|
|
||||||
requestedCapacity := resource.MustParse(t.claimSize)
|
|
||||||
claimCapacity := claim.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)]
|
|
||||||
Expect(claimCapacity.Value()).To(Equal(requestedCapacity.Value()), "claimCapacity is not equal to requestedCapacity")
|
|
||||||
|
|
||||||
// Check PV properties
|
|
||||||
By("checking the PV")
|
|
||||||
expectedAccessModes := []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}
|
|
||||||
Expect(pv.Spec.AccessModes).To(Equal(expectedAccessModes))
|
|
||||||
Expect(pv.Spec.ClaimRef.Name).To(Equal(claim.ObjectMeta.Name))
|
|
||||||
Expect(pv.Spec.ClaimRef.Namespace).To(Equal(claim.ObjectMeta.Namespace))
|
|
||||||
if class == nil {
|
|
||||||
Expect(pv.Spec.PersistentVolumeReclaimPolicy).To(Equal(v1.PersistentVolumeReclaimDelete))
|
|
||||||
} else {
|
|
||||||
Expect(pv.Spec.PersistentVolumeReclaimPolicy).To(Equal(*class.ReclaimPolicy))
|
|
||||||
Expect(pv.Spec.MountOptions).To(Equal(class.MountOptions))
|
|
||||||
}
|
|
||||||
if t.volumeMode != nil {
|
|
||||||
Expect(pv.Spec.VolumeMode).NotTo(BeNil())
|
|
||||||
Expect(*pv.Spec.VolumeMode).To(Equal(*t.volumeMode))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run the checker
|
|
||||||
if t.pvCheck != nil {
|
|
||||||
err = t.pvCheck(pv)
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
}
|
|
||||||
|
|
||||||
if !t.skipWriteReadCheck {
|
|
||||||
// We start two pods:
|
|
||||||
// - The first writes 'hello word' to the /mnt/test (= the volume).
|
|
||||||
// - The second one runs grep 'hello world' on /mnt/test.
|
|
||||||
// If both succeed, Kubernetes actually allocated something that is
|
|
||||||
// persistent across pods.
|
|
||||||
By("checking the created volume is writable and has the PV's mount options")
|
|
||||||
command := "echo 'hello world' > /mnt/test/data"
|
|
||||||
// We give the first pod the secondary responsibility of checking the volume has
|
|
||||||
// been mounted with the PV's mount options, if the PV was provisioned with any
|
|
||||||
for _, option := range pv.Spec.MountOptions {
|
|
||||||
// Get entry, get mount options at 6th word, replace brackets with commas
|
|
||||||
command += fmt.Sprintf(" && ( mount | grep 'on /mnt/test' | awk '{print $6}' | sed 's/^(/,/; s/)$/,/' | grep -q ,%s, )", option)
|
|
||||||
}
|
|
||||||
runInPodWithVolume(client, claim.Namespace, claim.Name, t.nodeName, command)
|
|
||||||
|
|
||||||
By("checking the created volume is readable and retains data")
|
|
||||||
runInPodWithVolume(client, claim.Namespace, claim.Name, t.nodeName, "grep 'hello world' /mnt/test/data")
|
|
||||||
}
|
|
||||||
By(fmt.Sprintf("deleting claim %q/%q", claim.Namespace, claim.Name))
|
|
||||||
framework.ExpectNoError(client.CoreV1().PersistentVolumeClaims(claim.Namespace).Delete(claim.Name, nil))
|
|
||||||
|
|
||||||
// Wait for the PV to get deleted if reclaim policy is Delete. (If it's
|
|
||||||
// Retain, there's no use waiting because the PV won't be auto-deleted and
|
|
||||||
// it's expected for the caller to do it.) Technically, the first few delete
|
|
||||||
// attempts may fail, as the volume is still attached to a node because
|
|
||||||
// kubelet is slowly cleaning up the previous pod, however it should succeed
|
|
||||||
// in a couple of minutes. Wait 20 minutes to recover from random cloud
|
|
||||||
// hiccups.
|
|
||||||
if pv.Spec.PersistentVolumeReclaimPolicy == v1.PersistentVolumeReclaimDelete {
|
|
||||||
By(fmt.Sprintf("deleting the claim's PV %q", pv.Name))
|
|
||||||
framework.ExpectNoError(framework.WaitForPersistentVolumeDeleted(client, pv.Name, 5*time.Second, 20*time.Minute))
|
|
||||||
}
|
|
||||||
|
|
||||||
return pv
|
|
||||||
}
|
|
||||||
|
|
||||||
func testBindingWaitForFirstConsumer(client clientset.Interface, claim *v1.PersistentVolumeClaim, class *storage.StorageClass) (*v1.PersistentVolume, *v1.Node) {
|
func testBindingWaitForFirstConsumer(client clientset.Interface, claim *v1.PersistentVolumeClaim, class *storage.StorageClass) (*v1.PersistentVolume, *v1.Node) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
@ -367,172 +249,172 @@ var _ = utils.SIGDescribe("Dynamic Provisioning", func() {
|
|||||||
|
|
||||||
// This test checks that dynamic provisioning can provision a volume
|
// This test checks that dynamic provisioning can provision a volume
|
||||||
// that can be used to persist data among pods.
|
// that can be used to persist data among pods.
|
||||||
tests := []storageClassTest{
|
tests := []testsuites.StorageClassTest{
|
||||||
// GCE/GKE
|
// GCE/GKE
|
||||||
{
|
{
|
||||||
name: "SSD PD on GCE/GKE",
|
Name: "SSD PD on GCE/GKE",
|
||||||
cloudProviders: []string{"gce", "gke"},
|
CloudProviders: []string{"gce", "gke"},
|
||||||
provisioner: "kubernetes.io/gce-pd",
|
Provisioner: "kubernetes.io/gce-pd",
|
||||||
parameters: map[string]string{
|
Parameters: map[string]string{
|
||||||
"type": "pd-ssd",
|
"type": "pd-ssd",
|
||||||
"zone": cloudZone,
|
"zone": cloudZone,
|
||||||
},
|
},
|
||||||
claimSize: "1.5Gi",
|
ClaimSize: "1.5Gi",
|
||||||
expectedSize: "2Gi",
|
ExpectedSize: "2Gi",
|
||||||
pvCheck: func(volume *v1.PersistentVolume) error {
|
PvCheck: func(volume *v1.PersistentVolume) error {
|
||||||
return checkGCEPD(volume, "pd-ssd")
|
return checkGCEPD(volume, "pd-ssd")
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "HDD PD on GCE/GKE",
|
Name: "HDD PD on GCE/GKE",
|
||||||
cloudProviders: []string{"gce", "gke"},
|
CloudProviders: []string{"gce", "gke"},
|
||||||
provisioner: "kubernetes.io/gce-pd",
|
Provisioner: "kubernetes.io/gce-pd",
|
||||||
parameters: map[string]string{
|
Parameters: map[string]string{
|
||||||
"type": "pd-standard",
|
"type": "pd-standard",
|
||||||
},
|
},
|
||||||
claimSize: "1.5Gi",
|
ClaimSize: "1.5Gi",
|
||||||
expectedSize: "2Gi",
|
ExpectedSize: "2Gi",
|
||||||
pvCheck: func(volume *v1.PersistentVolume) error {
|
PvCheck: func(volume *v1.PersistentVolume) error {
|
||||||
return checkGCEPD(volume, "pd-standard")
|
return checkGCEPD(volume, "pd-standard")
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// AWS
|
// AWS
|
||||||
{
|
{
|
||||||
name: "gp2 EBS on AWS",
|
Name: "gp2 EBS on AWS",
|
||||||
cloudProviders: []string{"aws"},
|
CloudProviders: []string{"aws"},
|
||||||
provisioner: "kubernetes.io/aws-ebs",
|
Provisioner: "kubernetes.io/aws-ebs",
|
||||||
parameters: map[string]string{
|
Parameters: map[string]string{
|
||||||
"type": "gp2",
|
"type": "gp2",
|
||||||
"zone": cloudZone,
|
"zone": cloudZone,
|
||||||
},
|
},
|
||||||
claimSize: "1.5Gi",
|
ClaimSize: "1.5Gi",
|
||||||
expectedSize: "2Gi",
|
ExpectedSize: "2Gi",
|
||||||
pvCheck: func(volume *v1.PersistentVolume) error {
|
PvCheck: func(volume *v1.PersistentVolume) error {
|
||||||
return checkAWSEBS(volume, "gp2", false)
|
return checkAWSEBS(volume, "gp2", false)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "io1 EBS on AWS",
|
Name: "io1 EBS on AWS",
|
||||||
cloudProviders: []string{"aws"},
|
CloudProviders: []string{"aws"},
|
||||||
provisioner: "kubernetes.io/aws-ebs",
|
Provisioner: "kubernetes.io/aws-ebs",
|
||||||
parameters: map[string]string{
|
Parameters: map[string]string{
|
||||||
"type": "io1",
|
"type": "io1",
|
||||||
"iopsPerGB": "50",
|
"iopsPerGB": "50",
|
||||||
},
|
},
|
||||||
claimSize: "3.5Gi",
|
ClaimSize: "3.5Gi",
|
||||||
expectedSize: "4Gi", // 4 GiB is minimum for io1
|
ExpectedSize: "4Gi", // 4 GiB is minimum for io1
|
||||||
pvCheck: func(volume *v1.PersistentVolume) error {
|
PvCheck: func(volume *v1.PersistentVolume) error {
|
||||||
return checkAWSEBS(volume, "io1", false)
|
return checkAWSEBS(volume, "io1", false)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "sc1 EBS on AWS",
|
Name: "sc1 EBS on AWS",
|
||||||
cloudProviders: []string{"aws"},
|
CloudProviders: []string{"aws"},
|
||||||
provisioner: "kubernetes.io/aws-ebs",
|
Provisioner: "kubernetes.io/aws-ebs",
|
||||||
parameters: map[string]string{
|
Parameters: map[string]string{
|
||||||
"type": "sc1",
|
"type": "sc1",
|
||||||
},
|
},
|
||||||
claimSize: "500Gi", // minimum for sc1
|
ClaimSize: "500Gi", // minimum for sc1
|
||||||
expectedSize: "500Gi",
|
ExpectedSize: "500Gi",
|
||||||
pvCheck: func(volume *v1.PersistentVolume) error {
|
PvCheck: func(volume *v1.PersistentVolume) error {
|
||||||
return checkAWSEBS(volume, "sc1", false)
|
return checkAWSEBS(volume, "sc1", false)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "st1 EBS on AWS",
|
Name: "st1 EBS on AWS",
|
||||||
cloudProviders: []string{"aws"},
|
CloudProviders: []string{"aws"},
|
||||||
provisioner: "kubernetes.io/aws-ebs",
|
Provisioner: "kubernetes.io/aws-ebs",
|
||||||
parameters: map[string]string{
|
Parameters: map[string]string{
|
||||||
"type": "st1",
|
"type": "st1",
|
||||||
},
|
},
|
||||||
claimSize: "500Gi", // minimum for st1
|
ClaimSize: "500Gi", // minimum for st1
|
||||||
expectedSize: "500Gi",
|
ExpectedSize: "500Gi",
|
||||||
pvCheck: func(volume *v1.PersistentVolume) error {
|
PvCheck: func(volume *v1.PersistentVolume) error {
|
||||||
return checkAWSEBS(volume, "st1", false)
|
return checkAWSEBS(volume, "st1", false)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "encrypted EBS on AWS",
|
Name: "encrypted EBS on AWS",
|
||||||
cloudProviders: []string{"aws"},
|
CloudProviders: []string{"aws"},
|
||||||
provisioner: "kubernetes.io/aws-ebs",
|
Provisioner: "kubernetes.io/aws-ebs",
|
||||||
parameters: map[string]string{
|
Parameters: map[string]string{
|
||||||
"encrypted": "true",
|
"encrypted": "true",
|
||||||
},
|
},
|
||||||
claimSize: "1Gi",
|
ClaimSize: "1Gi",
|
||||||
expectedSize: "1Gi",
|
ExpectedSize: "1Gi",
|
||||||
pvCheck: func(volume *v1.PersistentVolume) error {
|
PvCheck: func(volume *v1.PersistentVolume) error {
|
||||||
return checkAWSEBS(volume, "gp2", true)
|
return checkAWSEBS(volume, "gp2", true)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// OpenStack generic tests (works on all OpenStack deployments)
|
// OpenStack generic tests (works on all OpenStack deployments)
|
||||||
{
|
{
|
||||||
name: "generic Cinder volume on OpenStack",
|
Name: "generic Cinder volume on OpenStack",
|
||||||
cloudProviders: []string{"openstack"},
|
CloudProviders: []string{"openstack"},
|
||||||
provisioner: "kubernetes.io/cinder",
|
Provisioner: "kubernetes.io/cinder",
|
||||||
parameters: map[string]string{},
|
Parameters: map[string]string{},
|
||||||
claimSize: "1.5Gi",
|
ClaimSize: "1.5Gi",
|
||||||
expectedSize: "2Gi",
|
ExpectedSize: "2Gi",
|
||||||
pvCheck: nil, // there is currently nothing to check on OpenStack
|
PvCheck: nil, // there is currently nothing to check on OpenStack
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Cinder volume with empty volume type and zone on OpenStack",
|
Name: "Cinder volume with empty volume type and zone on OpenStack",
|
||||||
cloudProviders: []string{"openstack"},
|
CloudProviders: []string{"openstack"},
|
||||||
provisioner: "kubernetes.io/cinder",
|
Provisioner: "kubernetes.io/cinder",
|
||||||
parameters: map[string]string{
|
Parameters: map[string]string{
|
||||||
"type": "",
|
"type": "",
|
||||||
"availability": "",
|
"availability": "",
|
||||||
},
|
},
|
||||||
claimSize: "1.5Gi",
|
ClaimSize: "1.5Gi",
|
||||||
expectedSize: "2Gi",
|
ExpectedSize: "2Gi",
|
||||||
pvCheck: nil, // there is currently nothing to check on OpenStack
|
PvCheck: nil, // there is currently nothing to check on OpenStack
|
||||||
},
|
},
|
||||||
// vSphere generic test
|
// vSphere generic test
|
||||||
{
|
{
|
||||||
name: "generic vSphere volume",
|
Name: "generic vSphere volume",
|
||||||
cloudProviders: []string{"vsphere"},
|
CloudProviders: []string{"vsphere"},
|
||||||
provisioner: "kubernetes.io/vsphere-volume",
|
Provisioner: "kubernetes.io/vsphere-volume",
|
||||||
parameters: map[string]string{},
|
Parameters: map[string]string{},
|
||||||
claimSize: "1.5Gi",
|
ClaimSize: "1.5Gi",
|
||||||
expectedSize: "1.5Gi",
|
ExpectedSize: "1.5Gi",
|
||||||
pvCheck: nil,
|
PvCheck: nil,
|
||||||
},
|
},
|
||||||
// Azure
|
// Azure
|
||||||
{
|
{
|
||||||
name: "Azure disk volume with empty sku and location",
|
Name: "Azure disk volume with empty sku and location",
|
||||||
cloudProviders: []string{"azure"},
|
CloudProviders: []string{"azure"},
|
||||||
provisioner: "kubernetes.io/azure-disk",
|
Provisioner: "kubernetes.io/azure-disk",
|
||||||
parameters: map[string]string{},
|
Parameters: map[string]string{},
|
||||||
claimSize: "1Gi",
|
ClaimSize: "1Gi",
|
||||||
expectedSize: "1Gi",
|
ExpectedSize: "1Gi",
|
||||||
pvCheck: nil,
|
PvCheck: nil,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var betaTest *storageClassTest
|
var betaTest *testsuites.StorageClassTest
|
||||||
for i, t := range tests {
|
for i, t := range tests {
|
||||||
// Beware of clojure, use local variables instead of those from
|
// Beware of clojure, use local variables instead of those from
|
||||||
// outer scope
|
// outer scope
|
||||||
test := t
|
test := t
|
||||||
|
|
||||||
if !framework.ProviderIs(test.cloudProviders...) {
|
if !framework.ProviderIs(test.CloudProviders...) {
|
||||||
framework.Logf("Skipping %q: cloud providers is not %v", test.name, test.cloudProviders)
|
framework.Logf("Skipping %q: cloud providers is not %v", test.Name, test.CloudProviders)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remember the last supported test for subsequent test of beta API
|
// Remember the last supported test for subsequent test of beta API
|
||||||
betaTest = &test
|
betaTest = &test
|
||||||
|
|
||||||
By("Testing " + test.name)
|
By("Testing " + test.Name)
|
||||||
suffix := fmt.Sprintf("%d", i)
|
suffix := fmt.Sprintf("%d", i)
|
||||||
class := newStorageClass(test, ns, suffix)
|
class := newStorageClass(test, ns, suffix)
|
||||||
claim := newClaim(test, ns, suffix)
|
claim := newClaim(test, ns, suffix)
|
||||||
claim.Spec.StorageClassName = &class.Name
|
claim.Spec.StorageClassName = &class.Name
|
||||||
testDynamicProvisioning(test, c, claim, class)
|
testsuites.TestDynamicProvisioning(test, c, claim, class)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the last test with storage.k8s.io/v1beta1 on pvc
|
// Run the last test with storage.k8s.io/v1beta1 on pvc
|
||||||
if betaTest != nil {
|
if betaTest != nil {
|
||||||
By("Testing " + betaTest.name + " with beta volume provisioning")
|
By("Testing " + betaTest.Name + " with beta volume provisioning")
|
||||||
class := newBetaStorageClass(*betaTest, "beta")
|
class := newBetaStorageClass(*betaTest, "beta")
|
||||||
// we need to create the class manually, testDynamicProvisioning does not accept beta class
|
// we need to create the class manually, testDynamicProvisioning does not accept beta class
|
||||||
class, err := c.StorageV1beta1().StorageClasses().Create(class)
|
class, err := c.StorageV1beta1().StorageClasses().Create(class)
|
||||||
@ -541,67 +423,10 @@ var _ = utils.SIGDescribe("Dynamic Provisioning", func() {
|
|||||||
|
|
||||||
claim := newClaim(*betaTest, ns, "beta")
|
claim := newClaim(*betaTest, ns, "beta")
|
||||||
claim.Spec.StorageClassName = &(class.Name)
|
claim.Spec.StorageClassName = &(class.Name)
|
||||||
testDynamicProvisioning(*betaTest, c, claim, nil)
|
testsuites.TestDynamicProvisioning(*betaTest, c, claim, nil)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
It("should provision storage with non-default reclaim policy Retain", func() {
|
|
||||||
framework.SkipUnlessProviderIs("gce", "gke")
|
|
||||||
|
|
||||||
test := storageClassTest{
|
|
||||||
name: "HDD PD on GCE/GKE",
|
|
||||||
cloudProviders: []string{"gce", "gke"},
|
|
||||||
provisioner: "kubernetes.io/gce-pd",
|
|
||||||
parameters: map[string]string{
|
|
||||||
"type": "pd-standard",
|
|
||||||
},
|
|
||||||
claimSize: "1Gi",
|
|
||||||
expectedSize: "1Gi",
|
|
||||||
pvCheck: func(volume *v1.PersistentVolume) error {
|
|
||||||
return checkGCEPD(volume, "pd-standard")
|
|
||||||
},
|
|
||||||
}
|
|
||||||
class := newStorageClass(test, ns, "reclaimpolicy")
|
|
||||||
retain := v1.PersistentVolumeReclaimRetain
|
|
||||||
class.ReclaimPolicy = &retain
|
|
||||||
claim := newClaim(test, ns, "reclaimpolicy")
|
|
||||||
claim.Spec.StorageClassName = &class.Name
|
|
||||||
pv := testDynamicProvisioning(test, c, claim, class)
|
|
||||||
|
|
||||||
By(fmt.Sprintf("waiting for the provisioned PV %q to enter phase %s", pv.Name, v1.VolumeReleased))
|
|
||||||
framework.ExpectNoError(framework.WaitForPersistentVolumePhase(v1.VolumeReleased, c, pv.Name, 1*time.Second, 30*time.Second))
|
|
||||||
|
|
||||||
By(fmt.Sprintf("deleting the storage asset backing the PV %q", pv.Name))
|
|
||||||
framework.ExpectNoError(framework.DeletePDWithRetry(pv.Spec.GCEPersistentDisk.PDName))
|
|
||||||
|
|
||||||
By(fmt.Sprintf("deleting the PV %q", pv.Name))
|
|
||||||
framework.ExpectNoError(framework.DeletePersistentVolume(c, pv.Name), "Failed to delete PV ", pv.Name)
|
|
||||||
framework.ExpectNoError(framework.WaitForPersistentVolumeDeleted(c, pv.Name, 1*time.Second, 30*time.Second))
|
|
||||||
})
|
|
||||||
|
|
||||||
It("should provision storage with mount options", func() {
|
|
||||||
framework.SkipUnlessProviderIs("gce", "gke")
|
|
||||||
|
|
||||||
test := storageClassTest{
|
|
||||||
name: "HDD PD on GCE/GKE",
|
|
||||||
cloudProviders: []string{"gce", "gke"},
|
|
||||||
provisioner: "kubernetes.io/gce-pd",
|
|
||||||
parameters: map[string]string{
|
|
||||||
"type": "pd-standard",
|
|
||||||
},
|
|
||||||
claimSize: "1Gi",
|
|
||||||
expectedSize: "1Gi",
|
|
||||||
pvCheck: func(volume *v1.PersistentVolume) error {
|
|
||||||
return checkGCEPD(volume, "pd-standard")
|
|
||||||
},
|
|
||||||
}
|
|
||||||
class := newStorageClass(test, ns, "mountoptions")
|
|
||||||
class.MountOptions = []string{"debug", "nouid32"}
|
|
||||||
claim := newClaim(test, ns, "mountoptions")
|
|
||||||
claim.Spec.StorageClassName = &class.Name
|
|
||||||
testDynamicProvisioning(test, c, claim, class)
|
|
||||||
})
|
|
||||||
|
|
||||||
It("should not provision a volume in an unmanaged GCE zone.", func() {
|
It("should not provision a volume in an unmanaged GCE zone.", func() {
|
||||||
framework.SkipUnlessProviderIs("gce", "gke")
|
framework.SkipUnlessProviderIs("gce", "gke")
|
||||||
var suffix string = "unmananged"
|
var suffix string = "unmananged"
|
||||||
@ -634,11 +459,11 @@ var _ = utils.SIGDescribe("Dynamic Provisioning", func() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
By("Creating a StorageClass for the unmanaged zone")
|
By("Creating a StorageClass for the unmanaged zone")
|
||||||
test := storageClassTest{
|
test := testsuites.StorageClassTest{
|
||||||
name: "unmanaged_zone",
|
Name: "unmanaged_zone",
|
||||||
provisioner: "kubernetes.io/gce-pd",
|
Provisioner: "kubernetes.io/gce-pd",
|
||||||
parameters: map[string]string{"zone": unmanagedZone},
|
Parameters: map[string]string{"zone": unmanagedZone},
|
||||||
claimSize: "1Gi",
|
ClaimSize: "1Gi",
|
||||||
}
|
}
|
||||||
sc := newStorageClass(test, ns, suffix)
|
sc := newStorageClass(test, ns, suffix)
|
||||||
sc, err = c.StorageV1().StorageClasses().Create(sc)
|
sc, err = c.StorageV1().StorageClasses().Create(sc)
|
||||||
@ -671,10 +496,10 @@ var _ = utils.SIGDescribe("Dynamic Provisioning", func() {
|
|||||||
const raceAttempts int = 100
|
const raceAttempts int = 100
|
||||||
var residualPVs []*v1.PersistentVolume
|
var residualPVs []*v1.PersistentVolume
|
||||||
By(fmt.Sprintf("Creating and deleting PersistentVolumeClaims %d times", raceAttempts))
|
By(fmt.Sprintf("Creating and deleting PersistentVolumeClaims %d times", raceAttempts))
|
||||||
test := storageClassTest{
|
test := testsuites.StorageClassTest{
|
||||||
name: "deletion race",
|
Name: "deletion race",
|
||||||
provisioner: "", // Use a native one based on current cloud provider
|
Provisioner: "", // Use a native one based on current cloud provider
|
||||||
claimSize: "1Gi",
|
ClaimSize: "1Gi",
|
||||||
}
|
}
|
||||||
|
|
||||||
class := newStorageClass(test, ns, "race")
|
class := newStorageClass(test, ns, "race")
|
||||||
@ -822,18 +647,18 @@ var _ = utils.SIGDescribe("Dynamic Provisioning", func() {
|
|||||||
defer framework.DeletePodOrFail(c, ns, pod.Name)
|
defer framework.DeletePodOrFail(c, ns, pod.Name)
|
||||||
|
|
||||||
By("creating a StorageClass")
|
By("creating a StorageClass")
|
||||||
test := storageClassTest{
|
test := testsuites.StorageClassTest{
|
||||||
name: "external provisioner test",
|
Name: "external provisioner test",
|
||||||
provisioner: externalPluginName,
|
Provisioner: externalPluginName,
|
||||||
claimSize: "1500Mi",
|
ClaimSize: "1500Mi",
|
||||||
expectedSize: "1500Mi",
|
ExpectedSize: "1500Mi",
|
||||||
}
|
}
|
||||||
class := newStorageClass(test, ns, "external")
|
class := newStorageClass(test, ns, "external")
|
||||||
claim := newClaim(test, ns, "external")
|
claim := newClaim(test, ns, "external")
|
||||||
claim.Spec.StorageClassName = &(class.Name)
|
claim.Spec.StorageClassName = &(class.Name)
|
||||||
|
|
||||||
By("creating a claim with a external provisioning annotation")
|
By("creating a claim with a external provisioning annotation")
|
||||||
testDynamicProvisioning(test, c, claim, class)
|
testsuites.TestDynamicProvisioning(test, c, claim, class)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -842,23 +667,23 @@ var _ = utils.SIGDescribe("Dynamic Provisioning", func() {
|
|||||||
framework.SkipUnlessProviderIs("openstack", "gce", "aws", "gke", "vsphere", "azure")
|
framework.SkipUnlessProviderIs("openstack", "gce", "aws", "gke", "vsphere", "azure")
|
||||||
|
|
||||||
By("creating a claim with no annotation")
|
By("creating a claim with no annotation")
|
||||||
test := storageClassTest{
|
test := testsuites.StorageClassTest{
|
||||||
name: "default",
|
Name: "default",
|
||||||
claimSize: "2Gi",
|
ClaimSize: "2Gi",
|
||||||
expectedSize: "2Gi",
|
ExpectedSize: "2Gi",
|
||||||
}
|
}
|
||||||
|
|
||||||
claim := newClaim(test, ns, "default")
|
claim := newClaim(test, ns, "default")
|
||||||
testDynamicProvisioning(test, c, claim, nil)
|
testsuites.TestDynamicProvisioning(test, c, claim, nil)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Modifying the default storage class can be disruptive to other tests that depend on it
|
// Modifying the default storage class can be disruptive to other tests that depend on it
|
||||||
It("should be disabled by changing the default annotation [Serial] [Disruptive]", func() {
|
It("should be disabled by changing the default annotation [Serial] [Disruptive]", func() {
|
||||||
framework.SkipUnlessProviderIs("openstack", "gce", "aws", "gke", "vsphere", "azure")
|
framework.SkipUnlessProviderIs("openstack", "gce", "aws", "gke", "vsphere", "azure")
|
||||||
scName := getDefaultStorageClassName(c)
|
scName := getDefaultStorageClassName(c)
|
||||||
test := storageClassTest{
|
test := testsuites.StorageClassTest{
|
||||||
name: "default",
|
Name: "default",
|
||||||
claimSize: "2Gi",
|
ClaimSize: "2Gi",
|
||||||
}
|
}
|
||||||
|
|
||||||
By("setting the is-default StorageClass annotation to false")
|
By("setting the is-default StorageClass annotation to false")
|
||||||
@ -887,9 +712,9 @@ var _ = utils.SIGDescribe("Dynamic Provisioning", func() {
|
|||||||
It("should be disabled by removing the default annotation [Serial] [Disruptive]", func() {
|
It("should be disabled by removing the default annotation [Serial] [Disruptive]", func() {
|
||||||
framework.SkipUnlessProviderIs("openstack", "gce", "aws", "gke", "vsphere", "azure")
|
framework.SkipUnlessProviderIs("openstack", "gce", "aws", "gke", "vsphere", "azure")
|
||||||
scName := getDefaultStorageClassName(c)
|
scName := getDefaultStorageClassName(c)
|
||||||
test := storageClassTest{
|
test := testsuites.StorageClassTest{
|
||||||
name: "default",
|
Name: "default",
|
||||||
claimSize: "2Gi",
|
ClaimSize: "2Gi",
|
||||||
}
|
}
|
||||||
|
|
||||||
By("removing the is-default StorageClass annotation")
|
By("removing the is-default StorageClass annotation")
|
||||||
@ -921,13 +746,13 @@ var _ = utils.SIGDescribe("Dynamic Provisioning", func() {
|
|||||||
pod := startGlusterDpServerPod(c, ns)
|
pod := startGlusterDpServerPod(c, ns)
|
||||||
serverUrl := "https://" + pod.Status.PodIP + ":8081"
|
serverUrl := "https://" + pod.Status.PodIP + ":8081"
|
||||||
By("creating a StorageClass")
|
By("creating a StorageClass")
|
||||||
test := storageClassTest{
|
test := testsuites.StorageClassTest{
|
||||||
name: "Gluster Dynamic provisioner test",
|
Name: "Gluster Dynamic provisioner test",
|
||||||
provisioner: "kubernetes.io/glusterfs",
|
Provisioner: "kubernetes.io/glusterfs",
|
||||||
claimSize: "2Gi",
|
ClaimSize: "2Gi",
|
||||||
expectedSize: "2Gi",
|
ExpectedSize: "2Gi",
|
||||||
parameters: map[string]string{"resturl": serverUrl},
|
Parameters: map[string]string{"resturl": serverUrl},
|
||||||
skipWriteReadCheck: true,
|
SkipWriteReadCheck: true,
|
||||||
}
|
}
|
||||||
suffix := fmt.Sprintf("glusterdptest")
|
suffix := fmt.Sprintf("glusterdptest")
|
||||||
class := newStorageClass(test, ns, suffix)
|
class := newStorageClass(test, ns, suffix)
|
||||||
@ -935,38 +760,18 @@ var _ = utils.SIGDescribe("Dynamic Provisioning", func() {
|
|||||||
By("creating a claim object with a suffix for gluster dynamic provisioner")
|
By("creating a claim object with a suffix for gluster dynamic provisioner")
|
||||||
claim := newClaim(test, ns, suffix)
|
claim := newClaim(test, ns, suffix)
|
||||||
|
|
||||||
testDynamicProvisioning(test, c, claim, class)
|
testsuites.TestDynamicProvisioning(test, c, claim, class)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Describe("Block volume provisioning [Feature:BlockVolume]", func() {
|
|
||||||
It("should create and delete block persistent volumes", func() {
|
|
||||||
|
|
||||||
// TODO: add openstack once Cinder volume plugin supports block volumes
|
|
||||||
framework.SkipUnlessProviderIs("gce", "aws", "gke", "vsphere", "azure")
|
|
||||||
|
|
||||||
By("creating a claim with default class")
|
|
||||||
block := v1.PersistentVolumeBlock
|
|
||||||
test := storageClassTest{
|
|
||||||
name: "default",
|
|
||||||
claimSize: "2Gi",
|
|
||||||
expectedSize: "2Gi",
|
|
||||||
volumeMode: &block,
|
|
||||||
skipWriteReadCheck: true,
|
|
||||||
}
|
|
||||||
claim := newClaim(test, ns, "default")
|
|
||||||
claim.Spec.VolumeMode = &block
|
|
||||||
testDynamicProvisioning(test, c, claim, nil)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
Describe("Invalid AWS KMS key", func() {
|
Describe("Invalid AWS KMS key", func() {
|
||||||
It("should report an error and create no PV", func() {
|
It("should report an error and create no PV", func() {
|
||||||
framework.SkipUnlessProviderIs("aws")
|
framework.SkipUnlessProviderIs("aws")
|
||||||
test := storageClassTest{
|
test := testsuites.StorageClassTest{
|
||||||
name: "AWS EBS with invalid KMS key",
|
Name: "AWS EBS with invalid KMS key",
|
||||||
provisioner: "kubernetes.io/aws-ebs",
|
Provisioner: "kubernetes.io/aws-ebs",
|
||||||
claimSize: "2Gi",
|
ClaimSize: "2Gi",
|
||||||
parameters: map[string]string{"kmsKeyId": "arn:aws:kms:us-east-1:123456789012:key/55555555-5555-5555-5555-555555555555"},
|
Parameters: map[string]string{"kmsKeyId": "arn:aws:kms:us-east-1:123456789012:key/55555555-5555-5555-5555-555555555555"},
|
||||||
}
|
}
|
||||||
|
|
||||||
By("creating a StorageClass")
|
By("creating a StorageClass")
|
||||||
@ -1008,25 +813,25 @@ var _ = utils.SIGDescribe("Dynamic Provisioning", func() {
|
|||||||
})
|
})
|
||||||
Describe("DynamicProvisioner delayed binding [Slow]", func() {
|
Describe("DynamicProvisioner delayed binding [Slow]", func() {
|
||||||
It("should create a persistent volume in the same zone as node after a pod mounting the claim is started", func() {
|
It("should create a persistent volume in the same zone as node after a pod mounting the claim is started", func() {
|
||||||
tests := []storageClassTest{
|
tests := []testsuites.StorageClassTest{
|
||||||
{
|
{
|
||||||
name: "Delayed binding EBS storage class test",
|
Name: "Delayed binding EBS storage class test",
|
||||||
cloudProviders: []string{"aws"},
|
CloudProviders: []string{"aws"},
|
||||||
provisioner: "kubernetes.io/aws-ebs",
|
Provisioner: "kubernetes.io/aws-ebs",
|
||||||
claimSize: "2Gi",
|
ClaimSize: "2Gi",
|
||||||
delayBinding: true,
|
DelayBinding: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Delayed binding GCE PD storage class test",
|
Name: "Delayed binding GCE PD storage class test",
|
||||||
cloudProviders: []string{"gce", "gke"},
|
CloudProviders: []string{"gce", "gke"},
|
||||||
provisioner: "kubernetes.io/gce-pd",
|
Provisioner: "kubernetes.io/gce-pd",
|
||||||
claimSize: "2Gi",
|
ClaimSize: "2Gi",
|
||||||
delayBinding: true,
|
DelayBinding: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
if !framework.ProviderIs(test.cloudProviders...) {
|
if !framework.ProviderIs(test.CloudProviders...) {
|
||||||
framework.Logf("Skipping %q: cloud providers is not %v", test.name, test.cloudProviders)
|
framework.Logf("Skipping %q: cloud providers is not %v", test.Name, test.CloudProviders)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
By("creating a claim with class with waitForFirstConsumer")
|
By("creating a claim with class with waitForFirstConsumer")
|
||||||
@ -1048,25 +853,25 @@ var _ = utils.SIGDescribe("Dynamic Provisioning", func() {
|
|||||||
})
|
})
|
||||||
Describe("DynamicProvisioner allowedTopologies", func() {
|
Describe("DynamicProvisioner allowedTopologies", func() {
|
||||||
It("should create persistent volume in the zone specified in allowedTopologies of storageclass", func() {
|
It("should create persistent volume in the zone specified in allowedTopologies of storageclass", func() {
|
||||||
tests := []storageClassTest{
|
tests := []testsuites.StorageClassTest{
|
||||||
{
|
{
|
||||||
name: "AllowedTopologies EBS storage class test",
|
Name: "AllowedTopologies EBS storage class test",
|
||||||
cloudProviders: []string{"aws"},
|
CloudProviders: []string{"aws"},
|
||||||
provisioner: "kubernetes.io/aws-ebs",
|
Provisioner: "kubernetes.io/aws-ebs",
|
||||||
claimSize: "2Gi",
|
ClaimSize: "2Gi",
|
||||||
expectedSize: "2Gi",
|
ExpectedSize: "2Gi",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "AllowedTopologies GCE PD storage class test",
|
Name: "AllowedTopologies GCE PD storage class test",
|
||||||
cloudProviders: []string{"gce", "gke"},
|
CloudProviders: []string{"gce", "gke"},
|
||||||
provisioner: "kubernetes.io/gce-pd",
|
Provisioner: "kubernetes.io/gce-pd",
|
||||||
claimSize: "2Gi",
|
ClaimSize: "2Gi",
|
||||||
expectedSize: "2Gi",
|
ExpectedSize: "2Gi",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
if !framework.ProviderIs(test.cloudProviders...) {
|
if !framework.ProviderIs(test.CloudProviders...) {
|
||||||
framework.Logf("Skipping %q: cloud providers is not %v", test.name, test.cloudProviders)
|
framework.Logf("Skipping %q: cloud providers is not %v", test.Name, test.CloudProviders)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
By("creating a claim with class with allowedTopologies set")
|
By("creating a claim with class with allowedTopologies set")
|
||||||
@ -1076,32 +881,32 @@ var _ = utils.SIGDescribe("Dynamic Provisioning", func() {
|
|||||||
addSingleZoneAllowedTopologyToStorageClass(c, class, zone)
|
addSingleZoneAllowedTopologyToStorageClass(c, class, zone)
|
||||||
claim := newClaim(test, ns, suffix)
|
claim := newClaim(test, ns, suffix)
|
||||||
claim.Spec.StorageClassName = &class.Name
|
claim.Spec.StorageClassName = &class.Name
|
||||||
pv := testDynamicProvisioning(test, c, claim, class)
|
pv := testsuites.TestDynamicProvisioning(test, c, claim, class)
|
||||||
checkZoneFromLabelAndAffinity(pv, zone, true)
|
checkZoneFromLabelAndAffinity(pv, zone, true)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
Describe("DynamicProvisioner delayed binding with allowedTopologies [Slow]", func() {
|
Describe("DynamicProvisioner delayed binding with allowedTopologies [Slow]", func() {
|
||||||
It("should create persistent volume in the same zone as specified in allowedTopologies after a pod mounting the claim is started", func() {
|
It("should create persistent volume in the same zone as specified in allowedTopologies after a pod mounting the claim is started", func() {
|
||||||
tests := []storageClassTest{
|
tests := []testsuites.StorageClassTest{
|
||||||
{
|
{
|
||||||
name: "AllowedTopologies and delayed binding EBS storage class test",
|
Name: "AllowedTopologies and delayed binding EBS storage class test",
|
||||||
cloudProviders: []string{"aws"},
|
CloudProviders: []string{"aws"},
|
||||||
provisioner: "kubernetes.io/aws-ebs",
|
Provisioner: "kubernetes.io/aws-ebs",
|
||||||
claimSize: "2Gi",
|
ClaimSize: "2Gi",
|
||||||
delayBinding: true,
|
DelayBinding: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "AllowedTopologies and delayed binding GCE PD storage class test",
|
Name: "AllowedTopologies and delayed binding GCE PD storage class test",
|
||||||
cloudProviders: []string{"gce", "gke"},
|
CloudProviders: []string{"gce", "gke"},
|
||||||
provisioner: "kubernetes.io/gce-pd",
|
Provisioner: "kubernetes.io/gce-pd",
|
||||||
claimSize: "2Gi",
|
ClaimSize: "2Gi",
|
||||||
delayBinding: true,
|
DelayBinding: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
if !framework.ProviderIs(test.cloudProviders...) {
|
if !framework.ProviderIs(test.CloudProviders...) {
|
||||||
framework.Logf("Skipping %q: cloud providers is not %v", test.name, test.cloudProviders)
|
framework.Logf("Skipping %q: cloud providers is not %v", test.Name, test.CloudProviders)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
By("creating a claim with class with WaitForFirstConsumer and allowedTopologies")
|
By("creating a claim with class with WaitForFirstConsumer and allowedTopologies")
|
||||||
@ -1202,59 +1007,8 @@ func getClaim(claimSize string, ns string) *v1.PersistentVolumeClaim {
|
|||||||
return &claim
|
return &claim
|
||||||
}
|
}
|
||||||
|
|
||||||
func newClaim(t storageClassTest, ns, suffix string) *v1.PersistentVolumeClaim {
|
func newClaim(t testsuites.StorageClassTest, ns, suffix string) *v1.PersistentVolumeClaim {
|
||||||
return getClaim(t.claimSize, ns)
|
return getClaim(t.ClaimSize, ns)
|
||||||
}
|
|
||||||
|
|
||||||
// runInPodWithVolume runs a command in a pod with given claim mounted to /mnt directory.
|
|
||||||
func runInPodWithVolume(c clientset.Interface, ns, claimName, nodeName, command string) {
|
|
||||||
pod := &v1.Pod{
|
|
||||||
TypeMeta: metav1.TypeMeta{
|
|
||||||
Kind: "Pod",
|
|
||||||
APIVersion: "v1",
|
|
||||||
},
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
GenerateName: "pvc-volume-tester-",
|
|
||||||
},
|
|
||||||
Spec: v1.PodSpec{
|
|
||||||
Containers: []v1.Container{
|
|
||||||
{
|
|
||||||
Name: "volume-tester",
|
|
||||||
Image: imageutils.GetE2EImage(imageutils.BusyBox),
|
|
||||||
Command: []string{"/bin/sh"},
|
|
||||||
Args: []string{"-c", command},
|
|
||||||
VolumeMounts: []v1.VolumeMount{
|
|
||||||
{
|
|
||||||
Name: "my-volume",
|
|
||||||
MountPath: "/mnt/test",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
RestartPolicy: v1.RestartPolicyNever,
|
|
||||||
Volumes: []v1.Volume{
|
|
||||||
{
|
|
||||||
Name: "my-volume",
|
|
||||||
VolumeSource: v1.VolumeSource{
|
|
||||||
PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
|
|
||||||
ClaimName: claimName,
|
|
||||||
ReadOnly: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(nodeName) != 0 {
|
|
||||||
pod.Spec.NodeName = nodeName
|
|
||||||
}
|
|
||||||
pod, err := c.CoreV1().Pods(ns).Create(pod)
|
|
||||||
framework.ExpectNoError(err, "Failed to create pod: %v", err)
|
|
||||||
defer func() {
|
|
||||||
framework.DeletePodOrFail(c, ns, pod.Name)
|
|
||||||
}()
|
|
||||||
framework.ExpectNoError(framework.WaitForPodSuccessInNamespaceSlow(c, pod.Name, pod.Namespace))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDefaultPluginName() string {
|
func getDefaultPluginName() string {
|
||||||
@ -1285,8 +1039,8 @@ func addSingleZoneAllowedTopologyToStorageClass(c clientset.Interface, sc *stora
|
|||||||
sc.AllowedTopologies = append(sc.AllowedTopologies, term)
|
sc.AllowedTopologies = append(sc.AllowedTopologies, term)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newStorageClass(t storageClassTest, ns string, suffix string) *storage.StorageClass {
|
func newStorageClass(t testsuites.StorageClassTest, ns string, suffix string) *storage.StorageClass {
|
||||||
pluginName := t.provisioner
|
pluginName := t.Provisioner
|
||||||
if pluginName == "" {
|
if pluginName == "" {
|
||||||
pluginName = getDefaultPluginName()
|
pluginName = getDefaultPluginName()
|
||||||
}
|
}
|
||||||
@ -1294,10 +1048,10 @@ func newStorageClass(t storageClassTest, ns string, suffix string) *storage.Stor
|
|||||||
suffix = "sc"
|
suffix = "sc"
|
||||||
}
|
}
|
||||||
bindingMode := storage.VolumeBindingImmediate
|
bindingMode := storage.VolumeBindingImmediate
|
||||||
if t.delayBinding {
|
if t.DelayBinding {
|
||||||
bindingMode = storage.VolumeBindingWaitForFirstConsumer
|
bindingMode = storage.VolumeBindingWaitForFirstConsumer
|
||||||
}
|
}
|
||||||
return getStorageClass(pluginName, t.parameters, &bindingMode, ns, suffix)
|
return getStorageClass(pluginName, t.Parameters, &bindingMode, ns, suffix)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getStorageClass(
|
func getStorageClass(
|
||||||
@ -1326,8 +1080,8 @@ func getStorageClass(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: remove when storage.k8s.io/v1beta1 is removed.
|
// TODO: remove when storage.k8s.io/v1beta1 is removed.
|
||||||
func newBetaStorageClass(t storageClassTest, suffix string) *storagebeta.StorageClass {
|
func newBetaStorageClass(t testsuites.StorageClassTest, suffix string) *storagebeta.StorageClass {
|
||||||
pluginName := t.provisioner
|
pluginName := t.Provisioner
|
||||||
|
|
||||||
if pluginName == "" {
|
if pluginName == "" {
|
||||||
pluginName = getDefaultPluginName()
|
pluginName = getDefaultPluginName()
|
||||||
@ -1344,7 +1098,7 @@ func newBetaStorageClass(t storageClassTest, suffix string) *storagebeta.Storage
|
|||||||
GenerateName: suffix + "-",
|
GenerateName: suffix + "-",
|
||||||
},
|
},
|
||||||
Provisioner: pluginName,
|
Provisioner: pluginName,
|
||||||
Parameters: t.parameters,
|
Parameters: t.Parameters,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user