vsphere_volume_node_poweroff.go 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. /*
  2. Copyright 2017 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 vsphere
  14. import (
  15. "context"
  16. "fmt"
  17. "time"
  18. "github.com/onsi/ginkgo"
  19. "github.com/onsi/gomega"
  20. "github.com/vmware/govmomi/object"
  21. vimtypes "github.com/vmware/govmomi/vim25/types"
  22. apps "k8s.io/api/apps/v1"
  23. v1 "k8s.io/api/core/v1"
  24. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  25. "k8s.io/apimachinery/pkg/util/wait"
  26. clientset "k8s.io/client-go/kubernetes"
  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/utils"
  31. )
  32. /*
  33. Test to verify volume status after node power off:
  34. 1. Verify the pod got provisioned on a different node with volume attached to it
  35. 2. Verify the volume is detached from the powered off node
  36. */
  37. var _ = utils.SIGDescribe("Node Poweroff [Feature:vsphere] [Slow] [Disruptive]", func() {
  38. f := framework.NewDefaultFramework("node-poweroff")
  39. var (
  40. client clientset.Interface
  41. namespace string
  42. )
  43. ginkgo.BeforeEach(func() {
  44. framework.SkipUnlessProviderIs("vsphere")
  45. Bootstrap(f)
  46. client = f.ClientSet
  47. namespace = f.Namespace.Name
  48. framework.ExpectNoError(framework.WaitForAllNodesSchedulable(client, framework.TestContext.NodeSchedulableTimeout))
  49. nodeList := framework.GetReadySchedulableNodesOrDie(f.ClientSet)
  50. gomega.Expect(nodeList.Items).NotTo(gomega.BeEmpty(), "Unable to find ready and schedulable Node")
  51. gomega.Expect(len(nodeList.Items) > 1).To(gomega.BeTrue(), "At least 2 nodes are required for this test")
  52. })
  53. /*
  54. Steps:
  55. 1. Create a StorageClass
  56. 2. Create a PVC with the StorageClass
  57. 3. Create a Deployment with 1 replica, using the PVC
  58. 4. Verify the pod got provisioned on a node
  59. 5. Verify the volume is attached to the node
  60. 6. Power off the node where pod got provisioned
  61. 7. Verify the pod got provisioned on a different node
  62. 8. Verify the volume is attached to the new node
  63. 9. Verify the volume is detached from the old node
  64. 10. Delete the Deployment and wait for the volume to be detached
  65. 11. Delete the PVC
  66. 12. Delete the StorageClass
  67. */
  68. ginkgo.It("verify volume status after node power off", func() {
  69. ginkgo.By("Creating a Storage Class")
  70. storageClassSpec := getVSphereStorageClassSpec("test-sc", nil, nil)
  71. storageclass, err := client.StorageV1().StorageClasses().Create(storageClassSpec)
  72. framework.ExpectNoError(err, fmt.Sprintf("Failed to create storage class with err: %v", err))
  73. defer client.StorageV1().StorageClasses().Delete(storageclass.Name, nil)
  74. ginkgo.By("Creating PVC using the Storage Class")
  75. pvclaimSpec := getVSphereClaimSpecWithStorageClass(namespace, "1Gi", storageclass)
  76. pvclaim, err := framework.CreatePVC(client, namespace, pvclaimSpec)
  77. framework.ExpectNoError(err, fmt.Sprintf("Failed to create PVC with err: %v", err))
  78. defer framework.DeletePersistentVolumeClaim(client, pvclaim.Name, namespace)
  79. ginkgo.By("Waiting for PVC to be in bound phase")
  80. pvclaims := []*v1.PersistentVolumeClaim{pvclaim}
  81. pvs, err := framework.WaitForPVClaimBoundPhase(client, pvclaims, framework.ClaimProvisionTimeout)
  82. framework.ExpectNoError(err, fmt.Sprintf("Failed to wait until PVC phase set to bound: %v", err))
  83. volumePath := pvs[0].Spec.VsphereVolume.VolumePath
  84. ginkgo.By("Creating a Deployment")
  85. deployment, err := e2edeploy.CreateDeployment(client, int32(1), map[string]string{"test": "app"}, nil, namespace, pvclaims, "")
  86. framework.ExpectNoError(err, fmt.Sprintf("Failed to create Deployment with err: %v", err))
  87. defer client.AppsV1().Deployments(namespace).Delete(deployment.Name, &metav1.DeleteOptions{})
  88. ginkgo.By("Get pod from the deployement")
  89. podList, err := e2edeploy.GetPodsForDeployment(client, deployment)
  90. framework.ExpectNoError(err, fmt.Sprintf("Failed to get pod from the deployement with err: %v", err))
  91. gomega.Expect(podList.Items).NotTo(gomega.BeEmpty())
  92. pod := podList.Items[0]
  93. node1 := pod.Spec.NodeName
  94. ginkgo.By(fmt.Sprintf("Verify disk is attached to the node: %v", node1))
  95. isAttached, err := diskIsAttached(volumePath, node1)
  96. framework.ExpectNoError(err)
  97. gomega.Expect(isAttached).To(gomega.BeTrue(), "Disk is not attached to the node")
  98. ginkgo.By(fmt.Sprintf("Power off the node: %v", node1))
  99. nodeInfo := TestContext.NodeMapper.GetNodeInfo(node1)
  100. vm := object.NewVirtualMachine(nodeInfo.VSphere.Client.Client, nodeInfo.VirtualMachineRef)
  101. ctx, cancel := context.WithCancel(context.Background())
  102. defer cancel()
  103. _, err = vm.PowerOff(ctx)
  104. framework.ExpectNoError(err)
  105. defer vm.PowerOn(ctx)
  106. err = vm.WaitForPowerState(ctx, vimtypes.VirtualMachinePowerStatePoweredOff)
  107. framework.ExpectNoError(err, "Unable to power off the node")
  108. // Waiting for the pod to be failed over to a different node
  109. node2, err := waitForPodToFailover(client, deployment, node1)
  110. framework.ExpectNoError(err, "Pod did not fail over to a different node")
  111. ginkgo.By(fmt.Sprintf("Waiting for disk to be attached to the new node: %v", node2))
  112. err = waitForVSphereDiskToAttach(volumePath, node2)
  113. framework.ExpectNoError(err, "Disk is not attached to the node")
  114. ginkgo.By(fmt.Sprintf("Waiting for disk to be detached from the previous node: %v", node1))
  115. err = waitForVSphereDiskToDetach(volumePath, node1)
  116. framework.ExpectNoError(err, "Disk is not detached from the node")
  117. ginkgo.By(fmt.Sprintf("Power on the previous node: %v", node1))
  118. vm.PowerOn(ctx)
  119. err = vm.WaitForPowerState(ctx, vimtypes.VirtualMachinePowerStatePoweredOn)
  120. framework.ExpectNoError(err, "Unable to power on the node")
  121. })
  122. })
  123. // Wait until the pod failed over to a different node, or time out after 3 minutes
  124. func waitForPodToFailover(client clientset.Interface, deployment *apps.Deployment, oldNode string) (string, error) {
  125. var (
  126. err error
  127. newNode string
  128. timeout = 3 * time.Minute
  129. pollTime = 10 * time.Second
  130. )
  131. err = wait.Poll(pollTime, timeout, func() (bool, error) {
  132. newNode, err = getNodeForDeployment(client, deployment)
  133. if err != nil {
  134. return true, err
  135. }
  136. if newNode != oldNode {
  137. e2elog.Logf("The pod has been failed over from %q to %q", oldNode, newNode)
  138. return true, nil
  139. }
  140. e2elog.Logf("Waiting for pod to be failed over from %q", oldNode)
  141. return false, nil
  142. })
  143. if err != nil {
  144. if err == wait.ErrWaitTimeout {
  145. e2elog.Logf("Time out after waiting for %v", timeout)
  146. }
  147. e2elog.Logf("Pod did not fail over from %q with error: %v", oldNode, err)
  148. return "", err
  149. }
  150. return getNodeForDeployment(client, deployment)
  151. }
  152. // getNodeForDeployment returns node name for the Deployment
  153. func getNodeForDeployment(client clientset.Interface, deployment *apps.Deployment) (string, error) {
  154. podList, err := e2edeploy.GetPodsForDeployment(client, deployment)
  155. if err != nil {
  156. return "", err
  157. }
  158. return podList.Items[0].Spec.NodeName, nil
  159. }