recycle_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. /*
  2. Copyright 2016 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 persistentvolume
  14. import (
  15. "errors"
  16. "testing"
  17. "k8s.io/api/core/v1"
  18. storage "k8s.io/api/storage/v1"
  19. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  20. pvtesting "k8s.io/kubernetes/pkg/controller/volume/persistentvolume/testing"
  21. pvutil "k8s.io/kubernetes/pkg/controller/volume/persistentvolume/util"
  22. )
  23. // Test single call to syncVolume, expecting recycling to happen.
  24. // 1. Fill in the controller with initial data
  25. // 2. Call the syncVolume *once*.
  26. // 3. Compare resulting volumes with expected volumes.
  27. func TestRecycleSync(t *testing.T) {
  28. runningPod := &v1.Pod{
  29. ObjectMeta: metav1.ObjectMeta{
  30. Name: "runningPod",
  31. Namespace: testNamespace,
  32. },
  33. Spec: v1.PodSpec{
  34. Volumes: []v1.Volume{
  35. {
  36. Name: "vol1",
  37. VolumeSource: v1.VolumeSource{
  38. PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
  39. ClaimName: "runningClaim",
  40. },
  41. },
  42. },
  43. },
  44. },
  45. Status: v1.PodStatus{
  46. Phase: v1.PodRunning,
  47. },
  48. }
  49. pendingPod := runningPod.DeepCopy()
  50. pendingPod.Name = "pendingPod"
  51. pendingPod.Status.Phase = v1.PodPending
  52. pendingPod.Spec.Volumes[0].PersistentVolumeClaim.ClaimName = "pendingClaim"
  53. completedPod := runningPod.DeepCopy()
  54. completedPod.Name = "completedPod"
  55. completedPod.Status.Phase = v1.PodSucceeded
  56. completedPod.Spec.Volumes[0].PersistentVolumeClaim.ClaimName = "completedClaim"
  57. pods := []*v1.Pod{
  58. runningPod,
  59. pendingPod,
  60. completedPod,
  61. }
  62. tests := []controllerTest{
  63. {
  64. // recycle volume bound by controller
  65. "6-1 - successful recycle",
  66. newVolumeArray("volume6-1", "1Gi", "uid6-1", "claim6-1", v1.VolumeBound, v1.PersistentVolumeReclaimRecycle, classEmpty, pvutil.AnnBoundByController),
  67. newVolumeArray("volume6-1", "1Gi", "", "", v1.VolumeAvailable, v1.PersistentVolumeReclaimRecycle, classEmpty),
  68. noclaims,
  69. noclaims,
  70. noevents, noerrors,
  71. // Inject recycler into the controller and call syncVolume. The
  72. // recycler simulates one recycle() call that succeeds.
  73. wrapTestWithReclaimCalls(operationRecycle, []error{nil}, testSyncVolume),
  74. },
  75. {
  76. // recycle volume bound by user
  77. "6-2 - successful recycle with prebound volume",
  78. newVolumeArray("volume6-2", "1Gi", "uid6-2", "claim6-2", v1.VolumeBound, v1.PersistentVolumeReclaimRecycle, classEmpty),
  79. newVolumeArray("volume6-2", "1Gi", "", "claim6-2", v1.VolumeAvailable, v1.PersistentVolumeReclaimRecycle, classEmpty),
  80. noclaims,
  81. noclaims,
  82. noevents, noerrors,
  83. // Inject recycler into the controller and call syncVolume. The
  84. // recycler simulates one recycle() call that succeeds.
  85. wrapTestWithReclaimCalls(operationRecycle, []error{nil}, testSyncVolume),
  86. },
  87. {
  88. // recycle failure - plugin not found
  89. "6-3 - plugin not found",
  90. newVolumeArray("volume6-3", "1Gi", "uid6-3", "claim6-3", v1.VolumeBound, v1.PersistentVolumeReclaimRecycle, classEmpty),
  91. withMessage("No recycler plugin found for the volume!", newVolumeArray("volume6-3", "1Gi", "uid6-3", "claim6-3", v1.VolumeFailed, v1.PersistentVolumeReclaimRecycle, classEmpty)),
  92. noclaims,
  93. noclaims,
  94. []string{"Warning VolumeFailedRecycle"}, noerrors, testSyncVolume,
  95. },
  96. {
  97. // recycle failure - Recycle returns error
  98. "6-4 - newRecycler returns error",
  99. newVolumeArray("volume6-4", "1Gi", "uid6-4", "claim6-4", v1.VolumeBound, v1.PersistentVolumeReclaimRecycle, classEmpty),
  100. withMessage("Recycle failed: Mock plugin error: no recycleCalls configured", newVolumeArray("volume6-4", "1Gi", "uid6-4", "claim6-4", v1.VolumeFailed, v1.PersistentVolumeReclaimRecycle, classEmpty)),
  101. noclaims,
  102. noclaims,
  103. []string{"Warning VolumeFailedRecycle"}, noerrors,
  104. wrapTestWithReclaimCalls(operationRecycle, []error{}, testSyncVolume),
  105. },
  106. {
  107. // recycle failure - recycle returns error
  108. "6-5 - recycle returns error",
  109. newVolumeArray("volume6-5", "1Gi", "uid6-5", "claim6-5", v1.VolumeBound, v1.PersistentVolumeReclaimRecycle, classEmpty),
  110. withMessage("Recycle failed: Mock recycle error", newVolumeArray("volume6-5", "1Gi", "uid6-5", "claim6-5", v1.VolumeFailed, v1.PersistentVolumeReclaimRecycle, classEmpty)),
  111. noclaims,
  112. noclaims,
  113. []string{"Warning VolumeFailedRecycle"}, noerrors,
  114. wrapTestWithReclaimCalls(operationRecycle, []error{errors.New("Mock recycle error")}, testSyncVolume),
  115. },
  116. {
  117. // recycle success(?) - volume is deleted before doRecycle() starts
  118. "6-6 - volume is deleted before recycling",
  119. newVolumeArray("volume6-6", "1Gi", "uid6-6", "claim6-6", v1.VolumeBound, v1.PersistentVolumeReclaimRecycle, classEmpty),
  120. novolumes,
  121. noclaims,
  122. noclaims,
  123. noevents, noerrors,
  124. wrapTestWithInjectedOperation(wrapTestWithReclaimCalls(operationRecycle, []error{}, testSyncVolume), func(ctrl *PersistentVolumeController, reactor *pvtesting.VolumeReactor) {
  125. // Delete the volume before recycle operation starts
  126. reactor.DeleteVolume("volume6-6")
  127. }),
  128. },
  129. {
  130. // recycle success(?) - volume is recycled by previous recycler just
  131. // at the time new doRecycle() starts. This simulates "volume no
  132. // longer needs recycling, skipping".
  133. "6-7 - volume is deleted before recycling",
  134. newVolumeArray("volume6-7", "1Gi", "uid6-7", "claim6-7", v1.VolumeBound, v1.PersistentVolumeReclaimRecycle, classEmpty, pvutil.AnnBoundByController),
  135. newVolumeArray("volume6-7", "1Gi", "", "", v1.VolumeAvailable, v1.PersistentVolumeReclaimRecycle, classEmpty),
  136. noclaims,
  137. noclaims,
  138. noevents, noerrors,
  139. wrapTestWithInjectedOperation(wrapTestWithReclaimCalls(operationRecycle, []error{}, testSyncVolume), func(ctrl *PersistentVolumeController, reactor *pvtesting.VolumeReactor) {
  140. // Mark the volume as Available before the recycler starts
  141. reactor.MarkVolumeAvaiable("volume6-7")
  142. }),
  143. },
  144. {
  145. // recycle success(?) - volume bound by user is recycled by previous
  146. // recycler just at the time new doRecycle() starts. This simulates
  147. // "volume no longer needs recycling, skipping" with volume bound by
  148. // user.
  149. "6-8 - prebound volume is deleted before recycling",
  150. newVolumeArray("volume6-8", "1Gi", "uid6-8", "claim6-8", v1.VolumeBound, v1.PersistentVolumeReclaimRecycle, classEmpty),
  151. newVolumeArray("volume6-8", "1Gi", "", "", v1.VolumeAvailable, v1.PersistentVolumeReclaimRecycle, classEmpty),
  152. noclaims,
  153. noclaims,
  154. noevents, noerrors,
  155. wrapTestWithInjectedOperation(wrapTestWithReclaimCalls(operationRecycle, []error{}, testSyncVolume), func(ctrl *PersistentVolumeController, reactor *pvtesting.VolumeReactor) {
  156. // Mark the volume as Available before the recycler starts
  157. reactor.MarkVolumeAvaiable("volume6-8")
  158. }),
  159. },
  160. {
  161. // recycle success - volume bound by user is recycled, while a new
  162. // claim is created with another UID.
  163. "6-9 - prebound volume is recycled while the claim exists",
  164. newVolumeArray("volume6-9", "1Gi", "uid6-9", "claim6-9", v1.VolumeBound, v1.PersistentVolumeReclaimRecycle, classEmpty),
  165. newVolumeArray("volume6-9", "1Gi", "", "claim6-9", v1.VolumeAvailable, v1.PersistentVolumeReclaimRecycle, classEmpty),
  166. newClaimArray("claim6-9", "uid6-9-x", "10Gi", "", v1.ClaimPending, nil),
  167. newClaimArray("claim6-9", "uid6-9-x", "10Gi", "", v1.ClaimPending, nil),
  168. noevents, noerrors,
  169. // Inject recycler into the controller and call syncVolume. The
  170. // recycler simulates one recycle() call that succeeds.
  171. wrapTestWithReclaimCalls(operationRecycle, []error{nil}, testSyncVolume),
  172. },
  173. {
  174. // volume has unknown reclaim policy - failure expected
  175. "6-10 - unknown reclaim policy",
  176. newVolumeArray("volume6-10", "1Gi", "uid6-10", "claim6-10", v1.VolumeBound, "Unknown", classEmpty),
  177. withMessage("Volume has unrecognized PersistentVolumeReclaimPolicy", newVolumeArray("volume6-10", "1Gi", "uid6-10", "claim6-10", v1.VolumeFailed, "Unknown", classEmpty)),
  178. noclaims,
  179. noclaims,
  180. []string{"Warning VolumeUnknownReclaimPolicy"}, noerrors, testSyncVolume,
  181. },
  182. {
  183. // volume is used by a running pod - failure expected
  184. "6-11 - used by running pod",
  185. newVolumeArray("volume6-11", "1Gi", "uid6-11", "runningClaim", v1.VolumeBound, v1.PersistentVolumeReclaimRecycle, classEmpty, pvutil.AnnBoundByController),
  186. newVolumeArray("volume6-11", "1Gi", "uid6-11", "runningClaim", v1.VolumeReleased, v1.PersistentVolumeReclaimRecycle, classEmpty, pvutil.AnnBoundByController),
  187. noclaims,
  188. noclaims,
  189. []string{"Normal VolumeFailedRecycle"}, noerrors, testSyncVolume,
  190. },
  191. {
  192. // volume is used by a pending pod - failure expected
  193. "6-12 - used by pending pod",
  194. newVolumeArray("volume6-12", "1Gi", "uid6-12", "pendingClaim", v1.VolumeBound, v1.PersistentVolumeReclaimRecycle, classEmpty, pvutil.AnnBoundByController),
  195. newVolumeArray("volume6-12", "1Gi", "uid6-12", "pendingClaim", v1.VolumeReleased, v1.PersistentVolumeReclaimRecycle, classEmpty, pvutil.AnnBoundByController),
  196. noclaims,
  197. noclaims,
  198. []string{"Normal VolumeFailedRecycle"}, noerrors, testSyncVolume,
  199. },
  200. {
  201. // volume is used by a completed pod - recycle succeeds
  202. "6-13 - used by completed pod",
  203. newVolumeArray("volume6-13", "1Gi", "uid6-13", "completedClaim", v1.VolumeBound, v1.PersistentVolumeReclaimRecycle, classEmpty, pvutil.AnnBoundByController),
  204. newVolumeArray("volume6-13", "1Gi", "", "", v1.VolumeAvailable, v1.PersistentVolumeReclaimRecycle, classEmpty),
  205. noclaims,
  206. noclaims,
  207. noevents, noerrors,
  208. // Inject recycler into the controller and call syncVolume. The
  209. // recycler simulates one recycle() call that succeeds.
  210. wrapTestWithReclaimCalls(operationRecycle, []error{nil}, testSyncVolume),
  211. },
  212. {
  213. // volume is used by a completed pod, pod using claim with the same name bound to different pv is running, should recycle
  214. "6-14 - seemingly used by running pod",
  215. newVolumeArray("volume6-14", "1Gi", "uid6-14", "completedClaim", v1.VolumeBound, v1.PersistentVolumeReclaimRecycle, classEmpty, pvutil.AnnBoundByController),
  216. newVolumeArray("volume6-14", "1Gi", "", "", v1.VolumeAvailable, v1.PersistentVolumeReclaimRecycle, classEmpty),
  217. newClaimArray("completedClaim", "uid6-14-x", "10Gi", "", v1.ClaimBound, nil),
  218. newClaimArray("completedClaim", "uid6-14-x", "10Gi", "", v1.ClaimBound, nil),
  219. noevents, noerrors,
  220. wrapTestWithReclaimCalls(operationRecycle, []error{nil}, testSyncVolume),
  221. },
  222. }
  223. runSyncTests(t, tests, []*storage.StorageClass{}, pods)
  224. }
  225. // Test multiple calls to syncClaim/syncVolume and periodic sync of all
  226. // volume/claims. The test follows this pattern:
  227. // 0. Load the controller with initial data.
  228. // 1. Call controllerTest.testCall() once as in TestSync()
  229. // 2. For all volumes/claims changed by previous syncVolume/syncClaim calls,
  230. // call appropriate syncVolume/syncClaim (simulating "volume/claim changed"
  231. // events). Go to 2. if these calls change anything.
  232. // 3. When all changes are processed and no new changes were made, call
  233. // syncVolume/syncClaim on all volumes/claims (simulating "periodic sync").
  234. // 4. If some changes were done by step 3., go to 2. (simulation of
  235. // "volume/claim updated" events, eventually performing step 3. again)
  236. // 5. When 3. does not do any changes, finish the tests and compare final set
  237. // of volumes/claims with expected claims/volumes and report differences.
  238. // Some limit of calls in enforced to prevent endless loops.
  239. func TestRecycleMultiSync(t *testing.T) {
  240. tests := []controllerTest{
  241. {
  242. // recycle failure - recycle returns error. The controller should
  243. // try again.
  244. "7-1 - recycle returns error",
  245. newVolumeArray("volume7-1", "1Gi", "uid7-1", "claim7-1", v1.VolumeBound, v1.PersistentVolumeReclaimRecycle, classEmpty),
  246. newVolumeArray("volume7-1", "1Gi", "", "claim7-1", v1.VolumeAvailable, v1.PersistentVolumeReclaimRecycle, classEmpty),
  247. noclaims,
  248. noclaims,
  249. []string{"Warning VolumeFailedRecycle"}, noerrors,
  250. wrapTestWithReclaimCalls(operationRecycle, []error{errors.New("Mock recycle error"), nil}, testSyncVolume),
  251. },
  252. }
  253. runMultisyncTests(t, tests, []*storage.StorageClass{}, "")
  254. }