mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-11-04 07:49:35 +00:00 
			
		
		
		
	The e2e test framework has ExpectNoError() for readable test code. This replaces Expect(err).NotTo(HaveOccurred()) with it for e2e/storage.
		
			
				
	
	
		
			262 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			262 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
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"
 | 
						|
	"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
 | 
						|
		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 := testsuites.StorageClassTest{
 | 
						|
			Name:                 "default",
 | 
						|
			ClaimSize:            "2Gi",
 | 
						|
			AllowVolumeExpansion: true,
 | 
						|
		}
 | 
						|
		resizableSc, err = createStorageClass(test, ns, "resizing", c)
 | 
						|
		framework.ExpectNoError(err, "Error creating resizable storage class")
 | 
						|
		Expect(resizableSc.AllowVolumeExpansion).NotTo(BeNil())
 | 
						|
		Expect(*resizableSc.AllowVolumeExpansion).To(BeTrue())
 | 
						|
 | 
						|
		pvc = newClaim(test, ns, "default")
 | 
						|
		pvc.Spec.StorageClassName = &resizableSc.Name
 | 
						|
		pvc, err = c.CoreV1().PersistentVolumeClaims(pvc.Namespace).Create(pvc)
 | 
						|
		framework.ExpectNoError(err, "Error creating pvc")
 | 
						|
	})
 | 
						|
 | 
						|
	AfterEach(func() {
 | 
						|
		framework.ExpectNoError(framework.DeletePersistentVolumeClaim(c, pvc.Name, pvc.Namespace))
 | 
						|
		framework.ExpectNoError(c.StorageV1().StorageClasses().Delete(resizableSc.Name, nil))
 | 
						|
	})
 | 
						|
 | 
						|
	It("should not allow expansion of pvcs without AllowVolumeExpansion property", func() {
 | 
						|
		test := testsuites.StorageClassTest{
 | 
						|
			Name:      "no-expansion",
 | 
						|
			ClaimSize: "2Gi",
 | 
						|
		}
 | 
						|
		regularSC, err := createStorageClass(test, ns, "noexpand", c)
 | 
						|
		framework.ExpectNoError(err, "Error creating non-expandable storage class")
 | 
						|
 | 
						|
		defer func() {
 | 
						|
			framework.ExpectNoError(c.StorageV1().StorageClasses().Delete(regularSC.Name, nil))
 | 
						|
		}()
 | 
						|
		Expect(regularSC.AllowVolumeExpansion).To(BeNil())
 | 
						|
 | 
						|
		noExpandPVC := newClaim(test, ns, "noexpand")
 | 
						|
		noExpandPVC.Spec.StorageClassName = ®ularSC.Name
 | 
						|
		noExpandPVC, err = c.CoreV1().PersistentVolumeClaims(noExpandPVC.Namespace).Create(noExpandPVC)
 | 
						|
		framework.ExpectNoError(err, "Error creating pvc")
 | 
						|
 | 
						|
		defer func() {
 | 
						|
			framework.ExpectNoError(framework.DeletePersistentVolumeClaim(c, noExpandPVC.Name, noExpandPVC.Namespace))
 | 
						|
		}()
 | 
						|
 | 
						|
		pvcClaims := []*v1.PersistentVolumeClaim{noExpandPVC}
 | 
						|
		pvs, err := framework.WaitForPVClaimBoundPhase(c, pvcClaims, framework.ClaimProvisionTimeout)
 | 
						|
		framework.ExpectNoError(err, "Failed waiting for PVC to be bound %v", err)
 | 
						|
		Expect(len(pvs)).To(Equal(1))
 | 
						|
 | 
						|
		By("Expanding non-expandable pvc")
 | 
						|
		newSize := resource.MustParse("6Gi")
 | 
						|
		noExpandPVC, err = expandPVCSize(noExpandPVC, newSize, c)
 | 
						|
		Expect(err).To(HaveOccurred(), "While updating non-expandable PVC")
 | 
						|
	})
 | 
						|
 | 
						|
	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)
 | 
						|
		framework.ExpectNoError(err, "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, "")
 | 
						|
		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")
 | 
						|
		}()
 | 
						|
 | 
						|
		By("Expanding current pvc")
 | 
						|
		newSize := resource.MustParse("6Gi")
 | 
						|
		pvc, err = expandPVCSize(pvc, newSize, c)
 | 
						|
		framework.ExpectNoError(err, "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, totalResizeWaitPeriod)
 | 
						|
		framework.ExpectNoError(err, "While waiting for pvc resize to finish")
 | 
						|
 | 
						|
		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
 | 
						|
		Expect(len(inProgressConditions)).To(Equal(1), "pvc must have file system resize pending condition")
 | 
						|
		Expect(inProgressConditions[0].Type).To(Equal(v1.PersistentVolumeClaimFileSystemResizePending), "pvc must have fs resizing condition")
 | 
						|
 | 
						|
		By("Deleting the previously created pod")
 | 
						|
		err = framework.DeletePodWithWait(f, c, pod)
 | 
						|
		framework.ExpectNoError(err, "while deleting pod for resizing")
 | 
						|
 | 
						|
		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")
 | 
						|
		}()
 | 
						|
 | 
						|
		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
 | 
						|
		Expect(len(pvcConditions)).To(Equal(0), "pvc should not have conditions")
 | 
						|
	})
 | 
						|
})
 | 
						|
 | 
						|
func createStorageClass(t testsuites.StorageClassTest, ns string, suffix string, c clientset.Interface) (*storage.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
 | 
						|
		}
 | 
						|
		framework.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
 | 
						|
}
 |