mounted_volume_resize.go 7.1 KB

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