Merge pull request #56639 from gnufied/add-resize-e2e-tests
Automatic merge from submit-queue (batch tested with PRs 56639, 56746, 56715, 56673, 56726). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. Add e2e test for volume resizing Fixes https://github.com/kubernetes/kubernetes/issues/56237
This commit is contained in:
		| @@ -19,6 +19,7 @@ go_library( | ||||
|         "persistent_volumes-vsphere.go", | ||||
|         "pv_reclaimpolicy.go", | ||||
|         "pvc_label_selector.go", | ||||
|         "volume_expand.go", | ||||
|         "volume_io.go", | ||||
|         "volume_metrics.go", | ||||
|         "volume_provisioning.go", | ||||
|   | ||||
							
								
								
									
										206
									
								
								test/e2e/storage/volume_expand.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										206
									
								
								test/e2e/storage/volume_expand.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,206 @@ | ||||
| /* | ||||
| 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" | ||||
|  | ||||
| 	"k8s.io/api/core/v1" | ||||
| 	storage "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" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	resizePollInterval = 2 * time.Second | ||||
| 	// total time to wait for cloudprovider or file system resize to finish | ||||
| 	totalResizeWaitPeriod = 20 * time.Minute | ||||
| ) | ||||
|  | ||||
| var _ = SIGDescribe("Volume expand [Feature:ExpandPersistentVolumes] [Slow]", func() { | ||||
| 	var ( | ||||
| 		c           clientset.Interface | ||||
| 		ns          string | ||||
| 		err         error | ||||
| 		pvc         *v1.PersistentVolumeClaim | ||||
| 		resizableSc *storage.StorageClass | ||||
| 	) | ||||
|  | ||||
| 	f := framework.NewDefaultFramework("volume-expand") | ||||
| 	BeforeEach(func() { | ||||
| 		framework.SkipUnlessProviderIs("aws", "gce") | ||||
| 		c = f.ClientSet | ||||
| 		ns = f.Namespace.Name | ||||
| 		framework.ExpectNoError(framework.WaitForAllNodesSchedulable(c, framework.TestContext.NodeSchedulableTimeout)) | ||||
| 		test := storageClassTest{ | ||||
| 			name:      "default", | ||||
| 			claimSize: "2Gi", | ||||
| 		} | ||||
| 		resizableSc, err = createResizableStorageClass(test, ns, "resizing", c) | ||||
| 		Expect(err).NotTo(HaveOccurred(), "Error creating resizable storage class") | ||||
| 		Expect(*resizableSc.AllowVolumeExpansion).To(BeTrue()) | ||||
|  | ||||
| 		pvc = newClaim(test, ns, "default") | ||||
| 		pvc.Spec.StorageClassName = &resizableSc.Name | ||||
| 		pvc, err = c.CoreV1().PersistentVolumeClaims(pvc.Namespace).Create(pvc) | ||||
| 		Expect(err).NotTo(HaveOccurred(), "Error creating pvc") | ||||
| 	}) | ||||
|  | ||||
| 	AfterEach(func() { | ||||
| 		framework.ExpectNoError(framework.DeletePersistentVolumeClaim(c, pvc.Name, pvc.Namespace)) | ||||
| 		framework.ExpectNoError(c.StorageV1().StorageClasses().Delete(resizableSc.Name, nil)) | ||||
| 	}) | ||||
|  | ||||
| 	It("Verify if editing PVC allows resize", func() { | ||||
| 		By("Waiting for pvc to be in bound phase") | ||||
| 		pvcClaims := []*v1.PersistentVolumeClaim{pvc} | ||||
| 		pvs, err := framework.WaitForPVClaimBoundPhase(c, pvcClaims, framework.ClaimProvisionTimeout) | ||||
| 		Expect(err).NotTo(HaveOccurred(), "Failed waiting for PVC to be bound %v", err) | ||||
| 		Expect(len(pvs)).To(Equal(1)) | ||||
|  | ||||
| 		By("Creating a pod with dynamically provisioned volume") | ||||
| 		pod, err := framework.CreatePod(c, ns, nil, pvcClaims, false, "") | ||||
| 		Expect(err).NotTo(HaveOccurred(), "While creating pods for resizing") | ||||
| 		defer func() { | ||||
| 			err = framework.DeletePodWithWait(f, c, pod) | ||||
| 			Expect(err).NotTo(HaveOccurred(), "while cleaning up pod already deleted in resize test") | ||||
| 		}() | ||||
|  | ||||
| 		By("Expanding current pvc") | ||||
| 		newSize := resource.MustParse("6Gi") | ||||
| 		pvc, err = expandPVCSize(pvc, newSize, c) | ||||
| 		Expect(err).NotTo(HaveOccurred(), "While updating pvc for more size") | ||||
| 		Expect(pvc).NotTo(BeNil()) | ||||
|  | ||||
| 		pvcSize := pvc.Spec.Resources.Requests[v1.ResourceStorage] | ||||
| 		if pvcSize.Cmp(newSize) != 0 { | ||||
| 			framework.Failf("error updating pvc size %q", pvc.Name) | ||||
| 		} | ||||
|  | ||||
| 		By("Waiting for cloudprovider resize to finish") | ||||
| 		err = waitForControllerVolumeResize(pvc, c) | ||||
| 		Expect(err).NotTo(HaveOccurred(), "While waiting for pvc resize to finish") | ||||
|  | ||||
| 		By("Checking for conditions on pvc") | ||||
| 		pvc, err = c.CoreV1().PersistentVolumeClaims(ns).Get(pvc.Name, metav1.GetOptions{}) | ||||
| 		Expect(err).NotTo(HaveOccurred(), "While fetching pvc after controller resize") | ||||
|  | ||||
| 		inProgressConditions := pvc.Status.Conditions | ||||
| 		Expect(len(inProgressConditions)).To(Equal(1), "pvc must have resize condition") | ||||
| 		Expect(inProgressConditions[0].Type).To(Equal(v1.PersistentVolumeClaimResizing), "pvc must have resizing condition") | ||||
|  | ||||
| 		By("Deleting the previously created pod") | ||||
| 		err = framework.DeletePodWithWait(f, c, pod) | ||||
| 		Expect(err).NotTo(HaveOccurred(), "while deleting pod for resizing") | ||||
|  | ||||
| 		By("Creating a new pod with same volume") | ||||
| 		pod2, err := framework.CreatePod(c, ns, nil, pvcClaims, false, "") | ||||
| 		Expect(err).NotTo(HaveOccurred(), "while recreating pod for resizing") | ||||
| 		defer func() { | ||||
| 			err = framework.DeletePodWithWait(f, c, pod2) | ||||
| 			Expect(err).NotTo(HaveOccurred(), "while cleaning up pod before exiting resizing test") | ||||
| 		}() | ||||
|  | ||||
| 		By("Waiting for file system resize to finish") | ||||
| 		pvc, err = waitForFSResize(pvc, c) | ||||
| 		Expect(err).NotTo(HaveOccurred(), "while waiting for fs resize to finish") | ||||
|  | ||||
| 		pvcConditions := pvc.Status.Conditions | ||||
| 		Expect(len(pvcConditions)).To(Equal(0), "pvc should not have conditions") | ||||
| 	}) | ||||
| }) | ||||
|  | ||||
| func createResizableStorageClass(t storageClassTest, ns string, suffix string, c clientset.Interface) (*storage.StorageClass, error) { | ||||
| 	stKlass := newStorageClass(t, ns, suffix) | ||||
| 	allowExpansion := true | ||||
| 	stKlass.AllowVolumeExpansion = &allowExpansion | ||||
|  | ||||
| 	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 | ||||
| 		} | ||||
| 		framework.Logf("Error updating pvc %s with %v", pvcName, err) | ||||
| 		return false, nil | ||||
| 	}) | ||||
| 	return updatedPVC, waitErr | ||||
| } | ||||
|  | ||||
| func waitForControllerVolumeResize(pvc *v1.PersistentVolumeClaim, c clientset.Interface) error { | ||||
| 	pvName := pvc.Spec.VolumeName | ||||
| 	return wait.PollImmediate(resizePollInterval, totalResizeWaitPeriod, 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 | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Kubernetes Submit Queue
					Kubernetes Submit Queue