detach_mounted.go 7.3 KB

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