scheduler_binder.go 28 KB

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