scheduler_binder_test.go 59 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697
  1. /*
  2. Copyright 2017 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package scheduling
  14. import (
  15. "context"
  16. "fmt"
  17. "reflect"
  18. "testing"
  19. "time"
  20. v1 "k8s.io/api/core/v1"
  21. storagev1 "k8s.io/api/storage/v1"
  22. "k8s.io/apimachinery/pkg/api/resource"
  23. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  24. "k8s.io/apimachinery/pkg/types"
  25. "k8s.io/apimachinery/pkg/util/diff"
  26. "k8s.io/apimachinery/pkg/util/wait"
  27. "k8s.io/apimachinery/pkg/watch"
  28. "k8s.io/client-go/informers"
  29. coreinformers "k8s.io/client-go/informers/core/v1"
  30. clientset "k8s.io/client-go/kubernetes"
  31. "k8s.io/client-go/kubernetes/fake"
  32. k8stesting "k8s.io/client-go/testing"
  33. "k8s.io/klog"
  34. "k8s.io/kubernetes/pkg/api/testapi"
  35. "k8s.io/kubernetes/pkg/controller"
  36. pvtesting "k8s.io/kubernetes/pkg/controller/volume/persistentvolume/testing"
  37. pvutil "k8s.io/kubernetes/pkg/controller/volume/persistentvolume/util"
  38. )
  39. var (
  40. // PVCs for manual binding
  41. // TODO: clean up all of these
  42. unboundPVC = makeTestPVC("unbound-pvc", "1G", "", pvcUnbound, "", "1", &waitClass)
  43. unboundPVC2 = makeTestPVC("unbound-pvc2", "5G", "", pvcUnbound, "", "1", &waitClass)
  44. preboundPVC = makeTestPVC("prebound-pvc", "1G", "", pvcPrebound, "pv-node1a", "1", &waitClass)
  45. preboundPVCNode1a = makeTestPVC("unbound-pvc", "1G", "", pvcPrebound, "pv-node1a", "1", &waitClass)
  46. boundPVC = makeTestPVC("bound-pvc", "1G", "", pvcBound, "pv-bound", "1", &waitClass)
  47. boundPVC2 = makeTestPVC("bound-pvc2", "1G", "", pvcBound, "pv-bound2", "1", &waitClass)
  48. boundPVCNode1a = makeTestPVC("unbound-pvc", "1G", "", pvcBound, "pv-node1a", "1", &waitClass)
  49. immediateUnboundPVC = makeTestPVC("immediate-unbound-pvc", "1G", "", pvcUnbound, "", "1", &immediateClass)
  50. immediateBoundPVC = makeTestPVC("immediate-bound-pvc", "1G", "", pvcBound, "pv-bound-immediate", "1", &immediateClass)
  51. // PVCs for dynamic provisioning
  52. provisionedPVC = makeTestPVC("provisioned-pvc", "1Gi", "", pvcUnbound, "", "1", &waitClassWithProvisioner)
  53. provisionedPVC2 = makeTestPVC("provisioned-pvc2", "1Gi", "", pvcUnbound, "", "1", &waitClassWithProvisioner)
  54. provisionedPVCHigherVersion = makeTestPVC("provisioned-pvc2", "1Gi", "", pvcUnbound, "", "2", &waitClassWithProvisioner)
  55. provisionedPVCBound = makeTestPVC("provisioned-pvc", "1Gi", "", pvcBound, "pv-bound", "1", &waitClassWithProvisioner)
  56. noProvisionerPVC = makeTestPVC("no-provisioner-pvc", "1Gi", "", pvcUnbound, "", "1", &waitClass)
  57. topoMismatchPVC = makeTestPVC("topo-mismatch-pvc", "1Gi", "", pvcUnbound, "", "1", &topoMismatchClass)
  58. selectedNodePVC = makeTestPVC("provisioned-pvc", "1Gi", nodeLabelValue, pvcSelectedNode, "", "1", &waitClassWithProvisioner)
  59. // PVs for manual binding
  60. pvNoNode = makeTestPV("pv-no-node", "", "1G", "1", nil, waitClass)
  61. pvNode1a = makeTestPV("pv-node1a", "node1", "5G", "1", nil, waitClass)
  62. pvNode1b = makeTestPV("pv-node1b", "node1", "10G", "1", nil, waitClass)
  63. pvNode1c = makeTestPV("pv-node1b", "node1", "5G", "1", nil, waitClass)
  64. pvNode2 = makeTestPV("pv-node2", "node2", "1G", "1", nil, waitClass)
  65. pvPrebound = makeTestPV("pv-prebound", "node1", "1G", "1", unboundPVC, waitClass)
  66. pvBound = makeTestPV("pv-bound", "node1", "1G", "1", boundPVC, waitClass)
  67. pvNode1aBound = makeTestPV("pv-node1a", "node1", "5G", "1", unboundPVC, waitClass)
  68. pvNode1bBound = makeTestPV("pv-node1b", "node1", "10G", "1", unboundPVC2, waitClass)
  69. pvNode1bBoundHigherVersion = makeTestPV("pv-node1b", "node1", "10G", "2", unboundPVC2, waitClass)
  70. pvBoundImmediate = makeTestPV("pv-bound-immediate", "node1", "1G", "1", immediateBoundPVC, immediateClass)
  71. pvBoundImmediateNode2 = makeTestPV("pv-bound-immediate", "node2", "1G", "1", immediateBoundPVC, immediateClass)
  72. // storage class names
  73. waitClass = "waitClass"
  74. immediateClass = "immediateClass"
  75. waitClassWithProvisioner = "waitClassWithProvisioner"
  76. topoMismatchClass = "topoMismatchClass"
  77. // nodes objects
  78. node1 = makeNode("node1", map[string]string{nodeLabelKey: "node1"})
  79. node2 = makeNode("node2", map[string]string{nodeLabelKey: "node2"})
  80. node1NoLabels = makeNode("node1", nil)
  81. // node topology
  82. nodeLabelKey = "nodeKey"
  83. nodeLabelValue = "node1"
  84. )
  85. type testEnv struct {
  86. client clientset.Interface
  87. reactor *pvtesting.VolumeReactor
  88. binder SchedulerVolumeBinder
  89. internalBinder *volumeBinder
  90. internalNodeInformer coreinformers.NodeInformer
  91. internalPVCache *assumeCache
  92. internalPVCCache *assumeCache
  93. }
  94. func newTestBinder(t *testing.T, stopCh <-chan struct{}) *testEnv {
  95. client := &fake.Clientset{}
  96. reactor := pvtesting.NewVolumeReactor(client, nil, nil, nil)
  97. // TODO refactor all tests to use real watch mechanism, see #72327
  98. client.AddWatchReactor("*", func(action k8stesting.Action) (handled bool, ret watch.Interface, err error) {
  99. gvr := action.GetResource()
  100. ns := action.GetNamespace()
  101. watch, err := reactor.Watch(gvr, ns)
  102. if err != nil {
  103. return false, nil, err
  104. }
  105. return true, watch, nil
  106. })
  107. informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc())
  108. nodeInformer := informerFactory.Core().V1().Nodes()
  109. pvcInformer := informerFactory.Core().V1().PersistentVolumeClaims()
  110. classInformer := informerFactory.Storage().V1().StorageClasses()
  111. binder := NewVolumeBinder(
  112. client,
  113. nodeInformer,
  114. pvcInformer,
  115. informerFactory.Core().V1().PersistentVolumes(),
  116. classInformer,
  117. 10*time.Second)
  118. // Wait for informers cache sync
  119. informerFactory.Start(stopCh)
  120. for v, synced := range informerFactory.WaitForCacheSync(stopCh) {
  121. if !synced {
  122. klog.Fatalf("Error syncing informer for %v", v)
  123. }
  124. }
  125. // Add storageclasses
  126. waitMode := storagev1.VolumeBindingWaitForFirstConsumer
  127. immediateMode := storagev1.VolumeBindingImmediate
  128. classes := []*storagev1.StorageClass{
  129. {
  130. ObjectMeta: metav1.ObjectMeta{
  131. Name: waitClassWithProvisioner,
  132. },
  133. VolumeBindingMode: &waitMode,
  134. Provisioner: "test-provisioner",
  135. AllowedTopologies: []v1.TopologySelectorTerm{
  136. {
  137. MatchLabelExpressions: []v1.TopologySelectorLabelRequirement{
  138. {
  139. Key: nodeLabelKey,
  140. Values: []string{nodeLabelValue, "reference-value"},
  141. },
  142. },
  143. },
  144. },
  145. },
  146. {
  147. ObjectMeta: metav1.ObjectMeta{
  148. Name: immediateClass,
  149. },
  150. VolumeBindingMode: &immediateMode,
  151. },
  152. {
  153. ObjectMeta: metav1.ObjectMeta{
  154. Name: waitClass,
  155. },
  156. VolumeBindingMode: &waitMode,
  157. Provisioner: "kubernetes.io/no-provisioner",
  158. },
  159. {
  160. ObjectMeta: metav1.ObjectMeta{
  161. Name: topoMismatchClass,
  162. },
  163. VolumeBindingMode: &waitMode,
  164. Provisioner: "test-provisioner",
  165. AllowedTopologies: []v1.TopologySelectorTerm{
  166. {
  167. MatchLabelExpressions: []v1.TopologySelectorLabelRequirement{
  168. {
  169. Key: nodeLabelKey,
  170. Values: []string{"reference-value"},
  171. },
  172. },
  173. },
  174. },
  175. },
  176. }
  177. for _, class := range classes {
  178. if err := classInformer.Informer().GetIndexer().Add(class); err != nil {
  179. t.Fatalf("Failed to add storage class to internal cache: %v", err)
  180. }
  181. }
  182. // Get internal types
  183. internalBinder, ok := binder.(*volumeBinder)
  184. if !ok {
  185. t.Fatalf("Failed to convert to internal binder")
  186. }
  187. pvCache := internalBinder.pvCache
  188. internalPVCache, ok := pvCache.(*pvAssumeCache).AssumeCache.(*assumeCache)
  189. if !ok {
  190. t.Fatalf("Failed to convert to internal PV cache")
  191. }
  192. pvcCache := internalBinder.pvcCache
  193. internalPVCCache, ok := pvcCache.(*pvcAssumeCache).AssumeCache.(*assumeCache)
  194. if !ok {
  195. t.Fatalf("Failed to convert to internal PVC cache")
  196. }
  197. return &testEnv{
  198. client: client,
  199. reactor: reactor,
  200. binder: binder,
  201. internalBinder: internalBinder,
  202. internalNodeInformer: nodeInformer,
  203. internalPVCache: internalPVCache,
  204. internalPVCCache: internalPVCCache,
  205. }
  206. }
  207. func (env *testEnv) initNodes(cachedNodes []*v1.Node) {
  208. nodeInformer := env.internalNodeInformer.Informer()
  209. for _, node := range cachedNodes {
  210. nodeInformer.GetIndexer().Add(node)
  211. }
  212. }
  213. func (env *testEnv) initClaims(cachedPVCs []*v1.PersistentVolumeClaim, apiPVCs []*v1.PersistentVolumeClaim) {
  214. internalPVCCache := env.internalPVCCache
  215. for _, pvc := range cachedPVCs {
  216. internalPVCCache.add(pvc)
  217. if apiPVCs == nil {
  218. env.reactor.AddClaim(pvc)
  219. }
  220. }
  221. for _, pvc := range apiPVCs {
  222. env.reactor.AddClaim(pvc)
  223. }
  224. }
  225. func (env *testEnv) initVolumes(cachedPVs []*v1.PersistentVolume, apiPVs []*v1.PersistentVolume) {
  226. internalPVCache := env.internalPVCache
  227. for _, pv := range cachedPVs {
  228. internalPVCache.add(pv)
  229. if apiPVs == nil {
  230. env.reactor.AddVolume(pv)
  231. }
  232. }
  233. for _, pv := range apiPVs {
  234. env.reactor.AddVolume(pv)
  235. }
  236. }
  237. func (env *testEnv) updateVolumes(t *testing.T, pvs []*v1.PersistentVolume, waitCache bool) {
  238. for _, pv := range pvs {
  239. if _, err := env.client.CoreV1().PersistentVolumes().Update(pv); err != nil {
  240. t.Fatalf("failed to update PV %q", pv.Name)
  241. }
  242. }
  243. if waitCache {
  244. wait.Poll(100*time.Millisecond, 3*time.Second, func() (bool, error) {
  245. for _, pv := range pvs {
  246. obj, err := env.internalPVCache.GetAPIObj(pv.Name)
  247. if obj == nil || err != nil {
  248. return false, nil
  249. }
  250. pvInCache, ok := obj.(*v1.PersistentVolume)
  251. if !ok {
  252. return false, fmt.Errorf("PV %s invalid object", pvInCache.Name)
  253. }
  254. return versioner.CompareResourceVersion(pvInCache, pv) == 0, nil
  255. }
  256. return true, nil
  257. })
  258. }
  259. }
  260. func (env *testEnv) updateClaims(t *testing.T, pvcs []*v1.PersistentVolumeClaim, waitCache bool) {
  261. for _, pvc := range pvcs {
  262. if _, err := env.client.CoreV1().PersistentVolumeClaims(pvc.Namespace).Update(pvc); err != nil {
  263. t.Fatalf("failed to update PVC %q", getPVCName(pvc))
  264. }
  265. }
  266. if waitCache {
  267. wait.Poll(100*time.Millisecond, 3*time.Second, func() (bool, error) {
  268. for _, pvc := range pvcs {
  269. obj, err := env.internalPVCCache.GetAPIObj(getPVCName(pvc))
  270. if obj == nil || err != nil {
  271. return false, nil
  272. }
  273. pvcInCache, ok := obj.(*v1.PersistentVolumeClaim)
  274. if !ok {
  275. return false, fmt.Errorf("PVC %s invalid object", pvcInCache.Name)
  276. }
  277. return versioner.CompareResourceVersion(pvcInCache, pvc) == 0, nil
  278. }
  279. return true, nil
  280. })
  281. }
  282. }
  283. func (env *testEnv) deleteVolumes(pvs []*v1.PersistentVolume) {
  284. for _, pv := range pvs {
  285. env.internalPVCache.delete(pv)
  286. }
  287. }
  288. func (env *testEnv) deleteClaims(pvcs []*v1.PersistentVolumeClaim) {
  289. for _, pvc := range pvcs {
  290. env.internalPVCCache.delete(pvc)
  291. }
  292. }
  293. func (env *testEnv) assumeVolumes(t *testing.T, name, node string, pod *v1.Pod, bindings []*bindingInfo, provisionings []*v1.PersistentVolumeClaim) {
  294. pvCache := env.internalBinder.pvCache
  295. for _, binding := range bindings {
  296. if err := pvCache.Assume(binding.pv); err != nil {
  297. t.Fatalf("Failed to setup test %q: error: %v", name, err)
  298. }
  299. }
  300. pvcCache := env.internalBinder.pvcCache
  301. for _, pvc := range provisionings {
  302. if err := pvcCache.Assume(pvc); err != nil {
  303. t.Fatalf("Failed to setup test %q: error: %v", name, err)
  304. }
  305. }
  306. env.internalBinder.podBindingCache.UpdateBindings(pod, node, bindings, provisionings)
  307. }
  308. func (env *testEnv) initPodCache(pod *v1.Pod, node string, bindings []*bindingInfo, provisionings []*v1.PersistentVolumeClaim) {
  309. cache := env.internalBinder.podBindingCache
  310. cache.UpdateBindings(pod, node, bindings, provisionings)
  311. }
  312. func (env *testEnv) validatePodCache(t *testing.T, name, node string, pod *v1.Pod, expectedBindings []*bindingInfo, expectedProvisionings []*v1.PersistentVolumeClaim) {
  313. cache := env.internalBinder.podBindingCache
  314. bindings := cache.GetBindings(pod, node)
  315. if aLen, eLen := len(bindings), len(expectedBindings); aLen != eLen {
  316. t.Errorf("Test %q failed. expected %v bindings, got %v", name, eLen, aLen)
  317. } else if expectedBindings == nil && bindings != nil {
  318. // nil and empty are different
  319. t.Errorf("Test %q failed. expected nil bindings, got empty", name)
  320. } else if expectedBindings != nil && bindings == nil {
  321. // nil and empty are different
  322. t.Errorf("Test %q failed. expected empty bindings, got nil", name)
  323. } else {
  324. for i := 0; i < aLen; i++ {
  325. // Validate PV
  326. if !reflect.DeepEqual(expectedBindings[i].pv, bindings[i].pv) {
  327. t.Errorf("Test %q failed. binding.pv doesn't match [A-expected, B-got]: %s", name, diff.ObjectDiff(expectedBindings[i].pv, bindings[i].pv))
  328. }
  329. // Validate PVC
  330. if !reflect.DeepEqual(expectedBindings[i].pvc, bindings[i].pvc) {
  331. t.Errorf("Test %q failed. binding.pvc doesn't match [A-expected, B-got]: %s", name, diff.ObjectDiff(expectedBindings[i].pvc, bindings[i].pvc))
  332. }
  333. }
  334. }
  335. provisionedClaims := cache.GetProvisionedPVCs(pod, node)
  336. if aLen, eLen := len(provisionedClaims), len(expectedProvisionings); aLen != eLen {
  337. t.Errorf("Test %q failed. expected %v provisioned claims, got %v", name, eLen, aLen)
  338. } else if expectedProvisionings == nil && provisionedClaims != nil {
  339. // nil and empty are different
  340. t.Errorf("Test %q failed. expected nil provisionings, got empty", name)
  341. } else if expectedProvisionings != nil && provisionedClaims == nil {
  342. // nil and empty are different
  343. t.Errorf("Test %q failed. expected empty provisionings, got nil", name)
  344. } else {
  345. for i := 0; i < aLen; i++ {
  346. if !reflect.DeepEqual(expectedProvisionings[i], provisionedClaims[i]) {
  347. t.Errorf("Test %q failed. provisioned claims doesn't match [A-expected, B-got]: %s", name, diff.ObjectDiff(expectedProvisionings[i], provisionedClaims[i]))
  348. }
  349. }
  350. }
  351. }
  352. func (env *testEnv) getPodBindings(t *testing.T, name, node string, pod *v1.Pod) []*bindingInfo {
  353. cache := env.internalBinder.podBindingCache
  354. return cache.GetBindings(pod, node)
  355. }
  356. func (env *testEnv) validateAssume(t *testing.T, name string, pod *v1.Pod, bindings []*bindingInfo, provisionings []*v1.PersistentVolumeClaim) {
  357. // Check pv cache
  358. pvCache := env.internalBinder.pvCache
  359. for _, b := range bindings {
  360. pv, err := pvCache.GetPV(b.pv.Name)
  361. if err != nil {
  362. t.Errorf("Test %q failed: GetPV %q returned error: %v", name, b.pv.Name, err)
  363. continue
  364. }
  365. if pv.Spec.ClaimRef == nil {
  366. t.Errorf("Test %q failed: PV %q ClaimRef is nil", name, b.pv.Name)
  367. continue
  368. }
  369. if pv.Spec.ClaimRef.Name != b.pvc.Name {
  370. t.Errorf("Test %q failed: expected PV.ClaimRef.Name %q, got %q", name, b.pvc.Name, pv.Spec.ClaimRef.Name)
  371. }
  372. if pv.Spec.ClaimRef.Namespace != b.pvc.Namespace {
  373. t.Errorf("Test %q failed: expected PV.ClaimRef.Namespace %q, got %q", name, b.pvc.Namespace, pv.Spec.ClaimRef.Namespace)
  374. }
  375. }
  376. // Check pvc cache
  377. pvcCache := env.internalBinder.pvcCache
  378. for _, p := range provisionings {
  379. pvcKey := getPVCName(p)
  380. pvc, err := pvcCache.GetPVC(pvcKey)
  381. if err != nil {
  382. t.Errorf("Test %q failed: GetPVC %q returned error: %v", name, pvcKey, err)
  383. continue
  384. }
  385. if pvc.Annotations[pvutil.AnnSelectedNode] != nodeLabelValue {
  386. t.Errorf("Test %q failed: expected pvutil.AnnSelectedNode of pvc %q to be %q, but got %q", name, pvcKey, nodeLabelValue, pvc.Annotations[pvutil.AnnSelectedNode])
  387. }
  388. }
  389. }
  390. func (env *testEnv) validateFailedAssume(t *testing.T, name string, pod *v1.Pod, bindings []*bindingInfo, provisionings []*v1.PersistentVolumeClaim) {
  391. // All PVs have been unmodified in cache
  392. pvCache := env.internalBinder.pvCache
  393. for _, b := range bindings {
  394. pv, _ := pvCache.GetPV(b.pv.Name)
  395. // PV could be nil if it's missing from cache
  396. if pv != nil && pv != b.pv {
  397. t.Errorf("Test %q failed: PV %q was modified in cache", name, b.pv.Name)
  398. }
  399. }
  400. // Check pvc cache
  401. pvcCache := env.internalBinder.pvcCache
  402. for _, p := range provisionings {
  403. pvcKey := getPVCName(p)
  404. pvc, err := pvcCache.GetPVC(pvcKey)
  405. if err != nil {
  406. t.Errorf("Test %q failed: GetPVC %q returned error: %v", name, pvcKey, err)
  407. continue
  408. }
  409. if pvc.Annotations[pvutil.AnnSelectedNode] != "" {
  410. t.Errorf("Test %q failed: expected pvutil.AnnSelectedNode of pvc %q empty, but got %q", name, pvcKey, pvc.Annotations[pvutil.AnnSelectedNode])
  411. }
  412. }
  413. }
  414. func (env *testEnv) validateBind(
  415. t *testing.T,
  416. name string,
  417. pod *v1.Pod,
  418. expectedPVs []*v1.PersistentVolume,
  419. expectedAPIPVs []*v1.PersistentVolume) {
  420. // Check pv cache
  421. pvCache := env.internalBinder.pvCache
  422. for _, pv := range expectedPVs {
  423. cachedPV, err := pvCache.GetPV(pv.Name)
  424. if err != nil {
  425. t.Errorf("Test %q failed: GetPV %q returned error: %v", name, pv.Name, err)
  426. }
  427. // Cache may be overridden by API object with higher version, compare but ignore resource version.
  428. newCachedPV := cachedPV.DeepCopy()
  429. newCachedPV.ResourceVersion = pv.ResourceVersion
  430. if !reflect.DeepEqual(newCachedPV, pv) {
  431. t.Errorf("Test %q failed: cached PV check failed [A-expected, B-got]:\n%s", name, diff.ObjectDiff(pv, cachedPV))
  432. }
  433. }
  434. // Check reactor for API updates
  435. if err := env.reactor.CheckVolumes(expectedAPIPVs); err != nil {
  436. t.Errorf("Test %q failed: API reactor validation failed: %v", name, err)
  437. }
  438. }
  439. func (env *testEnv) validateProvision(
  440. t *testing.T,
  441. name string,
  442. pod *v1.Pod,
  443. expectedPVCs []*v1.PersistentVolumeClaim,
  444. expectedAPIPVCs []*v1.PersistentVolumeClaim) {
  445. // Check pvc cache
  446. pvcCache := env.internalBinder.pvcCache
  447. for _, pvc := range expectedPVCs {
  448. cachedPVC, err := pvcCache.GetPVC(getPVCName(pvc))
  449. if err != nil {
  450. t.Errorf("Test %q failed: GetPVC %q returned error: %v", name, getPVCName(pvc), err)
  451. }
  452. // Cache may be overridden by API object with higher version, compare but ignore resource version.
  453. newCachedPVC := cachedPVC.DeepCopy()
  454. newCachedPVC.ResourceVersion = pvc.ResourceVersion
  455. if !reflect.DeepEqual(newCachedPVC, pvc) {
  456. t.Errorf("Test %q failed: cached PVC check failed [A-expected, B-got]:\n%s", name, diff.ObjectDiff(pvc, cachedPVC))
  457. }
  458. }
  459. // Check reactor for API updates
  460. if err := env.reactor.CheckClaims(expectedAPIPVCs); err != nil {
  461. t.Errorf("Test %q failed: API reactor validation failed: %v", name, err)
  462. }
  463. }
  464. const (
  465. pvcUnbound = iota
  466. pvcPrebound
  467. pvcBound
  468. pvcSelectedNode
  469. )
  470. func makeTestPVC(name, size, node string, pvcBoundState int, pvName, resourceVersion string, className *string) *v1.PersistentVolumeClaim {
  471. fs := v1.PersistentVolumeFilesystem
  472. pvc := &v1.PersistentVolumeClaim{
  473. TypeMeta: metav1.TypeMeta{
  474. Kind: "PersistentVolumeClaim",
  475. APIVersion: "v1",
  476. },
  477. ObjectMeta: metav1.ObjectMeta{
  478. Name: name,
  479. Namespace: "testns",
  480. UID: types.UID("pvc-uid"),
  481. ResourceVersion: resourceVersion,
  482. SelfLink: testapi.Default.SelfLink("pvc", name),
  483. },
  484. Spec: v1.PersistentVolumeClaimSpec{
  485. Resources: v1.ResourceRequirements{
  486. Requests: v1.ResourceList{
  487. v1.ResourceName(v1.ResourceStorage): resource.MustParse(size),
  488. },
  489. },
  490. StorageClassName: className,
  491. VolumeMode: &fs,
  492. },
  493. }
  494. switch pvcBoundState {
  495. case pvcSelectedNode:
  496. metav1.SetMetaDataAnnotation(&pvc.ObjectMeta, pvutil.AnnSelectedNode, node)
  497. // don't fallthrough
  498. case pvcBound:
  499. metav1.SetMetaDataAnnotation(&pvc.ObjectMeta, pvutil.AnnBindCompleted, "yes")
  500. fallthrough
  501. case pvcPrebound:
  502. pvc.Spec.VolumeName = pvName
  503. }
  504. return pvc
  505. }
  506. func makeTestPV(name, node, capacity, version string, boundToPVC *v1.PersistentVolumeClaim, className string) *v1.PersistentVolume {
  507. fs := v1.PersistentVolumeFilesystem
  508. pv := &v1.PersistentVolume{
  509. ObjectMeta: metav1.ObjectMeta{
  510. Name: name,
  511. ResourceVersion: version,
  512. },
  513. Spec: v1.PersistentVolumeSpec{
  514. Capacity: v1.ResourceList{
  515. v1.ResourceName(v1.ResourceStorage): resource.MustParse(capacity),
  516. },
  517. StorageClassName: className,
  518. VolumeMode: &fs,
  519. },
  520. Status: v1.PersistentVolumeStatus{
  521. Phase: v1.VolumeAvailable,
  522. },
  523. }
  524. if node != "" {
  525. pv.Spec.NodeAffinity = pvutil.GetVolumeNodeAffinity(nodeLabelKey, node)
  526. }
  527. if boundToPVC != nil {
  528. pv.Spec.ClaimRef = &v1.ObjectReference{
  529. Kind: boundToPVC.Kind,
  530. APIVersion: boundToPVC.APIVersion,
  531. ResourceVersion: boundToPVC.ResourceVersion,
  532. Name: boundToPVC.Name,
  533. Namespace: boundToPVC.Namespace,
  534. UID: boundToPVC.UID,
  535. }
  536. metav1.SetMetaDataAnnotation(&pv.ObjectMeta, pvutil.AnnBoundByController, "yes")
  537. }
  538. return pv
  539. }
  540. func pvcSetSelectedNode(pvc *v1.PersistentVolumeClaim, node string) *v1.PersistentVolumeClaim {
  541. newPVC := pvc.DeepCopy()
  542. metav1.SetMetaDataAnnotation(&newPVC.ObjectMeta, pvutil.AnnSelectedNode, node)
  543. return newPVC
  544. }
  545. func pvcSetEmptyAnnotations(pvc *v1.PersistentVolumeClaim) *v1.PersistentVolumeClaim {
  546. newPVC := pvc.DeepCopy()
  547. newPVC.Annotations = map[string]string{}
  548. return newPVC
  549. }
  550. func pvRemoveClaimUID(pv *v1.PersistentVolume) *v1.PersistentVolume {
  551. newPV := pv.DeepCopy()
  552. newPV.Spec.ClaimRef.UID = ""
  553. return newPV
  554. }
  555. func makeNode(name string, labels map[string]string) *v1.Node {
  556. return &v1.Node{
  557. ObjectMeta: metav1.ObjectMeta{
  558. Name: name,
  559. Labels: labels,
  560. },
  561. }
  562. }
  563. func makePod(pvcs []*v1.PersistentVolumeClaim) *v1.Pod {
  564. pod := &v1.Pod{
  565. ObjectMeta: metav1.ObjectMeta{
  566. Name: "test-pod",
  567. Namespace: "testns",
  568. },
  569. }
  570. volumes := []v1.Volume{}
  571. for i, pvc := range pvcs {
  572. pvcVol := v1.Volume{
  573. Name: fmt.Sprintf("vol%v", i),
  574. VolumeSource: v1.VolumeSource{
  575. PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
  576. ClaimName: pvc.Name,
  577. },
  578. },
  579. }
  580. volumes = append(volumes, pvcVol)
  581. }
  582. pod.Spec.Volumes = volumes
  583. pod.Spec.NodeName = "node1"
  584. return pod
  585. }
  586. func makePodWithoutPVC() *v1.Pod {
  587. pod := &v1.Pod{
  588. ObjectMeta: metav1.ObjectMeta{
  589. Name: "test-pod",
  590. Namespace: "testns",
  591. },
  592. Spec: v1.PodSpec{
  593. Volumes: []v1.Volume{
  594. {
  595. VolumeSource: v1.VolumeSource{
  596. EmptyDir: &v1.EmptyDirVolumeSource{},
  597. },
  598. },
  599. },
  600. },
  601. }
  602. return pod
  603. }
  604. func makeBinding(pvc *v1.PersistentVolumeClaim, pv *v1.PersistentVolume) *bindingInfo {
  605. return &bindingInfo{pvc: pvc, pv: pv}
  606. }
  607. func addProvisionAnn(pvc *v1.PersistentVolumeClaim) *v1.PersistentVolumeClaim {
  608. res := pvc.DeepCopy()
  609. // Add provision related annotations
  610. metav1.SetMetaDataAnnotation(&res.ObjectMeta, pvutil.AnnSelectedNode, nodeLabelValue)
  611. return res
  612. }
  613. func TestFindPodVolumesWithoutProvisioning(t *testing.T) {
  614. scenarios := map[string]struct {
  615. // Inputs
  616. pvs []*v1.PersistentVolume
  617. podPVCs []*v1.PersistentVolumeClaim
  618. // If nil, use pod PVCs
  619. cachePVCs []*v1.PersistentVolumeClaim
  620. // If nil, makePod with podPVCs
  621. pod *v1.Pod
  622. // Expected podBindingCache fields
  623. expectedBindings []*bindingInfo
  624. // Expected return values
  625. expectedUnbound bool
  626. expectedBound bool
  627. shouldFail bool
  628. }{
  629. "no-volumes": {
  630. pod: makePod(nil),
  631. expectedUnbound: true,
  632. expectedBound: true,
  633. },
  634. "no-pvcs": {
  635. pod: makePodWithoutPVC(),
  636. expectedUnbound: true,
  637. expectedBound: true,
  638. },
  639. "pvc-not-found": {
  640. cachePVCs: []*v1.PersistentVolumeClaim{},
  641. podPVCs: []*v1.PersistentVolumeClaim{boundPVC},
  642. expectedUnbound: false,
  643. expectedBound: false,
  644. shouldFail: true,
  645. },
  646. "bound-pvc": {
  647. podPVCs: []*v1.PersistentVolumeClaim{boundPVC},
  648. pvs: []*v1.PersistentVolume{pvBound},
  649. expectedUnbound: true,
  650. expectedBound: true,
  651. },
  652. "bound-pvc,pv-not-exists": {
  653. podPVCs: []*v1.PersistentVolumeClaim{boundPVC},
  654. expectedUnbound: false,
  655. expectedBound: false,
  656. shouldFail: true,
  657. },
  658. "prebound-pvc": {
  659. podPVCs: []*v1.PersistentVolumeClaim{preboundPVC},
  660. pvs: []*v1.PersistentVolume{pvNode1aBound},
  661. shouldFail: true,
  662. },
  663. "unbound-pvc,pv-same-node": {
  664. podPVCs: []*v1.PersistentVolumeClaim{unboundPVC},
  665. pvs: []*v1.PersistentVolume{pvNode2, pvNode1a, pvNode1b},
  666. expectedBindings: []*bindingInfo{makeBinding(unboundPVC, pvNode1a)},
  667. expectedUnbound: true,
  668. expectedBound: true,
  669. },
  670. "unbound-pvc,pv-different-node": {
  671. podPVCs: []*v1.PersistentVolumeClaim{unboundPVC},
  672. pvs: []*v1.PersistentVolume{pvNode2},
  673. expectedUnbound: false,
  674. expectedBound: true,
  675. },
  676. "two-unbound-pvcs": {
  677. podPVCs: []*v1.PersistentVolumeClaim{unboundPVC, unboundPVC2},
  678. pvs: []*v1.PersistentVolume{pvNode1a, pvNode1b},
  679. expectedBindings: []*bindingInfo{makeBinding(unboundPVC, pvNode1a), makeBinding(unboundPVC2, pvNode1b)},
  680. expectedUnbound: true,
  681. expectedBound: true,
  682. },
  683. "two-unbound-pvcs,order-by-size": {
  684. podPVCs: []*v1.PersistentVolumeClaim{unboundPVC2, unboundPVC},
  685. pvs: []*v1.PersistentVolume{pvNode1a, pvNode1b},
  686. expectedBindings: []*bindingInfo{makeBinding(unboundPVC, pvNode1a), makeBinding(unboundPVC2, pvNode1b)},
  687. expectedUnbound: true,
  688. expectedBound: true,
  689. },
  690. "two-unbound-pvcs,partial-match": {
  691. podPVCs: []*v1.PersistentVolumeClaim{unboundPVC, unboundPVC2},
  692. pvs: []*v1.PersistentVolume{pvNode1a},
  693. expectedBindings: []*bindingInfo{makeBinding(unboundPVC, pvNode1a)},
  694. expectedUnbound: false,
  695. expectedBound: true,
  696. },
  697. "one-bound,one-unbound": {
  698. podPVCs: []*v1.PersistentVolumeClaim{unboundPVC, boundPVC},
  699. pvs: []*v1.PersistentVolume{pvBound, pvNode1a},
  700. expectedBindings: []*bindingInfo{makeBinding(unboundPVC, pvNode1a)},
  701. expectedUnbound: true,
  702. expectedBound: true,
  703. },
  704. "one-bound,one-unbound,no-match": {
  705. podPVCs: []*v1.PersistentVolumeClaim{unboundPVC, boundPVC},
  706. pvs: []*v1.PersistentVolume{pvBound, pvNode2},
  707. expectedUnbound: false,
  708. expectedBound: true,
  709. },
  710. "one-prebound,one-unbound": {
  711. podPVCs: []*v1.PersistentVolumeClaim{unboundPVC, preboundPVC},
  712. pvs: []*v1.PersistentVolume{pvNode1a, pvNode1b},
  713. shouldFail: true,
  714. },
  715. "immediate-bound-pvc": {
  716. podPVCs: []*v1.PersistentVolumeClaim{immediateBoundPVC},
  717. pvs: []*v1.PersistentVolume{pvBoundImmediate},
  718. expectedUnbound: true,
  719. expectedBound: true,
  720. },
  721. "immediate-bound-pvc-wrong-node": {
  722. podPVCs: []*v1.PersistentVolumeClaim{immediateBoundPVC},
  723. pvs: []*v1.PersistentVolume{pvBoundImmediateNode2},
  724. expectedUnbound: true,
  725. expectedBound: false,
  726. },
  727. "immediate-unbound-pvc": {
  728. podPVCs: []*v1.PersistentVolumeClaim{immediateUnboundPVC},
  729. expectedUnbound: false,
  730. expectedBound: false,
  731. shouldFail: true,
  732. },
  733. "immediate-unbound-pvc,delayed-mode-bound": {
  734. podPVCs: []*v1.PersistentVolumeClaim{immediateUnboundPVC, boundPVC},
  735. pvs: []*v1.PersistentVolume{pvBound},
  736. expectedUnbound: false,
  737. expectedBound: false,
  738. shouldFail: true,
  739. },
  740. "immediate-unbound-pvc,delayed-mode-unbound": {
  741. podPVCs: []*v1.PersistentVolumeClaim{immediateUnboundPVC, unboundPVC},
  742. expectedUnbound: false,
  743. expectedBound: false,
  744. shouldFail: true,
  745. },
  746. }
  747. testNode := &v1.Node{
  748. ObjectMeta: metav1.ObjectMeta{
  749. Name: "node1",
  750. Labels: map[string]string{
  751. nodeLabelKey: "node1",
  752. },
  753. },
  754. }
  755. ctx, cancel := context.WithCancel(context.Background())
  756. defer cancel()
  757. for name, scenario := range scenarios {
  758. klog.V(5).Infof("Running test case %q", name)
  759. // Setup
  760. testEnv := newTestBinder(t, ctx.Done())
  761. testEnv.initVolumes(scenario.pvs, scenario.pvs)
  762. // a. Init pvc cache
  763. if scenario.cachePVCs == nil {
  764. scenario.cachePVCs = scenario.podPVCs
  765. }
  766. testEnv.initClaims(scenario.cachePVCs, scenario.cachePVCs)
  767. // b. Generate pod with given claims
  768. if scenario.pod == nil {
  769. scenario.pod = makePod(scenario.podPVCs)
  770. }
  771. // Execute
  772. unboundSatisfied, boundSatisfied, err := testEnv.binder.FindPodVolumes(scenario.pod, testNode)
  773. // Validate
  774. if !scenario.shouldFail && err != nil {
  775. t.Errorf("Test %q failed: returned error: %v", name, err)
  776. }
  777. if scenario.shouldFail && err == nil {
  778. t.Errorf("Test %q failed: returned success but expected error", name)
  779. }
  780. if boundSatisfied != scenario.expectedBound {
  781. t.Errorf("Test %q failed: expected boundSatsified %v, got %v", name, scenario.expectedBound, boundSatisfied)
  782. }
  783. if unboundSatisfied != scenario.expectedUnbound {
  784. t.Errorf("Test %q failed: expected unboundSatsified %v, got %v", name, scenario.expectedUnbound, unboundSatisfied)
  785. }
  786. testEnv.validatePodCache(t, name, testNode.Name, scenario.pod, scenario.expectedBindings, nil)
  787. }
  788. }
  789. func TestFindPodVolumesWithProvisioning(t *testing.T) {
  790. scenarios := map[string]struct {
  791. // Inputs
  792. pvs []*v1.PersistentVolume
  793. podPVCs []*v1.PersistentVolumeClaim
  794. // If nil, use pod PVCs
  795. cachePVCs []*v1.PersistentVolumeClaim
  796. // If nil, makePod with podPVCs
  797. pod *v1.Pod
  798. // Expected podBindingCache fields
  799. expectedBindings []*bindingInfo
  800. expectedProvisions []*v1.PersistentVolumeClaim
  801. // Expected return values
  802. expectedUnbound bool
  803. expectedBound bool
  804. shouldFail bool
  805. }{
  806. "one-provisioned": {
  807. podPVCs: []*v1.PersistentVolumeClaim{provisionedPVC},
  808. expectedProvisions: []*v1.PersistentVolumeClaim{provisionedPVC},
  809. expectedUnbound: true,
  810. expectedBound: true,
  811. },
  812. "two-unbound-pvcs,one-matched,one-provisioned": {
  813. podPVCs: []*v1.PersistentVolumeClaim{unboundPVC, provisionedPVC},
  814. pvs: []*v1.PersistentVolume{pvNode1a},
  815. expectedBindings: []*bindingInfo{makeBinding(unboundPVC, pvNode1a)},
  816. expectedProvisions: []*v1.PersistentVolumeClaim{provisionedPVC},
  817. expectedUnbound: true,
  818. expectedBound: true,
  819. },
  820. "one-bound,one-provisioned": {
  821. podPVCs: []*v1.PersistentVolumeClaim{boundPVC, provisionedPVC},
  822. pvs: []*v1.PersistentVolume{pvBound},
  823. expectedProvisions: []*v1.PersistentVolumeClaim{provisionedPVC},
  824. expectedUnbound: true,
  825. expectedBound: true,
  826. },
  827. "one-binding,one-selected-node": {
  828. podPVCs: []*v1.PersistentVolumeClaim{boundPVC, selectedNodePVC},
  829. pvs: []*v1.PersistentVolume{pvBound},
  830. expectedProvisions: []*v1.PersistentVolumeClaim{selectedNodePVC},
  831. expectedUnbound: true,
  832. expectedBound: true,
  833. },
  834. "immediate-unbound-pvc": {
  835. podPVCs: []*v1.PersistentVolumeClaim{immediateUnboundPVC},
  836. expectedUnbound: false,
  837. expectedBound: false,
  838. shouldFail: true,
  839. },
  840. "one-immediate-bound,one-provisioned": {
  841. podPVCs: []*v1.PersistentVolumeClaim{immediateBoundPVC, provisionedPVC},
  842. pvs: []*v1.PersistentVolume{pvBoundImmediate},
  843. expectedProvisions: []*v1.PersistentVolumeClaim{provisionedPVC},
  844. expectedUnbound: true,
  845. expectedBound: true,
  846. },
  847. "invalid-provisioner": {
  848. podPVCs: []*v1.PersistentVolumeClaim{noProvisionerPVC},
  849. expectedUnbound: false,
  850. expectedBound: true,
  851. },
  852. "volume-topology-unsatisfied": {
  853. podPVCs: []*v1.PersistentVolumeClaim{topoMismatchPVC},
  854. expectedUnbound: false,
  855. expectedBound: true,
  856. },
  857. }
  858. testNode := &v1.Node{
  859. ObjectMeta: metav1.ObjectMeta{
  860. Name: "node1",
  861. Labels: map[string]string{
  862. nodeLabelKey: "node1",
  863. },
  864. },
  865. }
  866. ctx, cancel := context.WithCancel(context.Background())
  867. defer cancel()
  868. for name, scenario := range scenarios {
  869. // Setup
  870. testEnv := newTestBinder(t, ctx.Done())
  871. testEnv.initVolumes(scenario.pvs, scenario.pvs)
  872. // a. Init pvc cache
  873. if scenario.cachePVCs == nil {
  874. scenario.cachePVCs = scenario.podPVCs
  875. }
  876. testEnv.initClaims(scenario.cachePVCs, scenario.cachePVCs)
  877. // b. Generate pod with given claims
  878. if scenario.pod == nil {
  879. scenario.pod = makePod(scenario.podPVCs)
  880. }
  881. // Execute
  882. unboundSatisfied, boundSatisfied, err := testEnv.binder.FindPodVolumes(scenario.pod, testNode)
  883. // Validate
  884. if !scenario.shouldFail && err != nil {
  885. t.Errorf("Test %q failed: returned error: %v", name, err)
  886. }
  887. if scenario.shouldFail && err == nil {
  888. t.Errorf("Test %q failed: returned success but expected error", name)
  889. }
  890. if boundSatisfied != scenario.expectedBound {
  891. t.Errorf("Test %q failed: expected boundSatsified %v, got %v", name, scenario.expectedBound, boundSatisfied)
  892. }
  893. if unboundSatisfied != scenario.expectedUnbound {
  894. t.Errorf("Test %q failed: expected unboundSatsified %v, got %v", name, scenario.expectedUnbound, unboundSatisfied)
  895. }
  896. testEnv.validatePodCache(t, name, testNode.Name, scenario.pod, scenario.expectedBindings, scenario.expectedProvisions)
  897. }
  898. }
  899. func TestAssumePodVolumes(t *testing.T) {
  900. scenarios := map[string]struct {
  901. // Inputs
  902. podPVCs []*v1.PersistentVolumeClaim
  903. pvs []*v1.PersistentVolume
  904. bindings []*bindingInfo
  905. provisionedPVCs []*v1.PersistentVolumeClaim
  906. // Expected return values
  907. shouldFail bool
  908. expectedAllBound bool
  909. expectedBindings []*bindingInfo
  910. expectedProvisionings []*v1.PersistentVolumeClaim
  911. }{
  912. "all-bound": {
  913. podPVCs: []*v1.PersistentVolumeClaim{boundPVC},
  914. pvs: []*v1.PersistentVolume{pvBound},
  915. expectedAllBound: true,
  916. },
  917. "one-binding": {
  918. podPVCs: []*v1.PersistentVolumeClaim{unboundPVC},
  919. bindings: []*bindingInfo{makeBinding(unboundPVC, pvNode1a)},
  920. pvs: []*v1.PersistentVolume{pvNode1a},
  921. expectedBindings: []*bindingInfo{makeBinding(unboundPVC, pvNode1aBound)},
  922. expectedProvisionings: []*v1.PersistentVolumeClaim{},
  923. },
  924. "two-bindings": {
  925. podPVCs: []*v1.PersistentVolumeClaim{unboundPVC, unboundPVC2},
  926. bindings: []*bindingInfo{makeBinding(unboundPVC, pvNode1a), makeBinding(unboundPVC2, pvNode1b)},
  927. pvs: []*v1.PersistentVolume{pvNode1a, pvNode1b},
  928. expectedBindings: []*bindingInfo{makeBinding(unboundPVC, pvNode1aBound), makeBinding(unboundPVC2, pvNode1bBound)},
  929. expectedProvisionings: []*v1.PersistentVolumeClaim{},
  930. },
  931. "pv-already-bound": {
  932. podPVCs: []*v1.PersistentVolumeClaim{unboundPVC},
  933. bindings: []*bindingInfo{makeBinding(unboundPVC, pvNode1aBound)},
  934. pvs: []*v1.PersistentVolume{pvNode1aBound},
  935. expectedBindings: []*bindingInfo{makeBinding(unboundPVC, pvNode1aBound)},
  936. expectedProvisionings: []*v1.PersistentVolumeClaim{},
  937. },
  938. "tmpupdate-failed": {
  939. podPVCs: []*v1.PersistentVolumeClaim{unboundPVC},
  940. bindings: []*bindingInfo{makeBinding(unboundPVC, pvNode1a), makeBinding(unboundPVC2, pvNode1b)},
  941. pvs: []*v1.PersistentVolume{pvNode1a},
  942. shouldFail: true,
  943. },
  944. "one-binding, one-pvc-provisioned": {
  945. podPVCs: []*v1.PersistentVolumeClaim{unboundPVC, provisionedPVC},
  946. bindings: []*bindingInfo{makeBinding(unboundPVC, pvNode1a)},
  947. pvs: []*v1.PersistentVolume{pvNode1a},
  948. provisionedPVCs: []*v1.PersistentVolumeClaim{provisionedPVC},
  949. expectedBindings: []*bindingInfo{makeBinding(unboundPVC, pvNode1aBound)},
  950. expectedProvisionings: []*v1.PersistentVolumeClaim{selectedNodePVC},
  951. },
  952. "one-binding, one-provision-tmpupdate-failed": {
  953. podPVCs: []*v1.PersistentVolumeClaim{unboundPVC, provisionedPVCHigherVersion},
  954. bindings: []*bindingInfo{makeBinding(unboundPVC, pvNode1a)},
  955. pvs: []*v1.PersistentVolume{pvNode1a},
  956. provisionedPVCs: []*v1.PersistentVolumeClaim{provisionedPVC2},
  957. shouldFail: true,
  958. },
  959. }
  960. ctx, cancel := context.WithCancel(context.Background())
  961. defer cancel()
  962. for name, scenario := range scenarios {
  963. klog.V(5).Infof("Running test case %q", name)
  964. // Setup
  965. testEnv := newTestBinder(t, ctx.Done())
  966. testEnv.initClaims(scenario.podPVCs, scenario.podPVCs)
  967. pod := makePod(scenario.podPVCs)
  968. testEnv.initPodCache(pod, "node1", scenario.bindings, scenario.provisionedPVCs)
  969. testEnv.initVolumes(scenario.pvs, scenario.pvs)
  970. // Execute
  971. allBound, err := testEnv.binder.AssumePodVolumes(pod, "node1")
  972. // Validate
  973. if !scenario.shouldFail && err != nil {
  974. t.Errorf("Test %q failed: returned error: %v", name, err)
  975. }
  976. if scenario.shouldFail && err == nil {
  977. t.Errorf("Test %q failed: returned success but expected error", name)
  978. }
  979. if scenario.expectedAllBound != allBound {
  980. t.Errorf("Test %q failed: returned unexpected allBound: %v", name, allBound)
  981. }
  982. if scenario.expectedBindings == nil {
  983. scenario.expectedBindings = scenario.bindings
  984. }
  985. if scenario.expectedProvisionings == nil {
  986. scenario.expectedProvisionings = scenario.provisionedPVCs
  987. }
  988. if scenario.shouldFail {
  989. testEnv.validateFailedAssume(t, name, pod, scenario.expectedBindings, scenario.expectedProvisionings)
  990. } else {
  991. testEnv.validateAssume(t, name, pod, scenario.expectedBindings, scenario.expectedProvisionings)
  992. }
  993. testEnv.validatePodCache(t, name, pod.Spec.NodeName, pod, scenario.expectedBindings, scenario.expectedProvisionings)
  994. }
  995. }
  996. func TestBindAPIUpdate(t *testing.T) {
  997. scenarios := map[string]struct {
  998. // Inputs
  999. bindings []*bindingInfo
  1000. cachedPVs []*v1.PersistentVolume
  1001. // if nil, use cachedPVs
  1002. apiPVs []*v1.PersistentVolume
  1003. provisionedPVCs []*v1.PersistentVolumeClaim
  1004. cachedPVCs []*v1.PersistentVolumeClaim
  1005. // if nil, use cachedPVCs
  1006. apiPVCs []*v1.PersistentVolumeClaim
  1007. // Expected return values
  1008. shouldFail bool
  1009. expectedPVs []*v1.PersistentVolume
  1010. // if nil, use expectedPVs
  1011. expectedAPIPVs []*v1.PersistentVolume
  1012. expectedPVCs []*v1.PersistentVolumeClaim
  1013. // if nil, use expectedPVCs
  1014. expectedAPIPVCs []*v1.PersistentVolumeClaim
  1015. }{
  1016. "nothing-to-bind-nil": {
  1017. shouldFail: true,
  1018. },
  1019. "nothing-to-bind-bindings-nil": {
  1020. provisionedPVCs: []*v1.PersistentVolumeClaim{},
  1021. shouldFail: true,
  1022. },
  1023. "nothing-to-bind-provisionings-nil": {
  1024. bindings: []*bindingInfo{},
  1025. shouldFail: true,
  1026. },
  1027. "nothing-to-bind-empty": {
  1028. bindings: []*bindingInfo{},
  1029. provisionedPVCs: []*v1.PersistentVolumeClaim{},
  1030. },
  1031. "one-binding": {
  1032. bindings: []*bindingInfo{makeBinding(unboundPVC, pvNode1aBound)},
  1033. cachedPVs: []*v1.PersistentVolume{pvNode1a},
  1034. expectedPVs: []*v1.PersistentVolume{pvNode1aBound},
  1035. provisionedPVCs: []*v1.PersistentVolumeClaim{},
  1036. },
  1037. "two-bindings": {
  1038. bindings: []*bindingInfo{makeBinding(unboundPVC, pvNode1aBound), makeBinding(unboundPVC2, pvNode1bBound)},
  1039. cachedPVs: []*v1.PersistentVolume{pvNode1a, pvNode1b},
  1040. expectedPVs: []*v1.PersistentVolume{pvNode1aBound, pvNode1bBound},
  1041. provisionedPVCs: []*v1.PersistentVolumeClaim{},
  1042. },
  1043. "api-already-updated": {
  1044. bindings: []*bindingInfo{makeBinding(unboundPVC, pvNode1aBound)},
  1045. cachedPVs: []*v1.PersistentVolume{pvNode1aBound},
  1046. expectedPVs: []*v1.PersistentVolume{pvNode1aBound},
  1047. provisionedPVCs: []*v1.PersistentVolumeClaim{},
  1048. },
  1049. "api-update-failed": {
  1050. bindings: []*bindingInfo{makeBinding(unboundPVC, pvNode1aBound), makeBinding(unboundPVC2, pvNode1bBound)},
  1051. cachedPVs: []*v1.PersistentVolume{pvNode1a, pvNode1b},
  1052. apiPVs: []*v1.PersistentVolume{pvNode1a, pvNode1bBoundHigherVersion},
  1053. expectedPVs: []*v1.PersistentVolume{pvNode1aBound, pvNode1b},
  1054. expectedAPIPVs: []*v1.PersistentVolume{pvNode1aBound, pvNode1bBoundHigherVersion},
  1055. provisionedPVCs: []*v1.PersistentVolumeClaim{},
  1056. shouldFail: true,
  1057. },
  1058. "one-provisioned-pvc": {
  1059. bindings: []*bindingInfo{},
  1060. provisionedPVCs: []*v1.PersistentVolumeClaim{addProvisionAnn(provisionedPVC)},
  1061. cachedPVCs: []*v1.PersistentVolumeClaim{provisionedPVC},
  1062. expectedPVCs: []*v1.PersistentVolumeClaim{addProvisionAnn(provisionedPVC)},
  1063. },
  1064. "provision-api-update-failed": {
  1065. bindings: []*bindingInfo{},
  1066. provisionedPVCs: []*v1.PersistentVolumeClaim{addProvisionAnn(provisionedPVC), addProvisionAnn(provisionedPVC2)},
  1067. cachedPVCs: []*v1.PersistentVolumeClaim{provisionedPVC, provisionedPVC2},
  1068. apiPVCs: []*v1.PersistentVolumeClaim{provisionedPVC, provisionedPVCHigherVersion},
  1069. expectedPVCs: []*v1.PersistentVolumeClaim{addProvisionAnn(provisionedPVC), provisionedPVC2},
  1070. expectedAPIPVCs: []*v1.PersistentVolumeClaim{addProvisionAnn(provisionedPVC), provisionedPVCHigherVersion},
  1071. shouldFail: true,
  1072. },
  1073. "binding-succeed, provision-api-update-failed": {
  1074. bindings: []*bindingInfo{makeBinding(unboundPVC, pvNode1aBound)},
  1075. cachedPVs: []*v1.PersistentVolume{pvNode1a},
  1076. expectedPVs: []*v1.PersistentVolume{pvNode1aBound},
  1077. provisionedPVCs: []*v1.PersistentVolumeClaim{addProvisionAnn(provisionedPVC), addProvisionAnn(provisionedPVC2)},
  1078. cachedPVCs: []*v1.PersistentVolumeClaim{provisionedPVC, provisionedPVC2},
  1079. apiPVCs: []*v1.PersistentVolumeClaim{provisionedPVC, provisionedPVCHigherVersion},
  1080. expectedPVCs: []*v1.PersistentVolumeClaim{addProvisionAnn(provisionedPVC), provisionedPVC2},
  1081. expectedAPIPVCs: []*v1.PersistentVolumeClaim{addProvisionAnn(provisionedPVC), provisionedPVCHigherVersion},
  1082. shouldFail: true,
  1083. },
  1084. }
  1085. ctx, cancel := context.WithCancel(context.Background())
  1086. defer cancel()
  1087. for name, scenario := range scenarios {
  1088. klog.V(4).Infof("Running test case %q", name)
  1089. // Setup
  1090. testEnv := newTestBinder(t, ctx.Done())
  1091. pod := makePod(nil)
  1092. if scenario.apiPVs == nil {
  1093. scenario.apiPVs = scenario.cachedPVs
  1094. }
  1095. if scenario.apiPVCs == nil {
  1096. scenario.apiPVCs = scenario.cachedPVCs
  1097. }
  1098. testEnv.initVolumes(scenario.cachedPVs, scenario.apiPVs)
  1099. testEnv.initClaims(scenario.cachedPVCs, scenario.apiPVCs)
  1100. testEnv.assumeVolumes(t, name, "node1", pod, scenario.bindings, scenario.provisionedPVCs)
  1101. // Execute
  1102. err := testEnv.internalBinder.bindAPIUpdate(pod.Name, scenario.bindings, scenario.provisionedPVCs)
  1103. // Validate
  1104. if !scenario.shouldFail && err != nil {
  1105. t.Errorf("Test %q failed: returned error: %v", name, err)
  1106. }
  1107. if scenario.shouldFail && err == nil {
  1108. t.Errorf("Test %q failed: returned success but expected error", name)
  1109. }
  1110. if scenario.expectedAPIPVs == nil {
  1111. scenario.expectedAPIPVs = scenario.expectedPVs
  1112. }
  1113. if scenario.expectedAPIPVCs == nil {
  1114. scenario.expectedAPIPVCs = scenario.expectedPVCs
  1115. }
  1116. testEnv.validateBind(t, name, pod, scenario.expectedPVs, scenario.expectedAPIPVs)
  1117. testEnv.validateProvision(t, name, pod, scenario.expectedPVCs, scenario.expectedAPIPVCs)
  1118. }
  1119. }
  1120. func TestCheckBindings(t *testing.T) {
  1121. scenarios := map[string]struct {
  1122. // Inputs
  1123. initPVs []*v1.PersistentVolume
  1124. initPVCs []*v1.PersistentVolumeClaim
  1125. bindings []*bindingInfo
  1126. provisionedPVCs []*v1.PersistentVolumeClaim
  1127. // api updates before checking
  1128. apiPVs []*v1.PersistentVolume
  1129. apiPVCs []*v1.PersistentVolumeClaim
  1130. // delete objects before checking
  1131. deletePVs bool
  1132. deletePVCs bool
  1133. // Expected return values
  1134. shouldFail bool
  1135. expectedBound bool
  1136. }{
  1137. "nothing-to-bind-nil": {
  1138. shouldFail: true,
  1139. },
  1140. "nothing-to-bind-bindings-nil": {
  1141. provisionedPVCs: []*v1.PersistentVolumeClaim{},
  1142. shouldFail: true,
  1143. },
  1144. "nothing-to-bind-provisionings-nil": {
  1145. bindings: []*bindingInfo{},
  1146. shouldFail: true,
  1147. },
  1148. "nothing-to-bind": {
  1149. bindings: []*bindingInfo{},
  1150. provisionedPVCs: []*v1.PersistentVolumeClaim{},
  1151. expectedBound: true,
  1152. },
  1153. "binding-bound": {
  1154. bindings: []*bindingInfo{makeBinding(unboundPVC, pvNode1aBound)},
  1155. provisionedPVCs: []*v1.PersistentVolumeClaim{},
  1156. initPVs: []*v1.PersistentVolume{pvNode1aBound},
  1157. initPVCs: []*v1.PersistentVolumeClaim{boundPVCNode1a},
  1158. expectedBound: true,
  1159. },
  1160. "binding-prebound": {
  1161. bindings: []*bindingInfo{makeBinding(unboundPVC, pvNode1aBound)},
  1162. provisionedPVCs: []*v1.PersistentVolumeClaim{},
  1163. initPVs: []*v1.PersistentVolume{pvNode1aBound},
  1164. initPVCs: []*v1.PersistentVolumeClaim{preboundPVCNode1a},
  1165. },
  1166. "binding-unbound": {
  1167. bindings: []*bindingInfo{makeBinding(unboundPVC, pvNode1aBound)},
  1168. provisionedPVCs: []*v1.PersistentVolumeClaim{},
  1169. initPVs: []*v1.PersistentVolume{pvNode1aBound},
  1170. initPVCs: []*v1.PersistentVolumeClaim{unboundPVC},
  1171. },
  1172. "binding-pvc-not-exists": {
  1173. bindings: []*bindingInfo{makeBinding(unboundPVC, pvNode1aBound)},
  1174. provisionedPVCs: []*v1.PersistentVolumeClaim{},
  1175. initPVs: []*v1.PersistentVolume{pvNode1aBound},
  1176. shouldFail: true,
  1177. },
  1178. "binding-pv-not-exists": {
  1179. bindings: []*bindingInfo{makeBinding(unboundPVC, pvNode1aBound)},
  1180. provisionedPVCs: []*v1.PersistentVolumeClaim{},
  1181. initPVs: []*v1.PersistentVolume{pvNode1aBound},
  1182. initPVCs: []*v1.PersistentVolumeClaim{boundPVCNode1a},
  1183. deletePVs: true,
  1184. shouldFail: true,
  1185. },
  1186. "binding-claimref-nil": {
  1187. bindings: []*bindingInfo{makeBinding(unboundPVC, pvNode1aBound)},
  1188. provisionedPVCs: []*v1.PersistentVolumeClaim{},
  1189. initPVs: []*v1.PersistentVolume{pvNode1a},
  1190. initPVCs: []*v1.PersistentVolumeClaim{boundPVCNode1a},
  1191. apiPVs: []*v1.PersistentVolume{pvNode1a},
  1192. apiPVCs: []*v1.PersistentVolumeClaim{boundPVCNode1a},
  1193. shouldFail: true,
  1194. },
  1195. "binding-claimref-uid-empty": {
  1196. bindings: []*bindingInfo{makeBinding(unboundPVC, pvNode1aBound)},
  1197. provisionedPVCs: []*v1.PersistentVolumeClaim{},
  1198. initPVs: []*v1.PersistentVolume{pvNode1aBound},
  1199. initPVCs: []*v1.PersistentVolumeClaim{boundPVCNode1a},
  1200. apiPVs: []*v1.PersistentVolume{pvRemoveClaimUID(pvNode1aBound)},
  1201. apiPVCs: []*v1.PersistentVolumeClaim{boundPVCNode1a},
  1202. shouldFail: true,
  1203. },
  1204. "binding-one-bound,one-unbound": {
  1205. bindings: []*bindingInfo{makeBinding(unboundPVC, pvNode1aBound), makeBinding(unboundPVC2, pvNode1bBound)},
  1206. provisionedPVCs: []*v1.PersistentVolumeClaim{},
  1207. initPVs: []*v1.PersistentVolume{pvNode1aBound, pvNode1bBound},
  1208. initPVCs: []*v1.PersistentVolumeClaim{boundPVCNode1a, unboundPVC2},
  1209. },
  1210. "provisioning-pvc-bound": {
  1211. bindings: []*bindingInfo{},
  1212. provisionedPVCs: []*v1.PersistentVolumeClaim{addProvisionAnn(provisionedPVC)},
  1213. initPVs: []*v1.PersistentVolume{pvBound},
  1214. initPVCs: []*v1.PersistentVolumeClaim{provisionedPVCBound},
  1215. apiPVCs: []*v1.PersistentVolumeClaim{addProvisionAnn(provisionedPVCBound)},
  1216. expectedBound: true,
  1217. },
  1218. "provisioning-pvc-unbound": {
  1219. bindings: []*bindingInfo{},
  1220. provisionedPVCs: []*v1.PersistentVolumeClaim{addProvisionAnn(provisionedPVC)},
  1221. initPVCs: []*v1.PersistentVolumeClaim{addProvisionAnn(provisionedPVC)},
  1222. },
  1223. "provisioning-pvc-not-exists": {
  1224. bindings: []*bindingInfo{},
  1225. provisionedPVCs: []*v1.PersistentVolumeClaim{addProvisionAnn(provisionedPVC)},
  1226. initPVCs: []*v1.PersistentVolumeClaim{provisionedPVC},
  1227. deletePVCs: true,
  1228. shouldFail: true,
  1229. },
  1230. "provisioning-pvc-annotations-nil": {
  1231. bindings: []*bindingInfo{},
  1232. provisionedPVCs: []*v1.PersistentVolumeClaim{addProvisionAnn(provisionedPVC)},
  1233. initPVCs: []*v1.PersistentVolumeClaim{provisionedPVC},
  1234. apiPVCs: []*v1.PersistentVolumeClaim{provisionedPVC},
  1235. shouldFail: true,
  1236. },
  1237. "provisioning-pvc-selected-node-dropped": {
  1238. bindings: []*bindingInfo{},
  1239. provisionedPVCs: []*v1.PersistentVolumeClaim{addProvisionAnn(provisionedPVC)},
  1240. initPVCs: []*v1.PersistentVolumeClaim{provisionedPVC},
  1241. apiPVCs: []*v1.PersistentVolumeClaim{pvcSetEmptyAnnotations(provisionedPVC)},
  1242. shouldFail: true,
  1243. },
  1244. "provisioning-pvc-selected-node-wrong-node": {
  1245. initPVCs: []*v1.PersistentVolumeClaim{provisionedPVC},
  1246. bindings: []*bindingInfo{},
  1247. provisionedPVCs: []*v1.PersistentVolumeClaim{addProvisionAnn(provisionedPVC)},
  1248. apiPVCs: []*v1.PersistentVolumeClaim{pvcSetSelectedNode(provisionedPVC, "wrong-node")},
  1249. shouldFail: true,
  1250. },
  1251. "binding-bound-provisioning-unbound": {
  1252. bindings: []*bindingInfo{makeBinding(unboundPVC, pvNode1aBound)},
  1253. provisionedPVCs: []*v1.PersistentVolumeClaim{addProvisionAnn(provisionedPVC)},
  1254. initPVs: []*v1.PersistentVolume{pvNode1aBound},
  1255. initPVCs: []*v1.PersistentVolumeClaim{boundPVCNode1a, addProvisionAnn(provisionedPVC)},
  1256. },
  1257. "tolerate-provisioning-pvc-bound-pv-not-found": {
  1258. initPVs: []*v1.PersistentVolume{pvNode1a},
  1259. initPVCs: []*v1.PersistentVolumeClaim{provisionedPVC},
  1260. bindings: []*bindingInfo{},
  1261. provisionedPVCs: []*v1.PersistentVolumeClaim{addProvisionAnn(provisionedPVC)},
  1262. apiPVCs: []*v1.PersistentVolumeClaim{addProvisionAnn(provisionedPVCBound)},
  1263. deletePVs: true,
  1264. },
  1265. }
  1266. ctx, cancel := context.WithCancel(context.Background())
  1267. defer cancel()
  1268. for name, scenario := range scenarios {
  1269. klog.V(4).Infof("Running test case %q", name)
  1270. // Setup
  1271. pod := makePod(nil)
  1272. testEnv := newTestBinder(t, ctx.Done())
  1273. testEnv.initNodes([]*v1.Node{node1})
  1274. testEnv.initVolumes(scenario.initPVs, nil)
  1275. testEnv.initClaims(scenario.initPVCs, nil)
  1276. testEnv.assumeVolumes(t, name, "node1", pod, scenario.bindings, scenario.provisionedPVCs)
  1277. // Before execute
  1278. if scenario.deletePVs {
  1279. testEnv.deleteVolumes(scenario.initPVs)
  1280. } else {
  1281. testEnv.updateVolumes(t, scenario.apiPVs, true)
  1282. }
  1283. if scenario.deletePVCs {
  1284. testEnv.deleteClaims(scenario.initPVCs)
  1285. } else {
  1286. testEnv.updateClaims(t, scenario.apiPVCs, true)
  1287. }
  1288. // Execute
  1289. allBound, err := testEnv.internalBinder.checkBindings(pod, scenario.bindings, scenario.provisionedPVCs)
  1290. // Validate
  1291. if !scenario.shouldFail && err != nil {
  1292. t.Errorf("Test %q failed: returned error: %v", name, err)
  1293. }
  1294. if scenario.shouldFail && err == nil {
  1295. t.Errorf("Test %q failed: returned success but expected error", name)
  1296. }
  1297. if scenario.expectedBound != allBound {
  1298. t.Errorf("Test %q failed: returned bound %v", name, allBound)
  1299. }
  1300. }
  1301. }
  1302. func TestBindPodVolumes(t *testing.T) {
  1303. type scenarioType struct {
  1304. // Inputs
  1305. bindingsNil bool // Pass in nil bindings slice
  1306. nodes []*v1.Node
  1307. // before assume
  1308. initPVs []*v1.PersistentVolume
  1309. initPVCs []*v1.PersistentVolumeClaim
  1310. // assume PV & PVC with these binding results
  1311. binding *bindingInfo
  1312. claimToProvision *v1.PersistentVolumeClaim
  1313. // API updates after assume before bind
  1314. apiPV *v1.PersistentVolume
  1315. apiPVC *v1.PersistentVolumeClaim
  1316. // This function runs with a delay of 5 seconds
  1317. delayFunc func(t *testing.T, testEnv *testEnv, pod *v1.Pod, pvs []*v1.PersistentVolume, pvcs []*v1.PersistentVolumeClaim)
  1318. // Expected return values
  1319. shouldFail bool
  1320. }
  1321. scenarios := map[string]scenarioType{
  1322. "nothing-to-bind-nil": {
  1323. bindingsNil: true,
  1324. shouldFail: true,
  1325. },
  1326. "nothing-to-bind-empty": {},
  1327. "already-bound": {
  1328. binding: makeBinding(unboundPVC, pvNode1aBound),
  1329. initPVs: []*v1.PersistentVolume{pvNode1aBound},
  1330. initPVCs: []*v1.PersistentVolumeClaim{boundPVCNode1a},
  1331. },
  1332. "binding-static-pv-succeeds-after-time": {
  1333. initPVs: []*v1.PersistentVolume{pvNode1a},
  1334. initPVCs: []*v1.PersistentVolumeClaim{unboundPVC},
  1335. binding: makeBinding(unboundPVC, pvNode1aBound),
  1336. shouldFail: false, // Will succeed after PVC is fully bound to this PV by pv controller.
  1337. delayFunc: func(t *testing.T, testEnv *testEnv, pod *v1.Pod, pvs []*v1.PersistentVolume, pvcs []*v1.PersistentVolumeClaim) {
  1338. pvc := pvcs[0]
  1339. pv := pvs[0]
  1340. // Update PVC to be fully bound to PV
  1341. newPVC := pvc.DeepCopy()
  1342. newPVC.Spec.VolumeName = pv.Name
  1343. metav1.SetMetaDataAnnotation(&newPVC.ObjectMeta, pvutil.AnnBindCompleted, "yes")
  1344. if _, err := testEnv.client.CoreV1().PersistentVolumeClaims(newPVC.Namespace).Update(newPVC); err != nil {
  1345. t.Errorf("failed to update PVC %q: %v", newPVC.Name, err)
  1346. }
  1347. },
  1348. },
  1349. "binding-dynamic-pv-succeeds-after-time": {
  1350. claimToProvision: pvcSetSelectedNode(provisionedPVC, "node1"),
  1351. initPVCs: []*v1.PersistentVolumeClaim{provisionedPVC},
  1352. delayFunc: func(t *testing.T, testEnv *testEnv, pod *v1.Pod, pvs []*v1.PersistentVolume, pvcs []*v1.PersistentVolumeClaim) {
  1353. pvc := pvcs[0]
  1354. // Update PVC to be fully bound to PV
  1355. newPVC, err := testEnv.client.CoreV1().PersistentVolumeClaims(pvc.Namespace).Get(pvc.Name, metav1.GetOptions{})
  1356. if err != nil {
  1357. t.Errorf("failed to get PVC %q: %v", pvc.Name, err)
  1358. return
  1359. }
  1360. dynamicPV := makeTestPV("dynamic-pv", "node1", "1G", "1", newPVC, waitClass)
  1361. dynamicPV, err = testEnv.client.CoreV1().PersistentVolumes().Create(dynamicPV)
  1362. if err != nil {
  1363. t.Errorf("failed to create PV %q: %v", dynamicPV.Name, err)
  1364. return
  1365. }
  1366. newPVC.Spec.VolumeName = dynamicPV.Name
  1367. metav1.SetMetaDataAnnotation(&newPVC.ObjectMeta, pvutil.AnnBindCompleted, "yes")
  1368. if _, err := testEnv.client.CoreV1().PersistentVolumeClaims(newPVC.Namespace).Update(newPVC); err != nil {
  1369. t.Errorf("failed to update PVC %q: %v", newPVC.Name, err)
  1370. }
  1371. },
  1372. },
  1373. "bound-by-pv-controller-before-bind": {
  1374. initPVs: []*v1.PersistentVolume{pvNode1a},
  1375. initPVCs: []*v1.PersistentVolumeClaim{unboundPVC},
  1376. binding: makeBinding(unboundPVC, pvNode1aBound),
  1377. apiPV: pvNode1aBound,
  1378. apiPVC: boundPVCNode1a,
  1379. shouldFail: true, // bindAPIUpdate will fail because API conflict
  1380. },
  1381. "pod-deleted-after-time": {
  1382. binding: makeBinding(unboundPVC, pvNode1aBound),
  1383. initPVs: []*v1.PersistentVolume{pvNode1a},
  1384. initPVCs: []*v1.PersistentVolumeClaim{unboundPVC},
  1385. delayFunc: func(t *testing.T, testEnv *testEnv, pod *v1.Pod, pvs []*v1.PersistentVolume, pvcs []*v1.PersistentVolumeClaim) {
  1386. bindingsCache := testEnv.binder.GetBindingsCache()
  1387. if bindingsCache == nil {
  1388. t.Fatalf("Failed to get bindings cache")
  1389. }
  1390. // Delete the pod from the cache
  1391. bindingsCache.DeleteBindings(pod)
  1392. // Check that it's deleted
  1393. bindings := bindingsCache.GetBindings(pod, "node1")
  1394. if bindings != nil {
  1395. t.Fatalf("Failed to delete bindings")
  1396. }
  1397. },
  1398. shouldFail: true,
  1399. },
  1400. "binding-times-out": {
  1401. binding: makeBinding(unboundPVC, pvNode1aBound),
  1402. initPVs: []*v1.PersistentVolume{pvNode1a},
  1403. initPVCs: []*v1.PersistentVolumeClaim{unboundPVC},
  1404. shouldFail: true,
  1405. },
  1406. "binding-fails": {
  1407. binding: makeBinding(unboundPVC2, pvNode1bBound),
  1408. initPVs: []*v1.PersistentVolume{pvNode1b},
  1409. initPVCs: []*v1.PersistentVolumeClaim{unboundPVC2},
  1410. shouldFail: true,
  1411. },
  1412. "check-fails": {
  1413. binding: makeBinding(unboundPVC, pvNode1aBound),
  1414. initPVs: []*v1.PersistentVolume{pvNode1a},
  1415. initPVCs: []*v1.PersistentVolumeClaim{unboundPVC},
  1416. delayFunc: func(t *testing.T, testEnv *testEnv, pod *v1.Pod, pvs []*v1.PersistentVolume, pvcs []*v1.PersistentVolumeClaim) {
  1417. pvc := pvcs[0]
  1418. // Delete PVC will fail check
  1419. if err := testEnv.client.CoreV1().PersistentVolumeClaims(pvc.Namespace).Delete(pvc.Name, &metav1.DeleteOptions{}); err != nil {
  1420. t.Errorf("failed to delete PVC %q: %v", pvc.Name, err)
  1421. }
  1422. },
  1423. shouldFail: true,
  1424. },
  1425. "node-affinity-fails": {
  1426. binding: makeBinding(unboundPVC, pvNode1aBound),
  1427. initPVs: []*v1.PersistentVolume{pvNode1aBound},
  1428. initPVCs: []*v1.PersistentVolumeClaim{boundPVCNode1a},
  1429. nodes: []*v1.Node{node1NoLabels},
  1430. shouldFail: true,
  1431. },
  1432. "node-affinity-fails-dynamic-provisioning": {
  1433. initPVs: []*v1.PersistentVolume{pvNode1a, pvNode2},
  1434. initPVCs: []*v1.PersistentVolumeClaim{selectedNodePVC},
  1435. claimToProvision: selectedNodePVC,
  1436. nodes: []*v1.Node{node1, node2},
  1437. delayFunc: func(t *testing.T, testEnv *testEnv, pod *v1.Pod, pvs []*v1.PersistentVolume, pvcs []*v1.PersistentVolumeClaim) {
  1438. // Update PVC to be fully bound to a PV with a different node
  1439. newPVC := pvcs[0].DeepCopy()
  1440. newPVC.Spec.VolumeName = pvNode2.Name
  1441. metav1.SetMetaDataAnnotation(&newPVC.ObjectMeta, pvutil.AnnBindCompleted, "yes")
  1442. if _, err := testEnv.client.CoreV1().PersistentVolumeClaims(newPVC.Namespace).Update(newPVC); err != nil {
  1443. t.Errorf("failed to update PVC %q: %v", newPVC.Name, err)
  1444. }
  1445. },
  1446. shouldFail: true,
  1447. },
  1448. }
  1449. ctx, cancel := context.WithCancel(context.Background())
  1450. defer cancel()
  1451. for name, scenario := range scenarios {
  1452. klog.V(4).Infof("Running test case %q", name)
  1453. // Setup
  1454. pod := makePod(nil)
  1455. testEnv := newTestBinder(t, ctx.Done())
  1456. if scenario.nodes == nil {
  1457. scenario.nodes = []*v1.Node{node1}
  1458. }
  1459. if !scenario.bindingsNil {
  1460. bindings := []*bindingInfo{}
  1461. if scenario.binding != nil {
  1462. bindings = []*bindingInfo{scenario.binding}
  1463. }
  1464. claimsToProvision := []*v1.PersistentVolumeClaim{}
  1465. if scenario.claimToProvision != nil {
  1466. claimsToProvision = []*v1.PersistentVolumeClaim{scenario.claimToProvision}
  1467. }
  1468. testEnv.initNodes(scenario.nodes)
  1469. testEnv.initVolumes(scenario.initPVs, scenario.initPVs)
  1470. testEnv.initClaims(scenario.initPVCs, scenario.initPVCs)
  1471. testEnv.assumeVolumes(t, name, "node1", pod, bindings, claimsToProvision)
  1472. }
  1473. // Before Execute
  1474. if scenario.apiPV != nil {
  1475. _, err := testEnv.client.CoreV1().PersistentVolumes().Update(scenario.apiPV)
  1476. if err != nil {
  1477. t.Fatalf("Test %q failed: failed to update PV %q", name, scenario.apiPV.Name)
  1478. }
  1479. }
  1480. if scenario.apiPVC != nil {
  1481. _, err := testEnv.client.CoreV1().PersistentVolumeClaims(scenario.apiPVC.Namespace).Update(scenario.apiPVC)
  1482. if err != nil {
  1483. t.Fatalf("Test %q failed: failed to update PVC %q", name, getPVCName(scenario.apiPVC))
  1484. }
  1485. }
  1486. if scenario.delayFunc != nil {
  1487. go func(scenario scenarioType) {
  1488. time.Sleep(5 * time.Second)
  1489. // Sleep a while to run after bindAPIUpdate in BindPodVolumes
  1490. klog.V(5).Infof("Running delay function")
  1491. scenario.delayFunc(t, testEnv, pod, scenario.initPVs, scenario.initPVCs)
  1492. }(scenario)
  1493. }
  1494. // Execute
  1495. err := testEnv.binder.BindPodVolumes(pod)
  1496. // Validate
  1497. if !scenario.shouldFail && err != nil {
  1498. t.Errorf("Test %q failed: returned error: %v", name, err)
  1499. }
  1500. if scenario.shouldFail && err == nil {
  1501. t.Errorf("Test %q failed: returned success but expected error", name)
  1502. }
  1503. }
  1504. }
  1505. func TestFindAssumeVolumes(t *testing.T) {
  1506. // Test case
  1507. podPVCs := []*v1.PersistentVolumeClaim{unboundPVC}
  1508. pvs := []*v1.PersistentVolume{pvNode2, pvNode1a, pvNode1c}
  1509. // Setup
  1510. ctx, cancel := context.WithCancel(context.Background())
  1511. defer cancel()
  1512. testEnv := newTestBinder(t, ctx.Done())
  1513. testEnv.initVolumes(pvs, pvs)
  1514. testEnv.initClaims(podPVCs, podPVCs)
  1515. pod := makePod(podPVCs)
  1516. testNode := &v1.Node{
  1517. ObjectMeta: metav1.ObjectMeta{
  1518. Name: "node1",
  1519. Labels: map[string]string{
  1520. nodeLabelKey: "node1",
  1521. },
  1522. },
  1523. }
  1524. // Execute
  1525. // 1. Find matching PVs
  1526. unboundSatisfied, _, err := testEnv.binder.FindPodVolumes(pod, testNode)
  1527. if err != nil {
  1528. t.Errorf("Test failed: FindPodVolumes returned error: %v", err)
  1529. }
  1530. if !unboundSatisfied {
  1531. t.Errorf("Test failed: couldn't find PVs for all PVCs")
  1532. }
  1533. expectedBindings := testEnv.getPodBindings(t, "before-assume", testNode.Name, pod)
  1534. // 2. Assume matches
  1535. allBound, err := testEnv.binder.AssumePodVolumes(pod, testNode.Name)
  1536. if err != nil {
  1537. t.Errorf("Test failed: AssumePodVolumes returned error: %v", err)
  1538. }
  1539. if allBound {
  1540. t.Errorf("Test failed: detected unbound volumes as bound")
  1541. }
  1542. testEnv.validateAssume(t, "assume", pod, expectedBindings, nil)
  1543. // After assume, claimref should be set on pv
  1544. expectedBindings = testEnv.getPodBindings(t, "after-assume", testNode.Name, pod)
  1545. // 3. Find matching PVs again
  1546. // This should always return the original chosen pv
  1547. // Run this many times in case sorting returns different orders for the two PVs.
  1548. t.Logf("Testing FindPodVolumes after Assume")
  1549. for i := 0; i < 50; i++ {
  1550. unboundSatisfied, _, err := testEnv.binder.FindPodVolumes(pod, testNode)
  1551. if err != nil {
  1552. t.Errorf("Test failed: FindPodVolumes returned error: %v", err)
  1553. }
  1554. if !unboundSatisfied {
  1555. t.Errorf("Test failed: couldn't find PVs for all PVCs")
  1556. }
  1557. testEnv.validatePodCache(t, "after-assume", testNode.Name, pod, expectedBindings, nil)
  1558. }
  1559. }