generic_scheduler_test.go 82 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645
  1. /*
  2. Copyright 2014 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 core
  14. import (
  15. "fmt"
  16. "math"
  17. "reflect"
  18. "strconv"
  19. "strings"
  20. "testing"
  21. "time"
  22. apps "k8s.io/api/apps/v1"
  23. v1 "k8s.io/api/core/v1"
  24. "k8s.io/apimachinery/pkg/api/resource"
  25. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  26. "k8s.io/apimachinery/pkg/types"
  27. "k8s.io/apimachinery/pkg/util/errors"
  28. "k8s.io/apimachinery/pkg/util/sets"
  29. "k8s.io/apimachinery/pkg/util/wait"
  30. "k8s.io/kubernetes/pkg/scheduler/algorithm"
  31. algorithmpredicates "k8s.io/kubernetes/pkg/scheduler/algorithm/predicates"
  32. "k8s.io/kubernetes/pkg/scheduler/algorithm/priorities"
  33. priorityutil "k8s.io/kubernetes/pkg/scheduler/algorithm/priorities/util"
  34. schedulerapi "k8s.io/kubernetes/pkg/scheduler/api"
  35. schedulerconfig "k8s.io/kubernetes/pkg/scheduler/apis/config"
  36. framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
  37. internalcache "k8s.io/kubernetes/pkg/scheduler/internal/cache"
  38. internalqueue "k8s.io/kubernetes/pkg/scheduler/internal/queue"
  39. schedulernodeinfo "k8s.io/kubernetes/pkg/scheduler/nodeinfo"
  40. schedulertesting "k8s.io/kubernetes/pkg/scheduler/testing"
  41. )
  42. var (
  43. errPrioritize = fmt.Errorf("priority map encounters an error")
  44. order = []string{"false", "true", "matches", "nopods", algorithmpredicates.MatchInterPodAffinityPred}
  45. )
  46. func falsePredicate(pod *v1.Pod, meta algorithmpredicates.PredicateMetadata, nodeInfo *schedulernodeinfo.NodeInfo) (bool, []algorithmpredicates.PredicateFailureReason, error) {
  47. return false, []algorithmpredicates.PredicateFailureReason{algorithmpredicates.ErrFakePredicate}, nil
  48. }
  49. func truePredicate(pod *v1.Pod, meta algorithmpredicates.PredicateMetadata, nodeInfo *schedulernodeinfo.NodeInfo) (bool, []algorithmpredicates.PredicateFailureReason, error) {
  50. return true, nil, nil
  51. }
  52. func matchesPredicate(pod *v1.Pod, meta algorithmpredicates.PredicateMetadata, nodeInfo *schedulernodeinfo.NodeInfo) (bool, []algorithmpredicates.PredicateFailureReason, error) {
  53. node := nodeInfo.Node()
  54. if node == nil {
  55. return false, nil, fmt.Errorf("node not found")
  56. }
  57. if pod.Name == node.Name {
  58. return true, nil, nil
  59. }
  60. return false, []algorithmpredicates.PredicateFailureReason{algorithmpredicates.ErrFakePredicate}, nil
  61. }
  62. func hasNoPodsPredicate(pod *v1.Pod, meta algorithmpredicates.PredicateMetadata, nodeInfo *schedulernodeinfo.NodeInfo) (bool, []algorithmpredicates.PredicateFailureReason, error) {
  63. if len(nodeInfo.Pods()) == 0 {
  64. return true, nil, nil
  65. }
  66. return false, []algorithmpredicates.PredicateFailureReason{algorithmpredicates.ErrFakePredicate}, nil
  67. }
  68. func numericPriority(pod *v1.Pod, nodeNameToInfo map[string]*schedulernodeinfo.NodeInfo, nodes []*v1.Node) (schedulerapi.HostPriorityList, error) {
  69. result := []schedulerapi.HostPriority{}
  70. for _, node := range nodes {
  71. score, err := strconv.Atoi(node.Name)
  72. if err != nil {
  73. return nil, err
  74. }
  75. result = append(result, schedulerapi.HostPriority{
  76. Host: node.Name,
  77. Score: score,
  78. })
  79. }
  80. return result, nil
  81. }
  82. func reverseNumericPriority(pod *v1.Pod, nodeNameToInfo map[string]*schedulernodeinfo.NodeInfo, nodes []*v1.Node) (schedulerapi.HostPriorityList, error) {
  83. var maxScore float64
  84. minScore := math.MaxFloat64
  85. reverseResult := []schedulerapi.HostPriority{}
  86. result, err := numericPriority(pod, nodeNameToInfo, nodes)
  87. if err != nil {
  88. return nil, err
  89. }
  90. for _, hostPriority := range result {
  91. maxScore = math.Max(maxScore, float64(hostPriority.Score))
  92. minScore = math.Min(minScore, float64(hostPriority.Score))
  93. }
  94. for _, hostPriority := range result {
  95. reverseResult = append(reverseResult, schedulerapi.HostPriority{
  96. Host: hostPriority.Host,
  97. Score: int(maxScore + minScore - float64(hostPriority.Score)),
  98. })
  99. }
  100. return reverseResult, nil
  101. }
  102. func trueMapPriority(pod *v1.Pod, meta interface{}, nodeInfo *schedulernodeinfo.NodeInfo) (schedulerapi.HostPriority, error) {
  103. return schedulerapi.HostPriority{
  104. Host: nodeInfo.Node().Name,
  105. Score: 1,
  106. }, nil
  107. }
  108. func falseMapPriority(pod *v1.Pod, meta interface{}, nodeInfo *schedulernodeinfo.NodeInfo) (schedulerapi.HostPriority, error) {
  109. return schedulerapi.HostPriority{}, errPrioritize
  110. }
  111. func getNodeReducePriority(pod *v1.Pod, meta interface{}, nodeNameToInfo map[string]*schedulernodeinfo.NodeInfo, result schedulerapi.HostPriorityList) error {
  112. for _, host := range result {
  113. if host.Host == "" {
  114. return fmt.Errorf("unexpected empty host name")
  115. }
  116. }
  117. return nil
  118. }
  119. // EmptyPluginRegistry is a test plugin set used by the default scheduler.
  120. var EmptyPluginRegistry = framework.Registry{}
  121. var emptyFramework, _ = framework.NewFramework(EmptyPluginRegistry, nil, []schedulerconfig.PluginConfig{})
  122. func makeNodeList(nodeNames []string) []*v1.Node {
  123. result := make([]*v1.Node, 0, len(nodeNames))
  124. for _, nodeName := range nodeNames {
  125. result = append(result, &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: nodeName}})
  126. }
  127. return result
  128. }
  129. func TestSelectHost(t *testing.T) {
  130. scheduler := genericScheduler{}
  131. tests := []struct {
  132. name string
  133. list schedulerapi.HostPriorityList
  134. possibleHosts sets.String
  135. expectsErr bool
  136. }{
  137. {
  138. name: "unique properly ordered scores",
  139. list: []schedulerapi.HostPriority{
  140. {Host: "machine1.1", Score: 1},
  141. {Host: "machine2.1", Score: 2},
  142. },
  143. possibleHosts: sets.NewString("machine2.1"),
  144. expectsErr: false,
  145. },
  146. {
  147. name: "equal scores",
  148. list: []schedulerapi.HostPriority{
  149. {Host: "machine1.1", Score: 1},
  150. {Host: "machine1.2", Score: 2},
  151. {Host: "machine1.3", Score: 2},
  152. {Host: "machine2.1", Score: 2},
  153. },
  154. possibleHosts: sets.NewString("machine1.2", "machine1.3", "machine2.1"),
  155. expectsErr: false,
  156. },
  157. {
  158. name: "out of order scores",
  159. list: []schedulerapi.HostPriority{
  160. {Host: "machine1.1", Score: 3},
  161. {Host: "machine1.2", Score: 3},
  162. {Host: "machine2.1", Score: 2},
  163. {Host: "machine3.1", Score: 1},
  164. {Host: "machine1.3", Score: 3},
  165. },
  166. possibleHosts: sets.NewString("machine1.1", "machine1.2", "machine1.3"),
  167. expectsErr: false,
  168. },
  169. {
  170. name: "empty priority list",
  171. list: []schedulerapi.HostPriority{},
  172. possibleHosts: sets.NewString(),
  173. expectsErr: true,
  174. },
  175. }
  176. for _, test := range tests {
  177. t.Run(test.name, func(t *testing.T) {
  178. // increase the randomness
  179. for i := 0; i < 10; i++ {
  180. got, err := scheduler.selectHost(test.list)
  181. if test.expectsErr {
  182. if err == nil {
  183. t.Error("Unexpected non-error")
  184. }
  185. } else {
  186. if err != nil {
  187. t.Errorf("Unexpected error: %v", err)
  188. }
  189. if !test.possibleHosts.Has(got) {
  190. t.Errorf("got %s is not in the possible map %v", got, test.possibleHosts)
  191. }
  192. }
  193. }
  194. })
  195. }
  196. }
  197. func TestGenericScheduler(t *testing.T) {
  198. defer algorithmpredicates.SetPredicatesOrderingDuringTest(order)()
  199. tests := []struct {
  200. name string
  201. predicates map[string]algorithmpredicates.FitPredicate
  202. prioritizers []priorities.PriorityConfig
  203. alwaysCheckAllPredicates bool
  204. nodes []string
  205. pvcs []*v1.PersistentVolumeClaim
  206. pod *v1.Pod
  207. pods []*v1.Pod
  208. expectedHosts sets.String
  209. expectsErr bool
  210. wErr error
  211. }{
  212. {
  213. predicates: map[string]algorithmpredicates.FitPredicate{"false": falsePredicate},
  214. prioritizers: []priorities.PriorityConfig{{Map: EqualPriorityMap, Weight: 1}},
  215. nodes: []string{"machine1", "machine2"},
  216. expectsErr: true,
  217. pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "2", UID: types.UID("2")}},
  218. name: "test 1",
  219. wErr: &FitError{
  220. Pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "2", UID: types.UID("2")}},
  221. NumAllNodes: 2,
  222. FailedPredicates: FailedPredicateMap{
  223. "machine1": []algorithmpredicates.PredicateFailureReason{algorithmpredicates.ErrFakePredicate},
  224. "machine2": []algorithmpredicates.PredicateFailureReason{algorithmpredicates.ErrFakePredicate},
  225. }},
  226. },
  227. {
  228. predicates: map[string]algorithmpredicates.FitPredicate{"true": truePredicate},
  229. prioritizers: []priorities.PriorityConfig{{Map: EqualPriorityMap, Weight: 1}},
  230. nodes: []string{"machine1", "machine2"},
  231. pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "ignore", UID: types.UID("ignore")}},
  232. expectedHosts: sets.NewString("machine1", "machine2"),
  233. name: "test 2",
  234. wErr: nil,
  235. },
  236. {
  237. // Fits on a machine where the pod ID matches the machine name
  238. predicates: map[string]algorithmpredicates.FitPredicate{"matches": matchesPredicate},
  239. prioritizers: []priorities.PriorityConfig{{Map: EqualPriorityMap, Weight: 1}},
  240. nodes: []string{"machine1", "machine2"},
  241. pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "machine2", UID: types.UID("machine2")}},
  242. expectedHosts: sets.NewString("machine2"),
  243. name: "test 3",
  244. wErr: nil,
  245. },
  246. {
  247. predicates: map[string]algorithmpredicates.FitPredicate{"true": truePredicate},
  248. prioritizers: []priorities.PriorityConfig{{Function: numericPriority, Weight: 1}},
  249. nodes: []string{"3", "2", "1"},
  250. pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "ignore", UID: types.UID("ignore")}},
  251. expectedHosts: sets.NewString("3"),
  252. name: "test 4",
  253. wErr: nil,
  254. },
  255. {
  256. predicates: map[string]algorithmpredicates.FitPredicate{"matches": matchesPredicate},
  257. prioritizers: []priorities.PriorityConfig{{Function: numericPriority, Weight: 1}},
  258. nodes: []string{"3", "2", "1"},
  259. pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "2", UID: types.UID("2")}},
  260. expectedHosts: sets.NewString("2"),
  261. name: "test 5",
  262. wErr: nil,
  263. },
  264. {
  265. predicates: map[string]algorithmpredicates.FitPredicate{"true": truePredicate},
  266. prioritizers: []priorities.PriorityConfig{{Function: numericPriority, Weight: 1}, {Function: reverseNumericPriority, Weight: 2}},
  267. nodes: []string{"3", "2", "1"},
  268. pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "2", UID: types.UID("2")}},
  269. expectedHosts: sets.NewString("1"),
  270. name: "test 6",
  271. wErr: nil,
  272. },
  273. {
  274. predicates: map[string]algorithmpredicates.FitPredicate{"true": truePredicate, "false": falsePredicate},
  275. prioritizers: []priorities.PriorityConfig{{Function: numericPriority, Weight: 1}},
  276. nodes: []string{"3", "2", "1"},
  277. pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "2", UID: types.UID("2")}},
  278. expectsErr: true,
  279. name: "test 7",
  280. wErr: &FitError{
  281. Pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "2", UID: types.UID("2")}},
  282. NumAllNodes: 3,
  283. FailedPredicates: FailedPredicateMap{
  284. "3": []algorithmpredicates.PredicateFailureReason{algorithmpredicates.ErrFakePredicate},
  285. "2": []algorithmpredicates.PredicateFailureReason{algorithmpredicates.ErrFakePredicate},
  286. "1": []algorithmpredicates.PredicateFailureReason{algorithmpredicates.ErrFakePredicate},
  287. },
  288. },
  289. },
  290. {
  291. predicates: map[string]algorithmpredicates.FitPredicate{
  292. "nopods": hasNoPodsPredicate,
  293. "matches": matchesPredicate,
  294. },
  295. pods: []*v1.Pod{
  296. {
  297. ObjectMeta: metav1.ObjectMeta{Name: "2", UID: types.UID("2")},
  298. Spec: v1.PodSpec{
  299. NodeName: "2",
  300. },
  301. Status: v1.PodStatus{
  302. Phase: v1.PodRunning,
  303. },
  304. },
  305. },
  306. pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "2", UID: types.UID("2")}},
  307. prioritizers: []priorities.PriorityConfig{{Function: numericPriority, Weight: 1}},
  308. nodes: []string{"1", "2"},
  309. expectsErr: true,
  310. name: "test 8",
  311. wErr: &FitError{
  312. Pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "2", UID: types.UID("2")}},
  313. NumAllNodes: 2,
  314. FailedPredicates: FailedPredicateMap{
  315. "1": []algorithmpredicates.PredicateFailureReason{algorithmpredicates.ErrFakePredicate},
  316. "2": []algorithmpredicates.PredicateFailureReason{algorithmpredicates.ErrFakePredicate},
  317. },
  318. },
  319. },
  320. {
  321. // Pod with existing PVC
  322. predicates: map[string]algorithmpredicates.FitPredicate{"true": truePredicate},
  323. prioritizers: []priorities.PriorityConfig{{Map: EqualPriorityMap, Weight: 1}},
  324. nodes: []string{"machine1", "machine2"},
  325. pvcs: []*v1.PersistentVolumeClaim{{ObjectMeta: metav1.ObjectMeta{Name: "existingPVC"}}},
  326. pod: &v1.Pod{
  327. ObjectMeta: metav1.ObjectMeta{Name: "ignore", UID: types.UID("ignore")},
  328. Spec: v1.PodSpec{
  329. Volumes: []v1.Volume{
  330. {
  331. VolumeSource: v1.VolumeSource{
  332. PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
  333. ClaimName: "existingPVC",
  334. },
  335. },
  336. },
  337. },
  338. },
  339. },
  340. expectedHosts: sets.NewString("machine1", "machine2"),
  341. name: "existing PVC",
  342. wErr: nil,
  343. },
  344. {
  345. // Pod with non existing PVC
  346. predicates: map[string]algorithmpredicates.FitPredicate{"true": truePredicate},
  347. prioritizers: []priorities.PriorityConfig{{Map: EqualPriorityMap, Weight: 1}},
  348. nodes: []string{"machine1", "machine2"},
  349. pod: &v1.Pod{
  350. ObjectMeta: metav1.ObjectMeta{Name: "ignore", UID: types.UID("ignore")},
  351. Spec: v1.PodSpec{
  352. Volumes: []v1.Volume{
  353. {
  354. VolumeSource: v1.VolumeSource{
  355. PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
  356. ClaimName: "unknownPVC",
  357. },
  358. },
  359. },
  360. },
  361. },
  362. },
  363. name: "unknown PVC",
  364. expectsErr: true,
  365. wErr: fmt.Errorf("persistentvolumeclaim \"unknownPVC\" not found"),
  366. },
  367. {
  368. // Pod with deleting PVC
  369. predicates: map[string]algorithmpredicates.FitPredicate{"true": truePredicate},
  370. prioritizers: []priorities.PriorityConfig{{Map: EqualPriorityMap, Weight: 1}},
  371. nodes: []string{"machine1", "machine2"},
  372. pvcs: []*v1.PersistentVolumeClaim{{ObjectMeta: metav1.ObjectMeta{Name: "existingPVC", DeletionTimestamp: &metav1.Time{}}}},
  373. pod: &v1.Pod{
  374. ObjectMeta: metav1.ObjectMeta{Name: "ignore", UID: types.UID("ignore")},
  375. Spec: v1.PodSpec{
  376. Volumes: []v1.Volume{
  377. {
  378. VolumeSource: v1.VolumeSource{
  379. PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
  380. ClaimName: "existingPVC",
  381. },
  382. },
  383. },
  384. },
  385. },
  386. },
  387. name: "deleted PVC",
  388. expectsErr: true,
  389. wErr: fmt.Errorf("persistentvolumeclaim \"existingPVC\" is being deleted"),
  390. },
  391. {
  392. // alwaysCheckAllPredicates is true
  393. predicates: map[string]algorithmpredicates.FitPredicate{"true": truePredicate, "matches": matchesPredicate, "false": falsePredicate},
  394. prioritizers: []priorities.PriorityConfig{{Map: EqualPriorityMap, Weight: 1}},
  395. alwaysCheckAllPredicates: true,
  396. nodes: []string{"1"},
  397. pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "2", UID: types.UID("2")}},
  398. name: "test alwaysCheckAllPredicates is true",
  399. wErr: &FitError{
  400. Pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "2", UID: types.UID("2")}},
  401. NumAllNodes: 1,
  402. FailedPredicates: FailedPredicateMap{
  403. "1": []algorithmpredicates.PredicateFailureReason{algorithmpredicates.ErrFakePredicate, algorithmpredicates.ErrFakePredicate},
  404. },
  405. },
  406. },
  407. {
  408. predicates: map[string]algorithmpredicates.FitPredicate{"true": truePredicate},
  409. prioritizers: []priorities.PriorityConfig{{Map: falseMapPriority, Weight: 1}, {Map: trueMapPriority, Reduce: getNodeReducePriority, Weight: 2}},
  410. nodes: []string{"2", "1"},
  411. pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "2"}},
  412. name: "test error with priority map",
  413. wErr: errors.NewAggregate([]error{errPrioritize, errPrioritize}),
  414. },
  415. }
  416. for _, test := range tests {
  417. t.Run(test.name, func(t *testing.T) {
  418. cache := internalcache.New(time.Duration(0), wait.NeverStop)
  419. for _, pod := range test.pods {
  420. cache.AddPod(pod)
  421. }
  422. for _, name := range test.nodes {
  423. cache.AddNode(&v1.Node{ObjectMeta: metav1.ObjectMeta{Name: name}})
  424. }
  425. pvcs := []*v1.PersistentVolumeClaim{}
  426. pvcs = append(pvcs, test.pvcs...)
  427. pvcLister := schedulertesting.FakePersistentVolumeClaimLister(pvcs)
  428. scheduler := NewGenericScheduler(
  429. cache,
  430. internalqueue.NewSchedulingQueue(nil, nil),
  431. test.predicates,
  432. algorithmpredicates.EmptyPredicateMetadataProducer,
  433. test.prioritizers,
  434. priorities.EmptyPriorityMetadataProducer,
  435. emptyFramework,
  436. []algorithm.SchedulerExtender{},
  437. nil,
  438. pvcLister,
  439. schedulertesting.FakePDBLister{},
  440. test.alwaysCheckAllPredicates,
  441. false,
  442. schedulerapi.DefaultPercentageOfNodesToScore,
  443. false)
  444. result, err := scheduler.Schedule(test.pod, schedulertesting.FakeNodeLister(makeNodeList(test.nodes)))
  445. if !reflect.DeepEqual(err, test.wErr) {
  446. t.Errorf("Unexpected error: %v, expected: %v", err, test.wErr)
  447. }
  448. if test.expectedHosts != nil && !test.expectedHosts.Has(result.SuggestedHost) {
  449. t.Errorf("Expected: %s, got: %s", test.expectedHosts, result.SuggestedHost)
  450. }
  451. })
  452. }
  453. }
  454. // makeScheduler makes a simple genericScheduler for testing.
  455. func makeScheduler(predicates map[string]algorithmpredicates.FitPredicate, nodes []*v1.Node) *genericScheduler {
  456. cache := internalcache.New(time.Duration(0), wait.NeverStop)
  457. for _, n := range nodes {
  458. cache.AddNode(n)
  459. }
  460. prioritizers := []priorities.PriorityConfig{{Map: EqualPriorityMap, Weight: 1}}
  461. s := NewGenericScheduler(
  462. cache,
  463. internalqueue.NewSchedulingQueue(nil, nil),
  464. predicates,
  465. algorithmpredicates.EmptyPredicateMetadataProducer,
  466. prioritizers,
  467. priorities.EmptyPriorityMetadataProducer,
  468. emptyFramework,
  469. nil, nil, nil, nil, false, false,
  470. schedulerapi.DefaultPercentageOfNodesToScore, false)
  471. cache.UpdateNodeInfoSnapshot(s.(*genericScheduler).nodeInfoSnapshot)
  472. return s.(*genericScheduler)
  473. }
  474. func TestFindFitAllError(t *testing.T) {
  475. defer algorithmpredicates.SetPredicatesOrderingDuringTest(order)()
  476. predicates := map[string]algorithmpredicates.FitPredicate{"true": truePredicate, "matches": matchesPredicate}
  477. nodes := makeNodeList([]string{"3", "2", "1"})
  478. scheduler := makeScheduler(predicates, nodes)
  479. _, predicateMap, err := scheduler.findNodesThatFit(&v1.Pod{}, nodes)
  480. if err != nil {
  481. t.Errorf("unexpected error: %v", err)
  482. }
  483. if len(predicateMap) != len(nodes) {
  484. t.Errorf("unexpected failed predicate map: %v", predicateMap)
  485. }
  486. for _, node := range nodes {
  487. t.Run(node.Name, func(t *testing.T) {
  488. failures, found := predicateMap[node.Name]
  489. if !found {
  490. t.Errorf("failed to find node in %v", predicateMap)
  491. }
  492. if len(failures) != 1 || failures[0] != algorithmpredicates.ErrFakePredicate {
  493. t.Errorf("unexpected failures: %v", failures)
  494. }
  495. })
  496. }
  497. }
  498. func TestFindFitSomeError(t *testing.T) {
  499. defer algorithmpredicates.SetPredicatesOrderingDuringTest(order)()
  500. predicates := map[string]algorithmpredicates.FitPredicate{"true": truePredicate, "matches": matchesPredicate}
  501. nodes := makeNodeList([]string{"3", "2", "1"})
  502. scheduler := makeScheduler(predicates, nodes)
  503. pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "1", UID: types.UID("1")}}
  504. _, predicateMap, err := scheduler.findNodesThatFit(pod, nodes)
  505. if err != nil {
  506. t.Errorf("unexpected error: %v", err)
  507. }
  508. if len(predicateMap) != (len(nodes) - 1) {
  509. t.Errorf("unexpected failed predicate map: %v", predicateMap)
  510. }
  511. for _, node := range nodes {
  512. if node.Name == pod.Name {
  513. continue
  514. }
  515. t.Run(node.Name, func(t *testing.T) {
  516. failures, found := predicateMap[node.Name]
  517. if !found {
  518. t.Errorf("failed to find node in %v", predicateMap)
  519. }
  520. if len(failures) != 1 || failures[0] != algorithmpredicates.ErrFakePredicate {
  521. t.Errorf("unexpected failures: %v", failures)
  522. }
  523. })
  524. }
  525. }
  526. func makeNode(node string, milliCPU, memory int64) *v1.Node {
  527. return &v1.Node{
  528. ObjectMeta: metav1.ObjectMeta{Name: node},
  529. Status: v1.NodeStatus{
  530. Capacity: v1.ResourceList{
  531. v1.ResourceCPU: *resource.NewMilliQuantity(milliCPU, resource.DecimalSI),
  532. v1.ResourceMemory: *resource.NewQuantity(memory, resource.BinarySI),
  533. "pods": *resource.NewQuantity(100, resource.DecimalSI),
  534. },
  535. Allocatable: v1.ResourceList{
  536. v1.ResourceCPU: *resource.NewMilliQuantity(milliCPU, resource.DecimalSI),
  537. v1.ResourceMemory: *resource.NewQuantity(memory, resource.BinarySI),
  538. "pods": *resource.NewQuantity(100, resource.DecimalSI),
  539. },
  540. },
  541. }
  542. }
  543. func TestHumanReadableFitError(t *testing.T) {
  544. err := &FitError{
  545. Pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "2", UID: types.UID("2")}},
  546. NumAllNodes: 3,
  547. FailedPredicates: FailedPredicateMap{
  548. "1": []algorithmpredicates.PredicateFailureReason{algorithmpredicates.ErrNodeUnderMemoryPressure},
  549. "2": []algorithmpredicates.PredicateFailureReason{algorithmpredicates.ErrNodeUnderDiskPressure},
  550. "3": []algorithmpredicates.PredicateFailureReason{algorithmpredicates.ErrNodeUnderDiskPressure},
  551. },
  552. }
  553. if strings.Contains(err.Error(), "0/3 nodes are available") {
  554. if strings.Contains(err.Error(), "2 node(s) had disk pressure") && strings.Contains(err.Error(), "1 node(s) had memory pressure") {
  555. return
  556. }
  557. }
  558. t.Errorf("Error message doesn't have all the information content: [" + err.Error() + "]")
  559. }
  560. // The point of this test is to show that you:
  561. // - get the same priority for a zero-request pod as for a pod with the defaults requests,
  562. // both when the zero-request pod is already on the machine and when the zero-request pod
  563. // is the one being scheduled.
  564. // - don't get the same score no matter what we schedule.
  565. func TestZeroRequest(t *testing.T) {
  566. // A pod with no resources. We expect spreading to count it as having the default resources.
  567. noResources := v1.PodSpec{
  568. Containers: []v1.Container{
  569. {},
  570. },
  571. }
  572. noResources1 := noResources
  573. noResources1.NodeName = "machine1"
  574. // A pod with the same resources as a 0-request pod gets by default as its resources (for spreading).
  575. small := v1.PodSpec{
  576. Containers: []v1.Container{
  577. {
  578. Resources: v1.ResourceRequirements{
  579. Requests: v1.ResourceList{
  580. v1.ResourceCPU: resource.MustParse(
  581. strconv.FormatInt(priorityutil.DefaultMilliCPURequest, 10) + "m"),
  582. v1.ResourceMemory: resource.MustParse(
  583. strconv.FormatInt(priorityutil.DefaultMemoryRequest, 10)),
  584. },
  585. },
  586. },
  587. },
  588. }
  589. small2 := small
  590. small2.NodeName = "machine2"
  591. // A larger pod.
  592. large := v1.PodSpec{
  593. Containers: []v1.Container{
  594. {
  595. Resources: v1.ResourceRequirements{
  596. Requests: v1.ResourceList{
  597. v1.ResourceCPU: resource.MustParse(
  598. strconv.FormatInt(priorityutil.DefaultMilliCPURequest*3, 10) + "m"),
  599. v1.ResourceMemory: resource.MustParse(
  600. strconv.FormatInt(priorityutil.DefaultMemoryRequest*3, 10)),
  601. },
  602. },
  603. },
  604. },
  605. }
  606. large1 := large
  607. large1.NodeName = "machine1"
  608. large2 := large
  609. large2.NodeName = "machine2"
  610. tests := []struct {
  611. pod *v1.Pod
  612. pods []*v1.Pod
  613. nodes []*v1.Node
  614. name string
  615. expectedScore int
  616. }{
  617. // The point of these next two tests is to show you get the same priority for a zero-request pod
  618. // as for a pod with the defaults requests, both when the zero-request pod is already on the machine
  619. // and when the zero-request pod is the one being scheduled.
  620. {
  621. pod: &v1.Pod{Spec: noResources},
  622. nodes: []*v1.Node{makeNode("machine1", 1000, priorityutil.DefaultMemoryRequest*10), makeNode("machine2", 1000, priorityutil.DefaultMemoryRequest*10)},
  623. name: "test priority of zero-request pod with machine with zero-request pod",
  624. pods: []*v1.Pod{
  625. {Spec: large1}, {Spec: noResources1},
  626. {Spec: large2}, {Spec: small2},
  627. },
  628. expectedScore: 25,
  629. },
  630. {
  631. pod: &v1.Pod{Spec: small},
  632. nodes: []*v1.Node{makeNode("machine1", 1000, priorityutil.DefaultMemoryRequest*10), makeNode("machine2", 1000, priorityutil.DefaultMemoryRequest*10)},
  633. name: "test priority of nonzero-request pod with machine with zero-request pod",
  634. pods: []*v1.Pod{
  635. {Spec: large1}, {Spec: noResources1},
  636. {Spec: large2}, {Spec: small2},
  637. },
  638. expectedScore: 25,
  639. },
  640. // The point of this test is to verify that we're not just getting the same score no matter what we schedule.
  641. {
  642. pod: &v1.Pod{Spec: large},
  643. nodes: []*v1.Node{makeNode("machine1", 1000, priorityutil.DefaultMemoryRequest*10), makeNode("machine2", 1000, priorityutil.DefaultMemoryRequest*10)},
  644. name: "test priority of larger pod with machine with zero-request pod",
  645. pods: []*v1.Pod{
  646. {Spec: large1}, {Spec: noResources1},
  647. {Spec: large2}, {Spec: small2},
  648. },
  649. expectedScore: 23,
  650. },
  651. }
  652. for _, test := range tests {
  653. t.Run(test.name, func(t *testing.T) {
  654. // This should match the configuration in defaultPriorities() in
  655. // pkg/scheduler/algorithmprovider/defaults/defaults.go if you want
  656. // to test what's actually in production.
  657. priorityConfigs := []priorities.PriorityConfig{
  658. {Map: priorities.LeastRequestedPriorityMap, Weight: 1},
  659. {Map: priorities.BalancedResourceAllocationMap, Weight: 1},
  660. }
  661. selectorSpreadPriorityMap, selectorSpreadPriorityReduce := priorities.NewSelectorSpreadPriority(
  662. schedulertesting.FakeServiceLister([]*v1.Service{}),
  663. schedulertesting.FakeControllerLister([]*v1.ReplicationController{}),
  664. schedulertesting.FakeReplicaSetLister([]*apps.ReplicaSet{}),
  665. schedulertesting.FakeStatefulSetLister([]*apps.StatefulSet{}))
  666. pc := priorities.PriorityConfig{Map: selectorSpreadPriorityMap, Reduce: selectorSpreadPriorityReduce, Weight: 1}
  667. priorityConfigs = append(priorityConfigs, pc)
  668. nodeNameToInfo := schedulernodeinfo.CreateNodeNameToInfoMap(test.pods, test.nodes)
  669. metaDataProducer := priorities.NewPriorityMetadataFactory(
  670. schedulertesting.FakeServiceLister([]*v1.Service{}),
  671. schedulertesting.FakeControllerLister([]*v1.ReplicationController{}),
  672. schedulertesting.FakeReplicaSetLister([]*apps.ReplicaSet{}),
  673. schedulertesting.FakeStatefulSetLister([]*apps.StatefulSet{}))
  674. metaData := metaDataProducer(test.pod, nodeNameToInfo)
  675. list, err := PrioritizeNodes(
  676. test.pod, nodeNameToInfo, metaData, priorityConfigs,
  677. schedulertesting.FakeNodeLister(test.nodes), []algorithm.SchedulerExtender{})
  678. if err != nil {
  679. t.Errorf("unexpected error: %v", err)
  680. }
  681. for _, hp := range list {
  682. if hp.Score != test.expectedScore {
  683. t.Errorf("expected %d for all priorities, got list %#v", test.expectedScore, list)
  684. }
  685. }
  686. })
  687. }
  688. }
  689. func printNodeToVictims(nodeToVictims map[*v1.Node]*schedulerapi.Victims) string {
  690. var output string
  691. for node, victims := range nodeToVictims {
  692. output += node.Name + ": ["
  693. for _, pod := range victims.Pods {
  694. output += pod.Name + ", "
  695. }
  696. output += "]"
  697. }
  698. return output
  699. }
  700. func checkPreemptionVictims(expected map[string]map[string]bool, nodeToPods map[*v1.Node]*schedulerapi.Victims) error {
  701. if len(expected) == len(nodeToPods) {
  702. for k, victims := range nodeToPods {
  703. if expPods, ok := expected[k.Name]; ok {
  704. if len(victims.Pods) != len(expPods) {
  705. return fmt.Errorf("unexpected number of pods. expected: %v, got: %v", expected, printNodeToVictims(nodeToPods))
  706. }
  707. prevPriority := int32(math.MaxInt32)
  708. for _, p := range victims.Pods {
  709. // Check that pods are sorted by their priority.
  710. if *p.Spec.Priority > prevPriority {
  711. return fmt.Errorf("pod %v of node %v was not sorted by priority", p.Name, k)
  712. }
  713. prevPriority = *p.Spec.Priority
  714. if _, ok := expPods[p.Name]; !ok {
  715. return fmt.Errorf("pod %v was not expected. Expected: %v", p.Name, expPods)
  716. }
  717. }
  718. } else {
  719. return fmt.Errorf("unexpected machines. expected: %v, got: %v", expected, printNodeToVictims(nodeToPods))
  720. }
  721. }
  722. } else {
  723. return fmt.Errorf("unexpected number of machines. expected: %v, got: %v", expected, printNodeToVictims(nodeToPods))
  724. }
  725. return nil
  726. }
  727. type FakeNodeInfo v1.Node
  728. func (n FakeNodeInfo) GetNodeInfo(nodeName string) (*v1.Node, error) {
  729. node := v1.Node(n)
  730. return &node, nil
  731. }
  732. func PredicateMetadata(p *v1.Pod, nodeInfo map[string]*schedulernodeinfo.NodeInfo) algorithmpredicates.PredicateMetadata {
  733. return algorithmpredicates.NewPredicateMetadataFactory(schedulertesting.FakePodLister{p})(p, nodeInfo)
  734. }
  735. var smallContainers = []v1.Container{
  736. {
  737. Resources: v1.ResourceRequirements{
  738. Requests: v1.ResourceList{
  739. "cpu": resource.MustParse(
  740. strconv.FormatInt(priorityutil.DefaultMilliCPURequest, 10) + "m"),
  741. "memory": resource.MustParse(
  742. strconv.FormatInt(priorityutil.DefaultMemoryRequest, 10)),
  743. },
  744. },
  745. },
  746. }
  747. var mediumContainers = []v1.Container{
  748. {
  749. Resources: v1.ResourceRequirements{
  750. Requests: v1.ResourceList{
  751. "cpu": resource.MustParse(
  752. strconv.FormatInt(priorityutil.DefaultMilliCPURequest*2, 10) + "m"),
  753. "memory": resource.MustParse(
  754. strconv.FormatInt(priorityutil.DefaultMemoryRequest*2, 10)),
  755. },
  756. },
  757. },
  758. }
  759. var largeContainers = []v1.Container{
  760. {
  761. Resources: v1.ResourceRequirements{
  762. Requests: v1.ResourceList{
  763. "cpu": resource.MustParse(
  764. strconv.FormatInt(priorityutil.DefaultMilliCPURequest*3, 10) + "m"),
  765. "memory": resource.MustParse(
  766. strconv.FormatInt(priorityutil.DefaultMemoryRequest*3, 10)),
  767. },
  768. },
  769. },
  770. }
  771. var veryLargeContainers = []v1.Container{
  772. {
  773. Resources: v1.ResourceRequirements{
  774. Requests: v1.ResourceList{
  775. "cpu": resource.MustParse(
  776. strconv.FormatInt(priorityutil.DefaultMilliCPURequest*5, 10) + "m"),
  777. "memory": resource.MustParse(
  778. strconv.FormatInt(priorityutil.DefaultMemoryRequest*5, 10)),
  779. },
  780. },
  781. },
  782. }
  783. var negPriority, lowPriority, midPriority, highPriority, veryHighPriority = int32(-100), int32(0), int32(100), int32(1000), int32(10000)
  784. var startTime = metav1.Date(2019, 1, 1, 1, 1, 1, 0, time.UTC)
  785. var startTime20190102 = metav1.Date(2019, 1, 2, 1, 1, 1, 0, time.UTC)
  786. var startTime20190103 = metav1.Date(2019, 1, 3, 1, 1, 1, 0, time.UTC)
  787. var startTime20190104 = metav1.Date(2019, 1, 4, 1, 1, 1, 0, time.UTC)
  788. var startTime20190105 = metav1.Date(2019, 1, 5, 1, 1, 1, 0, time.UTC)
  789. var startTime20190106 = metav1.Date(2019, 1, 6, 1, 1, 1, 0, time.UTC)
  790. var startTime20190107 = metav1.Date(2019, 1, 7, 1, 1, 1, 0, time.UTC)
  791. // TestSelectNodesForPreemption tests selectNodesForPreemption. This test assumes
  792. // that podsFitsOnNode works correctly and is tested separately.
  793. func TestSelectNodesForPreemption(t *testing.T) {
  794. defer algorithmpredicates.SetPredicatesOrderingDuringTest(order)()
  795. tests := []struct {
  796. name string
  797. predicates map[string]algorithmpredicates.FitPredicate
  798. nodes []string
  799. pod *v1.Pod
  800. pods []*v1.Pod
  801. expected map[string]map[string]bool // Map from node name to a list of pods names which should be preempted.
  802. addAffinityPredicate bool
  803. }{
  804. {
  805. name: "a pod that does not fit on any machine",
  806. predicates: map[string]algorithmpredicates.FitPredicate{"matches": falsePredicate},
  807. nodes: []string{"machine1", "machine2"},
  808. pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "new", UID: types.UID("new")}, Spec: v1.PodSpec{Priority: &highPriority}},
  809. pods: []*v1.Pod{
  810. {ObjectMeta: metav1.ObjectMeta{Name: "a", UID: types.UID("a")}, Spec: v1.PodSpec{Priority: &midPriority, NodeName: "machine1"}},
  811. {ObjectMeta: metav1.ObjectMeta{Name: "b", UID: types.UID("b")}, Spec: v1.PodSpec{Priority: &midPriority, NodeName: "machine2"}}},
  812. expected: map[string]map[string]bool{},
  813. },
  814. {
  815. name: "a pod that fits with no preemption",
  816. predicates: map[string]algorithmpredicates.FitPredicate{"matches": truePredicate},
  817. nodes: []string{"machine1", "machine2"},
  818. pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "new", UID: types.UID("new")}, Spec: v1.PodSpec{Priority: &highPriority}},
  819. pods: []*v1.Pod{
  820. {ObjectMeta: metav1.ObjectMeta{Name: "a", UID: types.UID("a")}, Spec: v1.PodSpec{Priority: &midPriority, NodeName: "machine1"}},
  821. {ObjectMeta: metav1.ObjectMeta{Name: "b", UID: types.UID("b")}, Spec: v1.PodSpec{Priority: &midPriority, NodeName: "machine2"}}},
  822. expected: map[string]map[string]bool{"machine1": {}, "machine2": {}},
  823. },
  824. {
  825. name: "a pod that fits on one machine with no preemption",
  826. predicates: map[string]algorithmpredicates.FitPredicate{"matches": matchesPredicate},
  827. nodes: []string{"machine1", "machine2"},
  828. pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "machine1", UID: types.UID("machine1")}, Spec: v1.PodSpec{Priority: &highPriority}},
  829. pods: []*v1.Pod{
  830. {ObjectMeta: metav1.ObjectMeta{Name: "a", UID: types.UID("a")}, Spec: v1.PodSpec{Priority: &midPriority, NodeName: "machine1"}},
  831. {ObjectMeta: metav1.ObjectMeta{Name: "b", UID: types.UID("b")}, Spec: v1.PodSpec{Priority: &midPriority, NodeName: "machine2"}}},
  832. expected: map[string]map[string]bool{"machine1": {}},
  833. },
  834. {
  835. name: "a pod that fits on both machines when lower priority pods are preempted",
  836. predicates: map[string]algorithmpredicates.FitPredicate{"matches": algorithmpredicates.PodFitsResources},
  837. nodes: []string{"machine1", "machine2"},
  838. pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "machine1", UID: types.UID("machine1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &highPriority}},
  839. pods: []*v1.Pod{
  840. {ObjectMeta: metav1.ObjectMeta{Name: "a", UID: types.UID("a")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &midPriority, NodeName: "machine1"}},
  841. {ObjectMeta: metav1.ObjectMeta{Name: "b", UID: types.UID("b")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &midPriority, NodeName: "machine2"}}},
  842. expected: map[string]map[string]bool{"machine1": {"a": true}, "machine2": {"b": true}},
  843. },
  844. {
  845. name: "a pod that would fit on the machines, but other pods running are higher priority",
  846. predicates: map[string]algorithmpredicates.FitPredicate{"matches": algorithmpredicates.PodFitsResources},
  847. nodes: []string{"machine1", "machine2"},
  848. pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "machine1", UID: types.UID("machine1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &lowPriority}},
  849. pods: []*v1.Pod{
  850. {ObjectMeta: metav1.ObjectMeta{Name: "a", UID: types.UID("a")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &midPriority, NodeName: "machine1"}},
  851. {ObjectMeta: metav1.ObjectMeta{Name: "b", UID: types.UID("b")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &midPriority, NodeName: "machine2"}}},
  852. expected: map[string]map[string]bool{},
  853. },
  854. {
  855. name: "medium priority pod is preempted, but lower priority one stays as it is small",
  856. predicates: map[string]algorithmpredicates.FitPredicate{"matches": algorithmpredicates.PodFitsResources},
  857. nodes: []string{"machine1", "machine2"},
  858. pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "machine1", UID: types.UID("machine1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &highPriority}},
  859. pods: []*v1.Pod{
  860. {ObjectMeta: metav1.ObjectMeta{Name: "a", UID: types.UID("a")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine1"}},
  861. {ObjectMeta: metav1.ObjectMeta{Name: "b", UID: types.UID("b")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &midPriority, NodeName: "machine1"}},
  862. {ObjectMeta: metav1.ObjectMeta{Name: "c", UID: types.UID("c")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &midPriority, NodeName: "machine2"}}},
  863. expected: map[string]map[string]bool{"machine1": {"b": true}, "machine2": {"c": true}},
  864. },
  865. {
  866. name: "mixed priority pods are preempted",
  867. predicates: map[string]algorithmpredicates.FitPredicate{"matches": algorithmpredicates.PodFitsResources},
  868. nodes: []string{"machine1", "machine2"},
  869. pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "machine1", UID: types.UID("machine1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &highPriority}},
  870. pods: []*v1.Pod{
  871. {ObjectMeta: metav1.ObjectMeta{Name: "a", UID: types.UID("a")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &midPriority, NodeName: "machine1"}},
  872. {ObjectMeta: metav1.ObjectMeta{Name: "b", UID: types.UID("b")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine1"}},
  873. {ObjectMeta: metav1.ObjectMeta{Name: "c", UID: types.UID("c")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine1"}},
  874. {ObjectMeta: metav1.ObjectMeta{Name: "d", UID: types.UID("d")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &highPriority, NodeName: "machine1"}},
  875. {ObjectMeta: metav1.ObjectMeta{Name: "e", UID: types.UID("e")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &highPriority, NodeName: "machine2"}}},
  876. expected: map[string]map[string]bool{"machine1": {"b": true, "c": true}},
  877. },
  878. {
  879. name: "mixed priority pods are preempted, pick later StartTime one when priorities are equal",
  880. predicates: map[string]algorithmpredicates.FitPredicate{"matches": algorithmpredicates.PodFitsResources},
  881. nodes: []string{"machine1", "machine2"},
  882. pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "machine1", UID: types.UID("machine1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &highPriority}},
  883. pods: []*v1.Pod{
  884. {ObjectMeta: metav1.ObjectMeta{Name: "a", UID: types.UID("a")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime20190107}},
  885. {ObjectMeta: metav1.ObjectMeta{Name: "b", UID: types.UID("b")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime20190106}},
  886. {ObjectMeta: metav1.ObjectMeta{Name: "c", UID: types.UID("c")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime20190105}},
  887. {ObjectMeta: metav1.ObjectMeta{Name: "d", UID: types.UID("d")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &highPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime20190104}},
  888. {ObjectMeta: metav1.ObjectMeta{Name: "e", UID: types.UID("e")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &highPriority, NodeName: "machine2"}, Status: v1.PodStatus{StartTime: &startTime20190103}}},
  889. expected: map[string]map[string]bool{"machine1": {"a": true, "c": true}},
  890. },
  891. {
  892. name: "pod with anti-affinity is preempted",
  893. predicates: map[string]algorithmpredicates.FitPredicate{"matches": algorithmpredicates.PodFitsResources},
  894. nodes: []string{"machine1", "machine2"},
  895. pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{
  896. Name: "machine1",
  897. Labels: map[string]string{"pod": "preemptor"}}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &highPriority}},
  898. pods: []*v1.Pod{
  899. {ObjectMeta: metav1.ObjectMeta{Name: "a", UID: types.UID("a"), Labels: map[string]string{"service": "securityscan"}}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine1", Affinity: &v1.Affinity{
  900. PodAntiAffinity: &v1.PodAntiAffinity{
  901. RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
  902. {
  903. LabelSelector: &metav1.LabelSelector{
  904. MatchExpressions: []metav1.LabelSelectorRequirement{
  905. {
  906. Key: "pod",
  907. Operator: metav1.LabelSelectorOpIn,
  908. Values: []string{"preemptor", "value2"},
  909. },
  910. },
  911. },
  912. TopologyKey: "hostname",
  913. },
  914. },
  915. }}}},
  916. {ObjectMeta: metav1.ObjectMeta{Name: "b", UID: types.UID("b")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &midPriority, NodeName: "machine1"}},
  917. {ObjectMeta: metav1.ObjectMeta{Name: "d", UID: types.UID("d")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &highPriority, NodeName: "machine1"}},
  918. {ObjectMeta: metav1.ObjectMeta{Name: "e", UID: types.UID("e")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &highPriority, NodeName: "machine2"}}},
  919. expected: map[string]map[string]bool{"machine1": {"a": true}, "machine2": {}},
  920. addAffinityPredicate: true,
  921. },
  922. }
  923. for _, test := range tests {
  924. t.Run(test.name, func(t *testing.T) {
  925. nodes := []*v1.Node{}
  926. for _, n := range test.nodes {
  927. node := makeNode(n, 1000*5, priorityutil.DefaultMemoryRequest*5)
  928. node.ObjectMeta.Labels = map[string]string{"hostname": node.Name}
  929. nodes = append(nodes, node)
  930. }
  931. if test.addAffinityPredicate {
  932. test.predicates[algorithmpredicates.MatchInterPodAffinityPred] = algorithmpredicates.NewPodAffinityPredicate(FakeNodeInfo(*nodes[0]), schedulertesting.FakePodLister(test.pods))
  933. }
  934. nodeNameToInfo := schedulernodeinfo.CreateNodeNameToInfoMap(test.pods, nodes)
  935. // newnode simulate a case that a new node is added to the cluster, but nodeNameToInfo
  936. // doesn't have it yet.
  937. newnode := makeNode("newnode", 1000*5, priorityutil.DefaultMemoryRequest*5)
  938. newnode.ObjectMeta.Labels = map[string]string{"hostname": "newnode"}
  939. nodes = append(nodes, newnode)
  940. nodeToPods, err := selectNodesForPreemption(test.pod, nodeNameToInfo, nodes, test.predicates, PredicateMetadata, nil, nil)
  941. if err != nil {
  942. t.Error(err)
  943. }
  944. if err := checkPreemptionVictims(test.expected, nodeToPods); err != nil {
  945. t.Error(err)
  946. }
  947. })
  948. }
  949. }
  950. // TestPickOneNodeForPreemption tests pickOneNodeForPreemption.
  951. func TestPickOneNodeForPreemption(t *testing.T) {
  952. defer algorithmpredicates.SetPredicatesOrderingDuringTest(order)()
  953. tests := []struct {
  954. name string
  955. predicates map[string]algorithmpredicates.FitPredicate
  956. nodes []string
  957. pod *v1.Pod
  958. pods []*v1.Pod
  959. expected []string // any of the items is valid
  960. }{
  961. {
  962. name: "No node needs preemption",
  963. predicates: map[string]algorithmpredicates.FitPredicate{"matches": algorithmpredicates.PodFitsResources},
  964. nodes: []string{"machine1"},
  965. pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "machine1", UID: types.UID("machine1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &highPriority}},
  966. pods: []*v1.Pod{
  967. {ObjectMeta: metav1.ObjectMeta{Name: "m1.1", UID: types.UID("m1.1")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime}}},
  968. expected: []string{"machine1"},
  969. },
  970. {
  971. name: "a pod that fits on both machines when lower priority pods are preempted",
  972. predicates: map[string]algorithmpredicates.FitPredicate{"matches": algorithmpredicates.PodFitsResources},
  973. nodes: []string{"machine1", "machine2"},
  974. pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "machine1", UID: types.UID("machine1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &highPriority}},
  975. pods: []*v1.Pod{
  976. {ObjectMeta: metav1.ObjectMeta{Name: "m1.1", UID: types.UID("m1.1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime}},
  977. {ObjectMeta: metav1.ObjectMeta{Name: "m2.1", UID: types.UID("m2.1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &midPriority, NodeName: "machine2"}, Status: v1.PodStatus{StartTime: &startTime}}},
  978. expected: []string{"machine1", "machine2"},
  979. },
  980. {
  981. name: "a pod that fits on a machine with no preemption",
  982. predicates: map[string]algorithmpredicates.FitPredicate{"matches": algorithmpredicates.PodFitsResources},
  983. nodes: []string{"machine1", "machine2", "machine3"},
  984. pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "machine1", UID: types.UID("machine1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &highPriority}},
  985. pods: []*v1.Pod{
  986. {ObjectMeta: metav1.ObjectMeta{Name: "m1.1", UID: types.UID("m1.1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime}},
  987. {ObjectMeta: metav1.ObjectMeta{Name: "m2.1", UID: types.UID("m2.1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &midPriority, NodeName: "machine2"}, Status: v1.PodStatus{StartTime: &startTime}}},
  988. expected: []string{"machine3"},
  989. },
  990. {
  991. name: "machine with min highest priority pod is picked",
  992. predicates: map[string]algorithmpredicates.FitPredicate{"matches": algorithmpredicates.PodFitsResources},
  993. nodes: []string{"machine1", "machine2", "machine3"},
  994. pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "machine1", UID: types.UID("machine1")}, Spec: v1.PodSpec{Containers: veryLargeContainers, Priority: &highPriority}},
  995. pods: []*v1.Pod{
  996. {ObjectMeta: metav1.ObjectMeta{Name: "m1.1", UID: types.UID("m1.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime}},
  997. {ObjectMeta: metav1.ObjectMeta{Name: "m1.2", UID: types.UID("m1.2")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime}},
  998. {ObjectMeta: metav1.ObjectMeta{Name: "m2.1", UID: types.UID("m2.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine2"}, Status: v1.PodStatus{StartTime: &startTime}},
  999. {ObjectMeta: metav1.ObjectMeta{Name: "m2.2", UID: types.UID("m2.2")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &lowPriority, NodeName: "machine2"}, Status: v1.PodStatus{StartTime: &startTime}},
  1000. {ObjectMeta: metav1.ObjectMeta{Name: "m3.1", UID: types.UID("m3.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &lowPriority, NodeName: "machine3"}, Status: v1.PodStatus{StartTime: &startTime}},
  1001. {ObjectMeta: metav1.ObjectMeta{Name: "m3.2", UID: types.UID("m3.2")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &lowPriority, NodeName: "machine3"}, Status: v1.PodStatus{StartTime: &startTime}},
  1002. },
  1003. expected: []string{"machine3"},
  1004. },
  1005. {
  1006. name: "when highest priorities are the same, minimum sum of priorities is picked",
  1007. predicates: map[string]algorithmpredicates.FitPredicate{"matches": algorithmpredicates.PodFitsResources},
  1008. nodes: []string{"machine1", "machine2", "machine3"},
  1009. pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "machine1", UID: types.UID("machine1")}, Spec: v1.PodSpec{Containers: veryLargeContainers, Priority: &highPriority}},
  1010. pods: []*v1.Pod{
  1011. {ObjectMeta: metav1.ObjectMeta{Name: "m1.1", UID: types.UID("m1.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime}},
  1012. {ObjectMeta: metav1.ObjectMeta{Name: "m1.2", UID: types.UID("m1.2")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime}},
  1013. {ObjectMeta: metav1.ObjectMeta{Name: "m2.1", UID: types.UID("m2.1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &midPriority, NodeName: "machine2"}, Status: v1.PodStatus{StartTime: &startTime}},
  1014. {ObjectMeta: metav1.ObjectMeta{Name: "m2.2", UID: types.UID("m2.2")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &lowPriority, NodeName: "machine2"}, Status: v1.PodStatus{StartTime: &startTime}},
  1015. {ObjectMeta: metav1.ObjectMeta{Name: "m3.1", UID: types.UID("m3.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine3"}, Status: v1.PodStatus{StartTime: &startTime}},
  1016. {ObjectMeta: metav1.ObjectMeta{Name: "m3.2", UID: types.UID("m3.2")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine3"}, Status: v1.PodStatus{StartTime: &startTime}},
  1017. },
  1018. expected: []string{"machine2"},
  1019. },
  1020. {
  1021. name: "when highest priority and sum are the same, minimum number of pods is picked",
  1022. predicates: map[string]algorithmpredicates.FitPredicate{"matches": algorithmpredicates.PodFitsResources},
  1023. nodes: []string{"machine1", "machine2", "machine3"},
  1024. pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "machine1", UID: types.UID("machine1")}, Spec: v1.PodSpec{Containers: veryLargeContainers, Priority: &highPriority}},
  1025. pods: []*v1.Pod{
  1026. {ObjectMeta: metav1.ObjectMeta{Name: "m1.1", UID: types.UID("m1.1")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime}},
  1027. {ObjectMeta: metav1.ObjectMeta{Name: "m1.2", UID: types.UID("m1.2")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &negPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime}},
  1028. {ObjectMeta: metav1.ObjectMeta{Name: "m1.3", UID: types.UID("m1.3")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime}},
  1029. {ObjectMeta: metav1.ObjectMeta{Name: "m1.4", UID: types.UID("m1.4")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &negPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime}},
  1030. {ObjectMeta: metav1.ObjectMeta{Name: "m2.1", UID: types.UID("m2.1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &midPriority, NodeName: "machine2"}, Status: v1.PodStatus{StartTime: &startTime}},
  1031. {ObjectMeta: metav1.ObjectMeta{Name: "m2.2", UID: types.UID("m2.2")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &negPriority, NodeName: "machine2"}, Status: v1.PodStatus{StartTime: &startTime}},
  1032. {ObjectMeta: metav1.ObjectMeta{Name: "m3.1", UID: types.UID("m3.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine3"}, Status: v1.PodStatus{StartTime: &startTime}},
  1033. {ObjectMeta: metav1.ObjectMeta{Name: "m3.2", UID: types.UID("m3.2")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &negPriority, NodeName: "machine3"}, Status: v1.PodStatus{StartTime: &startTime}},
  1034. {ObjectMeta: metav1.ObjectMeta{Name: "m3.3", UID: types.UID("m3.3")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine3"}, Status: v1.PodStatus{StartTime: &startTime}},
  1035. },
  1036. expected: []string{"machine2"},
  1037. },
  1038. {
  1039. // pickOneNodeForPreemption adjusts pod priorities when finding the sum of the victims. This
  1040. // test ensures that the logic works correctly.
  1041. name: "sum of adjusted priorities is considered",
  1042. predicates: map[string]algorithmpredicates.FitPredicate{"matches": algorithmpredicates.PodFitsResources},
  1043. nodes: []string{"machine1", "machine2", "machine3"},
  1044. pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "machine1", UID: types.UID("machine1")}, Spec: v1.PodSpec{Containers: veryLargeContainers, Priority: &highPriority}},
  1045. pods: []*v1.Pod{
  1046. {ObjectMeta: metav1.ObjectMeta{Name: "m1.1", UID: types.UID("m1.1")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime}},
  1047. {ObjectMeta: metav1.ObjectMeta{Name: "m1.2", UID: types.UID("m1.2")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &negPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime}},
  1048. {ObjectMeta: metav1.ObjectMeta{Name: "m1.3", UID: types.UID("m1.3")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &negPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime}},
  1049. {ObjectMeta: metav1.ObjectMeta{Name: "m2.1", UID: types.UID("m2.1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &midPriority, NodeName: "machine2"}, Status: v1.PodStatus{StartTime: &startTime}},
  1050. {ObjectMeta: metav1.ObjectMeta{Name: "m2.2", UID: types.UID("m2.2")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &negPriority, NodeName: "machine2"}, Status: v1.PodStatus{StartTime: &startTime}},
  1051. {ObjectMeta: metav1.ObjectMeta{Name: "m3.1", UID: types.UID("m3.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine3"}, Status: v1.PodStatus{StartTime: &startTime}},
  1052. {ObjectMeta: metav1.ObjectMeta{Name: "m3.2", UID: types.UID("m3.2")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &negPriority, NodeName: "machine3"}, Status: v1.PodStatus{StartTime: &startTime}},
  1053. {ObjectMeta: metav1.ObjectMeta{Name: "m3.3", UID: types.UID("m3.3")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine3"}, Status: v1.PodStatus{StartTime: &startTime}},
  1054. },
  1055. expected: []string{"machine2"},
  1056. },
  1057. {
  1058. name: "non-overlapping lowest high priority, sum priorities, and number of pods",
  1059. predicates: map[string]algorithmpredicates.FitPredicate{"matches": algorithmpredicates.PodFitsResources},
  1060. nodes: []string{"machine1", "machine2", "machine3", "machine4"},
  1061. pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod1", UID: types.UID("pod1")}, Spec: v1.PodSpec{Containers: veryLargeContainers, Priority: &veryHighPriority}},
  1062. pods: []*v1.Pod{
  1063. {ObjectMeta: metav1.ObjectMeta{Name: "m1.1", UID: types.UID("m1.1")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime}},
  1064. {ObjectMeta: metav1.ObjectMeta{Name: "m1.2", UID: types.UID("m1.2")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime}},
  1065. {ObjectMeta: metav1.ObjectMeta{Name: "m1.3", UID: types.UID("m1.3")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime}},
  1066. {ObjectMeta: metav1.ObjectMeta{Name: "m2.1", UID: types.UID("m2.1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &highPriority, NodeName: "machine2"}, Status: v1.PodStatus{StartTime: &startTime}},
  1067. {ObjectMeta: metav1.ObjectMeta{Name: "m3.1", UID: types.UID("m3.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine3"}, Status: v1.PodStatus{StartTime: &startTime}},
  1068. {ObjectMeta: metav1.ObjectMeta{Name: "m3.2", UID: types.UID("m3.2")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine3"}, Status: v1.PodStatus{StartTime: &startTime}},
  1069. {ObjectMeta: metav1.ObjectMeta{Name: "m3.3", UID: types.UID("m3.3")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine3"}, Status: v1.PodStatus{StartTime: &startTime}},
  1070. {ObjectMeta: metav1.ObjectMeta{Name: "m3.4", UID: types.UID("m3.4")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &lowPriority, NodeName: "machine3"}, Status: v1.PodStatus{StartTime: &startTime}},
  1071. {ObjectMeta: metav1.ObjectMeta{Name: "m4.1", UID: types.UID("m4.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine4"}, Status: v1.PodStatus{StartTime: &startTime}},
  1072. {ObjectMeta: metav1.ObjectMeta{Name: "m4.2", UID: types.UID("m4.2")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &midPriority, NodeName: "machine4"}, Status: v1.PodStatus{StartTime: &startTime}},
  1073. {ObjectMeta: metav1.ObjectMeta{Name: "m4.3", UID: types.UID("m4.3")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &midPriority, NodeName: "machine4"}, Status: v1.PodStatus{StartTime: &startTime}},
  1074. {ObjectMeta: metav1.ObjectMeta{Name: "m4.4", UID: types.UID("m4.4")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &negPriority, NodeName: "machine4"}, Status: v1.PodStatus{StartTime: &startTime}},
  1075. },
  1076. expected: []string{"machine1"},
  1077. },
  1078. {
  1079. name: "same priority, same number of victims, different start time for each machine's pod",
  1080. predicates: map[string]algorithmpredicates.FitPredicate{"matches": algorithmpredicates.PodFitsResources},
  1081. nodes: []string{"machine1", "machine2", "machine3"},
  1082. pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "machine1", UID: types.UID("machine1")}, Spec: v1.PodSpec{Containers: veryLargeContainers, Priority: &highPriority}},
  1083. pods: []*v1.Pod{
  1084. {ObjectMeta: metav1.ObjectMeta{Name: "m1.1", UID: types.UID("m1.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime20190103}},
  1085. {ObjectMeta: metav1.ObjectMeta{Name: "m1.2", UID: types.UID("m1.2")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime20190103}},
  1086. {ObjectMeta: metav1.ObjectMeta{Name: "m2.1", UID: types.UID("m2.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine2"}, Status: v1.PodStatus{StartTime: &startTime20190104}},
  1087. {ObjectMeta: metav1.ObjectMeta{Name: "m2.2", UID: types.UID("m2.2")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine2"}, Status: v1.PodStatus{StartTime: &startTime20190104}},
  1088. {ObjectMeta: metav1.ObjectMeta{Name: "m3.1", UID: types.UID("m3.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine3"}, Status: v1.PodStatus{StartTime: &startTime20190102}},
  1089. {ObjectMeta: metav1.ObjectMeta{Name: "m3.2", UID: types.UID("m3.2")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine3"}, Status: v1.PodStatus{StartTime: &startTime20190102}},
  1090. },
  1091. expected: []string{"machine2"},
  1092. },
  1093. {
  1094. name: "same priority, same number of victims, different start time for all pods",
  1095. predicates: map[string]algorithmpredicates.FitPredicate{"matches": algorithmpredicates.PodFitsResources},
  1096. nodes: []string{"machine1", "machine2", "machine3"},
  1097. pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "machine1", UID: types.UID("machine1")}, Spec: v1.PodSpec{Containers: veryLargeContainers, Priority: &highPriority}},
  1098. pods: []*v1.Pod{
  1099. {ObjectMeta: metav1.ObjectMeta{Name: "m1.1", UID: types.UID("m1.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime20190105}},
  1100. {ObjectMeta: metav1.ObjectMeta{Name: "m1.2", UID: types.UID("m1.2")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime20190103}},
  1101. {ObjectMeta: metav1.ObjectMeta{Name: "m2.1", UID: types.UID("m2.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine2"}, Status: v1.PodStatus{StartTime: &startTime20190106}},
  1102. {ObjectMeta: metav1.ObjectMeta{Name: "m2.2", UID: types.UID("m2.2")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine2"}, Status: v1.PodStatus{StartTime: &startTime20190102}},
  1103. {ObjectMeta: metav1.ObjectMeta{Name: "m3.1", UID: types.UID("m3.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine3"}, Status: v1.PodStatus{StartTime: &startTime20190104}},
  1104. {ObjectMeta: metav1.ObjectMeta{Name: "m3.2", UID: types.UID("m3.2")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine3"}, Status: v1.PodStatus{StartTime: &startTime20190107}},
  1105. },
  1106. expected: []string{"machine3"},
  1107. },
  1108. {
  1109. name: "different priority, same number of victims, different start time for all pods",
  1110. predicates: map[string]algorithmpredicates.FitPredicate{"matches": algorithmpredicates.PodFitsResources},
  1111. nodes: []string{"machine1", "machine2", "machine3"},
  1112. pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "machine1", UID: types.UID("machine1")}, Spec: v1.PodSpec{Containers: veryLargeContainers, Priority: &highPriority}},
  1113. pods: []*v1.Pod{
  1114. {ObjectMeta: metav1.ObjectMeta{Name: "m1.1", UID: types.UID("m1.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &lowPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime20190105}},
  1115. {ObjectMeta: metav1.ObjectMeta{Name: "m1.2", UID: types.UID("m1.2")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{StartTime: &startTime20190103}},
  1116. {ObjectMeta: metav1.ObjectMeta{Name: "m2.1", UID: types.UID("m2.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine2"}, Status: v1.PodStatus{StartTime: &startTime20190107}},
  1117. {ObjectMeta: metav1.ObjectMeta{Name: "m2.2", UID: types.UID("m2.2")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &lowPriority, NodeName: "machine2"}, Status: v1.PodStatus{StartTime: &startTime20190102}},
  1118. {ObjectMeta: metav1.ObjectMeta{Name: "m3.1", UID: types.UID("m3.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &lowPriority, NodeName: "machine3"}, Status: v1.PodStatus{StartTime: &startTime20190104}},
  1119. {ObjectMeta: metav1.ObjectMeta{Name: "m3.2", UID: types.UID("m3.2")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine3"}, Status: v1.PodStatus{StartTime: &startTime20190106}},
  1120. },
  1121. expected: []string{"machine2"},
  1122. },
  1123. }
  1124. for _, test := range tests {
  1125. t.Run(test.name, func(t *testing.T) {
  1126. nodes := []*v1.Node{}
  1127. for _, n := range test.nodes {
  1128. nodes = append(nodes, makeNode(n, priorityutil.DefaultMilliCPURequest*5, priorityutil.DefaultMemoryRequest*5))
  1129. }
  1130. nodeNameToInfo := schedulernodeinfo.CreateNodeNameToInfoMap(test.pods, nodes)
  1131. candidateNodes, _ := selectNodesForPreemption(test.pod, nodeNameToInfo, nodes, test.predicates, PredicateMetadata, nil, nil)
  1132. node := pickOneNodeForPreemption(candidateNodes)
  1133. found := false
  1134. for _, nodeName := range test.expected {
  1135. if node.Name == nodeName {
  1136. found = true
  1137. break
  1138. }
  1139. }
  1140. if !found {
  1141. t.Errorf("unexpected node: %v", node)
  1142. }
  1143. })
  1144. }
  1145. }
  1146. func TestNodesWherePreemptionMightHelp(t *testing.T) {
  1147. // Prepare 4 node names.
  1148. nodeNames := []string{}
  1149. for i := 1; i < 5; i++ {
  1150. nodeNames = append(nodeNames, fmt.Sprintf("machine%d", i))
  1151. }
  1152. tests := []struct {
  1153. name string
  1154. failedPredMap FailedPredicateMap
  1155. expected map[string]bool // set of expected node names. Value is ignored.
  1156. }{
  1157. {
  1158. name: "No node should be attempted",
  1159. failedPredMap: FailedPredicateMap{
  1160. "machine1": []algorithmpredicates.PredicateFailureReason{algorithmpredicates.ErrNodeSelectorNotMatch},
  1161. "machine2": []algorithmpredicates.PredicateFailureReason{algorithmpredicates.ErrPodNotMatchHostName},
  1162. "machine3": []algorithmpredicates.PredicateFailureReason{algorithmpredicates.ErrTaintsTolerationsNotMatch},
  1163. "machine4": []algorithmpredicates.PredicateFailureReason{algorithmpredicates.ErrNodeLabelPresenceViolated},
  1164. },
  1165. expected: map[string]bool{},
  1166. },
  1167. {
  1168. name: "ErrPodAffinityNotMatch should be tried as it indicates that the pod is unschedulable due to inter-pod affinity or anti-affinity",
  1169. failedPredMap: FailedPredicateMap{
  1170. "machine1": []algorithmpredicates.PredicateFailureReason{algorithmpredicates.ErrPodAffinityNotMatch},
  1171. "machine2": []algorithmpredicates.PredicateFailureReason{algorithmpredicates.ErrPodNotMatchHostName},
  1172. "machine3": []algorithmpredicates.PredicateFailureReason{algorithmpredicates.ErrNodeUnschedulable},
  1173. },
  1174. expected: map[string]bool{"machine1": true, "machine4": true},
  1175. },
  1176. {
  1177. name: "pod with both pod affinity and anti-affinity should be tried",
  1178. failedPredMap: FailedPredicateMap{
  1179. "machine1": []algorithmpredicates.PredicateFailureReason{algorithmpredicates.ErrPodAffinityNotMatch},
  1180. "machine2": []algorithmpredicates.PredicateFailureReason{algorithmpredicates.ErrPodNotMatchHostName},
  1181. },
  1182. expected: map[string]bool{"machine1": true, "machine3": true, "machine4": true},
  1183. },
  1184. {
  1185. name: "ErrPodAffinityRulesNotMatch should not be tried as it indicates that the pod is unschedulable due to inter-pod affinity, but ErrPodAffinityNotMatch should be tried as it indicates that the pod is unschedulable due to inter-pod affinity or anti-affinity",
  1186. failedPredMap: FailedPredicateMap{
  1187. "machine1": []algorithmpredicates.PredicateFailureReason{algorithmpredicates.ErrPodAffinityRulesNotMatch},
  1188. "machine2": []algorithmpredicates.PredicateFailureReason{algorithmpredicates.ErrPodAffinityNotMatch},
  1189. },
  1190. expected: map[string]bool{"machine2": true, "machine3": true, "machine4": true},
  1191. },
  1192. {
  1193. name: "Mix of failed predicates works fine",
  1194. failedPredMap: FailedPredicateMap{
  1195. "machine1": []algorithmpredicates.PredicateFailureReason{algorithmpredicates.ErrNodeSelectorNotMatch, algorithmpredicates.ErrNodeUnderDiskPressure, algorithmpredicates.NewInsufficientResourceError(v1.ResourceMemory, 1000, 500, 300)},
  1196. "machine2": []algorithmpredicates.PredicateFailureReason{algorithmpredicates.ErrPodNotMatchHostName, algorithmpredicates.ErrDiskConflict},
  1197. "machine3": []algorithmpredicates.PredicateFailureReason{algorithmpredicates.NewInsufficientResourceError(v1.ResourceMemory, 1000, 600, 400)},
  1198. "machine4": []algorithmpredicates.PredicateFailureReason{},
  1199. },
  1200. expected: map[string]bool{"machine3": true, "machine4": true},
  1201. },
  1202. {
  1203. name: "Node condition errors should be considered unresolvable",
  1204. failedPredMap: FailedPredicateMap{
  1205. "machine1": []algorithmpredicates.PredicateFailureReason{algorithmpredicates.ErrNodeUnderDiskPressure},
  1206. "machine2": []algorithmpredicates.PredicateFailureReason{algorithmpredicates.ErrNodeUnderPIDPressure},
  1207. "machine3": []algorithmpredicates.PredicateFailureReason{algorithmpredicates.ErrNodeUnderMemoryPressure},
  1208. },
  1209. expected: map[string]bool{"machine4": true},
  1210. },
  1211. {
  1212. name: "Node condition errors and ErrNodeUnknownCondition should be considered unresolvable",
  1213. failedPredMap: FailedPredicateMap{
  1214. "machine1": []algorithmpredicates.PredicateFailureReason{algorithmpredicates.ErrNodeNotReady},
  1215. "machine2": []algorithmpredicates.PredicateFailureReason{algorithmpredicates.ErrNodeNetworkUnavailable},
  1216. "machine3": []algorithmpredicates.PredicateFailureReason{algorithmpredicates.ErrNodeUnknownCondition},
  1217. },
  1218. expected: map[string]bool{"machine4": true},
  1219. },
  1220. {
  1221. name: "ErrVolume... errors should not be tried as it indicates that the pod is unschedulable due to no matching volumes for pod on node",
  1222. failedPredMap: FailedPredicateMap{
  1223. "machine1": []algorithmpredicates.PredicateFailureReason{algorithmpredicates.ErrVolumeZoneConflict},
  1224. "machine2": []algorithmpredicates.PredicateFailureReason{algorithmpredicates.ErrVolumeNodeConflict},
  1225. "machine3": []algorithmpredicates.PredicateFailureReason{algorithmpredicates.ErrVolumeBindConflict},
  1226. },
  1227. expected: map[string]bool{"machine4": true},
  1228. },
  1229. }
  1230. for _, test := range tests {
  1231. t.Run(test.name, func(t *testing.T) {
  1232. nodes := nodesWherePreemptionMightHelp(makeNodeList(nodeNames), test.failedPredMap)
  1233. if len(test.expected) != len(nodes) {
  1234. t.Errorf("number of nodes is not the same as expected. exptectd: %d, got: %d. Nodes: %v", len(test.expected), len(nodes), nodes)
  1235. }
  1236. for _, node := range nodes {
  1237. if _, found := test.expected[node.Name]; !found {
  1238. t.Errorf("node %v is not expected.", node.Name)
  1239. }
  1240. }
  1241. })
  1242. }
  1243. }
  1244. func TestPreempt(t *testing.T) {
  1245. defer algorithmpredicates.SetPredicatesOrderingDuringTest(order)()
  1246. failedPredMap := FailedPredicateMap{
  1247. "machine1": []algorithmpredicates.PredicateFailureReason{algorithmpredicates.NewInsufficientResourceError(v1.ResourceMemory, 1000, 500, 300)},
  1248. "machine2": []algorithmpredicates.PredicateFailureReason{algorithmpredicates.ErrDiskConflict},
  1249. "machine3": []algorithmpredicates.PredicateFailureReason{algorithmpredicates.NewInsufficientResourceError(v1.ResourceMemory, 1000, 600, 400)},
  1250. }
  1251. // Prepare 3 node names.
  1252. nodeNames := []string{}
  1253. for i := 1; i < 4; i++ {
  1254. nodeNames = append(nodeNames, fmt.Sprintf("machine%d", i))
  1255. }
  1256. var (
  1257. preemptLowerPriority = v1.PreemptLowerPriority
  1258. preemptNever = v1.PreemptNever
  1259. )
  1260. tests := []struct {
  1261. name string
  1262. pod *v1.Pod
  1263. pods []*v1.Pod
  1264. extenders []*FakeExtender
  1265. expectedNode string
  1266. expectedPods []string // list of preempted pods
  1267. }{
  1268. {
  1269. name: "basic preemption logic",
  1270. pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod1", UID: types.UID("pod1")}, Spec: v1.PodSpec{
  1271. Containers: veryLargeContainers,
  1272. Priority: &highPriority,
  1273. PreemptionPolicy: &preemptLowerPriority},
  1274. },
  1275. pods: []*v1.Pod{
  1276. {ObjectMeta: metav1.ObjectMeta{Name: "m1.1", UID: types.UID("m1.1")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine1"}, Status: v1.PodStatus{Phase: v1.PodRunning}},
  1277. {ObjectMeta: metav1.ObjectMeta{Name: "m1.2", UID: types.UID("m1.2")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine1"}, Status: v1.PodStatus{Phase: v1.PodRunning}},
  1278. {ObjectMeta: metav1.ObjectMeta{Name: "m2.1", UID: types.UID("m2.1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &highPriority, NodeName: "machine2"}, Status: v1.PodStatus{Phase: v1.PodRunning}},
  1279. {ObjectMeta: metav1.ObjectMeta{Name: "m3.1", UID: types.UID("m3.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine3"}, Status: v1.PodStatus{Phase: v1.PodRunning}},
  1280. },
  1281. expectedNode: "machine1",
  1282. expectedPods: []string{"m1.1", "m1.2"},
  1283. },
  1284. {
  1285. name: "One node doesn't need any preemption",
  1286. pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod1", UID: types.UID("pod1")}, Spec: v1.PodSpec{
  1287. Containers: veryLargeContainers,
  1288. Priority: &highPriority,
  1289. PreemptionPolicy: &preemptLowerPriority},
  1290. },
  1291. pods: []*v1.Pod{
  1292. {ObjectMeta: metav1.ObjectMeta{Name: "m1.1", UID: types.UID("m1.1")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine1"}, Status: v1.PodStatus{Phase: v1.PodRunning}},
  1293. {ObjectMeta: metav1.ObjectMeta{Name: "m1.2", UID: types.UID("m1.2")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine1"}, Status: v1.PodStatus{Phase: v1.PodRunning}},
  1294. {ObjectMeta: metav1.ObjectMeta{Name: "m2.1", UID: types.UID("m2.1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &highPriority, NodeName: "machine2"}, Status: v1.PodStatus{Phase: v1.PodRunning}},
  1295. },
  1296. expectedNode: "machine3",
  1297. expectedPods: []string{},
  1298. },
  1299. {
  1300. name: "Scheduler extenders allow only machine1, otherwise machine3 would have been chosen",
  1301. pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod1", UID: types.UID("pod1")}, Spec: v1.PodSpec{
  1302. Containers: veryLargeContainers,
  1303. Priority: &highPriority,
  1304. PreemptionPolicy: &preemptLowerPriority},
  1305. },
  1306. pods: []*v1.Pod{
  1307. {ObjectMeta: metav1.ObjectMeta{Name: "m1.1", UID: types.UID("m1.1")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{Phase: v1.PodRunning}},
  1308. {ObjectMeta: metav1.ObjectMeta{Name: "m1.2", UID: types.UID("m1.2")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine1"}, Status: v1.PodStatus{Phase: v1.PodRunning}},
  1309. {ObjectMeta: metav1.ObjectMeta{Name: "m2.1", UID: types.UID("m2.1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &midPriority, NodeName: "machine2"}, Status: v1.PodStatus{Phase: v1.PodRunning}},
  1310. },
  1311. extenders: []*FakeExtender{
  1312. {
  1313. predicates: []fitPredicate{truePredicateExtender},
  1314. },
  1315. {
  1316. predicates: []fitPredicate{machine1PredicateExtender},
  1317. },
  1318. },
  1319. expectedNode: "machine1",
  1320. expectedPods: []string{"m1.1", "m1.2"},
  1321. },
  1322. {
  1323. name: "Scheduler extenders do not allow any preemption",
  1324. pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod1", UID: types.UID("pod1")}, Spec: v1.PodSpec{
  1325. Containers: veryLargeContainers,
  1326. Priority: &highPriority,
  1327. PreemptionPolicy: &preemptLowerPriority},
  1328. },
  1329. pods: []*v1.Pod{
  1330. {ObjectMeta: metav1.ObjectMeta{Name: "m1.1", UID: types.UID("m1.1")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{Phase: v1.PodRunning}},
  1331. {ObjectMeta: metav1.ObjectMeta{Name: "m1.2", UID: types.UID("m1.2")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine1"}, Status: v1.PodStatus{Phase: v1.PodRunning}},
  1332. {ObjectMeta: metav1.ObjectMeta{Name: "m2.1", UID: types.UID("m2.1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &midPriority, NodeName: "machine2"}, Status: v1.PodStatus{Phase: v1.PodRunning}},
  1333. },
  1334. extenders: []*FakeExtender{
  1335. {
  1336. predicates: []fitPredicate{falsePredicateExtender},
  1337. },
  1338. },
  1339. expectedNode: "",
  1340. expectedPods: []string{},
  1341. },
  1342. {
  1343. name: "One scheduler extender allows only machine1, the other returns error but ignorable. Only machine1 would be chosen",
  1344. pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod1", UID: types.UID("pod1")}, Spec: v1.PodSpec{
  1345. Containers: veryLargeContainers,
  1346. Priority: &highPriority,
  1347. PreemptionPolicy: &preemptLowerPriority},
  1348. },
  1349. pods: []*v1.Pod{
  1350. {ObjectMeta: metav1.ObjectMeta{Name: "m1.1", UID: types.UID("m1.1")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{Phase: v1.PodRunning}},
  1351. {ObjectMeta: metav1.ObjectMeta{Name: "m1.2", UID: types.UID("m1.2")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine1"}, Status: v1.PodStatus{Phase: v1.PodRunning}},
  1352. {ObjectMeta: metav1.ObjectMeta{Name: "m2.1", UID: types.UID("m2.1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &midPriority, NodeName: "machine2"}, Status: v1.PodStatus{Phase: v1.PodRunning}},
  1353. },
  1354. extenders: []*FakeExtender{
  1355. {
  1356. predicates: []fitPredicate{errorPredicateExtender},
  1357. ignorable: true,
  1358. },
  1359. {
  1360. predicates: []fitPredicate{machine1PredicateExtender},
  1361. },
  1362. },
  1363. expectedNode: "machine1",
  1364. expectedPods: []string{"m1.1", "m1.2"},
  1365. },
  1366. {
  1367. name: "One scheduler extender allows only machine1, but it is not interested in given pod, otherwise machine1 would have been chosen",
  1368. pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod1", UID: types.UID("pod1")}, Spec: v1.PodSpec{
  1369. Containers: veryLargeContainers,
  1370. Priority: &highPriority,
  1371. PreemptionPolicy: &preemptLowerPriority},
  1372. },
  1373. pods: []*v1.Pod{
  1374. {ObjectMeta: metav1.ObjectMeta{Name: "m1.1", UID: types.UID("m1.1")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &midPriority, NodeName: "machine1"}, Status: v1.PodStatus{Phase: v1.PodRunning}},
  1375. {ObjectMeta: metav1.ObjectMeta{Name: "m1.2", UID: types.UID("m1.2")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine1"}, Status: v1.PodStatus{Phase: v1.PodRunning}},
  1376. {ObjectMeta: metav1.ObjectMeta{Name: "m2.1", UID: types.UID("m2.1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &midPriority, NodeName: "machine2"}, Status: v1.PodStatus{Phase: v1.PodRunning}},
  1377. },
  1378. extenders: []*FakeExtender{
  1379. {
  1380. predicates: []fitPredicate{machine1PredicateExtender},
  1381. unInterested: true,
  1382. },
  1383. {
  1384. predicates: []fitPredicate{truePredicateExtender},
  1385. },
  1386. },
  1387. expectedNode: "machine3",
  1388. expectedPods: []string{},
  1389. },
  1390. {
  1391. name: "no preempting in pod",
  1392. pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod1", UID: types.UID("pod1")}, Spec: v1.PodSpec{
  1393. Containers: veryLargeContainers,
  1394. Priority: &highPriority,
  1395. PreemptionPolicy: &preemptNever},
  1396. },
  1397. pods: []*v1.Pod{
  1398. {ObjectMeta: metav1.ObjectMeta{Name: "m1.1", UID: types.UID("m1.1")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine1"}, Status: v1.PodStatus{Phase: v1.PodRunning}},
  1399. {ObjectMeta: metav1.ObjectMeta{Name: "m1.2", UID: types.UID("m1.2")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine1"}, Status: v1.PodStatus{Phase: v1.PodRunning}},
  1400. {ObjectMeta: metav1.ObjectMeta{Name: "m2.1", UID: types.UID("m2.1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &highPriority, NodeName: "machine2"}, Status: v1.PodStatus{Phase: v1.PodRunning}},
  1401. {ObjectMeta: metav1.ObjectMeta{Name: "m3.1", UID: types.UID("m3.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine3"}, Status: v1.PodStatus{Phase: v1.PodRunning}},
  1402. },
  1403. expectedNode: "",
  1404. expectedPods: nil,
  1405. },
  1406. {
  1407. name: "PreemptionPolicy is nil",
  1408. pod: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod1", UID: types.UID("pod1")}, Spec: v1.PodSpec{
  1409. Containers: veryLargeContainers,
  1410. Priority: &highPriority,
  1411. PreemptionPolicy: nil},
  1412. },
  1413. pods: []*v1.Pod{
  1414. {ObjectMeta: metav1.ObjectMeta{Name: "m1.1", UID: types.UID("m1.1")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine1"}, Status: v1.PodStatus{Phase: v1.PodRunning}},
  1415. {ObjectMeta: metav1.ObjectMeta{Name: "m1.2", UID: types.UID("m1.2")}, Spec: v1.PodSpec{Containers: smallContainers, Priority: &lowPriority, NodeName: "machine1"}, Status: v1.PodStatus{Phase: v1.PodRunning}},
  1416. {ObjectMeta: metav1.ObjectMeta{Name: "m2.1", UID: types.UID("m2.1")}, Spec: v1.PodSpec{Containers: largeContainers, Priority: &highPriority, NodeName: "machine2"}, Status: v1.PodStatus{Phase: v1.PodRunning}},
  1417. {ObjectMeta: metav1.ObjectMeta{Name: "m3.1", UID: types.UID("m3.1")}, Spec: v1.PodSpec{Containers: mediumContainers, Priority: &midPriority, NodeName: "machine3"}, Status: v1.PodStatus{Phase: v1.PodRunning}},
  1418. },
  1419. expectedNode: "machine1",
  1420. expectedPods: []string{"m1.1", "m1.2"},
  1421. },
  1422. }
  1423. for _, test := range tests {
  1424. t.Run(test.name, func(t *testing.T) {
  1425. t.Logf("===== Running test %v", t.Name())
  1426. stop := make(chan struct{})
  1427. cache := internalcache.New(time.Duration(0), stop)
  1428. for _, pod := range test.pods {
  1429. cache.AddPod(pod)
  1430. }
  1431. cachedNodeInfoMap := map[string]*schedulernodeinfo.NodeInfo{}
  1432. for _, name := range nodeNames {
  1433. node := makeNode(name, 1000*5, priorityutil.DefaultMemoryRequest*5)
  1434. cache.AddNode(node)
  1435. // Set nodeInfo to extenders to mock extenders' cache for preemption.
  1436. cachedNodeInfo := schedulernodeinfo.NewNodeInfo()
  1437. cachedNodeInfo.SetNode(node)
  1438. cachedNodeInfoMap[name] = cachedNodeInfo
  1439. }
  1440. extenders := []algorithm.SchedulerExtender{}
  1441. for _, extender := range test.extenders {
  1442. // Set nodeInfoMap as extenders cached node information.
  1443. extender.cachedNodeNameToInfo = cachedNodeInfoMap
  1444. extenders = append(extenders, extender)
  1445. }
  1446. scheduler := NewGenericScheduler(
  1447. cache,
  1448. internalqueue.NewSchedulingQueue(nil, nil),
  1449. map[string]algorithmpredicates.FitPredicate{"matches": algorithmpredicates.PodFitsResources},
  1450. algorithmpredicates.EmptyPredicateMetadataProducer,
  1451. []priorities.PriorityConfig{{Function: numericPriority, Weight: 1}},
  1452. priorities.EmptyPriorityMetadataProducer,
  1453. emptyFramework,
  1454. extenders,
  1455. nil,
  1456. schedulertesting.FakePersistentVolumeClaimLister{},
  1457. schedulertesting.FakePDBLister{},
  1458. false,
  1459. false,
  1460. schedulerapi.DefaultPercentageOfNodesToScore,
  1461. true)
  1462. scheduler.(*genericScheduler).snapshot()
  1463. // Call Preempt and check the expected results.
  1464. node, victims, _, err := scheduler.Preempt(test.pod, schedulertesting.FakeNodeLister(makeNodeList(nodeNames)), error(&FitError{Pod: test.pod, FailedPredicates: failedPredMap}))
  1465. if err != nil {
  1466. t.Errorf("unexpected error in preemption: %v", err)
  1467. }
  1468. if node != nil && node.Name != test.expectedNode {
  1469. t.Errorf("expected node: %v, got: %v", test.expectedNode, node.GetName())
  1470. }
  1471. if node == nil && len(test.expectedNode) != 0 {
  1472. t.Errorf("expected node: %v, got: nothing", test.expectedNode)
  1473. }
  1474. if len(victims) != len(test.expectedPods) {
  1475. t.Errorf("expected %v pods, got %v.", len(test.expectedPods), len(victims))
  1476. }
  1477. for _, victim := range victims {
  1478. found := false
  1479. for _, expPod := range test.expectedPods {
  1480. if expPod == victim.Name {
  1481. found = true
  1482. break
  1483. }
  1484. }
  1485. if !found {
  1486. t.Errorf("pod %v is not expected to be a victim.", victim.Name)
  1487. }
  1488. // Mark the victims for deletion and record the preemptor's nominated node name.
  1489. now := metav1.Now()
  1490. victim.DeletionTimestamp = &now
  1491. test.pod.Status.NominatedNodeName = node.Name
  1492. }
  1493. // Call preempt again and make sure it doesn't preempt any more pods.
  1494. node, victims, _, err = scheduler.Preempt(test.pod, schedulertesting.FakeNodeLister(makeNodeList(nodeNames)), error(&FitError{Pod: test.pod, FailedPredicates: failedPredMap}))
  1495. if err != nil {
  1496. t.Errorf("unexpected error in preemption: %v", err)
  1497. }
  1498. if node != nil && len(victims) > 0 {
  1499. t.Errorf("didn't expect any more preemption. Node %v is selected for preemption.", node)
  1500. }
  1501. close(stop)
  1502. })
  1503. }
  1504. }
  1505. func TestNumFeasibleNodesToFind(t *testing.T) {
  1506. tests := []struct {
  1507. name string
  1508. percentageOfNodesToScore int32
  1509. numAllNodes int32
  1510. wantNumNodes int32
  1511. }{
  1512. {
  1513. name: "not set percentageOfNodesToScore and nodes number not more than 50",
  1514. numAllNodes: 10,
  1515. wantNumNodes: 10,
  1516. },
  1517. {
  1518. name: "set percentageOfNodesToScore and nodes number not more than 50",
  1519. percentageOfNodesToScore: 40,
  1520. numAllNodes: 10,
  1521. wantNumNodes: 10,
  1522. },
  1523. {
  1524. name: "not set percentageOfNodesToScore and nodes number more than 50",
  1525. numAllNodes: 1000,
  1526. wantNumNodes: 420,
  1527. },
  1528. {
  1529. name: "set percentageOfNodesToScore and nodes number more than 50",
  1530. percentageOfNodesToScore: 40,
  1531. numAllNodes: 1000,
  1532. wantNumNodes: 400,
  1533. },
  1534. {
  1535. name: "not set percentageOfNodesToScore and nodes number more than 50*125",
  1536. numAllNodes: 6000,
  1537. wantNumNodes: 300,
  1538. },
  1539. {
  1540. name: "set percentageOfNodesToScore and nodes number more than 50*125",
  1541. percentageOfNodesToScore: 40,
  1542. numAllNodes: 6000,
  1543. wantNumNodes: 2400,
  1544. },
  1545. }
  1546. for _, tt := range tests {
  1547. t.Run(tt.name, func(t *testing.T) {
  1548. g := &genericScheduler{
  1549. percentageOfNodesToScore: tt.percentageOfNodesToScore,
  1550. }
  1551. if gotNumNodes := g.numFeasibleNodesToFind(tt.numAllNodes); gotNumNodes != tt.wantNumNodes {
  1552. t.Errorf("genericScheduler.numFeasibleNodesToFind() = %v, want %v", gotNumNodes, tt.wantNumNodes)
  1553. }
  1554. })
  1555. }
  1556. }