vsphere_zone_support.go 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  1. /*
  2. Copyright 2019 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. "strings"
  18. "time"
  19. "github.com/onsi/ginkgo"
  20. "github.com/onsi/gomega"
  21. v1 "k8s.io/api/core/v1"
  22. storagev1 "k8s.io/api/storage/v1"
  23. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  24. clientset "k8s.io/client-go/kubernetes"
  25. volumeevents "k8s.io/kubernetes/pkg/controller/volume/events"
  26. "k8s.io/kubernetes/test/e2e/framework"
  27. e2enode "k8s.io/kubernetes/test/e2e/framework/node"
  28. e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
  29. e2epv "k8s.io/kubernetes/test/e2e/framework/pv"
  30. e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
  31. "k8s.io/kubernetes/test/e2e/storage/utils"
  32. )
  33. /*
  34. Test to verify multi-zone support for dynamic volume provisioning in kubernetes.
  35. The test environment is illustrated below:
  36. datacenter-1
  37. --->cluster-vsan-1 (zone-a) ____________________ _________________
  38. --->host-1 : master | | | |
  39. --->host-2 : node1 ___________________ | | | |
  40. --->host-3 (zone-c): node2 | || vsanDatastore | | |
  41. | localDatastore || | | |
  42. |___________________||____________________| | sharedVmfs-0 |
  43. --->cluster-vsan-2 (zone-b) ____________________ | |
  44. --->host-4 : node3 | | | |
  45. --->host-5 : node4 | vsanDatastore (1) | | |
  46. --->host-6 | | | |
  47. |____________________| |_________________|
  48. --->cluster-3 (zone-c) ___________________
  49. --->host-7 : node5 | |
  50. | localDatastore (1)|
  51. |___________________|
  52. datacenter-2
  53. --->cluster-1 (zone-d) ___________________
  54. --->host-8 : node6 | |
  55. | localDatastore |
  56. |___________________|
  57. Testbed description :
  58. 1. cluster-vsan-1 is tagged with zone-a. So, vsanDatastore inherits zone-a since all the hosts under zone-a have vsanDatastore mounted on them.
  59. 2. cluster-vsan-2 is tagged with zone-b. So, vsanDatastore (1) inherits zone-b since all the hosts under zone-b have vsanDatastore (1) mounted on them.
  60. 3. sharedVmfs-0 inherits both zone-a and zone-b since all the hosts in both zone-a and zone-b have this datastore mounted on them.
  61. 4. cluster-3 is tagged with zone-c. cluster-3 only contains host-7.
  62. 5. host-3 under cluster-vsan-1 is tagged with zone-c.
  63. 6. Since there are no shared datastores between host-7 under cluster-3 and host-3 under cluster-vsan-1, no datastores in the environment inherit zone-c.
  64. 7. host-8 under datacenter-2 and cluster-1 is tagged with zone-d. So, localDatastore attached to host-8 inherits zone-d.
  65. 8. The six worker nodes are distributed among the hosts as shown in the above illustration.
  66. 9. Two storage policies are created on VC. One is a VSAN storage policy named as compatpolicy with hostFailuresToTolerate capability set to 1.
  67. Testsuite description :
  68. 1. Tests to verify that zone labels are set correctly on a dynamically created PV.
  69. 2. Tests to verify dynamic pv creation fails if availability zones are not specified or if there are no shared datastores under the specified zones.
  70. 3. Tests to verify dynamic pv creation using availability zones works in combination with other storage class parameters such as storage policy,
  71. datastore and VSAN capabilities.
  72. 4. Tests to verify dynamic pv creation using availability zones fails in combination with other storage class parameters such as storage policy,
  73. datastore and VSAN capabilities specifications when any of the former mentioned parameters are incompatible with the rest.
  74. 5. Tests to verify dynamic pv creation using availability zones work across different datacenters in the same VC.
  75. */
  76. var _ = utils.SIGDescribe("Zone Support [Feature:vsphere]", func() {
  77. f := framework.NewDefaultFramework("zone-support")
  78. var (
  79. client clientset.Interface
  80. namespace string
  81. scParameters map[string]string
  82. zones []string
  83. vsanDatastore1 string
  84. vsanDatastore2 string
  85. localDatastore string
  86. compatPolicy string
  87. nonCompatPolicy string
  88. zoneA string
  89. zoneB string
  90. zoneC string
  91. zoneD string
  92. invalidZone string
  93. )
  94. ginkgo.BeforeEach(func() {
  95. e2eskipper.SkipUnlessProviderIs("vsphere")
  96. Bootstrap(f)
  97. client = f.ClientSet
  98. namespace = f.Namespace.Name
  99. vsanDatastore1 = GetAndExpectStringEnvVar(VCPZoneVsanDatastore1)
  100. vsanDatastore2 = GetAndExpectStringEnvVar(VCPZoneVsanDatastore2)
  101. localDatastore = GetAndExpectStringEnvVar(VCPZoneLocalDatastore)
  102. compatPolicy = GetAndExpectStringEnvVar(VCPZoneCompatPolicyName)
  103. nonCompatPolicy = GetAndExpectStringEnvVar(VCPZoneNonCompatPolicyName)
  104. zoneA = GetAndExpectStringEnvVar(VCPZoneA)
  105. zoneB = GetAndExpectStringEnvVar(VCPZoneB)
  106. zoneC = GetAndExpectStringEnvVar(VCPZoneC)
  107. zoneD = GetAndExpectStringEnvVar(VCPZoneD)
  108. invalidZone = GetAndExpectStringEnvVar(VCPInvalidZone)
  109. scParameters = make(map[string]string)
  110. zones = make([]string, 0)
  111. _, err := e2enode.GetRandomReadySchedulableNode(f.ClientSet)
  112. framework.ExpectNoError(err)
  113. })
  114. ginkgo.It("Verify dynamically created pv with allowed zones specified in storage class, shows the right zone information on its labels", func() {
  115. ginkgo.By(fmt.Sprintf("Creating storage class with the following zones : %s", zoneA))
  116. zones = append(zones, zoneA)
  117. verifyPVZoneLabels(client, namespace, nil, zones)
  118. })
  119. ginkgo.It("Verify dynamically created pv with multiple zones specified in the storage class, shows both the zones on its labels", func() {
  120. ginkgo.By(fmt.Sprintf("Creating storage class with the following zones : %s, %s", zoneA, zoneB))
  121. zones = append(zones, zoneA)
  122. zones = append(zones, zoneB)
  123. verifyPVZoneLabels(client, namespace, nil, zones)
  124. })
  125. ginkgo.It("Verify PVC creation with invalid zone specified in storage class fails", func() {
  126. ginkgo.By(fmt.Sprintf("Creating storage class with unknown zone : %s", invalidZone))
  127. zones = append(zones, invalidZone)
  128. err := verifyPVCCreationFails(client, namespace, nil, zones, "")
  129. framework.ExpectError(err)
  130. errorMsg := "Failed to find a shared datastore matching zone [" + invalidZone + "]"
  131. if !strings.Contains(err.Error(), errorMsg) {
  132. framework.ExpectNoError(err, errorMsg)
  133. }
  134. })
  135. ginkgo.It("Verify a pod is created and attached to a dynamically created PV, based on allowed zones specified in storage class ", func() {
  136. ginkgo.By(fmt.Sprintf("Creating storage class with zones :%s", zoneA))
  137. zones = append(zones, zoneA)
  138. verifyPVCAndPodCreationSucceeds(client, namespace, nil, zones, "")
  139. })
  140. ginkgo.It("Verify a pod is created and attached to a dynamically created PV, based on multiple zones specified in storage class ", func() {
  141. ginkgo.By(fmt.Sprintf("Creating storage class with zones :%s, %s", zoneA, zoneB))
  142. zones = append(zones, zoneA)
  143. zones = append(zones, zoneB)
  144. verifyPVCAndPodCreationSucceeds(client, namespace, nil, zones, "")
  145. })
  146. ginkgo.It("Verify a pod is created and attached to a dynamically created PV, based on the allowed zones and datastore specified in storage class", func() {
  147. ginkgo.By(fmt.Sprintf("Creating storage class with zone :%s and datastore :%s", zoneA, vsanDatastore1))
  148. scParameters[Datastore] = vsanDatastore1
  149. zones = append(zones, zoneA)
  150. verifyPVCAndPodCreationSucceeds(client, namespace, scParameters, zones, "")
  151. })
  152. ginkgo.It("Verify PVC creation with incompatible datastore and zone combination specified in storage class fails", func() {
  153. ginkgo.By(fmt.Sprintf("Creating storage class with zone :%s and datastore :%s", zoneC, vsanDatastore1))
  154. scParameters[Datastore] = vsanDatastore1
  155. zones = append(zones, zoneC)
  156. err := verifyPVCCreationFails(client, namespace, scParameters, zones, "")
  157. errorMsg := "No matching datastores found in the kubernetes cluster for zone " + zoneC
  158. if !strings.Contains(err.Error(), errorMsg) {
  159. framework.ExpectNoError(err, errorMsg)
  160. }
  161. })
  162. ginkgo.It("Verify a pod is created and attached to a dynamically created PV, based on the allowed zones and storage policy specified in storage class", func() {
  163. ginkgo.By(fmt.Sprintf("Creating storage class with zone :%s and storage policy :%s", zoneA, compatPolicy))
  164. scParameters[SpbmStoragePolicy] = compatPolicy
  165. zones = append(zones, zoneA)
  166. verifyPVCAndPodCreationSucceeds(client, namespace, scParameters, zones, "")
  167. })
  168. ginkgo.It("Verify a pod is created on a non-Workspace zone and attached to a dynamically created PV, based on the allowed zones and storage policy specified in storage class", func() {
  169. ginkgo.By(fmt.Sprintf("Creating storage class with zone :%s and storage policy :%s", zoneB, compatPolicy))
  170. scParameters[SpbmStoragePolicy] = compatPolicy
  171. zones = append(zones, zoneB)
  172. verifyPVCAndPodCreationSucceeds(client, namespace, scParameters, zones, "")
  173. })
  174. ginkgo.It("Verify PVC creation with incompatible storagePolicy and zone combination specified in storage class fails", func() {
  175. ginkgo.By(fmt.Sprintf("Creating storage class with zone :%s and storage policy :%s", zoneA, nonCompatPolicy))
  176. scParameters[SpbmStoragePolicy] = nonCompatPolicy
  177. zones = append(zones, zoneA)
  178. err := verifyPVCCreationFails(client, namespace, scParameters, zones, "")
  179. errorMsg := "No compatible datastores found that satisfy the storage policy requirements"
  180. if !strings.Contains(err.Error(), errorMsg) {
  181. framework.ExpectNoError(err, errorMsg)
  182. }
  183. })
  184. ginkgo.It("Verify a pod is created and attached to a dynamically created PV, based on the allowed zones, datastore and storage policy specified in storage class", func() {
  185. ginkgo.By(fmt.Sprintf("Creating storage class with zone :%s datastore :%s and storagePolicy :%s", zoneA, vsanDatastore1, compatPolicy))
  186. scParameters[SpbmStoragePolicy] = compatPolicy
  187. scParameters[Datastore] = vsanDatastore1
  188. zones = append(zones, zoneA)
  189. verifyPVCAndPodCreationSucceeds(client, namespace, scParameters, zones, "")
  190. })
  191. ginkgo.It("Verify PVC creation with incompatible storage policy along with compatible zone and datastore combination specified in storage class fails", func() {
  192. ginkgo.By(fmt.Sprintf("Creating storage class with zone :%s datastore :%s and storagePolicy :%s", zoneA, vsanDatastore1, nonCompatPolicy))
  193. scParameters[SpbmStoragePolicy] = nonCompatPolicy
  194. scParameters[Datastore] = vsanDatastore1
  195. zones = append(zones, zoneA)
  196. err := verifyPVCCreationFails(client, namespace, scParameters, zones, "")
  197. errorMsg := "User specified datastore is not compatible with the storagePolicy: \\\"" + nonCompatPolicy + "\\\"."
  198. if !strings.Contains(err.Error(), errorMsg) {
  199. framework.ExpectNoError(err, errorMsg)
  200. }
  201. })
  202. ginkgo.It("Verify PVC creation with incompatible zone along with compatible storagePolicy and datastore combination specified in storage class fails", func() {
  203. ginkgo.By(fmt.Sprintf("Creating storage class with zone :%s datastore :%s and storagePolicy :%s", zoneC, vsanDatastore2, compatPolicy))
  204. scParameters[SpbmStoragePolicy] = compatPolicy
  205. scParameters[Datastore] = vsanDatastore2
  206. zones = append(zones, zoneC)
  207. err := verifyPVCCreationFails(client, namespace, scParameters, zones, "")
  208. errorMsg := "No matching datastores found in the kubernetes cluster for zone " + zoneC
  209. if !strings.Contains(err.Error(), errorMsg) {
  210. framework.ExpectNoError(err, errorMsg)
  211. }
  212. })
  213. ginkgo.It("Verify PVC creation fails if no zones are specified in the storage class (No shared datastores exist among all the nodes)", func() {
  214. ginkgo.By(fmt.Sprintf("Creating storage class with no zones"))
  215. err := verifyPVCCreationFails(client, namespace, nil, nil, "")
  216. errorMsg := "No shared datastores found in the Kubernetes cluster"
  217. if !strings.Contains(err.Error(), errorMsg) {
  218. framework.ExpectNoError(err, errorMsg)
  219. }
  220. })
  221. ginkgo.It("Verify PVC creation fails if only datastore is specified in the storage class (No shared datastores exist among all the nodes)", func() {
  222. ginkgo.By(fmt.Sprintf("Creating storage class with datastore :%s", vsanDatastore1))
  223. scParameters[Datastore] = vsanDatastore1
  224. err := verifyPVCCreationFails(client, namespace, scParameters, nil, "")
  225. errorMsg := "No shared datastores found in the Kubernetes cluster"
  226. if !strings.Contains(err.Error(), errorMsg) {
  227. framework.ExpectNoError(err, errorMsg)
  228. }
  229. })
  230. ginkgo.It("Verify PVC creation fails if only storage policy is specified in the storage class (No shared datastores exist among all the nodes)", func() {
  231. ginkgo.By(fmt.Sprintf("Creating storage class with storage policy :%s", compatPolicy))
  232. scParameters[SpbmStoragePolicy] = compatPolicy
  233. err := verifyPVCCreationFails(client, namespace, scParameters, nil, "")
  234. errorMsg := "No shared datastores found in the Kubernetes cluster"
  235. if !strings.Contains(err.Error(), errorMsg) {
  236. framework.ExpectNoError(err, errorMsg)
  237. }
  238. })
  239. ginkgo.It("Verify PVC creation with compatible policy and datastore without any zones specified in the storage class fails (No shared datastores exist among all the nodes)", func() {
  240. ginkgo.By(fmt.Sprintf("Creating storage class with storage policy :%s and datastore :%s", compatPolicy, vsanDatastore1))
  241. scParameters[SpbmStoragePolicy] = compatPolicy
  242. scParameters[Datastore] = vsanDatastore1
  243. err := verifyPVCCreationFails(client, namespace, scParameters, nil, "")
  244. errorMsg := "No shared datastores found in the Kubernetes cluster"
  245. if !strings.Contains(err.Error(), errorMsg) {
  246. framework.ExpectNoError(err, errorMsg)
  247. }
  248. })
  249. ginkgo.It("Verify PVC creation fails if the availability zone specified in the storage class have no shared datastores under it.", func() {
  250. ginkgo.By(fmt.Sprintf("Creating storage class with zone :%s", zoneC))
  251. zones = append(zones, zoneC)
  252. err := verifyPVCCreationFails(client, namespace, nil, zones, "")
  253. errorMsg := "No matching datastores found in the kubernetes cluster for zone " + zoneC
  254. if !strings.Contains(err.Error(), errorMsg) {
  255. framework.ExpectNoError(err, errorMsg)
  256. }
  257. })
  258. ginkgo.It("Verify a pod is created and attached to a dynamically created PV, based on multiple zones specified in the storage class. (No shared datastores exist among both zones)", func() {
  259. ginkgo.By(fmt.Sprintf("Creating storage class with the following zones :%s and %s", zoneA, zoneC))
  260. zones = append(zones, zoneA)
  261. zones = append(zones, zoneC)
  262. err := verifyPVCCreationFails(client, namespace, nil, zones, "")
  263. errorMsg := "No matching datastores found in the kubernetes cluster for zone " + zoneC
  264. if !strings.Contains(err.Error(), errorMsg) {
  265. framework.ExpectNoError(err, errorMsg)
  266. }
  267. })
  268. ginkgo.It("Verify PVC creation with an invalid VSAN capability along with a compatible zone combination specified in storage class fails", func() {
  269. ginkgo.By(fmt.Sprintf("Creating storage class with %s :%s and zone :%s", PolicyHostFailuresToTolerate, HostFailuresToTolerateCapabilityInvalidVal, zoneA))
  270. scParameters[PolicyHostFailuresToTolerate] = HostFailuresToTolerateCapabilityInvalidVal
  271. zones = append(zones, zoneA)
  272. err := verifyPVCCreationFails(client, namespace, scParameters, zones, "")
  273. errorMsg := "Invalid value for " + PolicyHostFailuresToTolerate + "."
  274. if !strings.Contains(err.Error(), errorMsg) {
  275. framework.ExpectNoError(err, errorMsg)
  276. }
  277. })
  278. ginkgo.It("Verify a pod is created and attached to a dynamically created PV, based on a VSAN capability, datastore and compatible zone specified in storage class", func() {
  279. ginkgo.By(fmt.Sprintf("Creating storage class with %s :%s, %s :%s, datastore :%s and zone :%s", PolicyObjectSpaceReservation, ObjectSpaceReservationCapabilityVal, PolicyIopsLimit, IopsLimitCapabilityVal, vsanDatastore1, zoneA))
  280. scParameters[PolicyObjectSpaceReservation] = ObjectSpaceReservationCapabilityVal
  281. scParameters[PolicyIopsLimit] = IopsLimitCapabilityVal
  282. scParameters[Datastore] = vsanDatastore1
  283. zones = append(zones, zoneA)
  284. verifyPVCAndPodCreationSucceeds(client, namespace, scParameters, zones, "")
  285. })
  286. ginkgo.It("Verify a pod is created and attached to a dynamically created PV, based on the allowed zones specified in storage class when the datastore under the zone is present in another datacenter", func() {
  287. ginkgo.By(fmt.Sprintf("Creating storage class with zone :%s", zoneD))
  288. zones = append(zones, zoneD)
  289. verifyPVCAndPodCreationSucceeds(client, namespace, scParameters, zones, "")
  290. })
  291. ginkgo.It("Verify a pod is created and attached to a dynamically created PV, based on the allowed zones and datastore specified in storage class when there are multiple datastores with the same name under different zones across datacenters", func() {
  292. ginkgo.By(fmt.Sprintf("Creating storage class with zone :%s and datastore name :%s", zoneD, localDatastore))
  293. scParameters[Datastore] = localDatastore
  294. zones = append(zones, zoneD)
  295. verifyPVCAndPodCreationSucceeds(client, namespace, scParameters, zones, "")
  296. })
  297. ginkgo.It("Verify a pod is created and attached to a dynamically created PV with storage policy specified in storage class in waitForFirstConsumer binding mode", func() {
  298. ginkgo.By(fmt.Sprintf("Creating storage class with waitForFirstConsumer mode and storage policy :%s", compatPolicy))
  299. scParameters[SpbmStoragePolicy] = compatPolicy
  300. verifyPVCAndPodCreationSucceeds(client, namespace, scParameters, nil, storagev1.VolumeBindingWaitForFirstConsumer)
  301. })
  302. ginkgo.It("Verify a pod is created and attached to a dynamically created PV with storage policy specified in storage class in waitForFirstConsumer binding mode with allowedTopologies", func() {
  303. ginkgo.By(fmt.Sprintf("Creating storage class with waitForFirstConsumer mode, storage policy :%s and zone :%s", compatPolicy, zoneA))
  304. scParameters[SpbmStoragePolicy] = compatPolicy
  305. zones = append(zones, zoneA)
  306. verifyPVCAndPodCreationSucceeds(client, namespace, scParameters, zones, storagev1.VolumeBindingWaitForFirstConsumer)
  307. })
  308. ginkgo.It("Verify a pod is created and attached to a dynamically created PV with storage policy specified in storage class in waitForFirstConsumer binding mode with multiple allowedTopologies", func() {
  309. ginkgo.By(fmt.Sprintf("Creating storage class with waitForFirstConsumer mode and zones : %s, %s", zoneA, zoneB))
  310. zones = append(zones, zoneA)
  311. zones = append(zones, zoneB)
  312. verifyPVCAndPodCreationSucceeds(client, namespace, nil, zones, storagev1.VolumeBindingWaitForFirstConsumer)
  313. })
  314. ginkgo.It("Verify a PVC creation fails when multiple zones are specified in the storage class without shared datastores among the zones in waitForFirstConsumer binding mode", func() {
  315. ginkgo.By(fmt.Sprintf("Creating storage class with waitForFirstConsumer mode and following zones :%s and %s", zoneA, zoneC))
  316. zones = append(zones, zoneA)
  317. zones = append(zones, zoneC)
  318. err := verifyPodAndPvcCreationFailureOnWaitForFirstConsumerMode(client, namespace, nil, zones)
  319. framework.ExpectError(err)
  320. errorMsg := "No matching datastores found in the kubernetes cluster for zone " + zoneC
  321. if !strings.Contains(err.Error(), errorMsg) {
  322. framework.ExpectNoError(err, errorMsg)
  323. }
  324. })
  325. ginkgo.It("Verify a pod fails to get scheduled when conflicting volume topology (allowedTopologies) and pod scheduling constraints(nodeSelector) are specified", func() {
  326. ginkgo.By(fmt.Sprintf("Creating storage class with waitForFirstConsumerMode, storage policy :%s and zone :%s", compatPolicy, zoneA))
  327. scParameters[SpbmStoragePolicy] = compatPolicy
  328. // allowedTopologies set as zoneA
  329. zones = append(zones, zoneA)
  330. nodeSelectorMap := map[string]string{
  331. // nodeSelector set as zoneB
  332. v1.LabelZoneFailureDomain: zoneB,
  333. }
  334. verifyPodSchedulingFails(client, namespace, nodeSelectorMap, scParameters, zones, storagev1.VolumeBindingWaitForFirstConsumer)
  335. })
  336. })
  337. func verifyPVCAndPodCreationSucceeds(client clientset.Interface, namespace string, scParameters map[string]string, zones []string, volumeBindingMode storagev1.VolumeBindingMode) {
  338. storageclass, err := client.StorageV1().StorageClasses().Create(context.TODO(), getVSphereStorageClassSpec("zone-sc", scParameters, zones, volumeBindingMode), metav1.CreateOptions{})
  339. framework.ExpectNoError(err, fmt.Sprintf("Failed to create storage class with err: %v", err))
  340. defer client.StorageV1().StorageClasses().Delete(context.TODO(), storageclass.Name, nil)
  341. ginkgo.By("Creating PVC using the Storage Class")
  342. pvclaim, err := e2epv.CreatePVC(client, namespace, getVSphereClaimSpecWithStorageClass(namespace, "2Gi", storageclass))
  343. framework.ExpectNoError(err)
  344. defer e2epv.DeletePersistentVolumeClaim(client, pvclaim.Name, namespace)
  345. var pvclaims []*v1.PersistentVolumeClaim
  346. pvclaims = append(pvclaims, pvclaim)
  347. var persistentvolumes []*v1.PersistentVolume
  348. // If WaitForFirstConsumer mode, verify pvc binding status after pod creation. For immediate mode, do now.
  349. if volumeBindingMode != storagev1.VolumeBindingWaitForFirstConsumer {
  350. persistentvolumes = waitForPVClaimBoundPhase(client, pvclaims, framework.ClaimProvisionTimeout)
  351. }
  352. ginkgo.By("Creating pod to attach PV to the node")
  353. pod, err := e2epod.CreatePod(client, namespace, nil, pvclaims, false, "")
  354. framework.ExpectNoError(err)
  355. if volumeBindingMode == storagev1.VolumeBindingWaitForFirstConsumer {
  356. persistentvolumes = waitForPVClaimBoundPhase(client, pvclaims, framework.ClaimProvisionTimeout)
  357. }
  358. if zones != nil {
  359. ginkgo.By("Verify persistent volume was created on the right zone")
  360. verifyVolumeCreationOnRightZone(persistentvolumes, pod.Spec.NodeName, zones)
  361. }
  362. ginkgo.By("Verify the volume is accessible and available in the pod")
  363. verifyVSphereVolumesAccessible(client, pod, persistentvolumes)
  364. ginkgo.By("Deleting pod")
  365. e2epod.DeletePodWithWait(client, pod)
  366. ginkgo.By("Waiting for volumes to be detached from the node")
  367. waitForVSphereDiskToDetach(persistentvolumes[0].Spec.VsphereVolume.VolumePath, pod.Spec.NodeName)
  368. }
  369. func verifyPodAndPvcCreationFailureOnWaitForFirstConsumerMode(client clientset.Interface, namespace string, scParameters map[string]string, zones []string) error {
  370. storageclass, err := client.StorageV1().StorageClasses().Create(context.TODO(), getVSphereStorageClassSpec("zone-sc", scParameters, zones, storagev1.VolumeBindingWaitForFirstConsumer), metav1.CreateOptions{})
  371. framework.ExpectNoError(err, fmt.Sprintf("Failed to create storage class with err: %v", err))
  372. defer client.StorageV1().StorageClasses().Delete(context.TODO(), storageclass.Name, nil)
  373. ginkgo.By("Creating PVC using the Storage Class")
  374. pvclaim, err := e2epv.CreatePVC(client, namespace, getVSphereClaimSpecWithStorageClass(namespace, "2Gi", storageclass))
  375. framework.ExpectNoError(err)
  376. defer e2epv.DeletePersistentVolumeClaim(client, pvclaim.Name, namespace)
  377. var pvclaims []*v1.PersistentVolumeClaim
  378. pvclaims = append(pvclaims, pvclaim)
  379. ginkgo.By("Creating a pod")
  380. pod := e2epod.MakePod(namespace, nil, pvclaims, false, "")
  381. pod, err = client.CoreV1().Pods(namespace).Create(context.TODO(), pod, metav1.CreateOptions{})
  382. framework.ExpectNoError(err)
  383. defer e2epod.DeletePodWithWait(client, pod)
  384. ginkgo.By("Waiting for claim to be in bound phase")
  385. err = e2epv.WaitForPersistentVolumeClaimPhase(v1.ClaimBound, client, pvclaim.Namespace, pvclaim.Name, framework.Poll, 2*time.Minute)
  386. framework.ExpectError(err)
  387. eventList, err := client.CoreV1().Events(pvclaim.Namespace).List(context.TODO(), metav1.ListOptions{})
  388. framework.ExpectNoError(err)
  389. // Look for PVC ProvisioningFailed event and return the message.
  390. for _, event := range eventList.Items {
  391. if event.Source.Component == "persistentvolume-controller" && event.Reason == volumeevents.ProvisioningFailed {
  392. return fmt.Errorf("Failure message: %s", event.Message)
  393. }
  394. }
  395. return nil
  396. }
  397. func waitForPVClaimBoundPhase(client clientset.Interface, pvclaims []*v1.PersistentVolumeClaim, timeout time.Duration) []*v1.PersistentVolume {
  398. ginkgo.By("Waiting for claim to be in bound phase")
  399. persistentvolumes, err := e2epv.WaitForPVClaimBoundPhase(client, pvclaims, timeout)
  400. framework.ExpectNoError(err)
  401. return persistentvolumes
  402. }
  403. func verifyPodSchedulingFails(client clientset.Interface, namespace string, nodeSelector map[string]string, scParameters map[string]string, zones []string, volumeBindingMode storagev1.VolumeBindingMode) {
  404. storageclass, err := client.StorageV1().StorageClasses().Create(context.TODO(), getVSphereStorageClassSpec("zone-sc", scParameters, zones, volumeBindingMode), metav1.CreateOptions{})
  405. framework.ExpectNoError(err, fmt.Sprintf("Failed to create storage class with err: %v", err))
  406. defer client.StorageV1().StorageClasses().Delete(context.TODO(), storageclass.Name, nil)
  407. ginkgo.By("Creating PVC using the Storage Class")
  408. pvclaim, err := e2epv.CreatePVC(client, namespace, getVSphereClaimSpecWithStorageClass(namespace, "2Gi", storageclass))
  409. framework.ExpectNoError(err)
  410. defer e2epv.DeletePersistentVolumeClaim(client, pvclaim.Name, namespace)
  411. var pvclaims []*v1.PersistentVolumeClaim
  412. pvclaims = append(pvclaims, pvclaim)
  413. ginkgo.By("Creating a pod")
  414. pod, err := e2epod.CreateUnschedulablePod(client, namespace, nodeSelector, pvclaims, false, "")
  415. framework.ExpectNoError(err)
  416. defer e2epod.DeletePodWithWait(client, pod)
  417. }
  418. func verifyPVCCreationFails(client clientset.Interface, namespace string, scParameters map[string]string, zones []string, volumeBindingMode storagev1.VolumeBindingMode) error {
  419. storageclass, err := client.StorageV1().StorageClasses().Create(context.TODO(), getVSphereStorageClassSpec("zone-sc", scParameters, zones, volumeBindingMode), metav1.CreateOptions{})
  420. framework.ExpectNoError(err, fmt.Sprintf("Failed to create storage class with err: %v", err))
  421. defer client.StorageV1().StorageClasses().Delete(context.TODO(), storageclass.Name, nil)
  422. ginkgo.By("Creating PVC using the Storage Class")
  423. pvclaim, err := e2epv.CreatePVC(client, namespace, getVSphereClaimSpecWithStorageClass(namespace, "2Gi", storageclass))
  424. framework.ExpectNoError(err)
  425. defer e2epv.DeletePersistentVolumeClaim(client, pvclaim.Name, namespace)
  426. ginkgo.By("Waiting for claim to be in bound phase")
  427. err = e2epv.WaitForPersistentVolumeClaimPhase(v1.ClaimBound, client, pvclaim.Namespace, pvclaim.Name, framework.Poll, 2*time.Minute)
  428. framework.ExpectError(err)
  429. eventList, err := client.CoreV1().Events(pvclaim.Namespace).List(context.TODO(), metav1.ListOptions{})
  430. framework.ExpectNoError(err)
  431. framework.Logf("Failure message : %+q", eventList.Items[0].Message)
  432. return fmt.Errorf("Failure message: %+q", eventList.Items[0].Message)
  433. }
  434. func verifyPVZoneLabels(client clientset.Interface, namespace string, scParameters map[string]string, zones []string) {
  435. storageclass, err := client.StorageV1().StorageClasses().Create(context.TODO(), getVSphereStorageClassSpec("zone-sc", nil, zones, ""), metav1.CreateOptions{})
  436. framework.ExpectNoError(err, fmt.Sprintf("Failed to create storage class with err: %v", err))
  437. defer client.StorageV1().StorageClasses().Delete(context.TODO(), storageclass.Name, nil)
  438. ginkgo.By("Creating PVC using the storage class")
  439. pvclaim, err := e2epv.CreatePVC(client, namespace, getVSphereClaimSpecWithStorageClass(namespace, "2Gi", storageclass))
  440. framework.ExpectNoError(err)
  441. defer e2epv.DeletePersistentVolumeClaim(client, pvclaim.Name, namespace)
  442. var pvclaims []*v1.PersistentVolumeClaim
  443. pvclaims = append(pvclaims, pvclaim)
  444. ginkgo.By("Waiting for claim to be in bound phase")
  445. persistentvolumes, err := e2epv.WaitForPVClaimBoundPhase(client, pvclaims, framework.ClaimProvisionTimeout)
  446. framework.ExpectNoError(err)
  447. ginkgo.By("Verify zone information is present in the volume labels")
  448. for _, pv := range persistentvolumes {
  449. // Multiple zones are separated with "__"
  450. pvZoneLabels := strings.Split(pv.ObjectMeta.Labels["failure-domain.beta.kubernetes.io/zone"], "__")
  451. for _, zone := range zones {
  452. gomega.Expect(pvZoneLabels).Should(gomega.ContainElement(zone), "Incorrect or missing zone labels in pv.")
  453. }
  454. }
  455. }