predicate.go 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. /*
  2. Copyright 2016 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package lifecycle
  14. import (
  15. "fmt"
  16. "k8s.io/klog"
  17. pluginhelper "k8s.io/kubernetes/pkg/scheduler/framework/plugins/helper"
  18. "k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodeaffinity"
  19. "k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodename"
  20. "k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodeports"
  21. "k8s.io/kubernetes/pkg/scheduler/framework/plugins/noderesources"
  22. "k8s.io/api/core/v1"
  23. v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
  24. "k8s.io/kubernetes/pkg/kubelet/util/format"
  25. schedulernodeinfo "k8s.io/kubernetes/pkg/scheduler/nodeinfo"
  26. )
  27. type getNodeAnyWayFuncType func() (*v1.Node, error)
  28. type pluginResourceUpdateFuncType func(*schedulernodeinfo.NodeInfo, *PodAdmitAttributes) error
  29. // AdmissionFailureHandler is an interface which defines how to deal with a failure to admit a pod.
  30. // This allows for the graceful handling of pod admission failure.
  31. type AdmissionFailureHandler interface {
  32. HandleAdmissionFailure(admitPod *v1.Pod, failureReasons []PredicateFailureReason) ([]PredicateFailureReason, error)
  33. }
  34. type predicateAdmitHandler struct {
  35. getNodeAnyWayFunc getNodeAnyWayFuncType
  36. pluginResourceUpdateFunc pluginResourceUpdateFuncType
  37. admissionFailureHandler AdmissionFailureHandler
  38. }
  39. var _ PodAdmitHandler = &predicateAdmitHandler{}
  40. func NewPredicateAdmitHandler(getNodeAnyWayFunc getNodeAnyWayFuncType, admissionFailureHandler AdmissionFailureHandler, pluginResourceUpdateFunc pluginResourceUpdateFuncType) *predicateAdmitHandler {
  41. return &predicateAdmitHandler{
  42. getNodeAnyWayFunc,
  43. pluginResourceUpdateFunc,
  44. admissionFailureHandler,
  45. }
  46. }
  47. func (w *predicateAdmitHandler) Admit(attrs *PodAdmitAttributes) PodAdmitResult {
  48. node, err := w.getNodeAnyWayFunc()
  49. if err != nil {
  50. klog.Errorf("Cannot get Node info: %v", err)
  51. return PodAdmitResult{
  52. Admit: false,
  53. Reason: "InvalidNodeInfo",
  54. Message: "Kubelet cannot get node info.",
  55. }
  56. }
  57. admitPod := attrs.Pod
  58. pods := attrs.OtherPods
  59. nodeInfo := schedulernodeinfo.NewNodeInfo(pods...)
  60. nodeInfo.SetNode(node)
  61. // ensure the node has enough plugin resources for that required in pods
  62. if err = w.pluginResourceUpdateFunc(nodeInfo, attrs); err != nil {
  63. message := fmt.Sprintf("Update plugin resources failed due to %v, which is unexpected.", err)
  64. klog.Warningf("Failed to admit pod %v - %s", format.Pod(admitPod), message)
  65. return PodAdmitResult{
  66. Admit: false,
  67. Reason: "UnexpectedAdmissionError",
  68. Message: message,
  69. }
  70. }
  71. // Remove the requests of the extended resources that are missing in the
  72. // node info. This is required to support cluster-level resources, which
  73. // are extended resources unknown to nodes.
  74. //
  75. // Caveat: If a pod was manually bound to a node (e.g., static pod) where a
  76. // node-level extended resource it requires is not found, then kubelet will
  77. // not fail admission while it should. This issue will be addressed with
  78. // the Resource Class API in the future.
  79. podWithoutMissingExtendedResources := removeMissingExtendedResources(admitPod, nodeInfo)
  80. reasons, err := GeneralPredicates(podWithoutMissingExtendedResources, nodeInfo)
  81. fit := len(reasons) == 0 && err == nil
  82. if err != nil {
  83. message := fmt.Sprintf("GeneralPredicates failed due to %v, which is unexpected.", err)
  84. klog.Warningf("Failed to admit pod %v - %s", format.Pod(admitPod), message)
  85. return PodAdmitResult{
  86. Admit: fit,
  87. Reason: "UnexpectedAdmissionError",
  88. Message: message,
  89. }
  90. }
  91. if !fit {
  92. reasons, err = w.admissionFailureHandler.HandleAdmissionFailure(admitPod, reasons)
  93. fit = len(reasons) == 0 && err == nil
  94. if err != nil {
  95. message := fmt.Sprintf("Unexpected error while attempting to recover from admission failure: %v", err)
  96. klog.Warningf("Failed to admit pod %v - %s", format.Pod(admitPod), message)
  97. return PodAdmitResult{
  98. Admit: fit,
  99. Reason: "UnexpectedAdmissionError",
  100. Message: message,
  101. }
  102. }
  103. }
  104. if !fit {
  105. var reason string
  106. var message string
  107. if len(reasons) == 0 {
  108. message = fmt.Sprint("GeneralPredicates failed due to unknown reason, which is unexpected.")
  109. klog.Warningf("Failed to admit pod %v - %s", format.Pod(admitPod), message)
  110. return PodAdmitResult{
  111. Admit: fit,
  112. Reason: "UnknownReason",
  113. Message: message,
  114. }
  115. }
  116. // If there are failed predicates, we only return the first one as a reason.
  117. r := reasons[0]
  118. switch re := r.(type) {
  119. case *PredicateFailureError:
  120. reason = re.PredicateName
  121. message = re.Error()
  122. klog.V(2).Infof("Predicate failed on Pod: %v, for reason: %v", format.Pod(admitPod), message)
  123. case *InsufficientResourceError:
  124. reason = fmt.Sprintf("OutOf%s", re.ResourceName)
  125. message = re.Error()
  126. klog.V(2).Infof("Predicate failed on Pod: %v, for reason: %v", format.Pod(admitPod), message)
  127. default:
  128. reason = "UnexpectedPredicateFailureType"
  129. message = fmt.Sprintf("GeneralPredicates failed due to %v, which is unexpected.", r)
  130. klog.Warningf("Failed to admit pod %v - %s", format.Pod(admitPod), message)
  131. }
  132. return PodAdmitResult{
  133. Admit: fit,
  134. Reason: reason,
  135. Message: message,
  136. }
  137. }
  138. return PodAdmitResult{
  139. Admit: true,
  140. }
  141. }
  142. func removeMissingExtendedResources(pod *v1.Pod, nodeInfo *schedulernodeinfo.NodeInfo) *v1.Pod {
  143. podCopy := pod.DeepCopy()
  144. for i, c := range pod.Spec.Containers {
  145. // We only handle requests in Requests but not Limits because the
  146. // PodFitsResources predicate, to which the result pod will be passed,
  147. // does not use Limits.
  148. podCopy.Spec.Containers[i].Resources.Requests = make(v1.ResourceList)
  149. for rName, rQuant := range c.Resources.Requests {
  150. if v1helper.IsExtendedResourceName(rName) {
  151. if _, found := nodeInfo.AllocatableResource().ScalarResources[rName]; !found {
  152. continue
  153. }
  154. }
  155. podCopy.Spec.Containers[i].Resources.Requests[rName] = rQuant
  156. }
  157. }
  158. return podCopy
  159. }
  160. // InsufficientResourceError is an error type that indicates what kind of resource limit is
  161. // hit and caused the unfitting failure.
  162. type InsufficientResourceError struct {
  163. ResourceName v1.ResourceName
  164. Requested int64
  165. Used int64
  166. Capacity int64
  167. }
  168. func (e *InsufficientResourceError) Error() string {
  169. return fmt.Sprintf("Node didn't have enough resource: %s, requested: %d, used: %d, capacity: %d",
  170. e.ResourceName, e.Requested, e.Used, e.Capacity)
  171. }
  172. // PredicateFailureReason interface represents the failure reason of a predicate.
  173. type PredicateFailureReason interface {
  174. GetReason() string
  175. }
  176. // GetReason returns the reason of the InsufficientResourceError.
  177. func (e *InsufficientResourceError) GetReason() string {
  178. return fmt.Sprintf("Insufficient %v", e.ResourceName)
  179. }
  180. // GetInsufficientAmount returns the amount of the insufficient resource of the error.
  181. func (e *InsufficientResourceError) GetInsufficientAmount() int64 {
  182. return e.Requested - (e.Capacity - e.Used)
  183. }
  184. // PredicateFailureError describes a failure error of predicate.
  185. type PredicateFailureError struct {
  186. PredicateName string
  187. PredicateDesc string
  188. }
  189. func (e *PredicateFailureError) Error() string {
  190. return fmt.Sprintf("Predicate %s failed", e.PredicateName)
  191. }
  192. // GetReason returns the reason of the PredicateFailureError.
  193. func (e *PredicateFailureError) GetReason() string {
  194. return e.PredicateDesc
  195. }
  196. // GeneralPredicates checks a group of predicates that the kubelet cares about.
  197. func GeneralPredicates(pod *v1.Pod, nodeInfo *schedulernodeinfo.NodeInfo) ([]PredicateFailureReason, error) {
  198. if nodeInfo.Node() == nil {
  199. return nil, fmt.Errorf("node not found")
  200. }
  201. var reasons []PredicateFailureReason
  202. for _, r := range noderesources.Fits(pod, nodeInfo, nil) {
  203. reasons = append(reasons, &InsufficientResourceError{
  204. ResourceName: r.ResourceName,
  205. Requested: r.Requested,
  206. Used: r.Used,
  207. Capacity: r.Capacity,
  208. })
  209. }
  210. if !pluginhelper.PodMatchesNodeSelectorAndAffinityTerms(pod, nodeInfo.Node()) {
  211. reasons = append(reasons, &PredicateFailureError{nodeaffinity.Name, nodeaffinity.ErrReason})
  212. }
  213. if !nodename.Fits(pod, nodeInfo) {
  214. reasons = append(reasons, &PredicateFailureError{nodename.Name, nodename.ErrReason})
  215. }
  216. if !nodeports.Fits(pod, nodeInfo) {
  217. reasons = append(reasons, &PredicateFailureError{nodeports.Name, nodeports.ErrReason})
  218. }
  219. return reasons, nil
  220. }