scheduler_binder_test.go 69 KB

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