mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-19 08:40:42 +00:00
Merge pull request #78000 from mkimuram/issue/77999
Move volume_expand tests to storage e2e testsuites
This commit is contained in:
commit
b1bea169f5
@ -23,7 +23,6 @@ go_library(
|
|||||||
"pvc_protection.go",
|
"pvc_protection.go",
|
||||||
"regional_pd.go",
|
"regional_pd.go",
|
||||||
"subpath.go",
|
"subpath.go",
|
||||||
"volume_expand.go",
|
|
||||||
"volume_limits.go",
|
"volume_limits.go",
|
||||||
"volume_metrics.go",
|
"volume_metrics.go",
|
||||||
"volume_provisioning.go",
|
"volume_provisioning.go",
|
||||||
|
@ -439,7 +439,7 @@ var _ = utils.SIGDescribe("CSI mock volume", func() {
|
|||||||
|
|
||||||
ginkgo.By("Expanding current pvc")
|
ginkgo.By("Expanding current pvc")
|
||||||
newSize := resource.MustParse("6Gi")
|
newSize := resource.MustParse("6Gi")
|
||||||
pvc, err = expandPVCSize(pvc, newSize, m.cs)
|
pvc, err = testsuites.ExpandPVCSize(pvc, newSize, m.cs)
|
||||||
framework.ExpectNoError(err, "While updating pvc for more size")
|
framework.ExpectNoError(err, "While updating pvc for more size")
|
||||||
gomega.Expect(pvc).NotTo(gomega.BeNil())
|
gomega.Expect(pvc).NotTo(gomega.BeNil())
|
||||||
|
|
||||||
@ -448,18 +448,18 @@ var _ = utils.SIGDescribe("CSI mock volume", func() {
|
|||||||
e2elog.Failf("error updating pvc size %q", pvc.Name)
|
e2elog.Failf("error updating pvc size %q", pvc.Name)
|
||||||
}
|
}
|
||||||
if test.expectFailure {
|
if test.expectFailure {
|
||||||
err = waitForResizingCondition(pvc, m.cs, csiResizingConditionWait)
|
err = testsuites.WaitForResizingCondition(pvc, m.cs, csiResizingConditionWait)
|
||||||
framework.ExpectError(err, "unexpected resizing condition on PVC")
|
framework.ExpectError(err, "unexpected resizing condition on PVC")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ginkgo.By("Waiting for persistent volume resize to finish")
|
ginkgo.By("Waiting for persistent volume resize to finish")
|
||||||
err = waitForControllerVolumeResize(pvc, m.cs, csiResizeWaitPeriod)
|
err = testsuites.WaitForControllerVolumeResize(pvc, m.cs, csiResizeWaitPeriod)
|
||||||
framework.ExpectNoError(err, "While waiting for CSI PV resize to finish")
|
framework.ExpectNoError(err, "While waiting for CSI PV resize to finish")
|
||||||
|
|
||||||
checkPVCSize := func() {
|
checkPVCSize := func() {
|
||||||
ginkgo.By("Waiting for PVC resize to finish")
|
ginkgo.By("Waiting for PVC resize to finish")
|
||||||
pvc, err = waitForFSResize(pvc, m.cs)
|
pvc, err = testsuites.WaitForFSResize(pvc, m.cs)
|
||||||
framework.ExpectNoError(err, "while waiting for PVC resize to finish")
|
framework.ExpectNoError(err, "while waiting for PVC resize to finish")
|
||||||
|
|
||||||
pvcConditions := pvc.Status.Conditions
|
pvcConditions := pvc.Status.Conditions
|
||||||
@ -530,7 +530,7 @@ var _ = utils.SIGDescribe("CSI mock volume", func() {
|
|||||||
|
|
||||||
ginkgo.By("Expanding current pvc")
|
ginkgo.By("Expanding current pvc")
|
||||||
newSize := resource.MustParse("6Gi")
|
newSize := resource.MustParse("6Gi")
|
||||||
pvc, err = expandPVCSize(pvc, newSize, m.cs)
|
pvc, err = testsuites.ExpandPVCSize(pvc, newSize, m.cs)
|
||||||
framework.ExpectNoError(err, "While updating pvc for more size")
|
framework.ExpectNoError(err, "While updating pvc for more size")
|
||||||
gomega.Expect(pvc).NotTo(gomega.BeNil())
|
gomega.Expect(pvc).NotTo(gomega.BeNil())
|
||||||
|
|
||||||
@ -540,11 +540,11 @@ var _ = utils.SIGDescribe("CSI mock volume", func() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ginkgo.By("Waiting for persistent volume resize to finish")
|
ginkgo.By("Waiting for persistent volume resize to finish")
|
||||||
err = waitForControllerVolumeResize(pvc, m.cs, csiResizeWaitPeriod)
|
err = testsuites.WaitForControllerVolumeResize(pvc, m.cs, csiResizeWaitPeriod)
|
||||||
framework.ExpectNoError(err, "While waiting for PV resize to finish")
|
framework.ExpectNoError(err, "While waiting for PV resize to finish")
|
||||||
|
|
||||||
ginkgo.By("Waiting for PVC resize to finish")
|
ginkgo.By("Waiting for PVC resize to finish")
|
||||||
pvc, err = waitForFSResize(pvc, m.cs)
|
pvc, err = testsuites.WaitForFSResize(pvc, m.cs)
|
||||||
framework.ExpectNoError(err, "while waiting for PVC to finish")
|
framework.ExpectNoError(err, "while waiting for PVC to finish")
|
||||||
|
|
||||||
pvcConditions := pvc.Status.Conditions
|
pvcConditions := pvc.Status.Conditions
|
||||||
|
@ -1157,6 +1157,8 @@ func InitGcePdDriver() testsuites.TestDriver {
|
|||||||
testsuites.CapBlock: true,
|
testsuites.CapBlock: true,
|
||||||
testsuites.CapExec: true,
|
testsuites.CapExec: true,
|
||||||
testsuites.CapMultiPODs: true,
|
testsuites.CapMultiPODs: true,
|
||||||
|
testsuites.CapControllerExpansion: true,
|
||||||
|
testsuites.CapNodeExpansion: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -1532,6 +1534,8 @@ func InitAwsDriver() testsuites.TestDriver {
|
|||||||
testsuites.CapBlock: true,
|
testsuites.CapBlock: true,
|
||||||
testsuites.CapExec: true,
|
testsuites.CapExec: true,
|
||||||
testsuites.CapMultiPODs: true,
|
testsuites.CapMultiPODs: true,
|
||||||
|
testsuites.CapControllerExpansion: true,
|
||||||
|
testsuites.CapNodeExpansion: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
1
test/e2e/storage/external/external.go
vendored
1
test/e2e/storage/external/external.go
vendored
@ -47,6 +47,7 @@ var csiTestSuites = []func() testsuites.TestSuite{
|
|||||||
testsuites.InitVolumeIOTestSuite,
|
testsuites.InitVolumeIOTestSuite,
|
||||||
testsuites.InitVolumeModeTestSuite,
|
testsuites.InitVolumeModeTestSuite,
|
||||||
testsuites.InitVolumesTestSuite,
|
testsuites.InitVolumesTestSuite,
|
||||||
|
testsuites.InitVolumeExpandTestSuite,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -19,6 +19,7 @@ package storage
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"path"
|
"path"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/onsi/ginkgo"
|
"github.com/onsi/ginkgo"
|
||||||
"github.com/onsi/gomega"
|
"github.com/onsi/gomega"
|
||||||
@ -35,6 +36,11 @@ import (
|
|||||||
"k8s.io/kubernetes/test/e2e/storage/utils"
|
"k8s.io/kubernetes/test/e2e/storage/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// total time to wait for cloudprovider or file system resize to finish
|
||||||
|
totalResizeWaitPeriod = 5 * time.Minute
|
||||||
|
)
|
||||||
|
|
||||||
var _ = utils.SIGDescribe("Mounted flexvolume expand[Slow]", func() {
|
var _ = utils.SIGDescribe("Mounted flexvolume expand[Slow]", func() {
|
||||||
var (
|
var (
|
||||||
c clientset.Interface
|
c clientset.Interface
|
||||||
@ -83,7 +89,7 @@ var _ = utils.SIGDescribe("Mounted flexvolume expand[Slow]", func() {
|
|||||||
Provisioner: "flex-expand",
|
Provisioner: "flex-expand",
|
||||||
}
|
}
|
||||||
|
|
||||||
resizableSc, err = createStorageClass(test, ns, "resizing", c)
|
resizableSc, err = c.StorageV1().StorageClasses().Create(newStorageClass(test, ns, "resizing"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("storage class creation error: %v\n", err)
|
fmt.Printf("storage class creation error: %v\n", err)
|
||||||
}
|
}
|
||||||
@ -151,7 +157,7 @@ var _ = utils.SIGDescribe("Mounted flexvolume expand[Slow]", func() {
|
|||||||
|
|
||||||
ginkgo.By("Expanding current pvc")
|
ginkgo.By("Expanding current pvc")
|
||||||
newSize := resource.MustParse("6Gi")
|
newSize := resource.MustParse("6Gi")
|
||||||
pvc, err = expandPVCSize(pvc, newSize, c)
|
pvc, err = testsuites.ExpandPVCSize(pvc, newSize, c)
|
||||||
framework.ExpectNoError(err, "While updating pvc for more size")
|
framework.ExpectNoError(err, "While updating pvc for more size")
|
||||||
gomega.Expect(pvc).NotTo(gomega.BeNil())
|
gomega.Expect(pvc).NotTo(gomega.BeNil())
|
||||||
|
|
||||||
@ -161,7 +167,7 @@ var _ = utils.SIGDescribe("Mounted flexvolume expand[Slow]", func() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ginkgo.By("Waiting for cloudprovider resize to finish")
|
ginkgo.By("Waiting for cloudprovider resize to finish")
|
||||||
err = waitForControllerVolumeResize(pvc, c, totalResizeWaitPeriod)
|
err = testsuites.WaitForControllerVolumeResize(pvc, c, totalResizeWaitPeriod)
|
||||||
framework.ExpectNoError(err, "While waiting for pvc resize to finish")
|
framework.ExpectNoError(err, "While waiting for pvc resize to finish")
|
||||||
|
|
||||||
ginkgo.By("Getting a pod from deployment")
|
ginkgo.By("Getting a pod from deployment")
|
||||||
@ -178,7 +184,7 @@ var _ = utils.SIGDescribe("Mounted flexvolume expand[Slow]", func() {
|
|||||||
framework.ExpectNoError(err, "While waiting for pod to be recreated")
|
framework.ExpectNoError(err, "While waiting for pod to be recreated")
|
||||||
|
|
||||||
ginkgo.By("Waiting for file system resize to finish")
|
ginkgo.By("Waiting for file system resize to finish")
|
||||||
pvc, err = waitForFSResize(pvc, c)
|
pvc, err = testsuites.WaitForFSResize(pvc, c)
|
||||||
framework.ExpectNoError(err, "while waiting for fs resize to finish")
|
framework.ExpectNoError(err, "while waiting for fs resize to finish")
|
||||||
|
|
||||||
pvcConditions := pvc.Status.Conditions
|
pvcConditions := pvc.Status.Conditions
|
||||||
|
@ -81,7 +81,7 @@ var _ = utils.SIGDescribe("Mounted flexvolume volume expand [Slow] [Feature:Expa
|
|||||||
Provisioner: "flex-expand",
|
Provisioner: "flex-expand",
|
||||||
}
|
}
|
||||||
|
|
||||||
resizableSc, err = createStorageClass(test, ns, "resizing", c)
|
resizableSc, err = c.StorageV1().StorageClasses().Create(newStorageClass(test, ns, "resizing"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("storage class creation error: %v\n", err)
|
fmt.Printf("storage class creation error: %v\n", err)
|
||||||
}
|
}
|
||||||
@ -155,7 +155,7 @@ var _ = utils.SIGDescribe("Mounted flexvolume volume expand [Slow] [Feature:Expa
|
|||||||
|
|
||||||
ginkgo.By("Expanding current pvc")
|
ginkgo.By("Expanding current pvc")
|
||||||
newSize := resource.MustParse("6Gi")
|
newSize := resource.MustParse("6Gi")
|
||||||
pvc, err = expandPVCSize(pvc, newSize, c)
|
pvc, err = testsuites.ExpandPVCSize(pvc, newSize, c)
|
||||||
framework.ExpectNoError(err, "While updating pvc for more size")
|
framework.ExpectNoError(err, "While updating pvc for more size")
|
||||||
gomega.Expect(pvc).NotTo(gomega.BeNil())
|
gomega.Expect(pvc).NotTo(gomega.BeNil())
|
||||||
|
|
||||||
@ -165,11 +165,11 @@ var _ = utils.SIGDescribe("Mounted flexvolume volume expand [Slow] [Feature:Expa
|
|||||||
}
|
}
|
||||||
|
|
||||||
ginkgo.By("Waiting for cloudprovider resize to finish")
|
ginkgo.By("Waiting for cloudprovider resize to finish")
|
||||||
err = waitForControllerVolumeResize(pvc, c, totalResizeWaitPeriod)
|
err = testsuites.WaitForControllerVolumeResize(pvc, c, totalResizeWaitPeriod)
|
||||||
framework.ExpectNoError(err, "While waiting for pvc resize to finish")
|
framework.ExpectNoError(err, "While waiting for pvc resize to finish")
|
||||||
|
|
||||||
ginkgo.By("Waiting for file system resize to finish")
|
ginkgo.By("Waiting for file system resize to finish")
|
||||||
pvc, err = waitForFSResize(pvc, c)
|
pvc, err = testsuites.WaitForFSResize(pvc, c)
|
||||||
framework.ExpectNoError(err, "while waiting for fs resize to finish")
|
framework.ExpectNoError(err, "while waiting for fs resize to finish")
|
||||||
|
|
||||||
pvcConditions := pvc.Status.Conditions
|
pvcConditions := pvc.Status.Conditions
|
||||||
|
@ -56,6 +56,7 @@ var testSuites = []func() testsuites.TestSuite{
|
|||||||
testsuites.InitSubPathTestSuite,
|
testsuites.InitSubPathTestSuite,
|
||||||
testsuites.InitProvisioningTestSuite,
|
testsuites.InitProvisioningTestSuite,
|
||||||
testsuites.InitMultiVolumeTestSuite,
|
testsuites.InitMultiVolumeTestSuite,
|
||||||
|
testsuites.InitVolumeExpandTestSuite,
|
||||||
}
|
}
|
||||||
|
|
||||||
// This executes testSuites for in-tree volumes.
|
// This executes testSuites for in-tree volumes.
|
||||||
|
@ -81,7 +81,7 @@ var _ = utils.SIGDescribe("Mounted volume expand", func() {
|
|||||||
AllowVolumeExpansion: true,
|
AllowVolumeExpansion: true,
|
||||||
DelayBinding: true,
|
DelayBinding: true,
|
||||||
}
|
}
|
||||||
resizableSc, err = createStorageClass(test, ns, "resizing", c)
|
resizableSc, err = c.StorageV1().StorageClasses().Create(newStorageClass(test, ns, "resizing"))
|
||||||
framework.ExpectNoError(err, "Error creating resizable storage class")
|
framework.ExpectNoError(err, "Error creating resizable storage class")
|
||||||
gomega.Expect(*resizableSc.AllowVolumeExpansion).To(gomega.BeTrue())
|
gomega.Expect(*resizableSc.AllowVolumeExpansion).To(gomega.BeTrue())
|
||||||
|
|
||||||
@ -128,7 +128,7 @@ var _ = utils.SIGDescribe("Mounted volume expand", func() {
|
|||||||
|
|
||||||
ginkgo.By("Expanding current pvc")
|
ginkgo.By("Expanding current pvc")
|
||||||
newSize := resource.MustParse("6Gi")
|
newSize := resource.MustParse("6Gi")
|
||||||
pvc, err = expandPVCSize(pvc, newSize, c)
|
pvc, err = testsuites.ExpandPVCSize(pvc, newSize, c)
|
||||||
framework.ExpectNoError(err, "While updating pvc for more size")
|
framework.ExpectNoError(err, "While updating pvc for more size")
|
||||||
gomega.Expect(pvc).NotTo(gomega.BeNil())
|
gomega.Expect(pvc).NotTo(gomega.BeNil())
|
||||||
|
|
||||||
@ -138,7 +138,7 @@ var _ = utils.SIGDescribe("Mounted volume expand", func() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ginkgo.By("Waiting for cloudprovider resize to finish")
|
ginkgo.By("Waiting for cloudprovider resize to finish")
|
||||||
err = waitForControllerVolumeResize(pvc, c, totalResizeWaitPeriod)
|
err = testsuites.WaitForControllerVolumeResize(pvc, c, totalResizeWaitPeriod)
|
||||||
framework.ExpectNoError(err, "While waiting for pvc resize to finish")
|
framework.ExpectNoError(err, "While waiting for pvc resize to finish")
|
||||||
|
|
||||||
ginkgo.By("Getting a pod from deployment")
|
ginkgo.By("Getting a pod from deployment")
|
||||||
@ -155,7 +155,7 @@ var _ = utils.SIGDescribe("Mounted volume expand", func() {
|
|||||||
framework.ExpectNoError(err, "While waiting for pod to be recreated")
|
framework.ExpectNoError(err, "While waiting for pod to be recreated")
|
||||||
|
|
||||||
ginkgo.By("Waiting for file system resize to finish")
|
ginkgo.By("Waiting for file system resize to finish")
|
||||||
pvc, err = waitForFSResize(pvc, c)
|
pvc, err = testsuites.WaitForFSResize(pvc, c)
|
||||||
framework.ExpectNoError(err, "while waiting for fs resize to finish")
|
framework.ExpectNoError(err, "while waiting for fs resize to finish")
|
||||||
|
|
||||||
pvcConditions := pvc.Status.Conditions
|
pvcConditions := pvc.Status.Conditions
|
||||||
|
@ -7,6 +7,7 @@ go_library(
|
|||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [
|
deps = [
|
||||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||||
|
"//staging/src/k8s.io/api/storage/v1:go_default_library",
|
||||||
"//test/e2e/framework/volume:go_default_library",
|
"//test/e2e/framework/volume:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -18,6 +18,7 @@ package testpatterns
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"k8s.io/api/core/v1"
|
"k8s.io/api/core/v1"
|
||||||
|
storagev1 "k8s.io/api/storage/v1"
|
||||||
"k8s.io/kubernetes/test/e2e/framework/volume"
|
"k8s.io/kubernetes/test/e2e/framework/volume"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -61,6 +62,8 @@ type TestPattern struct {
|
|||||||
FsType string // Fstype of the volume
|
FsType string // Fstype of the volume
|
||||||
VolMode v1.PersistentVolumeMode // PersistentVolumeMode of the volume
|
VolMode v1.PersistentVolumeMode // PersistentVolumeMode of the volume
|
||||||
SnapshotType TestSnapshotType // Snapshot type of the snapshot
|
SnapshotType TestSnapshotType // Snapshot type of the snapshot
|
||||||
|
BindingMode storagev1.VolumeBindingMode // VolumeBindingMode of the volume
|
||||||
|
AllowExpansion bool // AllowVolumeExpansion flag of the StorageClass
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -195,7 +198,7 @@ var (
|
|||||||
VolType: PreprovisionedPV,
|
VolType: PreprovisionedPV,
|
||||||
VolMode: v1.PersistentVolumeBlock,
|
VolMode: v1.PersistentVolumeBlock,
|
||||||
}
|
}
|
||||||
// BlockVolModeDynamicPV is TestPattern for "Dynamic PV (block)(immediate bind)"
|
// BlockVolModeDynamicPV is TestPattern for "Dynamic PV (block)"
|
||||||
BlockVolModeDynamicPV = TestPattern{
|
BlockVolModeDynamicPV = TestPattern{
|
||||||
Name: "Dynamic PV (block volmode)",
|
Name: "Dynamic PV (block volmode)",
|
||||||
VolType: DynamicPV,
|
VolType: DynamicPV,
|
||||||
@ -209,4 +212,22 @@ var (
|
|||||||
Name: "Dynamic Snapshot",
|
Name: "Dynamic Snapshot",
|
||||||
SnapshotType: DynamicCreatedSnapshot,
|
SnapshotType: DynamicCreatedSnapshot,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Definitions for volume expansion case
|
||||||
|
|
||||||
|
// DefaultFsDynamicPVAllowExpansion is TestPattern for "Dynamic PV (default fs)(allowExpansion)"
|
||||||
|
DefaultFsDynamicPVAllowExpansion = TestPattern{
|
||||||
|
Name: "Dynamic PV (default fs)(allowExpansion)",
|
||||||
|
VolType: DynamicPV,
|
||||||
|
BindingMode: storagev1.VolumeBindingWaitForFirstConsumer,
|
||||||
|
AllowExpansion: true,
|
||||||
|
}
|
||||||
|
// BlockVolModeDynamicPVAllowExpansion is TestPattern for "Dynamic PV (block volmode)(allowExpansion)"
|
||||||
|
BlockVolModeDynamicPVAllowExpansion = TestPattern{
|
||||||
|
Name: "Dynamic PV (block volmode)(allowExpansion)",
|
||||||
|
VolType: DynamicPV,
|
||||||
|
VolMode: v1.PersistentVolumeBlock,
|
||||||
|
BindingMode: storagev1.VolumeBindingWaitForFirstConsumer,
|
||||||
|
AllowExpansion: true,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
@ -10,6 +10,7 @@ go_library(
|
|||||||
"snapshottable.go",
|
"snapshottable.go",
|
||||||
"subpath.go",
|
"subpath.go",
|
||||||
"testdriver.go",
|
"testdriver.go",
|
||||||
|
"volume_expand.go",
|
||||||
"volume_io.go",
|
"volume_io.go",
|
||||||
"volumemode.go",
|
"volumemode.go",
|
||||||
"volumes.go",
|
"volumes.go",
|
||||||
|
@ -186,23 +186,21 @@ func createGenericVolumeTestResource(driver TestDriver, config *PerTestConfig, p
|
|||||||
dInfo := driver.GetDriverInfo()
|
dInfo := driver.GetDriverInfo()
|
||||||
f := config.Framework
|
f := config.Framework
|
||||||
cs := f.ClientSet
|
cs := f.ClientSet
|
||||||
fsType := pattern.FsType
|
|
||||||
volType := pattern.VolType
|
|
||||||
|
|
||||||
// Create volume for pre-provisioned volume tests
|
// Create volume for pre-provisioned volume tests
|
||||||
r.volume = CreateVolume(driver, config, volType)
|
r.volume = CreateVolume(driver, config, pattern.VolType)
|
||||||
|
|
||||||
switch volType {
|
switch pattern.VolType {
|
||||||
case testpatterns.InlineVolume:
|
case testpatterns.InlineVolume:
|
||||||
e2elog.Logf("Creating resource for inline volume")
|
e2elog.Logf("Creating resource for inline volume")
|
||||||
if iDriver, ok := driver.(InlineVolumeTestDriver); ok {
|
if iDriver, ok := driver.(InlineVolumeTestDriver); ok {
|
||||||
r.volSource = iDriver.GetVolumeSource(false, fsType, r.volume)
|
r.volSource = iDriver.GetVolumeSource(false, pattern.FsType, r.volume)
|
||||||
r.volType = dInfo.Name
|
r.volType = dInfo.Name
|
||||||
}
|
}
|
||||||
case testpatterns.PreprovisionedPV:
|
case testpatterns.PreprovisionedPV:
|
||||||
e2elog.Logf("Creating resource for pre-provisioned PV")
|
e2elog.Logf("Creating resource for pre-provisioned PV")
|
||||||
if pDriver, ok := driver.(PreprovisionedPVTestDriver); ok {
|
if pDriver, ok := driver.(PreprovisionedPVTestDriver); ok {
|
||||||
pvSource, volumeNodeAffinity := pDriver.GetPersistentVolumeSource(false, fsType, r.volume)
|
pvSource, volumeNodeAffinity := pDriver.GetPersistentVolumeSource(false, pattern.FsType, r.volume)
|
||||||
if pvSource != nil {
|
if pvSource != nil {
|
||||||
r.volSource, r.pv, r.pvc = createVolumeSourceWithPVCPV(f, dInfo.Name, pvSource, volumeNodeAffinity, false, pattern.VolMode)
|
r.volSource, r.pv, r.pvc = createVolumeSourceWithPVCPV(f, dInfo.Name, pvSource, volumeNodeAffinity, false, pattern.VolMode)
|
||||||
}
|
}
|
||||||
@ -212,7 +210,14 @@ func createGenericVolumeTestResource(driver TestDriver, config *PerTestConfig, p
|
|||||||
e2elog.Logf("Creating resource for dynamic PV")
|
e2elog.Logf("Creating resource for dynamic PV")
|
||||||
if dDriver, ok := driver.(DynamicPVTestDriver); ok {
|
if dDriver, ok := driver.(DynamicPVTestDriver); ok {
|
||||||
claimSize := dDriver.GetClaimSize()
|
claimSize := dDriver.GetClaimSize()
|
||||||
r.sc = dDriver.GetDynamicProvisionStorageClass(r.config, fsType)
|
r.sc = dDriver.GetDynamicProvisionStorageClass(r.config, pattern.FsType)
|
||||||
|
|
||||||
|
if pattern.BindingMode != "" {
|
||||||
|
r.sc.VolumeBindingMode = &pattern.BindingMode
|
||||||
|
}
|
||||||
|
if pattern.AllowExpansion != false {
|
||||||
|
r.sc.AllowVolumeExpansion = &pattern.AllowExpansion
|
||||||
|
}
|
||||||
|
|
||||||
ginkgo.By("creating a StorageClass " + r.sc.Name)
|
ginkgo.By("creating a StorageClass " + r.sc.Name)
|
||||||
var err error
|
var err error
|
||||||
@ -226,11 +231,11 @@ func createGenericVolumeTestResource(driver TestDriver, config *PerTestConfig, p
|
|||||||
r.volType = fmt.Sprintf("%s-dynamicPV", dInfo.Name)
|
r.volType = fmt.Sprintf("%s-dynamicPV", dInfo.Name)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
e2elog.Failf("genericVolumeTestResource doesn't support: %s", volType)
|
e2elog.Failf("genericVolumeTestResource doesn't support: %s", pattern.VolType)
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.volSource == nil {
|
if r.volSource == nil {
|
||||||
framework.Skipf("Driver %s doesn't support %v -- skipping", dInfo.Name, volType)
|
framework.Skipf("Driver %s doesn't support %v -- skipping", dInfo.Name, pattern.VolType)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &r
|
return &r
|
||||||
@ -239,10 +244,9 @@ func createGenericVolumeTestResource(driver TestDriver, config *PerTestConfig, p
|
|||||||
// cleanupResource cleans up genericVolumeTestResource
|
// cleanupResource cleans up genericVolumeTestResource
|
||||||
func (r *genericVolumeTestResource) cleanupResource() {
|
func (r *genericVolumeTestResource) cleanupResource() {
|
||||||
f := r.config.Framework
|
f := r.config.Framework
|
||||||
volType := r.pattern.VolType
|
|
||||||
|
|
||||||
if r.pvc != nil || r.pv != nil {
|
if r.pvc != nil || r.pv != nil {
|
||||||
switch volType {
|
switch r.pattern.VolType {
|
||||||
case testpatterns.PreprovisionedPV:
|
case testpatterns.PreprovisionedPV:
|
||||||
ginkgo.By("Deleting pv and pvc")
|
ginkgo.By("Deleting pv and pvc")
|
||||||
if errs := framework.PVPVCCleanup(f.ClientSet, f.Namespace.Name, r.pv, r.pvc); len(errs) != 0 {
|
if errs := framework.PVPVCCleanup(f.ClientSet, f.Namespace.Name, r.pv, r.pvc); len(errs) != 0 {
|
||||||
|
@ -128,6 +128,8 @@ const (
|
|||||||
CapMultiPODs Capability = "multipods"
|
CapMultiPODs Capability = "multipods"
|
||||||
|
|
||||||
CapRWX Capability = "RWX" // support ReadWriteMany access modes
|
CapRWX Capability = "RWX" // support ReadWriteMany access modes
|
||||||
|
CapControllerExpansion Capability = "controllerExpansion" // support volume expansion for controller
|
||||||
|
CapNodeExpansion Capability = "nodeExpansion" // support volume expansion for node
|
||||||
)
|
)
|
||||||
|
|
||||||
// DriverInfo represents static information about a TestDriver.
|
// DriverInfo represents static information about a TestDriver.
|
||||||
|
344
test/e2e/storage/testsuites/volume_expand.go
Normal file
344
test/e2e/storage/testsuites/volume_expand.go
Normal file
@ -0,0 +1,344 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2017 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"
|
||||||
|
|
||||||
|
v1 "k8s.io/api/core/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
|
"k8s.io/kubernetes/test/e2e/framework"
|
||||||
|
e2elog "k8s.io/kubernetes/test/e2e/framework/log"
|
||||||
|
"k8s.io/kubernetes/test/e2e/storage/testpatterns"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
resizePollInterval = 2 * time.Second
|
||||||
|
// total time to wait for cloudprovider or file system resize to finish
|
||||||
|
totalResizeWaitPeriod = 10 * time.Minute
|
||||||
|
)
|
||||||
|
|
||||||
|
type volumeExpandTestSuite struct {
|
||||||
|
tsInfo TestSuiteInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ TestSuite = &volumeExpandTestSuite{}
|
||||||
|
|
||||||
|
// InitVolumeExpandTestSuite returns volumeExpandTestSuite that implements TestSuite interface
|
||||||
|
func InitVolumeExpandTestSuite() TestSuite {
|
||||||
|
return &volumeExpandTestSuite{
|
||||||
|
tsInfo: TestSuiteInfo{
|
||||||
|
name: "volume-expand",
|
||||||
|
testPatterns: []testpatterns.TestPattern{
|
||||||
|
testpatterns.DefaultFsDynamicPV,
|
||||||
|
testpatterns.BlockVolModeDynamicPV,
|
||||||
|
testpatterns.DefaultFsDynamicPVAllowExpansion,
|
||||||
|
testpatterns.BlockVolModeDynamicPVAllowExpansion,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *volumeExpandTestSuite) getTestSuiteInfo() TestSuiteInfo {
|
||||||
|
return v.tsInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *volumeExpandTestSuite) defineTests(driver TestDriver, pattern testpatterns.TestPattern) {
|
||||||
|
type local struct {
|
||||||
|
config *PerTestConfig
|
||||||
|
testCleanup func()
|
||||||
|
|
||||||
|
resource *genericVolumeTestResource
|
||||||
|
pod *v1.Pod
|
||||||
|
pod2 *v1.Pod
|
||||||
|
|
||||||
|
intreeOps opCounts
|
||||||
|
migratedOps opCounts
|
||||||
|
}
|
||||||
|
var l local
|
||||||
|
|
||||||
|
ginkgo.BeforeEach(func() {
|
||||||
|
// Check preconditions.
|
||||||
|
if !driver.GetDriverInfo().Capabilities[CapBlock] && pattern.VolMode == v1.PersistentVolumeBlock {
|
||||||
|
framework.Skipf("Driver %q does not support block volume mode - skipping", driver.GetDriverInfo().Name)
|
||||||
|
}
|
||||||
|
if !driver.GetDriverInfo().Capabilities[CapControllerExpansion] {
|
||||||
|
framework.Skipf("Driver %q does not support volume expansion - skipping", driver.GetDriverInfo().Name)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// This intentionally comes after checking the preconditions because it
|
||||||
|
// registers its own BeforeEach which creates the namespace. Beware that it
|
||||||
|
// also registers an AfterEach which renders f unusable. Any code using
|
||||||
|
// f must run inside an It or Context callback.
|
||||||
|
f := framework.NewDefaultFramework("volume-expand")
|
||||||
|
|
||||||
|
init := func() {
|
||||||
|
l = local{}
|
||||||
|
|
||||||
|
// Now do the more expensive test initialization.
|
||||||
|
l.config, l.testCleanup = driver.PrepareTest(f)
|
||||||
|
l.intreeOps, l.migratedOps = getMigrationVolumeOpCounts(f.ClientSet, driver.GetDriverInfo().InTreePluginName)
|
||||||
|
l.resource = createGenericVolumeTestResource(driver, l.config, pattern)
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup := func() {
|
||||||
|
if l.pod != nil {
|
||||||
|
ginkgo.By("Deleting pod")
|
||||||
|
err := framework.DeletePodWithWait(f, f.ClientSet, l.pod)
|
||||||
|
framework.ExpectNoError(err, "while deleting pod")
|
||||||
|
l.pod = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.pod2 != nil {
|
||||||
|
ginkgo.By("Deleting pod2")
|
||||||
|
err := framework.DeletePodWithWait(f, f.ClientSet, l.pod2)
|
||||||
|
framework.ExpectNoError(err, "while deleting pod2")
|
||||||
|
l.pod2 = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.resource != nil {
|
||||||
|
l.resource.cleanupResource()
|
||||||
|
l.resource = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.testCleanup != nil {
|
||||||
|
l.testCleanup()
|
||||||
|
l.testCleanup = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
validateMigrationVolumeOpCounts(f.ClientSet, driver.GetDriverInfo().InTreePluginName, l.intreeOps, l.migratedOps)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !pattern.AllowExpansion {
|
||||||
|
ginkgo.It("should not allow expansion of pvcs without AllowVolumeExpansion property", func() {
|
||||||
|
init()
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
var err error
|
||||||
|
gomega.Expect(l.resource.sc.AllowVolumeExpansion).To(gomega.BeNil())
|
||||||
|
ginkgo.By("Expanding non-expandable pvc")
|
||||||
|
currentPvcSize := l.resource.pvc.Spec.Resources.Requests[v1.ResourceStorage]
|
||||||
|
newSize := currentPvcSize.DeepCopy()
|
||||||
|
newSize.Add(resource.MustParse("1Gi"))
|
||||||
|
e2elog.Logf("currentPvcSize %v, newSize %v", currentPvcSize, newSize)
|
||||||
|
l.resource.pvc, err = ExpandPVCSize(l.resource.pvc, newSize, f.ClientSet)
|
||||||
|
framework.ExpectError(err, "While updating non-expandable PVC")
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
ginkgo.It("Verify if offline PVC expansion works", func() {
|
||||||
|
init()
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
var err error
|
||||||
|
ginkgo.By("Creating a pod with dynamically provisioned volume")
|
||||||
|
l.pod, err = framework.CreateSecPodWithNodeSelection(f.ClientSet, f.Namespace.Name, []*v1.PersistentVolumeClaim{l.resource.pvc}, false, "", false, false, framework.SELinuxLabel, nil, framework.NodeSelection{Name: l.config.ClientNodeName}, framework.PodStartTimeout)
|
||||||
|
defer func() {
|
||||||
|
err = framework.DeletePodWithWait(f, f.ClientSet, l.pod)
|
||||||
|
framework.ExpectNoError(err, "while cleaning up pod already deleted in resize test")
|
||||||
|
}()
|
||||||
|
framework.ExpectNoError(err, "While creating pods for resizing")
|
||||||
|
|
||||||
|
ginkgo.By("Deleting the previously created pod")
|
||||||
|
err = framework.DeletePodWithWait(f, f.ClientSet, l.pod)
|
||||||
|
framework.ExpectNoError(err, "while deleting pod for resizing")
|
||||||
|
|
||||||
|
// We expand the PVC while no pod is using it to ensure offline expansion
|
||||||
|
ginkgo.By("Expanding current pvc")
|
||||||
|
currentPvcSize := l.resource.pvc.Spec.Resources.Requests[v1.ResourceStorage]
|
||||||
|
newSize := currentPvcSize.DeepCopy()
|
||||||
|
newSize.Add(resource.MustParse("1Gi"))
|
||||||
|
e2elog.Logf("currentPvcSize %v, newSize %v", currentPvcSize, newSize)
|
||||||
|
l.resource.pvc, err = ExpandPVCSize(l.resource.pvc, newSize, f.ClientSet)
|
||||||
|
framework.ExpectNoError(err, "While updating pvc for more size")
|
||||||
|
gomega.Expect(l.resource.pvc).NotTo(gomega.BeNil())
|
||||||
|
|
||||||
|
pvcSize := l.resource.pvc.Spec.Resources.Requests[v1.ResourceStorage]
|
||||||
|
if pvcSize.Cmp(newSize) != 0 {
|
||||||
|
e2elog.Failf("error updating pvc size %q", l.resource.pvc.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
ginkgo.By("Waiting for cloudprovider resize to finish")
|
||||||
|
err = WaitForControllerVolumeResize(l.resource.pvc, f.ClientSet, totalResizeWaitPeriod)
|
||||||
|
framework.ExpectNoError(err, "While waiting for pvc resize to finish")
|
||||||
|
|
||||||
|
ginkgo.By("Checking for conditions on pvc")
|
||||||
|
l.resource.pvc, err = f.ClientSet.CoreV1().PersistentVolumeClaims(f.Namespace.Name).Get(l.resource.pvc.Name, metav1.GetOptions{})
|
||||||
|
framework.ExpectNoError(err, "While fetching pvc after controller resize")
|
||||||
|
|
||||||
|
if pattern.VolMode == v1.PersistentVolumeBlock || !l.resource.driver.GetDriverInfo().Capabilities[CapNodeExpansion] {
|
||||||
|
pvcConditions := l.resource.pvc.Status.Conditions
|
||||||
|
framework.ExpectEqual(len(pvcConditions), 0, "pvc should not have conditions")
|
||||||
|
} else {
|
||||||
|
inProgressConditions := l.resource.pvc.Status.Conditions
|
||||||
|
framework.ExpectEqual(len(inProgressConditions), 1, "pvc must have file system resize pending condition")
|
||||||
|
framework.ExpectEqual(inProgressConditions[0].Type, v1.PersistentVolumeClaimFileSystemResizePending, "pvc must have fs resizing condition")
|
||||||
|
}
|
||||||
|
|
||||||
|
ginkgo.By("Creating a new pod with same volume")
|
||||||
|
l.pod2, err = framework.CreateSecPodWithNodeSelection(f.ClientSet, f.Namespace.Name, []*v1.PersistentVolumeClaim{l.resource.pvc}, false, "", false, false, framework.SELinuxLabel, nil, framework.NodeSelection{Name: l.config.ClientNodeName}, framework.PodStartTimeout)
|
||||||
|
defer func() {
|
||||||
|
err = framework.DeletePodWithWait(f, f.ClientSet, l.pod2)
|
||||||
|
framework.ExpectNoError(err, "while cleaning up pod before exiting resizing test")
|
||||||
|
}()
|
||||||
|
framework.ExpectNoError(err, "while recreating pod for resizing")
|
||||||
|
|
||||||
|
ginkgo.By("Waiting for file system resize to finish")
|
||||||
|
l.resource.pvc, err = WaitForFSResize(l.resource.pvc, f.ClientSet)
|
||||||
|
framework.ExpectNoError(err, "while waiting for fs resize to finish")
|
||||||
|
|
||||||
|
pvcConditions := l.resource.pvc.Status.Conditions
|
||||||
|
framework.ExpectEqual(len(pvcConditions), 0, "pvc should not have conditions")
|
||||||
|
})
|
||||||
|
|
||||||
|
ginkgo.It("should resize volume when PVC is edited while pod is using it", func() {
|
||||||
|
init()
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
var err error
|
||||||
|
ginkgo.By("Creating a pod with dynamically provisioned volume")
|
||||||
|
l.pod, err = framework.CreateSecPodWithNodeSelection(f.ClientSet, f.Namespace.Name, []*v1.PersistentVolumeClaim{l.resource.pvc}, false, "", false, false, framework.SELinuxLabel, nil, framework.NodeSelection{Name: l.config.ClientNodeName}, framework.PodStartTimeout)
|
||||||
|
defer func() {
|
||||||
|
err = framework.DeletePodWithWait(f, f.ClientSet, l.pod)
|
||||||
|
framework.ExpectNoError(err, "while cleaning up pod already deleted in resize test")
|
||||||
|
}()
|
||||||
|
framework.ExpectNoError(err, "While creating pods for resizing")
|
||||||
|
|
||||||
|
// We expand the PVC while no pod is using it to ensure offline expansion
|
||||||
|
ginkgo.By("Expanding current pvc")
|
||||||
|
currentPvcSize := l.resource.pvc.Spec.Resources.Requests[v1.ResourceStorage]
|
||||||
|
newSize := currentPvcSize.DeepCopy()
|
||||||
|
newSize.Add(resource.MustParse("1Gi"))
|
||||||
|
e2elog.Logf("currentPvcSize %v, newSize %v", currentPvcSize, newSize)
|
||||||
|
l.resource.pvc, err = ExpandPVCSize(l.resource.pvc, newSize, f.ClientSet)
|
||||||
|
framework.ExpectNoError(err, "While updating pvc for more size")
|
||||||
|
gomega.Expect(l.resource.pvc).NotTo(gomega.BeNil())
|
||||||
|
|
||||||
|
pvcSize := l.resource.pvc.Spec.Resources.Requests[v1.ResourceStorage]
|
||||||
|
if pvcSize.Cmp(newSize) != 0 {
|
||||||
|
e2elog.Failf("error updating pvc size %q", l.resource.pvc.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
ginkgo.By("Waiting for cloudprovider resize to finish")
|
||||||
|
err = WaitForControllerVolumeResize(l.resource.pvc, f.ClientSet, totalResizeWaitPeriod)
|
||||||
|
framework.ExpectNoError(err, "While waiting for pvc resize to finish")
|
||||||
|
|
||||||
|
ginkgo.By("Waiting for file system resize to finish")
|
||||||
|
l.resource.pvc, err = WaitForFSResize(l.resource.pvc, f.ClientSet)
|
||||||
|
framework.ExpectNoError(err, "while waiting for fs resize to finish")
|
||||||
|
|
||||||
|
pvcConditions := l.resource.pvc.Status.Conditions
|
||||||
|
framework.ExpectEqual(len(pvcConditions), 0, "pvc should not have conditions")
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExpandPVCSize expands PVC size
|
||||||
|
func ExpandPVCSize(origPVC *v1.PersistentVolumeClaim, size resource.Quantity, c clientset.Interface) (*v1.PersistentVolumeClaim, error) {
|
||||||
|
pvcName := origPVC.Name
|
||||||
|
updatedPVC := origPVC.DeepCopy()
|
||||||
|
|
||||||
|
waitErr := wait.PollImmediate(resizePollInterval, 30*time.Second, func() (bool, error) {
|
||||||
|
var err error
|
||||||
|
updatedPVC, err = c.CoreV1().PersistentVolumeClaims(origPVC.Namespace).Get(pvcName, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("error fetching pvc %q for resizing with %v", pvcName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
updatedPVC.Spec.Resources.Requests[v1.ResourceStorage] = size
|
||||||
|
updatedPVC, err = c.CoreV1().PersistentVolumeClaims(origPVC.Namespace).Update(updatedPVC)
|
||||||
|
if err == nil {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
e2elog.Logf("Error updating pvc %s with %v", pvcName, err)
|
||||||
|
return false, nil
|
||||||
|
})
|
||||||
|
return updatedPVC, waitErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// WaitForResizingCondition waits for the pvc condition to be PersistentVolumeClaimResizing
|
||||||
|
func WaitForResizingCondition(pvc *v1.PersistentVolumeClaim, c clientset.Interface, duration time.Duration) error {
|
||||||
|
waitErr := wait.PollImmediate(resizePollInterval, duration, func() (bool, error) {
|
||||||
|
var err error
|
||||||
|
updatedPVC, err := c.CoreV1().PersistentVolumeClaims(pvc.Namespace).Get(pvc.Name, metav1.GetOptions{})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("error fetching pvc %q for checking for resize status : %v", pvc.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pvcConditions := updatedPVC.Status.Conditions
|
||||||
|
if len(pvcConditions) > 0 {
|
||||||
|
if pvcConditions[0].Type == v1.PersistentVolumeClaimResizing {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
})
|
||||||
|
return waitErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// WaitForControllerVolumeResize waits for the controller resize to be finished
|
||||||
|
func WaitForControllerVolumeResize(pvc *v1.PersistentVolumeClaim, c clientset.Interface, duration time.Duration) error {
|
||||||
|
pvName := pvc.Spec.VolumeName
|
||||||
|
return wait.PollImmediate(resizePollInterval, duration, func() (bool, error) {
|
||||||
|
pvcSize := pvc.Spec.Resources.Requests[v1.ResourceStorage]
|
||||||
|
|
||||||
|
pv, err := c.CoreV1().PersistentVolumes().Get(pvName, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("error fetching pv %q for resizing %v", pvName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pvSize := pv.Spec.Capacity[v1.ResourceStorage]
|
||||||
|
|
||||||
|
// If pv size is greater or equal to requested size that means controller resize is finished.
|
||||||
|
if pvSize.Cmp(pvcSize) >= 0 {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WaitForFSResize waits for the filesystem in the pv to be resized
|
||||||
|
func WaitForFSResize(pvc *v1.PersistentVolumeClaim, c clientset.Interface) (*v1.PersistentVolumeClaim, error) {
|
||||||
|
var updatedPVC *v1.PersistentVolumeClaim
|
||||||
|
waitErr := wait.PollImmediate(resizePollInterval, totalResizeWaitPeriod, func() (bool, error) {
|
||||||
|
var err error
|
||||||
|
updatedPVC, err = c.CoreV1().PersistentVolumeClaims(pvc.Namespace).Get(pvc.Name, metav1.GetOptions{})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("error fetching pvc %q for checking for resize status : %v", pvc.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pvcSize := updatedPVC.Spec.Resources.Requests[v1.ResourceStorage]
|
||||||
|
pvcStatusSize := updatedPVC.Status.Capacity[v1.ResourceStorage]
|
||||||
|
|
||||||
|
//If pvc's status field size is greater than or equal to pvc's size then done
|
||||||
|
if pvcStatusSize.Cmp(pvcSize) >= 0 {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
})
|
||||||
|
return updatedPVC, waitErr
|
||||||
|
}
|
@ -1,334 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2017 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 storage
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/onsi/ginkgo"
|
|
||||||
"github.com/onsi/gomega"
|
|
||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
|
||||||
storagev1 "k8s.io/api/storage/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
|
||||||
"k8s.io/kubernetes/test/e2e/framework"
|
|
||||||
e2elog "k8s.io/kubernetes/test/e2e/framework/log"
|
|
||||||
"k8s.io/kubernetes/test/e2e/storage/testsuites"
|
|
||||||
"k8s.io/kubernetes/test/e2e/storage/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
resizePollInterval = 2 * time.Second
|
|
||||||
// total time to wait for cloudprovider or file system resize to finish
|
|
||||||
totalResizeWaitPeriod = 20 * time.Minute
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ = utils.SIGDescribe("Volume expand", func() {
|
|
||||||
var (
|
|
||||||
c clientset.Interface
|
|
||||||
ns string
|
|
||||||
err error
|
|
||||||
pvc *v1.PersistentVolumeClaim
|
|
||||||
storageClassVar *storagev1.StorageClass
|
|
||||||
)
|
|
||||||
|
|
||||||
f := framework.NewDefaultFramework("volume-expand")
|
|
||||||
ginkgo.BeforeEach(func() {
|
|
||||||
framework.SkipUnlessProviderIs("aws", "gce")
|
|
||||||
c = f.ClientSet
|
|
||||||
ns = f.Namespace.Name
|
|
||||||
framework.ExpectNoError(framework.WaitForAllNodesSchedulable(c, framework.TestContext.NodeSchedulableTimeout))
|
|
||||||
})
|
|
||||||
|
|
||||||
setupFunc := func(allowExpansion bool, blockVolume bool) (*v1.PersistentVolumeClaim, *storagev1.StorageClass, error) {
|
|
||||||
test := testsuites.StorageClassTest{
|
|
||||||
Name: "default",
|
|
||||||
ClaimSize: "2Gi",
|
|
||||||
}
|
|
||||||
if allowExpansion {
|
|
||||||
test.AllowVolumeExpansion = true
|
|
||||||
}
|
|
||||||
if blockVolume {
|
|
||||||
test.VolumeMode = v1.PersistentVolumeBlock
|
|
||||||
}
|
|
||||||
|
|
||||||
sc, err := createStorageClass(test, ns, "resizing", c)
|
|
||||||
framework.ExpectNoError(err, "Error creating storage class for resizing")
|
|
||||||
|
|
||||||
tPVC := newClaim(test, ns, "default")
|
|
||||||
tPVC.Spec.StorageClassName = &sc.Name
|
|
||||||
tPVC, err = c.CoreV1().PersistentVolumeClaims(tPVC.Namespace).Create(tPVC)
|
|
||||||
if err != nil {
|
|
||||||
return nil, sc, err
|
|
||||||
}
|
|
||||||
return tPVC, sc, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
ginkgo.AfterEach(func() {
|
|
||||||
framework.ExpectNoError(framework.DeletePersistentVolumeClaim(c, pvc.Name, pvc.Namespace))
|
|
||||||
framework.ExpectNoError(c.StorageV1().StorageClasses().Delete(storageClassVar.Name, nil))
|
|
||||||
})
|
|
||||||
|
|
||||||
ginkgo.It("should not allow expansion of pvcs without AllowVolumeExpansion property", func() {
|
|
||||||
pvc, storageClassVar, err = setupFunc(false /* allowExpansion */, false /*BlockVolume*/)
|
|
||||||
framework.ExpectNoError(err, "Error creating non-expandable PVC")
|
|
||||||
|
|
||||||
gomega.Expect(storageClassVar.AllowVolumeExpansion).To(gomega.BeNil())
|
|
||||||
|
|
||||||
pvcClaims := []*v1.PersistentVolumeClaim{pvc}
|
|
||||||
pvs, err := framework.WaitForPVClaimBoundPhase(c, pvcClaims, framework.ClaimProvisionTimeout)
|
|
||||||
framework.ExpectNoError(err, "Failed waiting for PVC to be bound %v", err)
|
|
||||||
framework.ExpectEqual(len(pvs), 1)
|
|
||||||
|
|
||||||
ginkgo.By("Expanding non-expandable pvc")
|
|
||||||
newSize := resource.MustParse("6Gi")
|
|
||||||
pvc, err = expandPVCSize(pvc, newSize, c)
|
|
||||||
framework.ExpectError(err, "While updating non-expandable PVC")
|
|
||||||
})
|
|
||||||
|
|
||||||
ginkgo.It("Verify if offline PVC expansion works", func() {
|
|
||||||
pvc, storageClassVar, err = setupFunc(true /* allowExpansion */, false /*BlockVolume*/)
|
|
||||||
framework.ExpectNoError(err, "Error creating non-expandable PVC")
|
|
||||||
|
|
||||||
ginkgo.By("Waiting for pvc to be in bound phase")
|
|
||||||
pvcClaims := []*v1.PersistentVolumeClaim{pvc}
|
|
||||||
pvs, err := framework.WaitForPVClaimBoundPhase(c, pvcClaims, framework.ClaimProvisionTimeout)
|
|
||||||
framework.ExpectNoError(err, "Failed waiting for PVC to be bound %v", err)
|
|
||||||
framework.ExpectEqual(len(pvs), 1)
|
|
||||||
|
|
||||||
ginkgo.By("Creating a pod with dynamically provisioned volume")
|
|
||||||
pod, err := framework.CreatePod(c, ns, nil, pvcClaims, false, "")
|
|
||||||
framework.ExpectNoError(err, "While creating pods for resizing")
|
|
||||||
defer func() {
|
|
||||||
err = framework.DeletePodWithWait(f, c, pod)
|
|
||||||
framework.ExpectNoError(err, "while cleaning up pod already deleted in resize test")
|
|
||||||
}()
|
|
||||||
|
|
||||||
ginkgo.By("Deleting the previously created pod")
|
|
||||||
err = framework.DeletePodWithWait(f, c, pod)
|
|
||||||
framework.ExpectNoError(err, "while deleting pod for resizing")
|
|
||||||
|
|
||||||
// We expand the PVC while no pod is using it to ensure offline expansion
|
|
||||||
ginkgo.By("Expanding current pvc")
|
|
||||||
newSize := resource.MustParse("6Gi")
|
|
||||||
pvc, err = expandPVCSize(pvc, newSize, c)
|
|
||||||
framework.ExpectNoError(err, "While updating pvc for more size")
|
|
||||||
gomega.Expect(pvc).NotTo(gomega.BeNil())
|
|
||||||
|
|
||||||
pvcSize := pvc.Spec.Resources.Requests[v1.ResourceStorage]
|
|
||||||
if pvcSize.Cmp(newSize) != 0 {
|
|
||||||
e2elog.Failf("error updating pvc size %q", pvc.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
ginkgo.By("Waiting for cloudprovider resize to finish")
|
|
||||||
err = waitForControllerVolumeResize(pvc, c, totalResizeWaitPeriod)
|
|
||||||
framework.ExpectNoError(err, "While waiting for pvc resize to finish")
|
|
||||||
|
|
||||||
ginkgo.By("Checking for conditions on pvc")
|
|
||||||
pvc, err = c.CoreV1().PersistentVolumeClaims(ns).Get(pvc.Name, metav1.GetOptions{})
|
|
||||||
framework.ExpectNoError(err, "While fetching pvc after controller resize")
|
|
||||||
|
|
||||||
inProgressConditions := pvc.Status.Conditions
|
|
||||||
framework.ExpectEqual(len(inProgressConditions), 1, "pvc must have file system resize pending condition")
|
|
||||||
framework.ExpectEqual(inProgressConditions[0].Type, v1.PersistentVolumeClaimFileSystemResizePending, "pvc must have fs resizing condition")
|
|
||||||
|
|
||||||
ginkgo.By("Creating a new pod with same volume")
|
|
||||||
pod2, err := framework.CreatePod(c, ns, nil, pvcClaims, false, "")
|
|
||||||
framework.ExpectNoError(err, "while recreating pod for resizing")
|
|
||||||
defer func() {
|
|
||||||
err = framework.DeletePodWithWait(f, c, pod2)
|
|
||||||
framework.ExpectNoError(err, "while cleaning up pod before exiting resizing test")
|
|
||||||
}()
|
|
||||||
|
|
||||||
ginkgo.By("Waiting for file system resize to finish")
|
|
||||||
pvc, err = waitForFSResize(pvc, c)
|
|
||||||
framework.ExpectNoError(err, "while waiting for fs resize to finish")
|
|
||||||
|
|
||||||
pvcConditions := pvc.Status.Conditions
|
|
||||||
framework.ExpectEqual(len(pvcConditions), 0, "pvc should not have conditions")
|
|
||||||
})
|
|
||||||
|
|
||||||
ginkgo.It("should resize volume when PVC is edited while pod is using it", func() {
|
|
||||||
pvc, storageClassVar, err = setupFunc(true /* allowExpansion */, false /*BlockVolume*/)
|
|
||||||
framework.ExpectNoError(err, "Error creating non-expandable PVC")
|
|
||||||
|
|
||||||
ginkgo.By("Waiting for pvc to be in bound phase")
|
|
||||||
pvcClaims := []*v1.PersistentVolumeClaim{pvc}
|
|
||||||
pvs, err := framework.WaitForPVClaimBoundPhase(c, pvcClaims, framework.ClaimProvisionTimeout)
|
|
||||||
framework.ExpectNoError(err, "Failed waiting for PVC to be bound %v", err)
|
|
||||||
framework.ExpectEqual(len(pvs), 1)
|
|
||||||
|
|
||||||
ginkgo.By("Creating a pod with dynamically provisioned volume")
|
|
||||||
pod, err := framework.CreatePod(c, ns, nil, pvcClaims, false, "")
|
|
||||||
framework.ExpectNoError(err, "While creating pods for resizing")
|
|
||||||
defer func() {
|
|
||||||
err = framework.DeletePodWithWait(f, c, pod)
|
|
||||||
framework.ExpectNoError(err, "while cleaning up pod already deleted in resize test")
|
|
||||||
}()
|
|
||||||
|
|
||||||
// We expand the PVC while no pod is using it to ensure online expansion
|
|
||||||
ginkgo.By("Expanding current pvc")
|
|
||||||
newSize := resource.MustParse("6Gi")
|
|
||||||
pvc, err = expandPVCSize(pvc, newSize, c)
|
|
||||||
framework.ExpectNoError(err, "While updating pvc for more size")
|
|
||||||
gomega.Expect(pvc).NotTo(gomega.BeNil())
|
|
||||||
|
|
||||||
pvcSize := pvc.Spec.Resources.Requests[v1.ResourceStorage]
|
|
||||||
if pvcSize.Cmp(newSize) != 0 {
|
|
||||||
e2elog.Failf("error updating pvc size %q", pvc.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
ginkgo.By("Waiting for cloudprovider resize to finish")
|
|
||||||
err = waitForControllerVolumeResize(pvc, c, totalResizeWaitPeriod)
|
|
||||||
framework.ExpectNoError(err, "While waiting for pvc resize to finish")
|
|
||||||
|
|
||||||
ginkgo.By("Waiting for file system resize to finish")
|
|
||||||
pvc, err = waitForFSResize(pvc, c)
|
|
||||||
framework.ExpectNoError(err, "while waiting for fs resize to finish")
|
|
||||||
|
|
||||||
pvcConditions := pvc.Status.Conditions
|
|
||||||
framework.ExpectEqual(len(pvcConditions), 0, "pvc should not have conditions")
|
|
||||||
})
|
|
||||||
|
|
||||||
ginkgo.It("should allow expansion of block volumes", func() {
|
|
||||||
pvc, storageClassVar, err = setupFunc(true /*allowExpansion*/, true /*blockVolume*/)
|
|
||||||
|
|
||||||
ginkgo.By("Waiting for pvc to be in bound phase")
|
|
||||||
pvcClaims := []*v1.PersistentVolumeClaim{pvc}
|
|
||||||
pvs, err := framework.WaitForPVClaimBoundPhase(c, pvcClaims, framework.ClaimProvisionTimeout)
|
|
||||||
framework.ExpectNoError(err, "Failed waiting for PVC to be bound %v", err)
|
|
||||||
framework.ExpectEqual(len(pvs), 1)
|
|
||||||
|
|
||||||
ginkgo.By("Expanding current pvc")
|
|
||||||
newSize := resource.MustParse("6Gi")
|
|
||||||
pvc, err = expandPVCSize(pvc, newSize, c)
|
|
||||||
framework.ExpectNoError(err, "While updating pvc for more size")
|
|
||||||
gomega.Expect(pvc).NotTo(gomega.BeNil())
|
|
||||||
|
|
||||||
pvcSize := pvc.Spec.Resources.Requests[v1.ResourceStorage]
|
|
||||||
if pvcSize.Cmp(newSize) != 0 {
|
|
||||||
e2elog.Failf("error updating pvc size %q", pvc.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
ginkgo.By("Waiting for cloudprovider resize to finish")
|
|
||||||
err = waitForControllerVolumeResize(pvc, c, totalResizeWaitPeriod)
|
|
||||||
framework.ExpectNoError(err, "While waiting for pvc resize to finish")
|
|
||||||
|
|
||||||
ginkgo.By("Waiting for file system resize to finish")
|
|
||||||
pvc, err = waitForFSResize(pvc, c)
|
|
||||||
framework.ExpectNoError(err, "while waiting for fs resize to finish")
|
|
||||||
|
|
||||||
pvcConditions := pvc.Status.Conditions
|
|
||||||
framework.ExpectEqual(len(pvcConditions), 0, "pvc should not have conditions")
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
func createStorageClass(t testsuites.StorageClassTest, ns string, suffix string, c clientset.Interface) (*storagev1.StorageClass, error) {
|
|
||||||
stKlass := newStorageClass(t, ns, suffix)
|
|
||||||
|
|
||||||
var err error
|
|
||||||
stKlass, err = c.StorageV1().StorageClasses().Create(stKlass)
|
|
||||||
return stKlass, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func expandPVCSize(origPVC *v1.PersistentVolumeClaim, size resource.Quantity, c clientset.Interface) (*v1.PersistentVolumeClaim, error) {
|
|
||||||
pvcName := origPVC.Name
|
|
||||||
updatedPVC := origPVC.DeepCopy()
|
|
||||||
|
|
||||||
waitErr := wait.PollImmediate(resizePollInterval, 30*time.Second, func() (bool, error) {
|
|
||||||
var err error
|
|
||||||
updatedPVC, err = c.CoreV1().PersistentVolumeClaims(origPVC.Namespace).Get(pvcName, metav1.GetOptions{})
|
|
||||||
if err != nil {
|
|
||||||
return false, fmt.Errorf("error fetching pvc %q for resizing with %v", pvcName, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
updatedPVC.Spec.Resources.Requests[v1.ResourceStorage] = size
|
|
||||||
updatedPVC, err = c.CoreV1().PersistentVolumeClaims(origPVC.Namespace).Update(updatedPVC)
|
|
||||||
if err == nil {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
e2elog.Logf("Error updating pvc %s with %v", pvcName, err)
|
|
||||||
return false, nil
|
|
||||||
})
|
|
||||||
return updatedPVC, waitErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func waitForResizingCondition(pvc *v1.PersistentVolumeClaim, c clientset.Interface, duration time.Duration) error {
|
|
||||||
waitErr := wait.PollImmediate(resizePollInterval, duration, func() (bool, error) {
|
|
||||||
var err error
|
|
||||||
updatedPVC, err := c.CoreV1().PersistentVolumeClaims(pvc.Namespace).Get(pvc.Name, metav1.GetOptions{})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return false, fmt.Errorf("error fetching pvc %q for checking for resize status : %v", pvc.Name, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
pvcConditions := updatedPVC.Status.Conditions
|
|
||||||
if len(pvcConditions) > 0 {
|
|
||||||
if pvcConditions[0].Type == v1.PersistentVolumeClaimResizing {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
})
|
|
||||||
return waitErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func waitForControllerVolumeResize(pvc *v1.PersistentVolumeClaim, c clientset.Interface, duration time.Duration) error {
|
|
||||||
pvName := pvc.Spec.VolumeName
|
|
||||||
return wait.PollImmediate(resizePollInterval, duration, func() (bool, error) {
|
|
||||||
pvcSize := pvc.Spec.Resources.Requests[v1.ResourceStorage]
|
|
||||||
|
|
||||||
pv, err := c.CoreV1().PersistentVolumes().Get(pvName, metav1.GetOptions{})
|
|
||||||
if err != nil {
|
|
||||||
return false, fmt.Errorf("error fetching pv %q for resizing %v", pvName, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
pvSize := pv.Spec.Capacity[v1.ResourceStorage]
|
|
||||||
|
|
||||||
// If pv size is greater or equal to requested size that means controller resize is finished.
|
|
||||||
if pvSize.Cmp(pvcSize) >= 0 {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func waitForFSResize(pvc *v1.PersistentVolumeClaim, c clientset.Interface) (*v1.PersistentVolumeClaim, error) {
|
|
||||||
var updatedPVC *v1.PersistentVolumeClaim
|
|
||||||
waitErr := wait.PollImmediate(resizePollInterval, totalResizeWaitPeriod, func() (bool, error) {
|
|
||||||
var err error
|
|
||||||
updatedPVC, err = c.CoreV1().PersistentVolumeClaims(pvc.Namespace).Get(pvc.Name, metav1.GetOptions{})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return false, fmt.Errorf("error fetching pvc %q for checking for resize status : %v", pvc.Name, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
pvcSize := updatedPVC.Spec.Resources.Requests[v1.ResourceStorage]
|
|
||||||
pvcStatusSize := updatedPVC.Status.Capacity[v1.ResourceStorage]
|
|
||||||
|
|
||||||
//If pvc's status field size is greater than or equal to pvc's size then done
|
|
||||||
if pvcStatusSize.Cmp(pvcSize) >= 0 {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
})
|
|
||||||
return updatedPVC, waitErr
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user