k8s.io/kubernetes@v1.29.3/test/e2e/storage/mounted_volume_resize.go (about) 1 /* 2 Copyright 2018 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package storage 18 19 import ( 20 "context" 21 "fmt" 22 "time" 23 24 "github.com/onsi/ginkgo/v2" 25 "github.com/onsi/gomega" 26 appsv1 "k8s.io/api/apps/v1" 27 v1 "k8s.io/api/core/v1" 28 storagev1 "k8s.io/api/storage/v1" 29 "k8s.io/apimachinery/pkg/api/resource" 30 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 31 admissionapi "k8s.io/pod-security-admission/api" 32 33 utilerrors "k8s.io/apimachinery/pkg/util/errors" 34 "k8s.io/apimachinery/pkg/util/wait" 35 clientset "k8s.io/client-go/kubernetes" 36 "k8s.io/kubernetes/pkg/client/conditions" 37 "k8s.io/kubernetes/test/e2e/feature" 38 "k8s.io/kubernetes/test/e2e/framework" 39 e2edeployment "k8s.io/kubernetes/test/e2e/framework/deployment" 40 e2enode "k8s.io/kubernetes/test/e2e/framework/node" 41 e2epod "k8s.io/kubernetes/test/e2e/framework/pod" 42 e2epv "k8s.io/kubernetes/test/e2e/framework/pv" 43 e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper" 44 "k8s.io/kubernetes/test/e2e/storage/testsuites" 45 "k8s.io/kubernetes/test/e2e/storage/utils" 46 ) 47 48 var _ = utils.SIGDescribe("Mounted volume expand", feature.StorageProvider, func() { 49 var ( 50 c clientset.Interface 51 ns string 52 pvc *v1.PersistentVolumeClaim 53 sc *storagev1.StorageClass 54 nodeName string 55 nodeKeyValueLabel map[string]string 56 nodeLabelValue string 57 nodeKey string 58 ) 59 60 f := framework.NewDefaultFramework("mounted-volume-expand") 61 f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged 62 ginkgo.BeforeEach(func(ctx context.Context) { 63 e2eskipper.SkipUnlessProviderIs("aws", "gce") 64 c = f.ClientSet 65 ns = f.Namespace.Name 66 framework.ExpectNoError(e2enode.WaitForAllNodesSchedulable(ctx, c, f.Timeouts.NodeSchedulable)) 67 68 node, err := e2enode.GetRandomReadySchedulableNode(ctx, f.ClientSet) 69 framework.ExpectNoError(err) 70 nodeName = node.Name 71 72 nodeKey = "mounted_volume_expand_" + ns 73 nodeLabelValue = ns 74 nodeKeyValueLabel = map[string]string{nodeKey: nodeLabelValue} 75 e2enode.AddOrUpdateLabelOnNode(c, nodeName, nodeKey, nodeLabelValue) 76 ginkgo.DeferCleanup(e2enode.RemoveLabelOffNode, c, nodeName, nodeKey) 77 78 test := testsuites.StorageClassTest{ 79 Name: "default", 80 Timeouts: f.Timeouts, 81 ClaimSize: "2Gi", 82 AllowVolumeExpansion: true, 83 DelayBinding: true, 84 Parameters: make(map[string]string), 85 } 86 87 sc = testsuites.SetupStorageClass(ctx, c, newStorageClass(test, ns, "resizing")) 88 if !*sc.AllowVolumeExpansion { 89 framework.Failf("Class %s does not allow volume expansion", sc.Name) 90 } 91 92 pvc = e2epv.MakePersistentVolumeClaim(e2epv.PersistentVolumeClaimConfig{ 93 ClaimSize: test.ClaimSize, 94 StorageClassName: &(sc.Name), 95 VolumeMode: &test.VolumeMode, 96 }, ns) 97 pvc, err = c.CoreV1().PersistentVolumeClaims(pvc.Namespace).Create(ctx, pvc, metav1.CreateOptions{}) 98 framework.ExpectNoError(err, "Error creating pvc") 99 ginkgo.DeferCleanup(func(ctx context.Context) { 100 framework.Logf("AfterEach: Cleaning up resources for mounted volume resize") 101 102 if errs := e2epv.PVPVCCleanup(ctx, c, ns, nil, pvc); len(errs) > 0 { 103 framework.Failf("AfterEach: Failed to delete PVC and/or PV. Errors: %v", utilerrors.NewAggregate(errs)) 104 } 105 }) 106 }) 107 108 ginkgo.It("Should verify mounted devices can be resized", func(ctx context.Context) { 109 pvcClaims := []*v1.PersistentVolumeClaim{pvc} 110 111 // The reason we use a node selector is because we do not want pod to move to different node when pod is deleted. 112 // Keeping pod on same node reproduces the scenario that volume might already be mounted when resize is attempted. 113 // We should consider adding a unit test that exercises this better. 114 ginkgo.By("Creating a deployment with selected PVC") 115 deployment, err := e2edeployment.CreateDeployment(ctx, c, int32(1), map[string]string{"test": "app"}, nodeKeyValueLabel, ns, pvcClaims, admissionapi.LevelRestricted, "") 116 framework.ExpectNoError(err, "Failed creating deployment %v", err) 117 ginkgo.DeferCleanup(c.AppsV1().Deployments(ns).Delete, deployment.Name, metav1.DeleteOptions{}) 118 119 // PVC should be bound at this point 120 ginkgo.By("Checking for bound PVC") 121 pvs, err := e2epv.WaitForPVClaimBoundPhase(ctx, c, pvcClaims, framework.ClaimProvisionTimeout) 122 framework.ExpectNoError(err, "Failed waiting for PVC to be bound %v", err) 123 gomega.Expect(pvs).To(gomega.HaveLen(1)) 124 125 ginkgo.By("Wait for a pod from deployment to be running") 126 podList, err := e2edeployment.GetPodsForDeployment(ctx, c, deployment) 127 framework.ExpectNoError(err, "While getting pods from deployment") 128 gomega.Expect(podList.Items).NotTo(gomega.BeEmpty()) 129 pod := podList.Items[0] 130 err = e2epod.WaitTimeoutForPodRunningInNamespace(ctx, c, pod.Name, pod.Namespace, f.Timeouts.PodStart) 131 framework.ExpectNoError(err, "While waiting for pods to be ready") 132 133 ginkgo.By("Expanding current pvc") 134 newSize := resource.MustParse("6Gi") 135 newPVC, err := testsuites.ExpandPVCSize(ctx, pvc, newSize, c) 136 framework.ExpectNoError(err, "While updating pvc for more size") 137 pvc = newPVC 138 gomega.Expect(pvc).NotTo(gomega.BeNil()) 139 140 pvcSize := pvc.Spec.Resources.Requests[v1.ResourceStorage] 141 if pvcSize.Cmp(newSize) != 0 { 142 framework.Failf("error updating pvc size %q", pvc.Name) 143 } 144 145 ginkgo.By("Waiting for cloudprovider resize to finish") 146 err = testsuites.WaitForControllerVolumeResize(ctx, pvc, c, totalResizeWaitPeriod) 147 framework.ExpectNoError(err, "While waiting for pvc resize to finish") 148 149 ginkgo.By("Getting a pod from deployment") 150 podList, err = e2edeployment.GetPodsForDeployment(ctx, c, deployment) 151 framework.ExpectNoError(err, "While getting pods from deployment") 152 gomega.Expect(podList.Items).NotTo(gomega.BeEmpty()) 153 pod = podList.Items[0] 154 155 ginkgo.By("Deleting the pod from deployment") 156 err = e2epod.DeletePodWithWait(ctx, c, &pod) 157 framework.ExpectNoError(err, "while deleting pod for resizing") 158 159 ginkgo.By("Waiting for deployment to create new pod") 160 pod, err = waitForDeploymentToRecreatePod(ctx, c, deployment) 161 framework.ExpectNoError(err, "While waiting for pod to be recreated") 162 163 ginkgo.By("Waiting for file system resize to finish") 164 pvc, err = testsuites.WaitForFSResize(ctx, pvc, c) 165 framework.ExpectNoError(err, "while waiting for fs resize to finish") 166 167 pvcConditions := pvc.Status.Conditions 168 gomega.Expect(pvcConditions).To(gomega.BeEmpty(), "pvc should not have conditions") 169 }) 170 }) 171 172 func waitForDeploymentToRecreatePod(ctx context.Context, client clientset.Interface, deployment *appsv1.Deployment) (v1.Pod, error) { 173 var runningPod v1.Pod 174 waitErr := wait.PollImmediate(10*time.Second, 5*time.Minute, func() (bool, error) { 175 podList, err := e2edeployment.GetPodsForDeployment(ctx, client, deployment) 176 if err != nil { 177 return false, fmt.Errorf("failed to get pods for deployment: %w", err) 178 } 179 for _, pod := range podList.Items { 180 switch pod.Status.Phase { 181 case v1.PodRunning: 182 runningPod = pod 183 return true, nil 184 case v1.PodFailed, v1.PodSucceeded: 185 return false, conditions.ErrPodCompleted 186 } 187 } 188 return false, nil 189 }) 190 if waitErr != nil { 191 return runningPod, fmt.Errorf("error waiting for recreated pod: %v", waitErr) 192 } 193 return runningPod, nil 194 }