mounted_volume_resize.go 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  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. "time"
  16. "github.com/onsi/ginkgo"
  17. "github.com/onsi/gomega"
  18. apps "k8s.io/api/apps/v1"
  19. v1 "k8s.io/api/core/v1"
  20. storage "k8s.io/api/storage/v1"
  21. "k8s.io/apimachinery/pkg/api/resource"
  22. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  23. utilerrors "k8s.io/apimachinery/pkg/util/errors"
  24. "k8s.io/apimachinery/pkg/util/wait"
  25. clientset "k8s.io/client-go/kubernetes"
  26. "k8s.io/kubernetes/pkg/client/conditions"
  27. "k8s.io/kubernetes/test/e2e/framework"
  28. e2edeploy "k8s.io/kubernetes/test/e2e/framework/deployment"
  29. e2elog "k8s.io/kubernetes/test/e2e/framework/log"
  30. "k8s.io/kubernetes/test/e2e/storage/testsuites"
  31. "k8s.io/kubernetes/test/e2e/storage/utils"
  32. )
  33. var _ = utils.SIGDescribe("Mounted volume expand", func() {
  34. var (
  35. c clientset.Interface
  36. ns string
  37. err error
  38. pvc *v1.PersistentVolumeClaim
  39. resizableSc *storage.StorageClass
  40. nodeName string
  41. isNodeLabeled bool
  42. nodeKeyValueLabel map[string]string
  43. nodeLabelValue string
  44. nodeKey string
  45. )
  46. f := framework.NewDefaultFramework("mounted-volume-expand")
  47. ginkgo.BeforeEach(func() {
  48. framework.SkipUnlessProviderIs("aws", "gce")
  49. c = f.ClientSet
  50. ns = f.Namespace.Name
  51. framework.ExpectNoError(framework.WaitForAllNodesSchedulable(c, framework.TestContext.NodeSchedulableTimeout))
  52. nodeList := framework.GetReadySchedulableNodesOrDie(f.ClientSet)
  53. if len(nodeList.Items) != 0 {
  54. nodeName = nodeList.Items[0].Name
  55. } else {
  56. framework.Failf("Unable to find ready and schedulable Node")
  57. }
  58. nodeKey = "mounted_volume_expand"
  59. if !isNodeLabeled {
  60. nodeLabelValue = ns
  61. nodeKeyValueLabel = make(map[string]string)
  62. nodeKeyValueLabel[nodeKey] = nodeLabelValue
  63. framework.AddOrUpdateLabelOnNode(c, nodeName, nodeKey, nodeLabelValue)
  64. isNodeLabeled = true
  65. }
  66. test := testsuites.StorageClassTest{
  67. Name: "default",
  68. ClaimSize: "2Gi",
  69. AllowVolumeExpansion: true,
  70. DelayBinding: true,
  71. }
  72. resizableSc, err = createStorageClass(test, ns, "resizing", c)
  73. framework.ExpectNoError(err, "Error creating resizable storage class")
  74. gomega.Expect(*resizableSc.AllowVolumeExpansion).To(gomega.BeTrue())
  75. pvc = newClaim(test, ns, "default")
  76. pvc.Spec.StorageClassName = &resizableSc.Name
  77. pvc, err = c.CoreV1().PersistentVolumeClaims(pvc.Namespace).Create(pvc)
  78. framework.ExpectNoError(err, "Error creating pvc")
  79. })
  80. framework.AddCleanupAction(func() {
  81. if len(nodeLabelValue) > 0 {
  82. framework.RemoveLabelOffNode(c, nodeName, nodeKey)
  83. }
  84. })
  85. ginkgo.AfterEach(func() {
  86. e2elog.Logf("AfterEach: Cleaning up resources for mounted volume resize")
  87. if c != nil {
  88. if errs := framework.PVPVCCleanup(c, ns, nil, pvc); len(errs) > 0 {
  89. framework.Failf("AfterEach: Failed to delete PVC and/or PV. Errors: %v", utilerrors.NewAggregate(errs))
  90. }
  91. pvc, nodeName, isNodeLabeled, nodeLabelValue = nil, "", false, ""
  92. nodeKeyValueLabel = make(map[string]string)
  93. }
  94. })
  95. ginkgo.It("Should verify mounted devices can be resized", func() {
  96. pvcClaims := []*v1.PersistentVolumeClaim{pvc}
  97. // The reason we use a node selector is because we do not want pod to move to different node when pod is deleted.
  98. // Keeping pod on same node reproduces the scenario that volume might already be mounted when resize is attempted.
  99. // We should consider adding a unit test that exercises this better.
  100. ginkgo.By("Creating a deployment with selected PVC")
  101. deployment, err := e2edeploy.CreateDeployment(c, int32(1), map[string]string{"test": "app"}, nodeKeyValueLabel, ns, pvcClaims, "")
  102. framework.ExpectNoError(err, "Failed creating deployment %v", err)
  103. defer c.AppsV1().Deployments(ns).Delete(deployment.Name, &metav1.DeleteOptions{})
  104. // PVC should be bound at this point
  105. ginkgo.By("Checking for bound PVC")
  106. pvs, err := framework.WaitForPVClaimBoundPhase(c, pvcClaims, framework.ClaimProvisionTimeout)
  107. framework.ExpectNoError(err, "Failed waiting for PVC to be bound %v", err)
  108. gomega.Expect(len(pvs)).To(gomega.Equal(1))
  109. ginkgo.By("Expanding current pvc")
  110. newSize := resource.MustParse("6Gi")
  111. pvc, err = expandPVCSize(pvc, newSize, c)
  112. framework.ExpectNoError(err, "While updating pvc for more size")
  113. gomega.Expect(pvc).NotTo(gomega.BeNil())
  114. pvcSize := pvc.Spec.Resources.Requests[v1.ResourceStorage]
  115. if pvcSize.Cmp(newSize) != 0 {
  116. framework.Failf("error updating pvc size %q", pvc.Name)
  117. }
  118. ginkgo.By("Waiting for cloudprovider resize to finish")
  119. err = waitForControllerVolumeResize(pvc, c, totalResizeWaitPeriod)
  120. framework.ExpectNoError(err, "While waiting for pvc resize to finish")
  121. ginkgo.By("Getting a pod from deployment")
  122. podList, err := e2edeploy.GetPodsForDeployment(c, deployment)
  123. gomega.Expect(podList.Items).NotTo(gomega.BeEmpty())
  124. pod := podList.Items[0]
  125. ginkgo.By("Deleting the pod from deployment")
  126. err = framework.DeletePodWithWait(f, c, &pod)
  127. framework.ExpectNoError(err, "while deleting pod for resizing")
  128. ginkgo.By("Waiting for deployment to create new pod")
  129. pod, err = waitForDeploymentToRecreatePod(c, deployment)
  130. framework.ExpectNoError(err, "While waiting for pod to be recreated")
  131. ginkgo.By("Waiting for file system resize to finish")
  132. pvc, err = waitForFSResize(pvc, c)
  133. framework.ExpectNoError(err, "while waiting for fs resize to finish")
  134. pvcConditions := pvc.Status.Conditions
  135. gomega.Expect(len(pvcConditions)).To(gomega.Equal(0), "pvc should not have conditions")
  136. })
  137. })
  138. func waitForDeploymentToRecreatePod(client clientset.Interface, deployment *apps.Deployment) (v1.Pod, error) {
  139. var runningPod v1.Pod
  140. waitErr := wait.PollImmediate(10*time.Second, 5*time.Minute, func() (bool, error) {
  141. podList, err := e2edeploy.GetPodsForDeployment(client, deployment)
  142. for _, pod := range podList.Items {
  143. switch pod.Status.Phase {
  144. case v1.PodRunning:
  145. runningPod = pod
  146. return true, nil
  147. case v1.PodFailed, v1.PodSucceeded:
  148. return false, conditions.ErrPodCompleted
  149. }
  150. return false, nil
  151. }
  152. return false, err
  153. })
  154. return runningPod, waitErr
  155. }