detach_mounted.go 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  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 storage
  14. import (
  15. "context"
  16. "fmt"
  17. "path"
  18. "time"
  19. v1 "k8s.io/api/core/v1"
  20. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  21. "k8s.io/apimachinery/pkg/util/wait"
  22. clientset "k8s.io/client-go/kubernetes"
  23. "k8s.io/kubernetes/test/e2e/framework"
  24. e2enode "k8s.io/kubernetes/test/e2e/framework/node"
  25. e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
  26. e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
  27. "k8s.io/kubernetes/test/e2e/storage/utils"
  28. imageutils "k8s.io/kubernetes/test/utils/image"
  29. "github.com/onsi/ginkgo"
  30. )
  31. var (
  32. // BusyBoxImage is the image URI of BusyBox.
  33. BusyBoxImage = imageutils.GetE2EImage(imageutils.BusyBox)
  34. durationForStuckMount = 110 * time.Second
  35. )
  36. var _ = utils.SIGDescribe("Detaching volumes", func() {
  37. f := framework.NewDefaultFramework("flexvolume")
  38. // note that namespace deletion is handled by delete-namespace flag
  39. var cs clientset.Interface
  40. var ns *v1.Namespace
  41. var node *v1.Node
  42. var suffix string
  43. ginkgo.BeforeEach(func() {
  44. e2eskipper.SkipUnlessProviderIs("gce", "local")
  45. e2eskipper.SkipUnlessMasterOSDistroIs("debian", "ubuntu", "gci", "custom")
  46. e2eskipper.SkipUnlessNodeOSDistroIs("debian", "ubuntu", "gci", "custom")
  47. e2eskipper.SkipUnlessSSHKeyPresent()
  48. cs = f.ClientSet
  49. ns = f.Namespace
  50. var err error
  51. node, err = e2enode.GetRandomReadySchedulableNode(f.ClientSet)
  52. framework.ExpectNoError(err)
  53. suffix = ns.Name
  54. })
  55. ginkgo.It("should not work when mount is in progress [Slow]", func() {
  56. e2eskipper.SkipUnlessSSHKeyPresent()
  57. driver := "attachable-with-long-mount"
  58. driverInstallAs := driver + "-" + suffix
  59. ginkgo.By(fmt.Sprintf("installing flexvolume %s on node %s as %s", path.Join(driverDir, driver), node.Name, driverInstallAs))
  60. installFlex(cs, node, "k8s", driverInstallAs, path.Join(driverDir, driver))
  61. ginkgo.By(fmt.Sprintf("installing flexvolume %s on master as %s", path.Join(driverDir, driver), driverInstallAs))
  62. installFlex(cs, nil, "k8s", driverInstallAs, path.Join(driverDir, driver))
  63. volumeSource := v1.VolumeSource{
  64. FlexVolume: &v1.FlexVolumeSource{
  65. Driver: "k8s/" + driverInstallAs,
  66. },
  67. }
  68. clientPod := getFlexVolumePod(volumeSource, node.Name)
  69. ginkgo.By("Creating pod that uses slow format volume")
  70. pod, err := cs.CoreV1().Pods(ns.Name).Create(context.TODO(), clientPod, metav1.CreateOptions{})
  71. framework.ExpectNoError(err)
  72. uniqueVolumeName := getUniqueVolumeName(pod, driverInstallAs)
  73. ginkgo.By("waiting for volumes to be attached to node")
  74. err = waitForVolumesAttached(cs, node.Name, uniqueVolumeName)
  75. framework.ExpectNoError(err, "while waiting for volume to attach to %s node", node.Name)
  76. ginkgo.By("waiting for volume-in-use on the node after pod creation")
  77. err = waitForVolumesInUse(cs, node.Name, uniqueVolumeName)
  78. framework.ExpectNoError(err, "while waiting for volume in use")
  79. ginkgo.By("waiting for kubelet to start mounting the volume")
  80. time.Sleep(20 * time.Second)
  81. ginkgo.By("Deleting the flexvolume pod")
  82. err = e2epod.DeletePodWithWait(cs, pod)
  83. framework.ExpectNoError(err, "in deleting the pod")
  84. // Wait a bit for node to sync the volume status
  85. time.Sleep(30 * time.Second)
  86. ginkgo.By("waiting for volume-in-use on the node after pod deletion")
  87. err = waitForVolumesInUse(cs, node.Name, uniqueVolumeName)
  88. framework.ExpectNoError(err, "while waiting for volume in use")
  89. // Wait for 110s because mount device operation has a sleep of 120 seconds
  90. // we previously already waited for 30s.
  91. time.Sleep(durationForStuckMount)
  92. ginkgo.By("waiting for volume to disappear from node in-use")
  93. err = waitForVolumesNotInUse(cs, node.Name, uniqueVolumeName)
  94. framework.ExpectNoError(err, "while waiting for volume to be removed from in-use")
  95. ginkgo.By(fmt.Sprintf("uninstalling flexvolume %s from node %s", driverInstallAs, node.Name))
  96. uninstallFlex(cs, node, "k8s", driverInstallAs)
  97. ginkgo.By(fmt.Sprintf("uninstalling flexvolume %s from master", driverInstallAs))
  98. uninstallFlex(cs, nil, "k8s", driverInstallAs)
  99. })
  100. })
  101. func getUniqueVolumeName(pod *v1.Pod, driverName string) string {
  102. return fmt.Sprintf("flexvolume-k8s/%s/%s", driverName, pod.Spec.Volumes[0].Name)
  103. }
  104. func waitForVolumesNotInUse(client clientset.Interface, nodeName, volumeName string) error {
  105. waitErr := wait.PollImmediate(10*time.Second, 60*time.Second, func() (bool, error) {
  106. node, err := client.CoreV1().Nodes().Get(context.TODO(), nodeName, metav1.GetOptions{})
  107. if err != nil {
  108. return false, fmt.Errorf("error fetching node %s with %v", nodeName, err)
  109. }
  110. volumeInUse := node.Status.VolumesInUse
  111. for _, volume := range volumeInUse {
  112. if string(volume) == volumeName {
  113. return false, nil
  114. }
  115. }
  116. return true, nil
  117. })
  118. if waitErr != nil {
  119. return fmt.Errorf("error waiting for volumes to not be in use: %v", waitErr)
  120. }
  121. return nil
  122. }
  123. func waitForVolumesAttached(client clientset.Interface, nodeName, volumeName string) error {
  124. waitErr := wait.PollImmediate(2*time.Second, 2*time.Minute, func() (bool, error) {
  125. node, err := client.CoreV1().Nodes().Get(context.TODO(), nodeName, metav1.GetOptions{})
  126. if err != nil {
  127. return false, fmt.Errorf("error fetching node %s with %v", nodeName, err)
  128. }
  129. volumeAttached := node.Status.VolumesAttached
  130. for _, volume := range volumeAttached {
  131. if string(volume.Name) == volumeName {
  132. return true, nil
  133. }
  134. }
  135. return false, nil
  136. })
  137. if waitErr != nil {
  138. return fmt.Errorf("error waiting for volume %v to attach to node %v: %v", volumeName, nodeName, waitErr)
  139. }
  140. return nil
  141. }
  142. func waitForVolumesInUse(client clientset.Interface, nodeName, volumeName string) error {
  143. waitErr := wait.PollImmediate(10*time.Second, 60*time.Second, func() (bool, error) {
  144. node, err := client.CoreV1().Nodes().Get(context.TODO(), nodeName, metav1.GetOptions{})
  145. if err != nil {
  146. return false, fmt.Errorf("error fetching node %s with %v", nodeName, err)
  147. }
  148. volumeInUse := node.Status.VolumesInUse
  149. for _, volume := range volumeInUse {
  150. if string(volume) == volumeName {
  151. return true, nil
  152. }
  153. }
  154. return false, nil
  155. })
  156. if waitErr != nil {
  157. return fmt.Errorf("error waiting for volume %v to be in use on node %v: %v", volumeName, nodeName, waitErr)
  158. }
  159. return nil
  160. }
  161. func getFlexVolumePod(volumeSource v1.VolumeSource, nodeName string) *v1.Pod {
  162. var gracePeriod int64
  163. clientPod := &v1.Pod{
  164. TypeMeta: metav1.TypeMeta{
  165. Kind: "Pod",
  166. APIVersion: "v1",
  167. },
  168. ObjectMeta: metav1.ObjectMeta{
  169. Name: "flexvolume-detach-test" + "-client",
  170. Labels: map[string]string{
  171. "role": "flexvolume-detach-test" + "-client",
  172. },
  173. },
  174. Spec: v1.PodSpec{
  175. Containers: []v1.Container{
  176. {
  177. Name: "flexvolume-detach-test" + "-client",
  178. Image: BusyBoxImage,
  179. WorkingDir: "/opt",
  180. // An imperative and easily debuggable container which reads vol contents for
  181. // us to scan in the tests or by eye.
  182. // We expect that /opt is empty in the minimal containers which we use in this test.
  183. Command: []string{
  184. "/bin/sh",
  185. "-c",
  186. "while true ; do cat /opt/foo/index.html ; sleep 2 ; ls -altrh /opt/ ; sleep 2 ; done ",
  187. },
  188. VolumeMounts: []v1.VolumeMount{
  189. {
  190. Name: "test-long-detach-flex",
  191. MountPath: "/opt/foo",
  192. },
  193. },
  194. },
  195. },
  196. TerminationGracePeriodSeconds: &gracePeriod,
  197. SecurityContext: &v1.PodSecurityContext{
  198. SELinuxOptions: &v1.SELinuxOptions{
  199. Level: "s0:c0,c1",
  200. },
  201. },
  202. Volumes: []v1.Volume{
  203. {
  204. Name: "test-long-detach-flex",
  205. VolumeSource: volumeSource,
  206. },
  207. },
  208. NodeName: nodeName,
  209. },
  210. }
  211. return clientPod
  212. }