extender_test.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712
  1. /*
  2. Copyright 2015 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. "context"
  16. "fmt"
  17. "reflect"
  18. "sort"
  19. "testing"
  20. "time"
  21. v1 "k8s.io/api/core/v1"
  22. "k8s.io/apimachinery/pkg/api/resource"
  23. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  24. "k8s.io/apimachinery/pkg/runtime"
  25. "k8s.io/apimachinery/pkg/util/sets"
  26. "k8s.io/apimachinery/pkg/util/wait"
  27. "k8s.io/client-go/informers"
  28. clientsetfake "k8s.io/client-go/kubernetes/fake"
  29. podutil "k8s.io/kubernetes/pkg/api/v1/pod"
  30. schedulerapi "k8s.io/kubernetes/pkg/scheduler/apis/config"
  31. extenderv1 "k8s.io/kubernetes/pkg/scheduler/apis/extender/v1"
  32. "k8s.io/kubernetes/pkg/scheduler/framework/plugins/defaultbinder"
  33. "k8s.io/kubernetes/pkg/scheduler/framework/plugins/queuesort"
  34. framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
  35. internalcache "k8s.io/kubernetes/pkg/scheduler/internal/cache"
  36. internalqueue "k8s.io/kubernetes/pkg/scheduler/internal/queue"
  37. "k8s.io/kubernetes/pkg/scheduler/listers"
  38. schedulernodeinfo "k8s.io/kubernetes/pkg/scheduler/nodeinfo"
  39. st "k8s.io/kubernetes/pkg/scheduler/testing"
  40. "k8s.io/kubernetes/pkg/scheduler/util"
  41. )
  42. type fitPredicate func(pod *v1.Pod, node *v1.Node) (bool, error)
  43. type priorityFunc func(pod *v1.Pod, nodes []*v1.Node) (*framework.NodeScoreList, error)
  44. type priorityConfig struct {
  45. function priorityFunc
  46. weight int64
  47. }
  48. func errorPredicateExtender(pod *v1.Pod, node *v1.Node) (bool, error) {
  49. return false, fmt.Errorf("Some error")
  50. }
  51. func falsePredicateExtender(pod *v1.Pod, node *v1.Node) (bool, error) {
  52. return false, nil
  53. }
  54. func truePredicateExtender(pod *v1.Pod, node *v1.Node) (bool, error) {
  55. return true, nil
  56. }
  57. func machine1PredicateExtender(pod *v1.Pod, node *v1.Node) (bool, error) {
  58. if node.Name == "machine1" {
  59. return true, nil
  60. }
  61. return false, nil
  62. }
  63. func machine2PredicateExtender(pod *v1.Pod, node *v1.Node) (bool, error) {
  64. if node.Name == "machine2" {
  65. return true, nil
  66. }
  67. return false, nil
  68. }
  69. func errorPrioritizerExtender(pod *v1.Pod, nodes []*v1.Node) (*framework.NodeScoreList, error) {
  70. return &framework.NodeScoreList{}, fmt.Errorf("Some error")
  71. }
  72. func machine1PrioritizerExtender(pod *v1.Pod, nodes []*v1.Node) (*framework.NodeScoreList, error) {
  73. result := framework.NodeScoreList{}
  74. for _, node := range nodes {
  75. score := 1
  76. if node.Name == "machine1" {
  77. score = 10
  78. }
  79. result = append(result, framework.NodeScore{Name: node.Name, Score: int64(score)})
  80. }
  81. return &result, nil
  82. }
  83. func machine2PrioritizerExtender(pod *v1.Pod, nodes []*v1.Node) (*framework.NodeScoreList, error) {
  84. result := framework.NodeScoreList{}
  85. for _, node := range nodes {
  86. score := 1
  87. if node.Name == "machine2" {
  88. score = 10
  89. }
  90. result = append(result, framework.NodeScore{Name: node.Name, Score: int64(score)})
  91. }
  92. return &result, nil
  93. }
  94. type machine2PrioritizerPlugin struct{}
  95. func newMachine2PrioritizerPlugin() framework.PluginFactory {
  96. return func(_ *runtime.Unknown, _ framework.FrameworkHandle) (framework.Plugin, error) {
  97. return &machine2PrioritizerPlugin{}, nil
  98. }
  99. }
  100. func (pl *machine2PrioritizerPlugin) Name() string {
  101. return "Machine2Prioritizer"
  102. }
  103. func (pl *machine2PrioritizerPlugin) Score(_ context.Context, _ *framework.CycleState, _ *v1.Pod, nodeName string) (int64, *framework.Status) {
  104. score := 10
  105. if nodeName == "machine2" {
  106. score = 100
  107. }
  108. return int64(score), nil
  109. }
  110. func (pl *machine2PrioritizerPlugin) ScoreExtensions() framework.ScoreExtensions {
  111. return nil
  112. }
  113. type FakeExtender struct {
  114. predicates []fitPredicate
  115. prioritizers []priorityConfig
  116. weight int64
  117. nodeCacheCapable bool
  118. filteredNodes []*v1.Node
  119. unInterested bool
  120. ignorable bool
  121. // Cached node information for fake extender
  122. cachedNodeNameToInfo map[string]*schedulernodeinfo.NodeInfo
  123. }
  124. func (f *FakeExtender) Name() string {
  125. return "FakeExtender"
  126. }
  127. func (f *FakeExtender) IsIgnorable() bool {
  128. return f.ignorable
  129. }
  130. func (f *FakeExtender) SupportsPreemption() bool {
  131. // Assume preempt verb is always defined.
  132. return true
  133. }
  134. func (f *FakeExtender) ProcessPreemption(
  135. pod *v1.Pod,
  136. nodeToVictims map[*v1.Node]*extenderv1.Victims,
  137. nodeInfos listers.NodeInfoLister,
  138. ) (map[*v1.Node]*extenderv1.Victims, error) {
  139. nodeToVictimsCopy := map[*v1.Node]*extenderv1.Victims{}
  140. // We don't want to change the original nodeToVictims
  141. for k, v := range nodeToVictims {
  142. // In real world implementation, extender's user should have their own way to get node object
  143. // by name if needed (e.g. query kube-apiserver etc).
  144. //
  145. // For test purpose, we just use node from parameters directly.
  146. nodeToVictimsCopy[k] = v
  147. }
  148. for node, victims := range nodeToVictimsCopy {
  149. // Try to do preemption on extender side.
  150. extenderVictimPods, extendernPDBViolations, fits, err := f.selectVictimsOnNodeByExtender(pod, node)
  151. if err != nil {
  152. return nil, err
  153. }
  154. // If it's unfit after extender's preemption, this node is unresolvable by preemption overall,
  155. // let's remove it from potential preemption nodes.
  156. if !fits {
  157. delete(nodeToVictimsCopy, node)
  158. } else {
  159. // Append new victims to original victims
  160. nodeToVictimsCopy[node].Pods = append(victims.Pods, extenderVictimPods...)
  161. nodeToVictimsCopy[node].NumPDBViolations = victims.NumPDBViolations + int64(extendernPDBViolations)
  162. }
  163. }
  164. return nodeToVictimsCopy, nil
  165. }
  166. // selectVictimsOnNodeByExtender checks the given nodes->pods map with predicates on extender's side.
  167. // Returns:
  168. // 1. More victim pods (if any) amended by preemption phase of extender.
  169. // 2. Number of violating victim (used to calculate PDB).
  170. // 3. Fits or not after preemption phase on extender's side.
  171. func (f *FakeExtender) selectVictimsOnNodeByExtender(pod *v1.Pod, node *v1.Node) ([]*v1.Pod, int, bool, error) {
  172. // If a extender support preemption but have no cached node info, let's run filter to make sure
  173. // default scheduler's decision still stand with given pod and node.
  174. if !f.nodeCacheCapable {
  175. fits, err := f.runPredicate(pod, node)
  176. if err != nil {
  177. return nil, 0, false, err
  178. }
  179. if !fits {
  180. return nil, 0, false, nil
  181. }
  182. return []*v1.Pod{}, 0, true, nil
  183. }
  184. // Otherwise, as a extender support preemption and have cached node info, we will assume cachedNodeNameToInfo is available
  185. // and get cached node info by given node name.
  186. nodeInfoCopy := f.cachedNodeNameToInfo[node.GetName()].Clone()
  187. var potentialVictims []*v1.Pod
  188. removePod := func(rp *v1.Pod) {
  189. nodeInfoCopy.RemovePod(rp)
  190. }
  191. addPod := func(ap *v1.Pod) {
  192. nodeInfoCopy.AddPod(ap)
  193. }
  194. // As the first step, remove all the lower priority pods from the node and
  195. // check if the given pod can be scheduled.
  196. podPriority := podutil.GetPodPriority(pod)
  197. for _, p := range nodeInfoCopy.Pods() {
  198. if podutil.GetPodPriority(p) < podPriority {
  199. potentialVictims = append(potentialVictims, p)
  200. removePod(p)
  201. }
  202. }
  203. sort.Slice(potentialVictims, func(i, j int) bool { return util.MoreImportantPod(potentialVictims[i], potentialVictims[j]) })
  204. // If the new pod does not fit after removing all the lower priority pods,
  205. // we are almost done and this node is not suitable for preemption.
  206. fits, err := f.runPredicate(pod, nodeInfoCopy.Node())
  207. if err != nil {
  208. return nil, 0, false, err
  209. }
  210. if !fits {
  211. return nil, 0, false, nil
  212. }
  213. var victims []*v1.Pod
  214. // TODO(harry): handle PDBs in the future.
  215. numViolatingVictim := 0
  216. reprievePod := func(p *v1.Pod) bool {
  217. addPod(p)
  218. fits, _ := f.runPredicate(pod, nodeInfoCopy.Node())
  219. if !fits {
  220. removePod(p)
  221. victims = append(victims, p)
  222. }
  223. return fits
  224. }
  225. // For now, assume all potential victims to be non-violating.
  226. // Now we try to reprieve non-violating victims.
  227. for _, p := range potentialVictims {
  228. reprievePod(p)
  229. }
  230. return victims, numViolatingVictim, true, nil
  231. }
  232. // runPredicate run predicates of extender one by one for given pod and node.
  233. // Returns: fits or not.
  234. func (f *FakeExtender) runPredicate(pod *v1.Pod, node *v1.Node) (bool, error) {
  235. fits := true
  236. var err error
  237. for _, predicate := range f.predicates {
  238. fits, err = predicate(pod, node)
  239. if err != nil {
  240. return false, err
  241. }
  242. if !fits {
  243. break
  244. }
  245. }
  246. return fits, nil
  247. }
  248. func (f *FakeExtender) Filter(pod *v1.Pod, nodes []*v1.Node) ([]*v1.Node, extenderv1.FailedNodesMap, error) {
  249. filtered := []*v1.Node{}
  250. failedNodesMap := extenderv1.FailedNodesMap{}
  251. for _, node := range nodes {
  252. fits, err := f.runPredicate(pod, node)
  253. if err != nil {
  254. return []*v1.Node{}, extenderv1.FailedNodesMap{}, err
  255. }
  256. if fits {
  257. filtered = append(filtered, node)
  258. } else {
  259. failedNodesMap[node.Name] = "FakeExtender failed"
  260. }
  261. }
  262. f.filteredNodes = filtered
  263. if f.nodeCacheCapable {
  264. return filtered, failedNodesMap, nil
  265. }
  266. return filtered, failedNodesMap, nil
  267. }
  268. func (f *FakeExtender) Prioritize(pod *v1.Pod, nodes []*v1.Node) (*extenderv1.HostPriorityList, int64, error) {
  269. result := extenderv1.HostPriorityList{}
  270. combinedScores := map[string]int64{}
  271. for _, prioritizer := range f.prioritizers {
  272. weight := prioritizer.weight
  273. if weight == 0 {
  274. continue
  275. }
  276. priorityFunc := prioritizer.function
  277. prioritizedList, err := priorityFunc(pod, nodes)
  278. if err != nil {
  279. return &extenderv1.HostPriorityList{}, 0, err
  280. }
  281. for _, hostEntry := range *prioritizedList {
  282. combinedScores[hostEntry.Name] += hostEntry.Score * weight
  283. }
  284. }
  285. for host, score := range combinedScores {
  286. result = append(result, extenderv1.HostPriority{Host: host, Score: score})
  287. }
  288. return &result, f.weight, nil
  289. }
  290. func (f *FakeExtender) Bind(binding *v1.Binding) error {
  291. if len(f.filteredNodes) != 0 {
  292. for _, node := range f.filteredNodes {
  293. if node.Name == binding.Target.Name {
  294. f.filteredNodes = nil
  295. return nil
  296. }
  297. }
  298. err := fmt.Errorf("Node %v not in filtered nodes %v", binding.Target.Name, f.filteredNodes)
  299. f.filteredNodes = nil
  300. return err
  301. }
  302. return nil
  303. }
  304. func (f *FakeExtender) IsBinder() bool {
  305. return true
  306. }
  307. func (f *FakeExtender) IsInterested(pod *v1.Pod) bool {
  308. return !f.unInterested
  309. }
  310. var _ SchedulerExtender = &FakeExtender{}
  311. func TestGenericSchedulerWithExtenders(t *testing.T) {
  312. tests := []struct {
  313. name string
  314. registerPlugins []st.RegisterPluginFunc
  315. extenders []FakeExtender
  316. nodes []string
  317. expectedResult ScheduleResult
  318. expectsErr bool
  319. }{
  320. {
  321. registerPlugins: []st.RegisterPluginFunc{
  322. st.RegisterFilterPlugin("TrueFilter", NewTrueFilterPlugin),
  323. st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New),
  324. st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New),
  325. },
  326. extenders: []FakeExtender{
  327. {
  328. predicates: []fitPredicate{truePredicateExtender},
  329. },
  330. {
  331. predicates: []fitPredicate{errorPredicateExtender},
  332. },
  333. },
  334. nodes: []string{"machine1", "machine2"},
  335. expectsErr: true,
  336. name: "test 1",
  337. },
  338. {
  339. registerPlugins: []st.RegisterPluginFunc{
  340. st.RegisterFilterPlugin("TrueFilter", NewTrueFilterPlugin),
  341. st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New),
  342. st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New),
  343. },
  344. extenders: []FakeExtender{
  345. {
  346. predicates: []fitPredicate{truePredicateExtender},
  347. },
  348. {
  349. predicates: []fitPredicate{falsePredicateExtender},
  350. },
  351. },
  352. nodes: []string{"machine1", "machine2"},
  353. expectsErr: true,
  354. name: "test 2",
  355. },
  356. {
  357. registerPlugins: []st.RegisterPluginFunc{
  358. st.RegisterFilterPlugin("TrueFilter", NewTrueFilterPlugin),
  359. st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New),
  360. st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New),
  361. },
  362. extenders: []FakeExtender{
  363. {
  364. predicates: []fitPredicate{truePredicateExtender},
  365. },
  366. {
  367. predicates: []fitPredicate{machine1PredicateExtender},
  368. },
  369. },
  370. nodes: []string{"machine1", "machine2"},
  371. expectedResult: ScheduleResult{
  372. SuggestedHost: "machine1",
  373. EvaluatedNodes: 2,
  374. FeasibleNodes: 1,
  375. },
  376. name: "test 3",
  377. },
  378. {
  379. registerPlugins: []st.RegisterPluginFunc{
  380. st.RegisterFilterPlugin("TrueFilter", NewTrueFilterPlugin),
  381. st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New),
  382. st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New),
  383. },
  384. extenders: []FakeExtender{
  385. {
  386. predicates: []fitPredicate{machine2PredicateExtender},
  387. },
  388. {
  389. predicates: []fitPredicate{machine1PredicateExtender},
  390. },
  391. },
  392. nodes: []string{"machine1", "machine2"},
  393. expectsErr: true,
  394. name: "test 4",
  395. },
  396. {
  397. registerPlugins: []st.RegisterPluginFunc{
  398. st.RegisterFilterPlugin("TrueFilter", NewTrueFilterPlugin),
  399. st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New),
  400. st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New),
  401. },
  402. extenders: []FakeExtender{
  403. {
  404. predicates: []fitPredicate{truePredicateExtender},
  405. prioritizers: []priorityConfig{{errorPrioritizerExtender, 10}},
  406. weight: 1,
  407. },
  408. },
  409. nodes: []string{"machine1"},
  410. expectedResult: ScheduleResult{
  411. SuggestedHost: "machine1",
  412. EvaluatedNodes: 1,
  413. FeasibleNodes: 1,
  414. },
  415. name: "test 5",
  416. },
  417. {
  418. registerPlugins: []st.RegisterPluginFunc{
  419. st.RegisterFilterPlugin("TrueFilter", NewTrueFilterPlugin),
  420. st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New),
  421. st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New),
  422. },
  423. extenders: []FakeExtender{
  424. {
  425. predicates: []fitPredicate{truePredicateExtender},
  426. prioritizers: []priorityConfig{{machine1PrioritizerExtender, 10}},
  427. weight: 1,
  428. },
  429. {
  430. predicates: []fitPredicate{truePredicateExtender},
  431. prioritizers: []priorityConfig{{machine2PrioritizerExtender, 10}},
  432. weight: 5,
  433. },
  434. },
  435. nodes: []string{"machine1", "machine2"},
  436. expectedResult: ScheduleResult{
  437. SuggestedHost: "machine2",
  438. EvaluatedNodes: 2,
  439. FeasibleNodes: 2,
  440. },
  441. name: "test 6",
  442. },
  443. {
  444. registerPlugins: []st.RegisterPluginFunc{
  445. st.RegisterFilterPlugin("TrueFilter", NewTrueFilterPlugin),
  446. st.RegisterScorePlugin("Machine2Prioritizer", newMachine2PrioritizerPlugin(), 20),
  447. st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New),
  448. st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New),
  449. },
  450. extenders: []FakeExtender{
  451. {
  452. predicates: []fitPredicate{truePredicateExtender},
  453. prioritizers: []priorityConfig{{machine1PrioritizerExtender, 10}},
  454. weight: 1,
  455. },
  456. },
  457. nodes: []string{"machine1", "machine2"},
  458. expectedResult: ScheduleResult{
  459. SuggestedHost: "machine2",
  460. EvaluatedNodes: 2,
  461. FeasibleNodes: 2,
  462. }, // machine2 has higher score
  463. name: "test 7",
  464. },
  465. {
  466. // Scheduler is expected to not send pod to extender in
  467. // Filter/Prioritize phases if the extender is not interested in
  468. // the pod.
  469. //
  470. // If scheduler sends the pod by mistake, the test would fail
  471. // because of the errors from errorPredicateExtender and/or
  472. // errorPrioritizerExtender.
  473. registerPlugins: []st.RegisterPluginFunc{
  474. st.RegisterFilterPlugin("TrueFilter", NewTrueFilterPlugin),
  475. st.RegisterScorePlugin("Machine2Prioritizer", newMachine2PrioritizerPlugin(), 1),
  476. st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New),
  477. st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New),
  478. },
  479. extenders: []FakeExtender{
  480. {
  481. predicates: []fitPredicate{errorPredicateExtender},
  482. prioritizers: []priorityConfig{{errorPrioritizerExtender, 10}},
  483. unInterested: true,
  484. },
  485. },
  486. nodes: []string{"machine1", "machine2"},
  487. expectsErr: false,
  488. expectedResult: ScheduleResult{
  489. SuggestedHost: "machine2",
  490. EvaluatedNodes: 2,
  491. FeasibleNodes: 2,
  492. }, // machine2 has higher score
  493. name: "test 8",
  494. },
  495. {
  496. // Scheduling is expected to not fail in
  497. // Filter/Prioritize phases if the extender is not available and ignorable.
  498. //
  499. // If scheduler did not ignore the extender, the test would fail
  500. // because of the errors from errorPredicateExtender.
  501. registerPlugins: []st.RegisterPluginFunc{
  502. st.RegisterFilterPlugin("TrueFilter", NewTrueFilterPlugin),
  503. st.RegisterQueueSortPlugin(queuesort.Name, queuesort.New),
  504. st.RegisterBindPlugin(defaultbinder.Name, defaultbinder.New),
  505. },
  506. extenders: []FakeExtender{
  507. {
  508. predicates: []fitPredicate{errorPredicateExtender},
  509. ignorable: true,
  510. },
  511. {
  512. predicates: []fitPredicate{machine1PredicateExtender},
  513. },
  514. },
  515. nodes: []string{"machine1", "machine2"},
  516. expectsErr: false,
  517. expectedResult: ScheduleResult{
  518. SuggestedHost: "machine1",
  519. EvaluatedNodes: 2,
  520. FeasibleNodes: 1,
  521. },
  522. name: "test 9",
  523. },
  524. }
  525. for _, test := range tests {
  526. t.Run(test.name, func(t *testing.T) {
  527. client := clientsetfake.NewSimpleClientset()
  528. informerFactory := informers.NewSharedInformerFactory(client, 0)
  529. extenders := []SchedulerExtender{}
  530. for ii := range test.extenders {
  531. extenders = append(extenders, &test.extenders[ii])
  532. }
  533. cache := internalcache.New(time.Duration(0), wait.NeverStop)
  534. for _, name := range test.nodes {
  535. cache.AddNode(createNode(name))
  536. }
  537. queue := internalqueue.NewSchedulingQueue(nil)
  538. fwk, err := st.NewFramework(test.registerPlugins, framework.WithClientSet(client))
  539. if err != nil {
  540. t.Fatal(err)
  541. }
  542. scheduler := NewGenericScheduler(
  543. cache,
  544. queue,
  545. emptySnapshot,
  546. fwk,
  547. extenders,
  548. nil,
  549. informerFactory.Core().V1().PersistentVolumeClaims().Lister(),
  550. informerFactory.Policy().V1beta1().PodDisruptionBudgets().Lister(),
  551. false,
  552. schedulerapi.DefaultPercentageOfNodesToScore,
  553. false)
  554. podIgnored := &v1.Pod{}
  555. result, err := scheduler.Schedule(context.Background(), framework.NewCycleState(), podIgnored)
  556. if test.expectsErr {
  557. if err == nil {
  558. t.Errorf("Unexpected non-error, result %+v", result)
  559. }
  560. } else {
  561. if err != nil {
  562. t.Errorf("Unexpected error: %v", err)
  563. return
  564. }
  565. if !reflect.DeepEqual(result, test.expectedResult) {
  566. t.Errorf("Expected: %+v, Saw: %+v", test.expectedResult, result)
  567. }
  568. }
  569. })
  570. }
  571. }
  572. func createNode(name string) *v1.Node {
  573. return &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: name}}
  574. }
  575. func TestIsInterested(t *testing.T) {
  576. mem := &HTTPExtender{
  577. managedResources: sets.NewString(),
  578. }
  579. mem.managedResources.Insert("memory")
  580. for _, tc := range []struct {
  581. label string
  582. extender *HTTPExtender
  583. pod *v1.Pod
  584. want bool
  585. }{
  586. {
  587. label: "Empty managed resources",
  588. extender: &HTTPExtender{
  589. managedResources: sets.NewString(),
  590. },
  591. pod: &v1.Pod{},
  592. want: true,
  593. },
  594. {
  595. label: "Managed memory, empty resources",
  596. extender: mem,
  597. pod: &v1.Pod{
  598. Spec: v1.PodSpec{
  599. Containers: []v1.Container{
  600. {
  601. Name: "app",
  602. },
  603. },
  604. },
  605. },
  606. want: false,
  607. },
  608. {
  609. label: "Managed memory, container memory",
  610. extender: mem,
  611. pod: &v1.Pod{
  612. Spec: v1.PodSpec{
  613. Containers: []v1.Container{
  614. {
  615. Name: "app",
  616. Resources: v1.ResourceRequirements{
  617. Requests: v1.ResourceList{"memory": resource.Quantity{}},
  618. Limits: v1.ResourceList{"memory": resource.Quantity{}},
  619. },
  620. },
  621. },
  622. },
  623. },
  624. want: true,
  625. },
  626. {
  627. label: "Managed memory, init container memory",
  628. extender: mem,
  629. pod: &v1.Pod{
  630. Spec: v1.PodSpec{
  631. Containers: []v1.Container{
  632. {
  633. Name: "app",
  634. },
  635. },
  636. InitContainers: []v1.Container{
  637. {
  638. Name: "init",
  639. Resources: v1.ResourceRequirements{
  640. Requests: v1.ResourceList{"memory": resource.Quantity{}},
  641. Limits: v1.ResourceList{"memory": resource.Quantity{}},
  642. },
  643. },
  644. },
  645. },
  646. },
  647. want: true,
  648. },
  649. } {
  650. t.Run(tc.label, func(t *testing.T) {
  651. if got := tc.extender.IsInterested(tc.pod); got != tc.want {
  652. t.Fatalf("IsInterested(%v) = %v, wanted %v", tc.pod, got, tc.want)
  653. }
  654. })
  655. }
  656. }