generic_scheduler_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  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 scheduler
  14. import (
  15. "fmt"
  16. "math"
  17. "reflect"
  18. "strconv"
  19. "testing"
  20. "time"
  21. "k8s.io/kubernetes/pkg/api"
  22. "k8s.io/kubernetes/pkg/util/sets"
  23. "k8s.io/kubernetes/pkg/util/wait"
  24. "k8s.io/kubernetes/plugin/pkg/scheduler/algorithm"
  25. algorithmpredicates "k8s.io/kubernetes/plugin/pkg/scheduler/algorithm/predicates"
  26. schedulerapi "k8s.io/kubernetes/plugin/pkg/scheduler/api"
  27. "k8s.io/kubernetes/plugin/pkg/scheduler/schedulercache"
  28. )
  29. func falsePredicate(pod *api.Pod, meta interface{}, nodeInfo *schedulercache.NodeInfo) (bool, []algorithm.PredicateFailureReason, error) {
  30. return false, []algorithm.PredicateFailureReason{algorithmpredicates.ErrFakePredicate}, nil
  31. }
  32. func truePredicate(pod *api.Pod, meta interface{}, nodeInfo *schedulercache.NodeInfo) (bool, []algorithm.PredicateFailureReason, error) {
  33. return true, nil, nil
  34. }
  35. func matchesPredicate(pod *api.Pod, meta interface{}, nodeInfo *schedulercache.NodeInfo) (bool, []algorithm.PredicateFailureReason, error) {
  36. node := nodeInfo.Node()
  37. if node == nil {
  38. return false, nil, fmt.Errorf("node not found")
  39. }
  40. if pod.Name == node.Name {
  41. return true, nil, nil
  42. }
  43. return false, []algorithm.PredicateFailureReason{algorithmpredicates.ErrFakePredicate}, nil
  44. }
  45. func hasNoPodsPredicate(pod *api.Pod, meta interface{}, nodeInfo *schedulercache.NodeInfo) (bool, []algorithm.PredicateFailureReason, error) {
  46. if len(nodeInfo.Pods()) == 0 {
  47. return true, nil, nil
  48. }
  49. return false, []algorithm.PredicateFailureReason{algorithmpredicates.ErrFakePredicate}, nil
  50. }
  51. func numericPriority(pod *api.Pod, nodeNameToInfo map[string]*schedulercache.NodeInfo, nodes []*api.Node) (schedulerapi.HostPriorityList, error) {
  52. result := []schedulerapi.HostPriority{}
  53. for _, node := range nodes {
  54. score, err := strconv.Atoi(node.Name)
  55. if err != nil {
  56. return nil, err
  57. }
  58. result = append(result, schedulerapi.HostPriority{
  59. Host: node.Name,
  60. Score: score,
  61. })
  62. }
  63. return result, nil
  64. }
  65. func reverseNumericPriority(pod *api.Pod, nodeNameToInfo map[string]*schedulercache.NodeInfo, nodes []*api.Node) (schedulerapi.HostPriorityList, error) {
  66. var maxScore float64
  67. minScore := math.MaxFloat64
  68. reverseResult := []schedulerapi.HostPriority{}
  69. result, err := numericPriority(pod, nodeNameToInfo, nodes)
  70. if err != nil {
  71. return nil, err
  72. }
  73. for _, hostPriority := range result {
  74. maxScore = math.Max(maxScore, float64(hostPriority.Score))
  75. minScore = math.Min(minScore, float64(hostPriority.Score))
  76. }
  77. for _, hostPriority := range result {
  78. reverseResult = append(reverseResult, schedulerapi.HostPriority{
  79. Host: hostPriority.Host,
  80. Score: int(maxScore + minScore - float64(hostPriority.Score)),
  81. })
  82. }
  83. return reverseResult, nil
  84. }
  85. func makeNodeList(nodeNames []string) []*api.Node {
  86. result := make([]*api.Node, 0, len(nodeNames))
  87. for _, nodeName := range nodeNames {
  88. result = append(result, &api.Node{ObjectMeta: api.ObjectMeta{Name: nodeName}})
  89. }
  90. return result
  91. }
  92. func TestSelectHost(t *testing.T) {
  93. scheduler := genericScheduler{}
  94. tests := []struct {
  95. list schedulerapi.HostPriorityList
  96. possibleHosts sets.String
  97. expectsErr bool
  98. }{
  99. {
  100. list: []schedulerapi.HostPriority{
  101. {Host: "machine1.1", Score: 1},
  102. {Host: "machine2.1", Score: 2},
  103. },
  104. possibleHosts: sets.NewString("machine2.1"),
  105. expectsErr: false,
  106. },
  107. // equal scores
  108. {
  109. list: []schedulerapi.HostPriority{
  110. {Host: "machine1.1", Score: 1},
  111. {Host: "machine1.2", Score: 2},
  112. {Host: "machine1.3", Score: 2},
  113. {Host: "machine2.1", Score: 2},
  114. },
  115. possibleHosts: sets.NewString("machine1.2", "machine1.3", "machine2.1"),
  116. expectsErr: false,
  117. },
  118. // out of order scores
  119. {
  120. list: []schedulerapi.HostPriority{
  121. {Host: "machine1.1", Score: 3},
  122. {Host: "machine1.2", Score: 3},
  123. {Host: "machine2.1", Score: 2},
  124. {Host: "machine3.1", Score: 1},
  125. {Host: "machine1.3", Score: 3},
  126. },
  127. possibleHosts: sets.NewString("machine1.1", "machine1.2", "machine1.3"),
  128. expectsErr: false,
  129. },
  130. // empty priorityList
  131. {
  132. list: []schedulerapi.HostPriority{},
  133. possibleHosts: sets.NewString(),
  134. expectsErr: true,
  135. },
  136. }
  137. for _, test := range tests {
  138. // increase the randomness
  139. for i := 0; i < 10; i++ {
  140. got, err := scheduler.selectHost(test.list)
  141. if test.expectsErr {
  142. if err == nil {
  143. t.Error("Unexpected non-error")
  144. }
  145. } else {
  146. if err != nil {
  147. t.Errorf("Unexpected error: %v", err)
  148. }
  149. if !test.possibleHosts.Has(got) {
  150. t.Errorf("got %s is not in the possible map %v", got, test.possibleHosts)
  151. }
  152. }
  153. }
  154. }
  155. }
  156. func TestGenericScheduler(t *testing.T) {
  157. tests := []struct {
  158. name string
  159. predicates map[string]algorithm.FitPredicate
  160. prioritizers []algorithm.PriorityConfig
  161. nodes []string
  162. pod *api.Pod
  163. pods []*api.Pod
  164. expectedHosts sets.String
  165. expectsErr bool
  166. wErr error
  167. }{
  168. {
  169. predicates: map[string]algorithm.FitPredicate{"false": falsePredicate},
  170. prioritizers: []algorithm.PriorityConfig{{Function: EqualPriority, Weight: 1}},
  171. nodes: []string{"machine1", "machine2"},
  172. expectsErr: true,
  173. pod: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "2"}},
  174. name: "test 1",
  175. wErr: &FitError{
  176. Pod: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "2"}},
  177. FailedPredicates: FailedPredicateMap{
  178. "machine1": []algorithm.PredicateFailureReason{algorithmpredicates.ErrFakePredicate},
  179. "machine2": []algorithm.PredicateFailureReason{algorithmpredicates.ErrFakePredicate},
  180. }},
  181. },
  182. {
  183. predicates: map[string]algorithm.FitPredicate{"true": truePredicate},
  184. prioritizers: []algorithm.PriorityConfig{{Function: EqualPriority, Weight: 1}},
  185. nodes: []string{"machine1", "machine2"},
  186. expectedHosts: sets.NewString("machine1", "machine2"),
  187. name: "test 2",
  188. wErr: nil,
  189. },
  190. {
  191. // Fits on a machine where the pod ID matches the machine name
  192. predicates: map[string]algorithm.FitPredicate{"matches": matchesPredicate},
  193. prioritizers: []algorithm.PriorityConfig{{Function: EqualPriority, Weight: 1}},
  194. nodes: []string{"machine1", "machine2"},
  195. pod: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "machine2"}},
  196. expectedHosts: sets.NewString("machine2"),
  197. name: "test 3",
  198. wErr: nil,
  199. },
  200. {
  201. predicates: map[string]algorithm.FitPredicate{"true": truePredicate},
  202. prioritizers: []algorithm.PriorityConfig{{Function: numericPriority, Weight: 1}},
  203. nodes: []string{"3", "2", "1"},
  204. expectedHosts: sets.NewString("3"),
  205. name: "test 4",
  206. wErr: nil,
  207. },
  208. {
  209. predicates: map[string]algorithm.FitPredicate{"matches": matchesPredicate},
  210. prioritizers: []algorithm.PriorityConfig{{Function: numericPriority, Weight: 1}},
  211. nodes: []string{"3", "2", "1"},
  212. pod: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "2"}},
  213. expectedHosts: sets.NewString("2"),
  214. name: "test 5",
  215. wErr: nil,
  216. },
  217. {
  218. predicates: map[string]algorithm.FitPredicate{"true": truePredicate},
  219. prioritizers: []algorithm.PriorityConfig{{Function: numericPriority, Weight: 1}, {Function: reverseNumericPriority, Weight: 2}},
  220. nodes: []string{"3", "2", "1"},
  221. pod: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "2"}},
  222. expectedHosts: sets.NewString("1"),
  223. name: "test 6",
  224. wErr: nil,
  225. },
  226. {
  227. predicates: map[string]algorithm.FitPredicate{"true": truePredicate, "false": falsePredicate},
  228. prioritizers: []algorithm.PriorityConfig{{Function: numericPriority, Weight: 1}},
  229. nodes: []string{"3", "2", "1"},
  230. pod: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "2"}},
  231. expectsErr: true,
  232. name: "test 7",
  233. wErr: &FitError{
  234. Pod: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "2"}},
  235. FailedPredicates: FailedPredicateMap{
  236. "3": []algorithm.PredicateFailureReason{algorithmpredicates.ErrFakePredicate},
  237. "2": []algorithm.PredicateFailureReason{algorithmpredicates.ErrFakePredicate},
  238. "1": []algorithm.PredicateFailureReason{algorithmpredicates.ErrFakePredicate},
  239. },
  240. },
  241. },
  242. {
  243. predicates: map[string]algorithm.FitPredicate{
  244. "nopods": hasNoPodsPredicate,
  245. "matches": matchesPredicate,
  246. },
  247. pods: []*api.Pod{
  248. {
  249. ObjectMeta: api.ObjectMeta{Name: "2"},
  250. Spec: api.PodSpec{
  251. NodeName: "2",
  252. },
  253. Status: api.PodStatus{
  254. Phase: api.PodRunning,
  255. },
  256. },
  257. },
  258. pod: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "2"}},
  259. prioritizers: []algorithm.PriorityConfig{{Function: numericPriority, Weight: 1}},
  260. nodes: []string{"1", "2"},
  261. expectsErr: true,
  262. name: "test 8",
  263. wErr: &FitError{
  264. Pod: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "2"}},
  265. FailedPredicates: FailedPredicateMap{
  266. "1": []algorithm.PredicateFailureReason{algorithmpredicates.ErrFakePredicate},
  267. "2": []algorithm.PredicateFailureReason{algorithmpredicates.ErrFakePredicate},
  268. },
  269. },
  270. },
  271. }
  272. for _, test := range tests {
  273. cache := schedulercache.New(time.Duration(0), wait.NeverStop)
  274. for _, pod := range test.pods {
  275. cache.AddPod(pod)
  276. }
  277. for _, name := range test.nodes {
  278. cache.AddNode(&api.Node{ObjectMeta: api.ObjectMeta{Name: name}})
  279. }
  280. scheduler := NewGenericScheduler(cache, test.predicates, test.prioritizers, []algorithm.SchedulerExtender{})
  281. machine, err := scheduler.Schedule(test.pod, algorithm.FakeNodeLister(makeNodeList(test.nodes)))
  282. if !reflect.DeepEqual(err, test.wErr) {
  283. t.Errorf("Failed : %s, Unexpected error: %v, expected: %v", test.name, err, test.wErr)
  284. }
  285. if test.expectedHosts != nil && !test.expectedHosts.Has(machine) {
  286. t.Errorf("Failed : %s, Expected: %s, got: %s", test.name, test.expectedHosts, machine)
  287. }
  288. }
  289. }
  290. func TestFindFitAllError(t *testing.T) {
  291. nodes := []string{"3", "2", "1"}
  292. predicates := map[string]algorithm.FitPredicate{"true": truePredicate, "false": falsePredicate}
  293. nodeNameToInfo := map[string]*schedulercache.NodeInfo{
  294. "3": schedulercache.NewNodeInfo(),
  295. "2": schedulercache.NewNodeInfo(),
  296. "1": schedulercache.NewNodeInfo(),
  297. }
  298. _, predicateMap, err := findNodesThatFit(&api.Pod{}, nodeNameToInfo, makeNodeList(nodes), predicates, nil)
  299. if err != nil {
  300. t.Errorf("unexpected error: %v", err)
  301. }
  302. if len(predicateMap) != len(nodes) {
  303. t.Errorf("unexpected failed predicate map: %v", predicateMap)
  304. }
  305. for _, node := range nodes {
  306. failures, found := predicateMap[node]
  307. if !found {
  308. t.Errorf("failed to find node: %s in %v", node, predicateMap)
  309. }
  310. if len(failures) != 1 || failures[0] != algorithmpredicates.ErrFakePredicate {
  311. t.Errorf("unexpected failures: %v", failures)
  312. }
  313. }
  314. }
  315. func TestFindFitSomeError(t *testing.T) {
  316. nodes := []string{"3", "2", "1"}
  317. predicates := map[string]algorithm.FitPredicate{"true": truePredicate, "match": matchesPredicate}
  318. pod := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "1"}}
  319. nodeNameToInfo := map[string]*schedulercache.NodeInfo{
  320. "3": schedulercache.NewNodeInfo(),
  321. "2": schedulercache.NewNodeInfo(),
  322. "1": schedulercache.NewNodeInfo(pod),
  323. }
  324. for name := range nodeNameToInfo {
  325. nodeNameToInfo[name].SetNode(&api.Node{ObjectMeta: api.ObjectMeta{Name: name}})
  326. }
  327. _, predicateMap, err := findNodesThatFit(pod, nodeNameToInfo, makeNodeList(nodes), predicates, nil)
  328. if err != nil {
  329. t.Errorf("unexpected error: %v", err)
  330. }
  331. if len(predicateMap) != (len(nodes) - 1) {
  332. t.Errorf("unexpected failed predicate map: %v", predicateMap)
  333. }
  334. for _, node := range nodes {
  335. if node == pod.Name {
  336. continue
  337. }
  338. failures, found := predicateMap[node]
  339. if !found {
  340. t.Errorf("failed to find node: %s in %v", node, predicateMap)
  341. }
  342. if len(failures) != 1 || failures[0] != algorithmpredicates.ErrFakePredicate {
  343. t.Errorf("unexpected failures: %v", failures)
  344. }
  345. }
  346. }