scheduler_binder.go 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905
  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. "sort"
  18. "strings"
  19. "time"
  20. v1 "k8s.io/api/core/v1"
  21. storagev1 "k8s.io/api/storage/v1"
  22. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  23. "k8s.io/apimachinery/pkg/labels"
  24. "k8s.io/apimachinery/pkg/util/sets"
  25. "k8s.io/apimachinery/pkg/util/wait"
  26. "k8s.io/apiserver/pkg/storage/etcd3"
  27. utilfeature "k8s.io/apiserver/pkg/util/feature"
  28. coreinformers "k8s.io/client-go/informers/core/v1"
  29. storageinformers "k8s.io/client-go/informers/storage/v1"
  30. clientset "k8s.io/client-go/kubernetes"
  31. storagelisters "k8s.io/client-go/listers/storage/v1"
  32. csitrans "k8s.io/csi-translation-lib"
  33. csiplugins "k8s.io/csi-translation-lib/plugins"
  34. "k8s.io/klog"
  35. v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
  36. pvutil "k8s.io/kubernetes/pkg/controller/volume/persistentvolume/util"
  37. "k8s.io/kubernetes/pkg/controller/volume/scheduling/metrics"
  38. "k8s.io/kubernetes/pkg/features"
  39. volumeutil "k8s.io/kubernetes/pkg/volume/util"
  40. )
  41. // InTreeToCSITranslator contains methods required to check migratable status
  42. // and perform translations from InTree PV's to CSI
  43. type InTreeToCSITranslator interface {
  44. IsPVMigratable(pv *v1.PersistentVolume) bool
  45. GetInTreePluginNameFromSpec(pv *v1.PersistentVolume, vol *v1.Volume) (string, error)
  46. TranslateInTreePVToCSI(pv *v1.PersistentVolume) (*v1.PersistentVolume, error)
  47. }
  48. // SchedulerVolumeBinder is used by the scheduler to handle PVC/PV binding
  49. // and dynamic provisioning. The binding decisions are integrated into the pod scheduling
  50. // workflow so that the PV NodeAffinity is also considered along with the pod's other
  51. // scheduling requirements.
  52. //
  53. // This integrates into the existing default scheduler workflow as follows:
  54. // 1. The scheduler takes a Pod off the scheduler queue and processes it serially:
  55. // a. Invokes all predicate functions, parallelized across nodes. FindPodVolumes() is invoked here.
  56. // b. Invokes all priority functions. Future/TBD
  57. // c. Selects the best node for the Pod.
  58. // d. Cache the node selection for the Pod. AssumePodVolumes() is invoked here.
  59. // i. If PVC binding is required, cache in-memory only:
  60. // * For manual binding: update PV objects for prebinding to the corresponding PVCs.
  61. // * For dynamic provisioning: update PVC object with a selected node from c)
  62. // * For the pod, which PVCs and PVs need API updates.
  63. // ii. Afterwards, the main scheduler caches the Pod->Node binding in the scheduler's pod cache,
  64. // This is handled in the scheduler and not here.
  65. // e. Asynchronously bind volumes and pod in a separate goroutine
  66. // i. BindPodVolumes() is called first. It makes all the necessary API updates and waits for
  67. // PV controller to fully bind and provision the PVCs. If binding fails, the Pod is sent
  68. // back through the scheduler.
  69. // ii. After BindPodVolumes() is complete, then the scheduler does the final Pod->Node binding.
  70. // 2. Once all the assume operations are done in d), the scheduler processes the next Pod in the scheduler queue
  71. // while the actual binding operation occurs in the background.
  72. type SchedulerVolumeBinder interface {
  73. // FindPodVolumes checks if all of a Pod's PVCs can be satisfied by the node.
  74. //
  75. // If a PVC is bound, it checks if the PV's NodeAffinity matches the Node.
  76. // Otherwise, it tries to find an available PV to bind to the PVC.
  77. //
  78. // It returns true if all of the Pod's PVCs have matching PVs or can be dynamic provisioned,
  79. // and returns true if bound volumes satisfy the PV NodeAffinity.
  80. //
  81. // This function is called by the volume binding scheduler predicate and can be called in parallel
  82. FindPodVolumes(pod *v1.Pod, node *v1.Node) (unboundVolumesSatisified, boundVolumesSatisfied bool, err error)
  83. // AssumePodVolumes will:
  84. // 1. Take the PV matches for unbound PVCs and update the PV cache assuming
  85. // that the PV is prebound to the PVC.
  86. // 2. Take the PVCs that need provisioning and update the PVC cache with related
  87. // annotations set.
  88. //
  89. // It returns true if all volumes are fully bound
  90. //
  91. // This function will modify assumedPod with the node name.
  92. // This function is called serially.
  93. AssumePodVolumes(assumedPod *v1.Pod, nodeName string) (allFullyBound bool, err error)
  94. // BindPodVolumes will:
  95. // 1. Initiate the volume binding by making the API call to prebind the PV
  96. // to its matching PVC.
  97. // 2. Trigger the volume provisioning by making the API call to set related
  98. // annotations on the PVC
  99. // 3. Wait for PVCs to be completely bound by the PV controller
  100. //
  101. // This function can be called in parallel.
  102. BindPodVolumes(assumedPod *v1.Pod) error
  103. // GetBindingsCache returns the cache used (if any) to store volume binding decisions.
  104. GetBindingsCache() PodBindingCache
  105. }
  106. type volumeBinder struct {
  107. kubeClient clientset.Interface
  108. classLister storagelisters.StorageClassLister
  109. nodeInformer coreinformers.NodeInformer
  110. csiNodeInformer storageinformers.CSINodeInformer
  111. pvcCache PVCAssumeCache
  112. pvCache PVAssumeCache
  113. // Stores binding decisions that were made in FindPodVolumes for use in AssumePodVolumes.
  114. // AssumePodVolumes modifies the bindings again for use in BindPodVolumes.
  115. podBindingCache PodBindingCache
  116. // Amount of time to wait for the bind operation to succeed
  117. bindTimeout time.Duration
  118. translator InTreeToCSITranslator
  119. }
  120. // NewVolumeBinder sets up all the caches needed for the scheduler to make volume binding decisions.
  121. func NewVolumeBinder(
  122. kubeClient clientset.Interface,
  123. nodeInformer coreinformers.NodeInformer,
  124. csiNodeInformer storageinformers.CSINodeInformer,
  125. pvcInformer coreinformers.PersistentVolumeClaimInformer,
  126. pvInformer coreinformers.PersistentVolumeInformer,
  127. storageClassInformer storageinformers.StorageClassInformer,
  128. bindTimeout time.Duration) SchedulerVolumeBinder {
  129. b := &volumeBinder{
  130. kubeClient: kubeClient,
  131. classLister: storageClassInformer.Lister(),
  132. nodeInformer: nodeInformer,
  133. csiNodeInformer: csiNodeInformer,
  134. pvcCache: NewPVCAssumeCache(pvcInformer.Informer()),
  135. pvCache: NewPVAssumeCache(pvInformer.Informer()),
  136. podBindingCache: NewPodBindingCache(),
  137. bindTimeout: bindTimeout,
  138. translator: csitrans.New(),
  139. }
  140. return b
  141. }
  142. func (b *volumeBinder) GetBindingsCache() PodBindingCache {
  143. return b.podBindingCache
  144. }
  145. // FindPodVolumes caches the matching PVs and PVCs to provision per node in podBindingCache.
  146. // This method intentionally takes in a *v1.Node object instead of using volumebinder.nodeInformer.
  147. // That's necessary because some operations will need to pass in to the predicate fake node objects.
  148. func (b *volumeBinder) FindPodVolumes(pod *v1.Pod, node *v1.Node) (unboundVolumesSatisfied, boundVolumesSatisfied bool, err error) {
  149. podName := getPodName(pod)
  150. // Warning: Below log needs high verbosity as it can be printed several times (#60933).
  151. klog.V(5).Infof("FindPodVolumes for pod %q, node %q", podName, node.Name)
  152. // Initialize to true for pods that don't have volumes
  153. unboundVolumesSatisfied = true
  154. boundVolumesSatisfied = true
  155. start := time.Now()
  156. defer func() {
  157. metrics.VolumeSchedulingStageLatency.WithLabelValues("predicate").Observe(time.Since(start).Seconds())
  158. if err != nil {
  159. metrics.VolumeSchedulingStageFailed.WithLabelValues("predicate").Inc()
  160. }
  161. }()
  162. var (
  163. matchedBindings []*bindingInfo
  164. provisionedClaims []*v1.PersistentVolumeClaim
  165. )
  166. defer func() {
  167. // We recreate bindings for each new schedule loop.
  168. if len(matchedBindings) == 0 && len(provisionedClaims) == 0 {
  169. // Clear cache if no claims to bind or provision for this node.
  170. b.podBindingCache.ClearBindings(pod, node.Name)
  171. return
  172. }
  173. // Although we do not distinguish nil from empty in this function, for
  174. // easier testing, we normalize empty to nil.
  175. if len(matchedBindings) == 0 {
  176. matchedBindings = nil
  177. }
  178. if len(provisionedClaims) == 0 {
  179. provisionedClaims = nil
  180. }
  181. // Mark cache with all matched and provisioned claims for this node
  182. b.podBindingCache.UpdateBindings(pod, node.Name, matchedBindings, provisionedClaims)
  183. }()
  184. // The pod's volumes need to be processed in one call to avoid the race condition where
  185. // volumes can get bound/provisioned in between calls.
  186. boundClaims, claimsToBind, unboundClaimsImmediate, err := b.getPodVolumes(pod)
  187. if err != nil {
  188. return false, false, err
  189. }
  190. // Immediate claims should be bound
  191. if len(unboundClaimsImmediate) > 0 {
  192. return false, false, fmt.Errorf("pod has unbound immediate PersistentVolumeClaims")
  193. }
  194. // Check PV node affinity on bound volumes
  195. if len(boundClaims) > 0 {
  196. boundVolumesSatisfied, err = b.checkBoundClaims(boundClaims, node, podName)
  197. if err != nil {
  198. return false, false, err
  199. }
  200. }
  201. // Find matching volumes and node for unbound claims
  202. if len(claimsToBind) > 0 {
  203. var (
  204. claimsToFindMatching []*v1.PersistentVolumeClaim
  205. claimsToProvision []*v1.PersistentVolumeClaim
  206. )
  207. // Filter out claims to provision
  208. for _, claim := range claimsToBind {
  209. if selectedNode, ok := claim.Annotations[pvutil.AnnSelectedNode]; ok {
  210. if selectedNode != node.Name {
  211. // Fast path, skip unmatched node
  212. return false, boundVolumesSatisfied, nil
  213. }
  214. claimsToProvision = append(claimsToProvision, claim)
  215. } else {
  216. claimsToFindMatching = append(claimsToFindMatching, claim)
  217. }
  218. }
  219. // Find matching volumes
  220. if len(claimsToFindMatching) > 0 {
  221. var unboundClaims []*v1.PersistentVolumeClaim
  222. unboundVolumesSatisfied, matchedBindings, unboundClaims, err = b.findMatchingVolumes(pod, claimsToFindMatching, node)
  223. if err != nil {
  224. return false, false, err
  225. }
  226. claimsToProvision = append(claimsToProvision, unboundClaims...)
  227. }
  228. // Check for claims to provision
  229. if len(claimsToProvision) > 0 {
  230. unboundVolumesSatisfied, provisionedClaims, err = b.checkVolumeProvisions(pod, claimsToProvision, node)
  231. if err != nil {
  232. return false, false, err
  233. }
  234. }
  235. }
  236. return unboundVolumesSatisfied, boundVolumesSatisfied, nil
  237. }
  238. // AssumePodVolumes will take the cached matching PVs and PVCs to provision
  239. // in podBindingCache for the chosen node, and:
  240. // 1. Update the pvCache with the new prebound PV.
  241. // 2. Update the pvcCache with the new PVCs with annotations set
  242. // 3. Update podBindingCache again with cached API updates for PVs and PVCs.
  243. func (b *volumeBinder) AssumePodVolumes(assumedPod *v1.Pod, nodeName string) (allFullyBound bool, err error) {
  244. podName := getPodName(assumedPod)
  245. klog.V(4).Infof("AssumePodVolumes for pod %q, node %q", podName, nodeName)
  246. start := time.Now()
  247. defer func() {
  248. metrics.VolumeSchedulingStageLatency.WithLabelValues("assume").Observe(time.Since(start).Seconds())
  249. if err != nil {
  250. metrics.VolumeSchedulingStageFailed.WithLabelValues("assume").Inc()
  251. }
  252. }()
  253. if allBound := b.arePodVolumesBound(assumedPod); allBound {
  254. klog.V(4).Infof("AssumePodVolumes for pod %q, node %q: all PVCs bound and nothing to do", podName, nodeName)
  255. return true, nil
  256. }
  257. assumedPod.Spec.NodeName = nodeName
  258. claimsToBind := b.podBindingCache.GetBindings(assumedPod, nodeName)
  259. claimsToProvision := b.podBindingCache.GetProvisionedPVCs(assumedPod, nodeName)
  260. // Assume PV
  261. newBindings := []*bindingInfo{}
  262. for _, binding := range claimsToBind {
  263. newPV, dirty, err := pvutil.GetBindVolumeToClaim(binding.pv, binding.pvc)
  264. klog.V(5).Infof("AssumePodVolumes: GetBindVolumeToClaim for pod %q, PV %q, PVC %q. newPV %p, dirty %v, err: %v",
  265. podName,
  266. binding.pv.Name,
  267. binding.pvc.Name,
  268. newPV,
  269. dirty,
  270. err)
  271. if err != nil {
  272. b.revertAssumedPVs(newBindings)
  273. return false, err
  274. }
  275. // TODO: can we assume everytime?
  276. if dirty {
  277. err = b.pvCache.Assume(newPV)
  278. if err != nil {
  279. b.revertAssumedPVs(newBindings)
  280. return false, err
  281. }
  282. }
  283. newBindings = append(newBindings, &bindingInfo{pv: newPV, pvc: binding.pvc})
  284. }
  285. // Assume PVCs
  286. newProvisionedPVCs := []*v1.PersistentVolumeClaim{}
  287. for _, claim := range claimsToProvision {
  288. // The claims from method args can be pointing to watcher cache. We must not
  289. // modify these, therefore create a copy.
  290. claimClone := claim.DeepCopy()
  291. metav1.SetMetaDataAnnotation(&claimClone.ObjectMeta, pvutil.AnnSelectedNode, nodeName)
  292. err = b.pvcCache.Assume(claimClone)
  293. if err != nil {
  294. b.revertAssumedPVs(newBindings)
  295. b.revertAssumedPVCs(newProvisionedPVCs)
  296. return
  297. }
  298. newProvisionedPVCs = append(newProvisionedPVCs, claimClone)
  299. }
  300. // Update cache with the assumed pvcs and pvs
  301. // Even if length is zero, update the cache with an empty slice to indicate that no
  302. // operations are needed
  303. b.podBindingCache.UpdateBindings(assumedPod, nodeName, newBindings, newProvisionedPVCs)
  304. return
  305. }
  306. // BindPodVolumes gets the cached bindings and PVCs to provision in podBindingCache,
  307. // makes the API update for those PVs/PVCs, and waits for the PVCs to be completely bound
  308. // by the PV controller.
  309. func (b *volumeBinder) BindPodVolumes(assumedPod *v1.Pod) (err error) {
  310. podName := getPodName(assumedPod)
  311. klog.V(4).Infof("BindPodVolumes for pod %q, node %q", podName, assumedPod.Spec.NodeName)
  312. start := time.Now()
  313. defer func() {
  314. metrics.VolumeSchedulingStageLatency.WithLabelValues("bind").Observe(time.Since(start).Seconds())
  315. if err != nil {
  316. metrics.VolumeSchedulingStageFailed.WithLabelValues("bind").Inc()
  317. }
  318. }()
  319. bindings := b.podBindingCache.GetBindings(assumedPod, assumedPod.Spec.NodeName)
  320. claimsToProvision := b.podBindingCache.GetProvisionedPVCs(assumedPod, assumedPod.Spec.NodeName)
  321. // Start API operations
  322. err = b.bindAPIUpdate(podName, bindings, claimsToProvision)
  323. if err != nil {
  324. return err
  325. }
  326. err = wait.Poll(time.Second, b.bindTimeout, func() (bool, error) {
  327. b, err := b.checkBindings(assumedPod, bindings, claimsToProvision)
  328. return b, err
  329. })
  330. if err != nil {
  331. return fmt.Errorf("Failed to bind volumes: %v", err)
  332. }
  333. return nil
  334. }
  335. func getPodName(pod *v1.Pod) string {
  336. return pod.Namespace + "/" + pod.Name
  337. }
  338. func getPVCName(pvc *v1.PersistentVolumeClaim) string {
  339. return pvc.Namespace + "/" + pvc.Name
  340. }
  341. // bindAPIUpdate gets the cached bindings and PVCs to provision in podBindingCache
  342. // and makes the API update for those PVs/PVCs.
  343. func (b *volumeBinder) bindAPIUpdate(podName string, bindings []*bindingInfo, claimsToProvision []*v1.PersistentVolumeClaim) error {
  344. if bindings == nil {
  345. return fmt.Errorf("failed to get cached bindings for pod %q", podName)
  346. }
  347. if claimsToProvision == nil {
  348. return fmt.Errorf("failed to get cached claims to provision for pod %q", podName)
  349. }
  350. lastProcessedBinding := 0
  351. lastProcessedProvisioning := 0
  352. defer func() {
  353. // only revert assumed cached updates for volumes we haven't successfully bound
  354. if lastProcessedBinding < len(bindings) {
  355. b.revertAssumedPVs(bindings[lastProcessedBinding:])
  356. }
  357. // only revert assumed cached updates for claims we haven't updated,
  358. if lastProcessedProvisioning < len(claimsToProvision) {
  359. b.revertAssumedPVCs(claimsToProvision[lastProcessedProvisioning:])
  360. }
  361. }()
  362. var (
  363. binding *bindingInfo
  364. i int
  365. claim *v1.PersistentVolumeClaim
  366. )
  367. // Do the actual prebinding. Let the PV controller take care of the rest
  368. // There is no API rollback if the actual binding fails
  369. for _, binding = range bindings {
  370. klog.V(5).Infof("bindAPIUpdate: Pod %q, binding PV %q to PVC %q", podName, binding.pv.Name, binding.pvc.Name)
  371. // TODO: does it hurt if we make an api call and nothing needs to be updated?
  372. claimKey := claimToClaimKey(binding.pvc)
  373. klog.V(2).Infof("claim %q bound to volume %q", claimKey, binding.pv.Name)
  374. newPV, err := b.kubeClient.CoreV1().PersistentVolumes().Update(context.TODO(), binding.pv, metav1.UpdateOptions{})
  375. if err != nil {
  376. klog.V(4).Infof("updating PersistentVolume[%s]: binding to %q failed: %v", binding.pv.Name, claimKey, err)
  377. return err
  378. }
  379. klog.V(4).Infof("updating PersistentVolume[%s]: bound to %q", binding.pv.Name, claimKey)
  380. // Save updated object from apiserver for later checking.
  381. binding.pv = newPV
  382. lastProcessedBinding++
  383. }
  384. // Update claims objects to trigger volume provisioning. Let the PV controller take care of the rest
  385. // PV controller is expect to signal back by removing related annotations if actual provisioning fails
  386. for i, claim = range claimsToProvision {
  387. klog.V(5).Infof("bindAPIUpdate: Pod %q, PVC %q", podName, getPVCName(claim))
  388. newClaim, err := b.kubeClient.CoreV1().PersistentVolumeClaims(claim.Namespace).Update(context.TODO(), claim, metav1.UpdateOptions{})
  389. if err != nil {
  390. return err
  391. }
  392. // Save updated object from apiserver for later checking.
  393. claimsToProvision[i] = newClaim
  394. lastProcessedProvisioning++
  395. }
  396. return nil
  397. }
  398. var (
  399. versioner = etcd3.APIObjectVersioner{}
  400. )
  401. // checkBindings runs through all the PVCs in the Pod and checks:
  402. // * if the PVC is fully bound
  403. // * if there are any conditions that require binding to fail and be retried
  404. //
  405. // It returns true when all of the Pod's PVCs are fully bound, and error if
  406. // binding (and scheduling) needs to be retried
  407. // Note that it checks on API objects not PV/PVC cache, this is because
  408. // PV/PVC cache can be assumed again in main scheduler loop, we must check
  409. // latest state in API server which are shared with PV controller and
  410. // provisioners
  411. func (b *volumeBinder) checkBindings(pod *v1.Pod, bindings []*bindingInfo, claimsToProvision []*v1.PersistentVolumeClaim) (bool, error) {
  412. podName := getPodName(pod)
  413. if bindings == nil {
  414. return false, fmt.Errorf("failed to get cached bindings for pod %q", podName)
  415. }
  416. if claimsToProvision == nil {
  417. return false, fmt.Errorf("failed to get cached claims to provision for pod %q", podName)
  418. }
  419. node, err := b.nodeInformer.Lister().Get(pod.Spec.NodeName)
  420. if err != nil {
  421. return false, fmt.Errorf("failed to get node %q: %v", pod.Spec.NodeName, err)
  422. }
  423. csiNode, err := b.csiNodeInformer.Lister().Get(node.Name)
  424. if err != nil {
  425. // TODO: return the error once CSINode is created by default
  426. klog.V(4).Infof("Could not get a CSINode object for the node %q: %v", node.Name, err)
  427. }
  428. // Check for any conditions that might require scheduling retry
  429. // When pod is removed from scheduling queue because of deletion or any
  430. // other reasons, binding operation should be cancelled. There is no need
  431. // to check PV/PVC bindings any more.
  432. // We check pod binding cache here which will be cleared when pod is
  433. // removed from scheduling queue.
  434. if b.podBindingCache.GetDecisions(pod) == nil {
  435. return false, fmt.Errorf("pod %q does not exist any more", podName)
  436. }
  437. for _, binding := range bindings {
  438. pv, err := b.pvCache.GetAPIPV(binding.pv.Name)
  439. if err != nil {
  440. return false, fmt.Errorf("failed to check binding: %v", err)
  441. }
  442. pvc, err := b.pvcCache.GetAPIPVC(getPVCName(binding.pvc))
  443. if err != nil {
  444. return false, fmt.Errorf("failed to check binding: %v", err)
  445. }
  446. // Because we updated PV in apiserver, skip if API object is older
  447. // and wait for new API object propagated from apiserver.
  448. if versioner.CompareResourceVersion(binding.pv, pv) > 0 {
  449. return false, nil
  450. }
  451. pv, err = b.tryTranslatePVToCSI(pv, csiNode)
  452. if err != nil {
  453. return false, fmt.Errorf("failed to translate pv to csi: %v", err)
  454. }
  455. // Check PV's node affinity (the node might not have the proper label)
  456. if err := volumeutil.CheckNodeAffinity(pv, node.Labels); err != nil {
  457. return false, fmt.Errorf("pv %q node affinity doesn't match node %q: %v", pv.Name, node.Name, err)
  458. }
  459. // Check if pv.ClaimRef got dropped by unbindVolume()
  460. if pv.Spec.ClaimRef == nil || pv.Spec.ClaimRef.UID == "" {
  461. return false, fmt.Errorf("ClaimRef got reset for pv %q", pv.Name)
  462. }
  463. // Check if pvc is fully bound
  464. if !b.isPVCFullyBound(pvc) {
  465. return false, nil
  466. }
  467. }
  468. for _, claim := range claimsToProvision {
  469. pvc, err := b.pvcCache.GetAPIPVC(getPVCName(claim))
  470. if err != nil {
  471. return false, fmt.Errorf("failed to check provisioning pvc: %v", err)
  472. }
  473. // Because we updated PVC in apiserver, skip if API object is older
  474. // and wait for new API object propagated from apiserver.
  475. if versioner.CompareResourceVersion(claim, pvc) > 0 {
  476. return false, nil
  477. }
  478. // Check if selectedNode annotation is still set
  479. if pvc.Annotations == nil {
  480. return false, fmt.Errorf("selectedNode annotation reset for PVC %q", pvc.Name)
  481. }
  482. selectedNode := pvc.Annotations[pvutil.AnnSelectedNode]
  483. if selectedNode != pod.Spec.NodeName {
  484. // If provisioner fails to provision a volume, selectedNode
  485. // annotation will be removed to signal back to the scheduler to
  486. // retry.
  487. return false, fmt.Errorf("provisioning failed for PVC %q", pvc.Name)
  488. }
  489. // If the PVC is bound to a PV, check its node affinity
  490. if pvc.Spec.VolumeName != "" {
  491. pv, err := b.pvCache.GetAPIPV(pvc.Spec.VolumeName)
  492. if err != nil {
  493. if _, ok := err.(*errNotFound); ok {
  494. // We tolerate NotFound error here, because PV is possibly
  495. // not found because of API delay, we can check next time.
  496. // And if PV does not exist because it's deleted, PVC will
  497. // be unbound eventually.
  498. return false, nil
  499. }
  500. return false, fmt.Errorf("failed to get pv %q from cache: %v", pvc.Spec.VolumeName, err)
  501. }
  502. pv, err = b.tryTranslatePVToCSI(pv, csiNode)
  503. if err != nil {
  504. return false, err
  505. }
  506. if err := volumeutil.CheckNodeAffinity(pv, node.Labels); err != nil {
  507. return false, fmt.Errorf("pv %q node affinity doesn't match node %q: %v", pv.Name, node.Name, err)
  508. }
  509. }
  510. // Check if pvc is fully bound
  511. if !b.isPVCFullyBound(pvc) {
  512. return false, nil
  513. }
  514. }
  515. // All pvs and pvcs that we operated on are bound
  516. klog.V(4).Infof("All PVCs for pod %q are bound", podName)
  517. return true, nil
  518. }
  519. func (b *volumeBinder) isVolumeBound(namespace string, vol *v1.Volume) (bool, *v1.PersistentVolumeClaim, error) {
  520. if vol.PersistentVolumeClaim == nil {
  521. return true, nil, nil
  522. }
  523. pvcName := vol.PersistentVolumeClaim.ClaimName
  524. return b.isPVCBound(namespace, pvcName)
  525. }
  526. func (b *volumeBinder) isPVCBound(namespace, pvcName string) (bool, *v1.PersistentVolumeClaim, error) {
  527. claim := &v1.PersistentVolumeClaim{
  528. ObjectMeta: metav1.ObjectMeta{
  529. Name: pvcName,
  530. Namespace: namespace,
  531. },
  532. }
  533. pvcKey := getPVCName(claim)
  534. pvc, err := b.pvcCache.GetPVC(pvcKey)
  535. if err != nil || pvc == nil {
  536. return false, nil, fmt.Errorf("error getting PVC %q: %v", pvcKey, err)
  537. }
  538. fullyBound := b.isPVCFullyBound(pvc)
  539. if fullyBound {
  540. klog.V(5).Infof("PVC %q is fully bound to PV %q", pvcKey, pvc.Spec.VolumeName)
  541. } else {
  542. if pvc.Spec.VolumeName != "" {
  543. klog.V(5).Infof("PVC %q is not fully bound to PV %q", pvcKey, pvc.Spec.VolumeName)
  544. } else {
  545. klog.V(5).Infof("PVC %q is not bound", pvcKey)
  546. }
  547. }
  548. return fullyBound, pvc, nil
  549. }
  550. func (b *volumeBinder) isPVCFullyBound(pvc *v1.PersistentVolumeClaim) bool {
  551. return pvc.Spec.VolumeName != "" && metav1.HasAnnotation(pvc.ObjectMeta, pvutil.AnnBindCompleted)
  552. }
  553. // arePodVolumesBound returns true if all volumes are fully bound
  554. func (b *volumeBinder) arePodVolumesBound(pod *v1.Pod) bool {
  555. for _, vol := range pod.Spec.Volumes {
  556. if isBound, _, _ := b.isVolumeBound(pod.Namespace, &vol); !isBound {
  557. // Pod has at least one PVC that needs binding
  558. return false
  559. }
  560. }
  561. return true
  562. }
  563. // getPodVolumes returns a pod's PVCs separated into bound, unbound with delayed binding (including provisioning)
  564. // and unbound with immediate binding (including prebound)
  565. func (b *volumeBinder) getPodVolumes(pod *v1.Pod) (boundClaims []*v1.PersistentVolumeClaim, unboundClaimsDelayBinding []*v1.PersistentVolumeClaim, unboundClaimsImmediate []*v1.PersistentVolumeClaim, err error) {
  566. boundClaims = []*v1.PersistentVolumeClaim{}
  567. unboundClaimsImmediate = []*v1.PersistentVolumeClaim{}
  568. unboundClaimsDelayBinding = []*v1.PersistentVolumeClaim{}
  569. for _, vol := range pod.Spec.Volumes {
  570. volumeBound, pvc, err := b.isVolumeBound(pod.Namespace, &vol)
  571. if err != nil {
  572. return nil, nil, nil, err
  573. }
  574. if pvc == nil {
  575. continue
  576. }
  577. if volumeBound {
  578. boundClaims = append(boundClaims, pvc)
  579. } else {
  580. delayBindingMode, err := pvutil.IsDelayBindingMode(pvc, b.classLister)
  581. if err != nil {
  582. return nil, nil, nil, err
  583. }
  584. // Prebound PVCs are treated as unbound immediate binding
  585. if delayBindingMode && pvc.Spec.VolumeName == "" {
  586. // Scheduler path
  587. unboundClaimsDelayBinding = append(unboundClaimsDelayBinding, pvc)
  588. } else {
  589. // !delayBindingMode || pvc.Spec.VolumeName != ""
  590. // Immediate binding should have already been bound
  591. unboundClaimsImmediate = append(unboundClaimsImmediate, pvc)
  592. }
  593. }
  594. }
  595. return boundClaims, unboundClaimsDelayBinding, unboundClaimsImmediate, nil
  596. }
  597. func (b *volumeBinder) checkBoundClaims(claims []*v1.PersistentVolumeClaim, node *v1.Node, podName string) (bool, error) {
  598. csiNode, err := b.csiNodeInformer.Lister().Get(node.Name)
  599. if err != nil {
  600. // TODO: return the error once CSINode is created by default
  601. klog.V(4).Infof("Could not get a CSINode object for the node %q: %v", node.Name, err)
  602. }
  603. for _, pvc := range claims {
  604. pvName := pvc.Spec.VolumeName
  605. pv, err := b.pvCache.GetPV(pvName)
  606. if err != nil {
  607. return false, err
  608. }
  609. pv, err = b.tryTranslatePVToCSI(pv, csiNode)
  610. if err != nil {
  611. return false, err
  612. }
  613. err = volumeutil.CheckNodeAffinity(pv, node.Labels)
  614. if err != nil {
  615. klog.V(4).Infof("PersistentVolume %q, Node %q mismatch for Pod %q: %v", pvName, node.Name, podName, err)
  616. return false, nil
  617. }
  618. klog.V(5).Infof("PersistentVolume %q, Node %q matches for Pod %q", pvName, node.Name, podName)
  619. }
  620. klog.V(4).Infof("All bound volumes for Pod %q match with Node %q", podName, node.Name)
  621. return true, nil
  622. }
  623. // findMatchingVolumes tries to find matching volumes for given claims,
  624. // and return unbound claims for further provision.
  625. func (b *volumeBinder) findMatchingVolumes(pod *v1.Pod, claimsToBind []*v1.PersistentVolumeClaim, node *v1.Node) (foundMatches bool, bindings []*bindingInfo, unboundClaims []*v1.PersistentVolumeClaim, err error) {
  626. podName := getPodName(pod)
  627. // Sort all the claims by increasing size request to get the smallest fits
  628. sort.Sort(byPVCSize(claimsToBind))
  629. chosenPVs := map[string]*v1.PersistentVolume{}
  630. foundMatches = true
  631. for _, pvc := range claimsToBind {
  632. // Get storage class name from each PVC
  633. storageClassName := v1helper.GetPersistentVolumeClaimClass(pvc)
  634. allPVs := b.pvCache.ListPVs(storageClassName)
  635. pvcName := getPVCName(pvc)
  636. // Find a matching PV
  637. pv, err := pvutil.FindMatchingVolume(pvc, allPVs, node, chosenPVs, true)
  638. if err != nil {
  639. return false, nil, nil, err
  640. }
  641. if pv == nil {
  642. klog.V(4).Infof("No matching volumes for Pod %q, PVC %q on node %q", podName, pvcName, node.Name)
  643. unboundClaims = append(unboundClaims, pvc)
  644. foundMatches = false
  645. continue
  646. }
  647. // matching PV needs to be excluded so we don't select it again
  648. chosenPVs[pv.Name] = pv
  649. bindings = append(bindings, &bindingInfo{pv: pv, pvc: pvc})
  650. klog.V(5).Infof("Found matching PV %q for PVC %q on node %q for pod %q", pv.Name, pvcName, node.Name, podName)
  651. }
  652. if foundMatches {
  653. klog.V(4).Infof("Found matching volumes for pod %q on node %q", podName, node.Name)
  654. }
  655. return
  656. }
  657. // checkVolumeProvisions checks given unbound claims (the claims have gone through func
  658. // findMatchingVolumes, and do not have matching volumes for binding), and return true
  659. // if all of the claims are eligible for dynamic provision.
  660. func (b *volumeBinder) checkVolumeProvisions(pod *v1.Pod, claimsToProvision []*v1.PersistentVolumeClaim, node *v1.Node) (provisionSatisfied bool, provisionedClaims []*v1.PersistentVolumeClaim, err error) {
  661. podName := getPodName(pod)
  662. provisionedClaims = []*v1.PersistentVolumeClaim{}
  663. for _, claim := range claimsToProvision {
  664. pvcName := getPVCName(claim)
  665. className := v1helper.GetPersistentVolumeClaimClass(claim)
  666. if className == "" {
  667. return false, nil, fmt.Errorf("no class for claim %q", pvcName)
  668. }
  669. class, err := b.classLister.Get(className)
  670. if err != nil {
  671. return false, nil, fmt.Errorf("failed to find storage class %q", className)
  672. }
  673. provisioner := class.Provisioner
  674. if provisioner == "" || provisioner == pvutil.NotSupportedProvisioner {
  675. klog.V(4).Infof("storage class %q of claim %q does not support dynamic provisioning", className, pvcName)
  676. return false, nil, nil
  677. }
  678. // Check if the node can satisfy the topology requirement in the class
  679. if !v1helper.MatchTopologySelectorTerms(class.AllowedTopologies, labels.Set(node.Labels)) {
  680. klog.V(4).Infof("Node %q cannot satisfy provisioning topology requirements of claim %q", node.Name, pvcName)
  681. return false, nil, nil
  682. }
  683. // TODO: Check if capacity of the node domain in the storage class
  684. // can satisfy resource requirement of given claim
  685. provisionedClaims = append(provisionedClaims, claim)
  686. }
  687. klog.V(4).Infof("Provisioning for claims of pod %q that has no matching volumes on node %q ...", podName, node.Name)
  688. return true, provisionedClaims, nil
  689. }
  690. func (b *volumeBinder) revertAssumedPVs(bindings []*bindingInfo) {
  691. for _, bindingInfo := range bindings {
  692. b.pvCache.Restore(bindingInfo.pv.Name)
  693. }
  694. }
  695. func (b *volumeBinder) revertAssumedPVCs(claims []*v1.PersistentVolumeClaim) {
  696. for _, claim := range claims {
  697. b.pvcCache.Restore(getPVCName(claim))
  698. }
  699. }
  700. type bindingInfo struct {
  701. // Claim that needs to be bound
  702. pvc *v1.PersistentVolumeClaim
  703. // Proposed PV to bind to this claim
  704. pv *v1.PersistentVolume
  705. }
  706. type byPVCSize []*v1.PersistentVolumeClaim
  707. func (a byPVCSize) Len() int {
  708. return len(a)
  709. }
  710. func (a byPVCSize) Swap(i, j int) {
  711. a[i], a[j] = a[j], a[i]
  712. }
  713. func (a byPVCSize) Less(i, j int) bool {
  714. iSize := a[i].Spec.Resources.Requests[v1.ResourceStorage]
  715. jSize := a[j].Spec.Resources.Requests[v1.ResourceStorage]
  716. // return true if iSize is less than jSize
  717. return iSize.Cmp(jSize) == -1
  718. }
  719. func claimToClaimKey(claim *v1.PersistentVolumeClaim) string {
  720. return fmt.Sprintf("%s/%s", claim.Namespace, claim.Name)
  721. }
  722. // isCSIMigrationOnForPlugin checks if CSI migrartion is enabled for a given plugin.
  723. func isCSIMigrationOnForPlugin(pluginName string) bool {
  724. switch pluginName {
  725. case csiplugins.AWSEBSInTreePluginName:
  726. return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationAWS)
  727. case csiplugins.GCEPDInTreePluginName:
  728. return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationGCE)
  729. case csiplugins.AzureDiskInTreePluginName:
  730. return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationAzureDisk)
  731. case csiplugins.CinderInTreePluginName:
  732. return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationOpenStack)
  733. }
  734. return false
  735. }
  736. // isPluginMigratedToCSIOnNode checks if an in-tree plugin has been migrated to a CSI driver on the node.
  737. func isPluginMigratedToCSIOnNode(pluginName string, csiNode *storagev1.CSINode) bool {
  738. if csiNode == nil {
  739. return false
  740. }
  741. csiNodeAnn := csiNode.GetAnnotations()
  742. if csiNodeAnn == nil {
  743. return false
  744. }
  745. var mpaSet sets.String
  746. mpa := csiNodeAnn[v1.MigratedPluginsAnnotationKey]
  747. if len(mpa) == 0 {
  748. mpaSet = sets.NewString()
  749. } else {
  750. tok := strings.Split(mpa, ",")
  751. mpaSet = sets.NewString(tok...)
  752. }
  753. return mpaSet.Has(pluginName)
  754. }
  755. // tryTranslatePVToCSI will translate the in-tree PV to CSI if it meets the criteria. If not, it returns the unmodified in-tree PV.
  756. func (b *volumeBinder) tryTranslatePVToCSI(pv *v1.PersistentVolume, csiNode *storagev1.CSINode) (*v1.PersistentVolume, error) {
  757. if !b.translator.IsPVMigratable(pv) {
  758. return pv, nil
  759. }
  760. if !utilfeature.DefaultFeatureGate.Enabled(features.CSIMigration) {
  761. return pv, nil
  762. }
  763. pluginName, err := b.translator.GetInTreePluginNameFromSpec(pv, nil)
  764. if err != nil {
  765. return nil, fmt.Errorf("could not get plugin name from pv: %v", err)
  766. }
  767. if !isCSIMigrationOnForPlugin(pluginName) {
  768. return pv, nil
  769. }
  770. if !isPluginMigratedToCSIOnNode(pluginName, csiNode) {
  771. return pv, nil
  772. }
  773. transPV, err := b.translator.TranslateInTreePVToCSI(pv)
  774. if err != nil {
  775. return nil, fmt.Errorf("could not translate pv: %v", err)
  776. }
  777. return transPV, nil
  778. }