pv_util.go 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056
  1. /*
  2. Copyright 2015 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 framework
  14. import (
  15. "fmt"
  16. "time"
  17. "github.com/onsi/ginkgo"
  18. v1 "k8s.io/api/core/v1"
  19. apierrs "k8s.io/apimachinery/pkg/api/errors"
  20. "k8s.io/apimachinery/pkg/api/resource"
  21. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  22. "k8s.io/apimachinery/pkg/labels"
  23. "k8s.io/apimachinery/pkg/types"
  24. "k8s.io/apimachinery/pkg/util/uuid"
  25. clientset "k8s.io/client-go/kubernetes"
  26. storageutil "k8s.io/kubernetes/pkg/apis/storage/v1/util"
  27. "k8s.io/kubernetes/pkg/volume/util"
  28. e2elog "k8s.io/kubernetes/test/e2e/framework/log"
  29. imageutils "k8s.io/kubernetes/test/utils/image"
  30. )
  31. const (
  32. pdRetryTimeout = 5 * time.Minute
  33. pdRetryPollTime = 5 * time.Second
  34. // VolumeSelectorKey is the key for volume selector.
  35. VolumeSelectorKey = "e2e-pv-pool"
  36. )
  37. var (
  38. // SELinuxLabel is common selinux labels.
  39. SELinuxLabel = &v1.SELinuxOptions{
  40. Level: "s0:c0,c1"}
  41. )
  42. type pvval struct{}
  43. // PVMap is a map of all PVs used in the multi pv-pvc tests. The key is the PV's name, which is
  44. // guaranteed to be unique. The value is {} (empty struct) since we're only interested
  45. // in the PV's name and if it is present. We must always Get the pv object before
  46. // referencing any of its values, eg its ClaimRef.
  47. type PVMap map[string]pvval
  48. type pvcval struct{}
  49. // PVCMap is a map of all PVCs used in the multi pv-pvc tests. The key is "namespace/pvc.Name". The
  50. // value is {} (empty struct) since we're only interested in the PVC's name and if it is
  51. // present. We must always Get the pvc object before referencing any of its values, eg.
  52. // its VolumeName.
  53. // Note: It's unsafe to add keys to a map in a loop. Their insertion in the map is
  54. // unpredictable and can result in the same key being iterated over again.
  55. type PVCMap map[types.NamespacedName]pvcval
  56. // PersistentVolumeConfig is consumed by MakePersistentVolume() to generate a PV object
  57. // for varying storage options (NFS, ceph, glusterFS, etc.).
  58. // (+optional) prebind holds a pre-bound PVC
  59. // Example pvSource:
  60. // pvSource: api.PersistentVolumeSource{
  61. // NFS: &api.NFSVolumeSource{
  62. // ...
  63. // },
  64. // }
  65. type PersistentVolumeConfig struct {
  66. PVSource v1.PersistentVolumeSource
  67. Prebind *v1.PersistentVolumeClaim
  68. ReclaimPolicy v1.PersistentVolumeReclaimPolicy
  69. NamePrefix string
  70. Labels labels.Set
  71. StorageClassName string
  72. NodeAffinity *v1.VolumeNodeAffinity
  73. VolumeMode *v1.PersistentVolumeMode
  74. }
  75. // PersistentVolumeClaimConfig is consumed by MakePersistentVolumeClaim() to generate a PVC object.
  76. // AccessModes defaults to all modes (RWO, RWX, ROX) if left empty
  77. // (+optional) Annotations defines the PVC's annotations
  78. type PersistentVolumeClaimConfig struct {
  79. AccessModes []v1.PersistentVolumeAccessMode
  80. Annotations map[string]string
  81. Selector *metav1.LabelSelector
  82. StorageClassName *string
  83. VolumeMode *v1.PersistentVolumeMode
  84. }
  85. // NodeSelection specifies where to run a pod, using a combination of fixed node name,
  86. // node selector and/or affinity.
  87. type NodeSelection struct {
  88. Name string
  89. Selector map[string]string
  90. Affinity *v1.Affinity
  91. }
  92. // PVPVCCleanup cleans up a pv and pvc in a single pv/pvc test case.
  93. // Note: delete errors are appended to []error so that we can attempt to delete both the pvc and pv.
  94. func PVPVCCleanup(c clientset.Interface, ns string, pv *v1.PersistentVolume, pvc *v1.PersistentVolumeClaim) []error {
  95. var errs []error
  96. if pvc != nil {
  97. err := DeletePersistentVolumeClaim(c, pvc.Name, ns)
  98. if err != nil {
  99. errs = append(errs, fmt.Errorf("failed to delete PVC %q: %v", pvc.Name, err))
  100. }
  101. } else {
  102. e2elog.Logf("pvc is nil")
  103. }
  104. if pv != nil {
  105. err := DeletePersistentVolume(c, pv.Name)
  106. if err != nil {
  107. errs = append(errs, fmt.Errorf("failed to delete PV %q: %v", pv.Name, err))
  108. }
  109. } else {
  110. e2elog.Logf("pv is nil")
  111. }
  112. return errs
  113. }
  114. // PVPVCMapCleanup Cleans up pvs and pvcs in multi-pv-pvc test cases. Entries found in the pv and claim maps are
  115. // deleted as long as the Delete api call succeeds.
  116. // Note: delete errors are appended to []error so that as many pvcs and pvs as possible are deleted.
  117. func PVPVCMapCleanup(c clientset.Interface, ns string, pvols PVMap, claims PVCMap) []error {
  118. var errs []error
  119. for pvcKey := range claims {
  120. err := DeletePersistentVolumeClaim(c, pvcKey.Name, ns)
  121. if err != nil {
  122. errs = append(errs, fmt.Errorf("failed to delete PVC %q: %v", pvcKey.Name, err))
  123. } else {
  124. delete(claims, pvcKey)
  125. }
  126. }
  127. for pvKey := range pvols {
  128. err := DeletePersistentVolume(c, pvKey)
  129. if err != nil {
  130. errs = append(errs, fmt.Errorf("failed to delete PV %q: %v", pvKey, err))
  131. } else {
  132. delete(pvols, pvKey)
  133. }
  134. }
  135. return errs
  136. }
  137. // DeletePersistentVolume deletes the PV.
  138. func DeletePersistentVolume(c clientset.Interface, pvName string) error {
  139. if c != nil && len(pvName) > 0 {
  140. e2elog.Logf("Deleting PersistentVolume %q", pvName)
  141. err := c.CoreV1().PersistentVolumes().Delete(pvName, nil)
  142. if err != nil && !apierrs.IsNotFound(err) {
  143. return fmt.Errorf("PV Delete API error: %v", err)
  144. }
  145. }
  146. return nil
  147. }
  148. // DeletePersistentVolumeClaim deletes the Claim.
  149. func DeletePersistentVolumeClaim(c clientset.Interface, pvcName string, ns string) error {
  150. if c != nil && len(pvcName) > 0 {
  151. e2elog.Logf("Deleting PersistentVolumeClaim %q", pvcName)
  152. err := c.CoreV1().PersistentVolumeClaims(ns).Delete(pvcName, nil)
  153. if err != nil && !apierrs.IsNotFound(err) {
  154. return fmt.Errorf("PVC Delete API error: %v", err)
  155. }
  156. }
  157. return nil
  158. }
  159. // DeletePVCandValidatePV deletes the PVC and waits for the PV to enter its expected phase. Validate that the PV
  160. // has been reclaimed (assumption here about reclaimPolicy). Caller tells this func which
  161. // phase value to expect for the pv bound to the to-be-deleted claim.
  162. func DeletePVCandValidatePV(c clientset.Interface, ns string, pvc *v1.PersistentVolumeClaim, pv *v1.PersistentVolume, expectPVPhase v1.PersistentVolumePhase) error {
  163. pvname := pvc.Spec.VolumeName
  164. e2elog.Logf("Deleting PVC %v to trigger reclamation of PV %v", pvc.Name, pvname)
  165. err := DeletePersistentVolumeClaim(c, pvc.Name, ns)
  166. if err != nil {
  167. return err
  168. }
  169. // Wait for the PV's phase to return to be `expectPVPhase`
  170. e2elog.Logf("Waiting for reclaim process to complete.")
  171. err = WaitForPersistentVolumePhase(expectPVPhase, c, pv.Name, Poll, PVReclaimingTimeout)
  172. if err != nil {
  173. return fmt.Errorf("pv %q phase did not become %v: %v", pv.Name, expectPVPhase, err)
  174. }
  175. // examine the pv's ClaimRef and UID and compare to expected values
  176. pv, err = c.CoreV1().PersistentVolumes().Get(pv.Name, metav1.GetOptions{})
  177. if err != nil {
  178. return fmt.Errorf("PV Get API error: %v", err)
  179. }
  180. cr := pv.Spec.ClaimRef
  181. if expectPVPhase == v1.VolumeAvailable {
  182. if cr != nil && len(cr.UID) > 0 {
  183. return fmt.Errorf("PV is 'Available' but ClaimRef.UID is not empty")
  184. }
  185. } else if expectPVPhase == v1.VolumeBound {
  186. if cr == nil {
  187. return fmt.Errorf("PV is 'Bound' but ClaimRef is nil")
  188. }
  189. if len(cr.UID) == 0 {
  190. return fmt.Errorf("PV is 'Bound' but ClaimRef.UID is empty")
  191. }
  192. }
  193. e2elog.Logf("PV %v now in %q phase", pv.Name, expectPVPhase)
  194. return nil
  195. }
  196. // DeletePVCandValidatePVGroup wraps deletePVCandValidatePV() by calling the function in a loop over the PV map. Only bound PVs
  197. // are deleted. Validates that the claim was deleted and the PV is in the expected Phase (Released,
  198. // Available, Bound).
  199. // Note: if there are more claims than pvs then some of the remaining claims may bind to just made
  200. // available pvs.
  201. func DeletePVCandValidatePVGroup(c clientset.Interface, ns string, pvols PVMap, claims PVCMap, expectPVPhase v1.PersistentVolumePhase) error {
  202. var boundPVs, deletedPVCs int
  203. for pvName := range pvols {
  204. pv, err := c.CoreV1().PersistentVolumes().Get(pvName, metav1.GetOptions{})
  205. if err != nil {
  206. return fmt.Errorf("PV Get API error: %v", err)
  207. }
  208. cr := pv.Spec.ClaimRef
  209. // if pv is bound then delete the pvc it is bound to
  210. if cr != nil && len(cr.Name) > 0 {
  211. boundPVs++
  212. // Assert bound PVC is tracked in this test. Failing this might
  213. // indicate external PVCs interfering with the test.
  214. pvcKey := makePvcKey(ns, cr.Name)
  215. if _, found := claims[pvcKey]; !found {
  216. return fmt.Errorf("internal: claims map is missing pvc %q", pvcKey)
  217. }
  218. // get the pvc for the delete call below
  219. pvc, err := c.CoreV1().PersistentVolumeClaims(ns).Get(cr.Name, metav1.GetOptions{})
  220. if err == nil {
  221. if err = DeletePVCandValidatePV(c, ns, pvc, pv, expectPVPhase); err != nil {
  222. return err
  223. }
  224. } else if !apierrs.IsNotFound(err) {
  225. return fmt.Errorf("PVC Get API error: %v", err)
  226. }
  227. // delete pvckey from map even if apierrs.IsNotFound above is true and thus the
  228. // claim was not actually deleted here
  229. delete(claims, pvcKey)
  230. deletedPVCs++
  231. }
  232. }
  233. if boundPVs != deletedPVCs {
  234. return fmt.Errorf("expect number of bound PVs (%v) to equal number of deleted PVCs (%v)", boundPVs, deletedPVCs)
  235. }
  236. return nil
  237. }
  238. // create the PV resource. Fails test on error.
  239. func createPV(c clientset.Interface, pv *v1.PersistentVolume) (*v1.PersistentVolume, error) {
  240. pv, err := c.CoreV1().PersistentVolumes().Create(pv)
  241. if err != nil {
  242. return nil, fmt.Errorf("PV Create API error: %v", err)
  243. }
  244. return pv, nil
  245. }
  246. // CreatePV creates the PV resource. Fails test on error.
  247. func CreatePV(c clientset.Interface, pv *v1.PersistentVolume) (*v1.PersistentVolume, error) {
  248. return createPV(c, pv)
  249. }
  250. // CreatePVC creates the PVC resource. Fails test on error.
  251. func CreatePVC(c clientset.Interface, ns string, pvc *v1.PersistentVolumeClaim) (*v1.PersistentVolumeClaim, error) {
  252. pvc, err := c.CoreV1().PersistentVolumeClaims(ns).Create(pvc)
  253. if err != nil {
  254. return nil, fmt.Errorf("PVC Create API error: %v", err)
  255. }
  256. return pvc, nil
  257. }
  258. // CreatePVCPV creates a PVC followed by the PV based on the passed in nfs-server ip and
  259. // namespace. If the "preBind" bool is true then pre-bind the PV to the PVC
  260. // via the PV's ClaimRef. Return the pv and pvc to reflect the created objects.
  261. // Note: in the pre-bind case the real PVC name, which is generated, is not
  262. // known until after the PVC is instantiated. This is why the pvc is created
  263. // before the pv.
  264. func CreatePVCPV(c clientset.Interface, pvConfig PersistentVolumeConfig, pvcConfig PersistentVolumeClaimConfig, ns string, preBind bool) (*v1.PersistentVolume, *v1.PersistentVolumeClaim, error) {
  265. // make the pvc spec
  266. pvc := MakePersistentVolumeClaim(pvcConfig, ns)
  267. preBindMsg := ""
  268. if preBind {
  269. preBindMsg = " pre-bound"
  270. pvConfig.Prebind = pvc
  271. }
  272. // make the pv spec
  273. pv := MakePersistentVolume(pvConfig)
  274. ginkgo.By(fmt.Sprintf("Creating a PVC followed by a%s PV", preBindMsg))
  275. pvc, err := CreatePVC(c, ns, pvc)
  276. if err != nil {
  277. return nil, nil, err
  278. }
  279. // instantiate the pv, handle pre-binding by ClaimRef if needed
  280. if preBind {
  281. pv.Spec.ClaimRef.Name = pvc.Name
  282. }
  283. pv, err = createPV(c, pv)
  284. if err != nil {
  285. return nil, pvc, err
  286. }
  287. return pv, pvc, nil
  288. }
  289. // CreatePVPVC creates a PV followed by the PVC based on the passed in nfs-server ip and
  290. // namespace. If the "preBind" bool is true then pre-bind the PVC to the PV
  291. // via the PVC's VolumeName. Return the pv and pvc to reflect the created
  292. // objects.
  293. // Note: in the pre-bind case the real PV name, which is generated, is not
  294. // known until after the PV is instantiated. This is why the pv is created
  295. // before the pvc.
  296. func CreatePVPVC(c clientset.Interface, pvConfig PersistentVolumeConfig, pvcConfig PersistentVolumeClaimConfig, ns string, preBind bool) (*v1.PersistentVolume, *v1.PersistentVolumeClaim, error) {
  297. preBindMsg := ""
  298. if preBind {
  299. preBindMsg = " pre-bound"
  300. }
  301. e2elog.Logf("Creating a PV followed by a%s PVC", preBindMsg)
  302. // make the pv and pvc definitions
  303. pv := MakePersistentVolume(pvConfig)
  304. pvc := MakePersistentVolumeClaim(pvcConfig, ns)
  305. // instantiate the pv
  306. pv, err := createPV(c, pv)
  307. if err != nil {
  308. return nil, nil, err
  309. }
  310. // instantiate the pvc, handle pre-binding by VolumeName if needed
  311. if preBind {
  312. pvc.Spec.VolumeName = pv.Name
  313. }
  314. pvc, err = CreatePVC(c, ns, pvc)
  315. if err != nil {
  316. return pv, nil, err
  317. }
  318. return pv, pvc, nil
  319. }
  320. // CreatePVsPVCs creates the desired number of PVs and PVCs and returns them in separate maps. If the
  321. // number of PVs != the number of PVCs then the min of those two counts is the number of
  322. // PVs expected to bind. If a Create error occurs, the returned maps may contain pv and pvc
  323. // entries for the resources that were successfully created. In other words, when the caller
  324. // sees an error returned, it needs to decide what to do about entries in the maps.
  325. // Note: when the test suite deletes the namespace orphaned pvcs and pods are deleted. However,
  326. // orphaned pvs are not deleted and will remain after the suite completes.
  327. func CreatePVsPVCs(numpvs, numpvcs int, c clientset.Interface, ns string, pvConfig PersistentVolumeConfig, pvcConfig PersistentVolumeClaimConfig) (PVMap, PVCMap, error) {
  328. pvMap := make(PVMap, numpvs)
  329. pvcMap := make(PVCMap, numpvcs)
  330. extraPVCs := 0
  331. extraPVs := numpvs - numpvcs
  332. if extraPVs < 0 {
  333. extraPVCs = -extraPVs
  334. extraPVs = 0
  335. }
  336. pvsToCreate := numpvs - extraPVs // want the min(numpvs, numpvcs)
  337. // create pvs and pvcs
  338. for i := 0; i < pvsToCreate; i++ {
  339. pv, pvc, err := CreatePVPVC(c, pvConfig, pvcConfig, ns, false)
  340. if err != nil {
  341. return pvMap, pvcMap, err
  342. }
  343. pvMap[pv.Name] = pvval{}
  344. pvcMap[makePvcKey(ns, pvc.Name)] = pvcval{}
  345. }
  346. // create extra pvs or pvcs as needed
  347. for i := 0; i < extraPVs; i++ {
  348. pv := MakePersistentVolume(pvConfig)
  349. pv, err := createPV(c, pv)
  350. if err != nil {
  351. return pvMap, pvcMap, err
  352. }
  353. pvMap[pv.Name] = pvval{}
  354. }
  355. for i := 0; i < extraPVCs; i++ {
  356. pvc := MakePersistentVolumeClaim(pvcConfig, ns)
  357. pvc, err := CreatePVC(c, ns, pvc)
  358. if err != nil {
  359. return pvMap, pvcMap, err
  360. }
  361. pvcMap[makePvcKey(ns, pvc.Name)] = pvcval{}
  362. }
  363. return pvMap, pvcMap, nil
  364. }
  365. // WaitOnPVandPVC waits for the pv and pvc to bind to each other.
  366. func WaitOnPVandPVC(c clientset.Interface, ns string, pv *v1.PersistentVolume, pvc *v1.PersistentVolumeClaim) error {
  367. // Wait for newly created PVC to bind to the PV
  368. e2elog.Logf("Waiting for PV %v to bind to PVC %v", pv.Name, pvc.Name)
  369. err := WaitForPersistentVolumeClaimPhase(v1.ClaimBound, c, ns, pvc.Name, Poll, ClaimBindingTimeout)
  370. if err != nil {
  371. return fmt.Errorf("PVC %q did not become Bound: %v", pvc.Name, err)
  372. }
  373. // Wait for PersistentVolume.Status.Phase to be Bound, which it should be
  374. // since the PVC is already bound.
  375. err = WaitForPersistentVolumePhase(v1.VolumeBound, c, pv.Name, Poll, PVBindingTimeout)
  376. if err != nil {
  377. return fmt.Errorf("PV %q did not become Bound: %v", pv.Name, err)
  378. }
  379. // Re-get the pv and pvc objects
  380. pv, err = c.CoreV1().PersistentVolumes().Get(pv.Name, metav1.GetOptions{})
  381. if err != nil {
  382. return fmt.Errorf("PV Get API error: %v", err)
  383. }
  384. pvc, err = c.CoreV1().PersistentVolumeClaims(ns).Get(pvc.Name, metav1.GetOptions{})
  385. if err != nil {
  386. return fmt.Errorf("PVC Get API error: %v", err)
  387. }
  388. // The pv and pvc are both bound, but to each other?
  389. // Check that the PersistentVolume.ClaimRef matches the PVC
  390. if pv.Spec.ClaimRef == nil {
  391. return fmt.Errorf("PV %q ClaimRef is nil", pv.Name)
  392. }
  393. if pv.Spec.ClaimRef.Name != pvc.Name {
  394. return fmt.Errorf("PV %q ClaimRef's name (%q) should be %q", pv.Name, pv.Spec.ClaimRef.Name, pvc.Name)
  395. }
  396. if pvc.Spec.VolumeName != pv.Name {
  397. return fmt.Errorf("PVC %q VolumeName (%q) should be %q", pvc.Name, pvc.Spec.VolumeName, pv.Name)
  398. }
  399. if pv.Spec.ClaimRef.UID != pvc.UID {
  400. return fmt.Errorf("PV %q ClaimRef's UID (%q) should be %q", pv.Name, pv.Spec.ClaimRef.UID, pvc.UID)
  401. }
  402. return nil
  403. }
  404. // WaitAndVerifyBinds searches for bound PVs and PVCs by examining pvols for non-nil claimRefs.
  405. // NOTE: Each iteration waits for a maximum of 3 minutes per PV and, if the PV is bound,
  406. // up to 3 minutes for the PVC. When the number of PVs != number of PVCs, this can lead
  407. // to situations where the maximum wait times are reached several times in succession,
  408. // extending test time. Thus, it is recommended to keep the delta between PVs and PVCs
  409. // small.
  410. func WaitAndVerifyBinds(c clientset.Interface, ns string, pvols PVMap, claims PVCMap, testExpected bool) error {
  411. var actualBinds int
  412. expectedBinds := len(pvols)
  413. if expectedBinds > len(claims) { // want the min of # pvs or #pvcs
  414. expectedBinds = len(claims)
  415. }
  416. for pvName := range pvols {
  417. err := WaitForPersistentVolumePhase(v1.VolumeBound, c, pvName, Poll, PVBindingTimeout)
  418. if err != nil && len(pvols) > len(claims) {
  419. e2elog.Logf("WARN: pv %v is not bound after max wait", pvName)
  420. e2elog.Logf(" This may be ok since there are more pvs than pvcs")
  421. continue
  422. }
  423. if err != nil {
  424. return fmt.Errorf("PV %q did not become Bound: %v", pvName, err)
  425. }
  426. pv, err := c.CoreV1().PersistentVolumes().Get(pvName, metav1.GetOptions{})
  427. if err != nil {
  428. return fmt.Errorf("PV Get API error: %v", err)
  429. }
  430. cr := pv.Spec.ClaimRef
  431. if cr != nil && len(cr.Name) > 0 {
  432. // Assert bound pvc is a test resource. Failing assertion could
  433. // indicate non-test PVC interference or a bug in the test
  434. pvcKey := makePvcKey(ns, cr.Name)
  435. if _, found := claims[pvcKey]; !found {
  436. return fmt.Errorf("internal: claims map is missing pvc %q", pvcKey)
  437. }
  438. err := WaitForPersistentVolumeClaimPhase(v1.ClaimBound, c, ns, cr.Name, Poll, ClaimBindingTimeout)
  439. if err != nil {
  440. return fmt.Errorf("PVC %q did not become Bound: %v", cr.Name, err)
  441. }
  442. actualBinds++
  443. }
  444. }
  445. if testExpected && actualBinds != expectedBinds {
  446. return fmt.Errorf("expect number of bound PVs (%v) to equal number of claims (%v)", actualBinds, expectedBinds)
  447. }
  448. return nil
  449. }
  450. // Test the pod's exit code to be zero.
  451. func testPodSuccessOrFail(c clientset.Interface, ns string, pod *v1.Pod) error {
  452. ginkgo.By("Pod should terminate with exitcode 0 (success)")
  453. if err := WaitForPodSuccessInNamespace(c, pod.Name, ns); err != nil {
  454. return fmt.Errorf("pod %q failed to reach Success: %v", pod.Name, err)
  455. }
  456. e2elog.Logf("Pod %v succeeded ", pod.Name)
  457. return nil
  458. }
  459. // DeletePodWithWait deletes the passed-in pod and waits for the pod to be terminated. Resilient to the pod
  460. // not existing.
  461. func DeletePodWithWait(f *Framework, c clientset.Interface, pod *v1.Pod) error {
  462. if pod == nil {
  463. return nil
  464. }
  465. return DeletePodWithWaitByName(f, c, pod.GetName(), pod.GetNamespace())
  466. }
  467. // DeletePodWithWaitByName deletes the named and namespaced pod and waits for the pod to be terminated. Resilient to the pod
  468. // not existing.
  469. func DeletePodWithWaitByName(f *Framework, c clientset.Interface, podName, podNamespace string) error {
  470. e2elog.Logf("Deleting pod %q in namespace %q", podName, podNamespace)
  471. err := c.CoreV1().Pods(podNamespace).Delete(podName, nil)
  472. if err != nil {
  473. if apierrs.IsNotFound(err) {
  474. return nil // assume pod was already deleted
  475. }
  476. return fmt.Errorf("pod Delete API error: %v", err)
  477. }
  478. e2elog.Logf("Wait up to %v for pod %q to be fully deleted", PodDeleteTimeout, podName)
  479. err = f.WaitForPodNotFound(podName, PodDeleteTimeout)
  480. if err != nil {
  481. return fmt.Errorf("pod %q was not deleted: %v", podName, err)
  482. }
  483. return nil
  484. }
  485. // CreateWaitAndDeletePod creates the test pod, wait for (hopefully) success, and then delete the pod.
  486. // Note: need named return value so that the err assignment in the defer sets the returned error.
  487. // Has been shown to be necessary using Go 1.7.
  488. func CreateWaitAndDeletePod(f *Framework, c clientset.Interface, ns string, pvc *v1.PersistentVolumeClaim) (err error) {
  489. e2elog.Logf("Creating nfs test pod")
  490. pod := MakeWritePod(ns, pvc)
  491. runPod, err := c.CoreV1().Pods(ns).Create(pod)
  492. if err != nil {
  493. return fmt.Errorf("pod Create API error: %v", err)
  494. }
  495. defer func() {
  496. delErr := DeletePodWithWait(f, c, runPod)
  497. if err == nil { // don't override previous err value
  498. err = delErr // assign to returned err, can be nil
  499. }
  500. }()
  501. err = testPodSuccessOrFail(c, ns, runPod)
  502. if err != nil {
  503. return fmt.Errorf("pod %q did not exit with Success: %v", runPod.Name, err)
  504. }
  505. return // note: named return value
  506. }
  507. // Return a pvckey struct.
  508. func makePvcKey(ns, name string) types.NamespacedName {
  509. return types.NamespacedName{Namespace: ns, Name: name}
  510. }
  511. // MakePersistentVolume returns a PV definition based on the nfs server IP. If the PVC is not nil
  512. // then the PV is defined with a ClaimRef which includes the PVC's namespace.
  513. // If the PVC is nil then the PV is not defined with a ClaimRef. If no reclaimPolicy
  514. // is assigned, assumes "Retain". Specs are expected to match the test's PVC.
  515. // Note: the passed-in claim does not have a name until it is created and thus the PV's
  516. // ClaimRef cannot be completely filled-in in this func. Therefore, the ClaimRef's name
  517. // is added later in CreatePVCPV.
  518. func MakePersistentVolume(pvConfig PersistentVolumeConfig) *v1.PersistentVolume {
  519. var claimRef *v1.ObjectReference
  520. // If the reclaimPolicy is not provided, assume Retain
  521. if pvConfig.ReclaimPolicy == "" {
  522. e2elog.Logf("PV ReclaimPolicy unspecified, default: Retain")
  523. pvConfig.ReclaimPolicy = v1.PersistentVolumeReclaimRetain
  524. }
  525. if pvConfig.Prebind != nil {
  526. claimRef = &v1.ObjectReference{
  527. Name: pvConfig.Prebind.Name,
  528. Namespace: pvConfig.Prebind.Namespace,
  529. }
  530. }
  531. return &v1.PersistentVolume{
  532. ObjectMeta: metav1.ObjectMeta{
  533. GenerateName: pvConfig.NamePrefix,
  534. Labels: pvConfig.Labels,
  535. Annotations: map[string]string{
  536. util.VolumeGidAnnotationKey: "777",
  537. },
  538. },
  539. Spec: v1.PersistentVolumeSpec{
  540. PersistentVolumeReclaimPolicy: pvConfig.ReclaimPolicy,
  541. Capacity: v1.ResourceList{
  542. v1.ResourceName(v1.ResourceStorage): resource.MustParse("2Gi"),
  543. },
  544. PersistentVolumeSource: pvConfig.PVSource,
  545. AccessModes: []v1.PersistentVolumeAccessMode{
  546. v1.ReadWriteOnce,
  547. v1.ReadOnlyMany,
  548. v1.ReadWriteMany,
  549. },
  550. ClaimRef: claimRef,
  551. StorageClassName: pvConfig.StorageClassName,
  552. NodeAffinity: pvConfig.NodeAffinity,
  553. VolumeMode: pvConfig.VolumeMode,
  554. },
  555. }
  556. }
  557. // MakePersistentVolumeClaim returns a PVC definition based on the namespace.
  558. // Note: if this PVC is intended to be pre-bound to a PV, whose name is not
  559. // known until the PV is instantiated, then the func CreatePVPVC will add
  560. // pvc.Spec.VolumeName to this claim.
  561. func MakePersistentVolumeClaim(cfg PersistentVolumeClaimConfig, ns string) *v1.PersistentVolumeClaim {
  562. // Specs are expected to match this test's PersistentVolume
  563. if len(cfg.AccessModes) == 0 {
  564. e2elog.Logf("AccessModes unspecified, default: all modes (RWO, RWX, ROX).")
  565. cfg.AccessModes = append(cfg.AccessModes, v1.ReadWriteOnce, v1.ReadOnlyMany, v1.ReadOnlyMany)
  566. }
  567. return &v1.PersistentVolumeClaim{
  568. ObjectMeta: metav1.ObjectMeta{
  569. GenerateName: "pvc-",
  570. Namespace: ns,
  571. Annotations: cfg.Annotations,
  572. },
  573. Spec: v1.PersistentVolumeClaimSpec{
  574. Selector: cfg.Selector,
  575. AccessModes: cfg.AccessModes,
  576. Resources: v1.ResourceRequirements{
  577. Requests: v1.ResourceList{
  578. v1.ResourceName(v1.ResourceStorage): resource.MustParse("1Gi"),
  579. },
  580. },
  581. StorageClassName: cfg.StorageClassName,
  582. VolumeMode: cfg.VolumeMode,
  583. },
  584. }
  585. }
  586. func createPDWithRetry(zone string) (string, error) {
  587. var err error
  588. for start := time.Now(); time.Since(start) < pdRetryTimeout; time.Sleep(pdRetryPollTime) {
  589. newDiskName, err := createPD(zone)
  590. if err != nil {
  591. e2elog.Logf("Couldn't create a new PD, sleeping 5 seconds: %v", err)
  592. continue
  593. }
  594. e2elog.Logf("Successfully created a new PD: %q.", newDiskName)
  595. return newDiskName, nil
  596. }
  597. return "", err
  598. }
  599. // CreatePDWithRetry creates PD with retry.
  600. func CreatePDWithRetry() (string, error) {
  601. return createPDWithRetry("")
  602. }
  603. // CreatePDWithRetryAndZone creates PD on zone with retry.
  604. func CreatePDWithRetryAndZone(zone string) (string, error) {
  605. return createPDWithRetry(zone)
  606. }
  607. // DeletePDWithRetry deletes PD with retry.
  608. func DeletePDWithRetry(diskName string) error {
  609. var err error
  610. for start := time.Now(); time.Since(start) < pdRetryTimeout; time.Sleep(pdRetryPollTime) {
  611. err = deletePD(diskName)
  612. if err != nil {
  613. e2elog.Logf("Couldn't delete PD %q, sleeping %v: %v", diskName, pdRetryPollTime, err)
  614. continue
  615. }
  616. e2elog.Logf("Successfully deleted PD %q.", diskName)
  617. return nil
  618. }
  619. return fmt.Errorf("unable to delete PD %q: %v", diskName, err)
  620. }
  621. func createPD(zone string) (string, error) {
  622. if zone == "" {
  623. zone = TestContext.CloudConfig.Zone
  624. }
  625. return TestContext.CloudConfig.Provider.CreatePD(zone)
  626. }
  627. func deletePD(pdName string) error {
  628. return TestContext.CloudConfig.Provider.DeletePD(pdName)
  629. }
  630. // MakeWritePod returns a pod definition based on the namespace. The pod references the PVC's
  631. // name.
  632. func MakeWritePod(ns string, pvc *v1.PersistentVolumeClaim) *v1.Pod {
  633. return MakePod(ns, nil, []*v1.PersistentVolumeClaim{pvc}, true, "touch /mnt/volume1/SUCCESS && (id -G | grep -E '\\b777\\b')")
  634. }
  635. // MakePod returns a pod definition based on the namespace. The pod references the PVC's
  636. // name. A slice of BASH commands can be supplied as args to be run by the pod
  637. func MakePod(ns string, nodeSelector map[string]string, pvclaims []*v1.PersistentVolumeClaim, isPrivileged bool, command string) *v1.Pod {
  638. if len(command) == 0 {
  639. command = "trap exit TERM; while true; do sleep 1; done"
  640. }
  641. podSpec := &v1.Pod{
  642. TypeMeta: metav1.TypeMeta{
  643. Kind: "Pod",
  644. APIVersion: "v1",
  645. },
  646. ObjectMeta: metav1.ObjectMeta{
  647. GenerateName: "pvc-tester-",
  648. Namespace: ns,
  649. },
  650. Spec: v1.PodSpec{
  651. Containers: []v1.Container{
  652. {
  653. Name: "write-pod",
  654. Image: BusyBoxImage,
  655. Command: []string{"/bin/sh"},
  656. Args: []string{"-c", command},
  657. SecurityContext: &v1.SecurityContext{
  658. Privileged: &isPrivileged,
  659. },
  660. },
  661. },
  662. RestartPolicy: v1.RestartPolicyOnFailure,
  663. },
  664. }
  665. var volumeMounts = make([]v1.VolumeMount, len(pvclaims))
  666. var volumes = make([]v1.Volume, len(pvclaims))
  667. for index, pvclaim := range pvclaims {
  668. volumename := fmt.Sprintf("volume%v", index+1)
  669. volumeMounts[index] = v1.VolumeMount{Name: volumename, MountPath: "/mnt/" + volumename}
  670. volumes[index] = v1.Volume{Name: volumename, VolumeSource: v1.VolumeSource{PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ClaimName: pvclaim.Name, ReadOnly: false}}}
  671. }
  672. podSpec.Spec.Containers[0].VolumeMounts = volumeMounts
  673. podSpec.Spec.Volumes = volumes
  674. if nodeSelector != nil {
  675. podSpec.Spec.NodeSelector = nodeSelector
  676. }
  677. return podSpec
  678. }
  679. // makeNginxPod returns a pod definition based on the namespace using nginx image
  680. func makeNginxPod(ns string, nodeSelector map[string]string, pvclaims []*v1.PersistentVolumeClaim) *v1.Pod {
  681. podSpec := &v1.Pod{
  682. TypeMeta: metav1.TypeMeta{
  683. Kind: "Pod",
  684. APIVersion: "v1",
  685. },
  686. ObjectMeta: metav1.ObjectMeta{
  687. GenerateName: "pvc-tester-",
  688. Namespace: ns,
  689. },
  690. Spec: v1.PodSpec{
  691. Containers: []v1.Container{
  692. {
  693. Name: "write-pod",
  694. Image: "nginx",
  695. Ports: []v1.ContainerPort{
  696. {
  697. Name: "http-server",
  698. ContainerPort: 80,
  699. },
  700. },
  701. },
  702. },
  703. },
  704. }
  705. var volumeMounts = make([]v1.VolumeMount, len(pvclaims))
  706. var volumes = make([]v1.Volume, len(pvclaims))
  707. for index, pvclaim := range pvclaims {
  708. volumename := fmt.Sprintf("volume%v", index+1)
  709. volumeMounts[index] = v1.VolumeMount{Name: volumename, MountPath: "/mnt/" + volumename}
  710. volumes[index] = v1.Volume{Name: volumename, VolumeSource: v1.VolumeSource{PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ClaimName: pvclaim.Name, ReadOnly: false}}}
  711. }
  712. podSpec.Spec.Containers[0].VolumeMounts = volumeMounts
  713. podSpec.Spec.Volumes = volumes
  714. if nodeSelector != nil {
  715. podSpec.Spec.NodeSelector = nodeSelector
  716. }
  717. return podSpec
  718. }
  719. // MakeSecPod returns a pod definition based on the namespace. The pod references the PVC's
  720. // name. A slice of BASH commands can be supplied as args to be run by the pod.
  721. // SELinux testing requires to pass HostIPC and HostPID as booleansi arguments.
  722. func MakeSecPod(ns string, pvclaims []*v1.PersistentVolumeClaim, isPrivileged bool, command string, hostIPC bool, hostPID bool, seLinuxLabel *v1.SELinuxOptions, fsGroup *int64) *v1.Pod {
  723. if len(command) == 0 {
  724. command = "trap exit TERM; while true; do sleep 1; done"
  725. }
  726. podName := "security-context-" + string(uuid.NewUUID())
  727. if fsGroup == nil {
  728. fsGroup = func(i int64) *int64 {
  729. return &i
  730. }(1000)
  731. }
  732. podSpec := &v1.Pod{
  733. TypeMeta: metav1.TypeMeta{
  734. Kind: "Pod",
  735. APIVersion: "v1",
  736. },
  737. ObjectMeta: metav1.ObjectMeta{
  738. Name: podName,
  739. Namespace: ns,
  740. },
  741. Spec: v1.PodSpec{
  742. HostIPC: hostIPC,
  743. HostPID: hostPID,
  744. SecurityContext: &v1.PodSecurityContext{
  745. FSGroup: fsGroup,
  746. },
  747. Containers: []v1.Container{
  748. {
  749. Name: "write-pod",
  750. Image: imageutils.GetE2EImage(imageutils.BusyBox),
  751. Command: []string{"/bin/sh"},
  752. Args: []string{"-c", command},
  753. SecurityContext: &v1.SecurityContext{
  754. Privileged: &isPrivileged,
  755. },
  756. },
  757. },
  758. RestartPolicy: v1.RestartPolicyOnFailure,
  759. },
  760. }
  761. var volumeMounts = make([]v1.VolumeMount, 0)
  762. var volumeDevices = make([]v1.VolumeDevice, 0)
  763. var volumes = make([]v1.Volume, len(pvclaims))
  764. for index, pvclaim := range pvclaims {
  765. volumename := fmt.Sprintf("volume%v", index+1)
  766. if pvclaim.Spec.VolumeMode != nil && *pvclaim.Spec.VolumeMode == v1.PersistentVolumeBlock {
  767. volumeDevices = append(volumeDevices, v1.VolumeDevice{Name: volumename, DevicePath: "/mnt/" + volumename})
  768. } else {
  769. volumeMounts = append(volumeMounts, v1.VolumeMount{Name: volumename, MountPath: "/mnt/" + volumename})
  770. }
  771. volumes[index] = v1.Volume{Name: volumename, VolumeSource: v1.VolumeSource{PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ClaimName: pvclaim.Name, ReadOnly: false}}}
  772. }
  773. podSpec.Spec.Containers[0].VolumeMounts = volumeMounts
  774. podSpec.Spec.Containers[0].VolumeDevices = volumeDevices
  775. podSpec.Spec.Volumes = volumes
  776. podSpec.Spec.SecurityContext.SELinuxOptions = seLinuxLabel
  777. return podSpec
  778. }
  779. // CreatePod with given claims based on node selector
  780. func CreatePod(client clientset.Interface, namespace string, nodeSelector map[string]string, pvclaims []*v1.PersistentVolumeClaim, isPrivileged bool, command string) (*v1.Pod, error) {
  781. pod := MakePod(namespace, nodeSelector, pvclaims, isPrivileged, command)
  782. pod, err := client.CoreV1().Pods(namespace).Create(pod)
  783. if err != nil {
  784. return nil, fmt.Errorf("pod Create API error: %v", err)
  785. }
  786. // Waiting for pod to be running
  787. err = WaitForPodNameRunningInNamespace(client, pod.Name, namespace)
  788. if err != nil {
  789. return pod, fmt.Errorf("pod %q is not Running: %v", pod.Name, err)
  790. }
  791. // get fresh pod info
  792. pod, err = client.CoreV1().Pods(namespace).Get(pod.Name, metav1.GetOptions{})
  793. if err != nil {
  794. return pod, fmt.Errorf("pod Get API error: %v", err)
  795. }
  796. return pod, nil
  797. }
  798. // CreateNginxPod creates an enginx pod.
  799. func CreateNginxPod(client clientset.Interface, namespace string, nodeSelector map[string]string, pvclaims []*v1.PersistentVolumeClaim) (*v1.Pod, error) {
  800. pod := makeNginxPod(namespace, nodeSelector, pvclaims)
  801. pod, err := client.CoreV1().Pods(namespace).Create(pod)
  802. if err != nil {
  803. return nil, fmt.Errorf("pod Create API error: %v", err)
  804. }
  805. // Waiting for pod to be running
  806. err = WaitForPodNameRunningInNamespace(client, pod.Name, namespace)
  807. if err != nil {
  808. return pod, fmt.Errorf("pod %q is not Running: %v", pod.Name, err)
  809. }
  810. // get fresh pod info
  811. pod, err = client.CoreV1().Pods(namespace).Get(pod.Name, metav1.GetOptions{})
  812. if err != nil {
  813. return pod, fmt.Errorf("pod Get API error: %v", err)
  814. }
  815. return pod, nil
  816. }
  817. // CreateSecPod creates security pod with given claims
  818. func CreateSecPod(client clientset.Interface, namespace string, pvclaims []*v1.PersistentVolumeClaim, isPrivileged bool, command string, hostIPC bool, hostPID bool, seLinuxLabel *v1.SELinuxOptions, fsGroup *int64, timeout time.Duration) (*v1.Pod, error) {
  819. return CreateSecPodWithNodeSelection(client, namespace, pvclaims, isPrivileged, command, hostIPC, hostPID, seLinuxLabel, fsGroup, NodeSelection{}, timeout)
  820. }
  821. // CreateSecPodWithNodeSelection creates security pod with given claims
  822. func CreateSecPodWithNodeSelection(client clientset.Interface, namespace string, pvclaims []*v1.PersistentVolumeClaim, isPrivileged bool, command string, hostIPC bool, hostPID bool, seLinuxLabel *v1.SELinuxOptions, fsGroup *int64, node NodeSelection, timeout time.Duration) (*v1.Pod, error) {
  823. pod := MakeSecPod(namespace, pvclaims, isPrivileged, command, hostIPC, hostPID, seLinuxLabel, fsGroup)
  824. // Setting node
  825. pod.Spec.NodeName = node.Name
  826. pod.Spec.NodeSelector = node.Selector
  827. pod.Spec.Affinity = node.Affinity
  828. pod, err := client.CoreV1().Pods(namespace).Create(pod)
  829. if err != nil {
  830. return nil, fmt.Errorf("pod Create API error: %v", err)
  831. }
  832. // Waiting for pod to be running
  833. err = WaitTimeoutForPodRunningInNamespace(client, pod.Name, namespace, timeout)
  834. if err != nil {
  835. return pod, fmt.Errorf("pod %q is not Running: %v", pod.Name, err)
  836. }
  837. // get fresh pod info
  838. pod, err = client.CoreV1().Pods(namespace).Get(pod.Name, metav1.GetOptions{})
  839. if err != nil {
  840. return pod, fmt.Errorf("pod Get API error: %v", err)
  841. }
  842. return pod, nil
  843. }
  844. // SetNodeAffinityRequirement sets affinity with specified operator to nodeName to nodeSelection
  845. func SetNodeAffinityRequirement(nodeSelection *NodeSelection, operator v1.NodeSelectorOperator, nodeName string) {
  846. // Add node-anti-affinity.
  847. if nodeSelection.Affinity == nil {
  848. nodeSelection.Affinity = &v1.Affinity{}
  849. }
  850. if nodeSelection.Affinity.NodeAffinity == nil {
  851. nodeSelection.Affinity.NodeAffinity = &v1.NodeAffinity{}
  852. }
  853. if nodeSelection.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution == nil {
  854. nodeSelection.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution = &v1.NodeSelector{}
  855. }
  856. nodeSelection.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms = append(nodeSelection.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms,
  857. v1.NodeSelectorTerm{
  858. MatchFields: []v1.NodeSelectorRequirement{
  859. {Key: "metadata.name", Operator: operator, Values: []string{nodeName}},
  860. },
  861. })
  862. }
  863. // SetAffinity sets affinity to nodeName to nodeSelection
  864. func SetAffinity(nodeSelection *NodeSelection, nodeName string) {
  865. SetNodeAffinityRequirement(nodeSelection, v1.NodeSelectorOpIn, nodeName)
  866. }
  867. // SetAntiAffinity sets anti-affinity to nodeName to nodeSelection
  868. func SetAntiAffinity(nodeSelection *NodeSelection, nodeName string) {
  869. SetNodeAffinityRequirement(nodeSelection, v1.NodeSelectorOpNotIn, nodeName)
  870. }
  871. // CreateClientPod defines and creates a pod with a mounted PV. Pod runs infinite loop until killed.
  872. func CreateClientPod(c clientset.Interface, ns string, pvc *v1.PersistentVolumeClaim) (*v1.Pod, error) {
  873. return CreatePod(c, ns, nil, []*v1.PersistentVolumeClaim{pvc}, true, "")
  874. }
  875. // CreateUnschedulablePod with given claims based on node selector
  876. func CreateUnschedulablePod(client clientset.Interface, namespace string, nodeSelector map[string]string, pvclaims []*v1.PersistentVolumeClaim, isPrivileged bool, command string) (*v1.Pod, error) {
  877. pod := MakePod(namespace, nodeSelector, pvclaims, isPrivileged, command)
  878. pod, err := client.CoreV1().Pods(namespace).Create(pod)
  879. if err != nil {
  880. return nil, fmt.Errorf("pod Create API error: %v", err)
  881. }
  882. // Waiting for pod to become Unschedulable
  883. err = WaitForPodNameUnschedulableInNamespace(client, pod.Name, namespace)
  884. if err != nil {
  885. return pod, fmt.Errorf("pod %q is not Unschedulable: %v", pod.Name, err)
  886. }
  887. // get fresh pod info
  888. pod, err = client.CoreV1().Pods(namespace).Get(pod.Name, metav1.GetOptions{})
  889. if err != nil {
  890. return pod, fmt.Errorf("pod Get API error: %v", err)
  891. }
  892. return pod, nil
  893. }
  894. // WaitForPVClaimBoundPhase waits until all pvcs phase set to bound
  895. func WaitForPVClaimBoundPhase(client clientset.Interface, pvclaims []*v1.PersistentVolumeClaim, timeout time.Duration) ([]*v1.PersistentVolume, error) {
  896. persistentvolumes := make([]*v1.PersistentVolume, len(pvclaims))
  897. for index, claim := range pvclaims {
  898. err := WaitForPersistentVolumeClaimPhase(v1.ClaimBound, client, claim.Namespace, claim.Name, Poll, timeout)
  899. if err != nil {
  900. return persistentvolumes, err
  901. }
  902. // Get new copy of the claim
  903. claim, err = client.CoreV1().PersistentVolumeClaims(claim.Namespace).Get(claim.Name, metav1.GetOptions{})
  904. if err != nil {
  905. return persistentvolumes, fmt.Errorf("PVC Get API error: %v", err)
  906. }
  907. // Get the bounded PV
  908. persistentvolumes[index], err = client.CoreV1().PersistentVolumes().Get(claim.Spec.VolumeName, metav1.GetOptions{})
  909. if err != nil {
  910. return persistentvolumes, fmt.Errorf("PV Get API error: %v", err)
  911. }
  912. }
  913. return persistentvolumes, nil
  914. }
  915. // CreatePVSource creates a PV source.
  916. func CreatePVSource(zone string) (*v1.PersistentVolumeSource, error) {
  917. diskName, err := CreatePDWithRetryAndZone(zone)
  918. if err != nil {
  919. return nil, err
  920. }
  921. return TestContext.CloudConfig.Provider.CreatePVSource(zone, diskName)
  922. }
  923. // DeletePVSource deletes a PV source.
  924. func DeletePVSource(pvSource *v1.PersistentVolumeSource) error {
  925. return TestContext.CloudConfig.Provider.DeletePVSource(pvSource)
  926. }
  927. // GetBoundPV returns a PV details.
  928. func GetBoundPV(client clientset.Interface, pvc *v1.PersistentVolumeClaim) (*v1.PersistentVolume, error) {
  929. // Get new copy of the claim
  930. claim, err := client.CoreV1().PersistentVolumeClaims(pvc.Namespace).Get(pvc.Name, metav1.GetOptions{})
  931. if err != nil {
  932. return nil, err
  933. }
  934. // Get the bound PV
  935. pv, err := client.CoreV1().PersistentVolumes().Get(claim.Spec.VolumeName, metav1.GetOptions{})
  936. return pv, err
  937. }
  938. // GetDefaultStorageClassName returns default storageClass or return error
  939. func GetDefaultStorageClassName(c clientset.Interface) (string, error) {
  940. list, err := c.StorageV1().StorageClasses().List(metav1.ListOptions{})
  941. if err != nil {
  942. return "", fmt.Errorf("Error listing storage classes: %v", err)
  943. }
  944. var scName string
  945. for _, sc := range list.Items {
  946. if storageutil.IsDefaultAnnotation(sc.ObjectMeta) {
  947. if len(scName) != 0 {
  948. return "", fmt.Errorf("Multiple default storage classes found: %q and %q", scName, sc.Name)
  949. }
  950. scName = sc.Name
  951. }
  952. }
  953. if len(scName) == 0 {
  954. return "", fmt.Errorf("No default storage class found")
  955. }
  956. e2elog.Logf("Default storage class: %q", scName)
  957. return scName, nil
  958. }
  959. // SkipIfNoDefaultStorageClass skips tests if no default SC can be found.
  960. func SkipIfNoDefaultStorageClass(c clientset.Interface) {
  961. _, err := GetDefaultStorageClassName(c)
  962. if err != nil {
  963. Skipf("error finding default storageClass : %v", err)
  964. }
  965. }