factory_test.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619
  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 factory
  14. import (
  15. "errors"
  16. "fmt"
  17. "reflect"
  18. "testing"
  19. "time"
  20. "k8s.io/api/core/v1"
  21. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  22. "k8s.io/apimachinery/pkg/runtime"
  23. "k8s.io/apimachinery/pkg/util/clock"
  24. "k8s.io/apimachinery/pkg/util/sets"
  25. "k8s.io/client-go/informers"
  26. clientset "k8s.io/client-go/kubernetes"
  27. "k8s.io/client-go/kubernetes/fake"
  28. fakeV1 "k8s.io/client-go/kubernetes/typed/core/v1/fake"
  29. clienttesting "k8s.io/client-go/testing"
  30. "k8s.io/client-go/tools/cache"
  31. apitesting "k8s.io/kubernetes/pkg/api/testing"
  32. "k8s.io/kubernetes/pkg/scheduler/algorithm"
  33. "k8s.io/kubernetes/pkg/scheduler/algorithm/predicates"
  34. schedulerapi "k8s.io/kubernetes/pkg/scheduler/api"
  35. latestschedulerapi "k8s.io/kubernetes/pkg/scheduler/api/latest"
  36. "k8s.io/kubernetes/pkg/scheduler/apis/config"
  37. framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
  38. internalcache "k8s.io/kubernetes/pkg/scheduler/internal/cache"
  39. internalqueue "k8s.io/kubernetes/pkg/scheduler/internal/queue"
  40. schedulernodeinfo "k8s.io/kubernetes/pkg/scheduler/nodeinfo"
  41. )
  42. const (
  43. disablePodPreemption = false
  44. bindTimeoutSeconds = 600
  45. )
  46. func TestCreate(t *testing.T) {
  47. client := fake.NewSimpleClientset()
  48. stopCh := make(chan struct{})
  49. defer close(stopCh)
  50. factory := newConfigFactory(client, v1.DefaultHardPodAffinitySymmetricWeight, stopCh)
  51. factory.Create()
  52. }
  53. // Test configures a scheduler from a policies defined in a file
  54. // It combines some configurable predicate/priorities with some pre-defined ones
  55. func TestCreateFromConfig(t *testing.T) {
  56. var configData []byte
  57. var policy schedulerapi.Policy
  58. client := fake.NewSimpleClientset()
  59. stopCh := make(chan struct{})
  60. defer close(stopCh)
  61. factory := newConfigFactory(client, v1.DefaultHardPodAffinitySymmetricWeight, stopCh)
  62. // Pre-register some predicate and priority functions
  63. RegisterFitPredicate("PredicateOne", PredicateOne)
  64. RegisterFitPredicate("PredicateTwo", PredicateTwo)
  65. RegisterPriorityFunction("PriorityOne", PriorityOne, 1)
  66. RegisterPriorityFunction("PriorityTwo", PriorityTwo, 1)
  67. configData = []byte(`{
  68. "kind" : "Policy",
  69. "apiVersion" : "v1",
  70. "predicates" : [
  71. {"name" : "TestZoneAffinity", "argument" : {"serviceAffinity" : {"labels" : ["zone"]}}},
  72. {"name" : "TestRequireZone", "argument" : {"labelsPresence" : {"labels" : ["zone"], "presence" : true}}},
  73. {"name" : "PredicateOne"},
  74. {"name" : "PredicateTwo"}
  75. ],
  76. "priorities" : [
  77. {"name" : "RackSpread", "weight" : 3, "argument" : {"serviceAntiAffinity" : {"label" : "rack"}}},
  78. {"name" : "PriorityOne", "weight" : 2},
  79. {"name" : "PriorityTwo", "weight" : 1} ]
  80. }`)
  81. if err := runtime.DecodeInto(latestschedulerapi.Codec, configData, &policy); err != nil {
  82. t.Errorf("Invalid configuration: %v", err)
  83. }
  84. factory.CreateFromConfig(policy)
  85. hpa := factory.GetHardPodAffinitySymmetricWeight()
  86. if hpa != v1.DefaultHardPodAffinitySymmetricWeight {
  87. t.Errorf("Wrong hardPodAffinitySymmetricWeight, ecpected: %d, got: %d", v1.DefaultHardPodAffinitySymmetricWeight, hpa)
  88. }
  89. }
  90. func TestCreateFromConfigWithHardPodAffinitySymmetricWeight(t *testing.T) {
  91. var configData []byte
  92. var policy schedulerapi.Policy
  93. client := fake.NewSimpleClientset()
  94. stopCh := make(chan struct{})
  95. defer close(stopCh)
  96. factory := newConfigFactory(client, v1.DefaultHardPodAffinitySymmetricWeight, stopCh)
  97. // Pre-register some predicate and priority functions
  98. RegisterFitPredicate("PredicateOne", PredicateOne)
  99. RegisterFitPredicate("PredicateTwo", PredicateTwo)
  100. RegisterPriorityFunction("PriorityOne", PriorityOne, 1)
  101. RegisterPriorityFunction("PriorityTwo", PriorityTwo, 1)
  102. configData = []byte(`{
  103. "kind" : "Policy",
  104. "apiVersion" : "v1",
  105. "predicates" : [
  106. {"name" : "TestZoneAffinity", "argument" : {"serviceAffinity" : {"labels" : ["zone"]}}},
  107. {"name" : "TestRequireZone", "argument" : {"labelsPresence" : {"labels" : ["zone"], "presence" : true}}},
  108. {"name" : "PredicateOne"},
  109. {"name" : "PredicateTwo"}
  110. ],
  111. "priorities" : [
  112. {"name" : "RackSpread", "weight" : 3, "argument" : {"serviceAntiAffinity" : {"label" : "rack"}}},
  113. {"name" : "PriorityOne", "weight" : 2},
  114. {"name" : "PriorityTwo", "weight" : 1}
  115. ],
  116. "hardPodAffinitySymmetricWeight" : 10
  117. }`)
  118. if err := runtime.DecodeInto(latestschedulerapi.Codec, configData, &policy); err != nil {
  119. t.Errorf("Invalid configuration: %v", err)
  120. }
  121. factory.CreateFromConfig(policy)
  122. hpa := factory.GetHardPodAffinitySymmetricWeight()
  123. if hpa != 10 {
  124. t.Errorf("Wrong hardPodAffinitySymmetricWeight, ecpected: %d, got: %d", 10, hpa)
  125. }
  126. }
  127. func TestCreateFromEmptyConfig(t *testing.T) {
  128. var configData []byte
  129. var policy schedulerapi.Policy
  130. client := fake.NewSimpleClientset()
  131. stopCh := make(chan struct{})
  132. defer close(stopCh)
  133. factory := newConfigFactory(client, v1.DefaultHardPodAffinitySymmetricWeight, stopCh)
  134. configData = []byte(`{}`)
  135. if err := runtime.DecodeInto(latestschedulerapi.Codec, configData, &policy); err != nil {
  136. t.Errorf("Invalid configuration: %v", err)
  137. }
  138. factory.CreateFromConfig(policy)
  139. }
  140. // Test configures a scheduler from a policy that does not specify any
  141. // predicate/priority.
  142. // The predicate/priority from DefaultProvider will be used.
  143. func TestCreateFromConfigWithUnspecifiedPredicatesOrPriorities(t *testing.T) {
  144. client := fake.NewSimpleClientset()
  145. stopCh := make(chan struct{})
  146. defer close(stopCh)
  147. factory := newConfigFactory(client, v1.DefaultHardPodAffinitySymmetricWeight, stopCh)
  148. RegisterFitPredicate("PredicateOne", PredicateOne)
  149. RegisterPriorityFunction("PriorityOne", PriorityOne, 1)
  150. RegisterAlgorithmProvider(DefaultProvider, sets.NewString("PredicateOne"), sets.NewString("PriorityOne"))
  151. configData := []byte(`{
  152. "kind" : "Policy",
  153. "apiVersion" : "v1"
  154. }`)
  155. var policy schedulerapi.Policy
  156. if err := runtime.DecodeInto(latestschedulerapi.Codec, configData, &policy); err != nil {
  157. t.Fatalf("Invalid configuration: %v", err)
  158. }
  159. config, err := factory.CreateFromConfig(policy)
  160. if err != nil {
  161. t.Fatalf("Failed to create scheduler from configuration: %v", err)
  162. }
  163. if _, found := config.Algorithm.Predicates()["PredicateOne"]; !found {
  164. t.Errorf("Expected predicate PredicateOne from %q", DefaultProvider)
  165. }
  166. if len(config.Algorithm.Prioritizers()) != 1 || config.Algorithm.Prioritizers()[0].Name != "PriorityOne" {
  167. t.Errorf("Expected priority PriorityOne from %q", DefaultProvider)
  168. }
  169. }
  170. // Test configures a scheduler from a policy that contains empty
  171. // predicate/priority.
  172. // Empty predicate/priority sets will be used.
  173. func TestCreateFromConfigWithEmptyPredicatesOrPriorities(t *testing.T) {
  174. client := fake.NewSimpleClientset()
  175. stopCh := make(chan struct{})
  176. defer close(stopCh)
  177. factory := newConfigFactory(client, v1.DefaultHardPodAffinitySymmetricWeight, stopCh)
  178. RegisterFitPredicate("PredicateOne", PredicateOne)
  179. RegisterPriorityFunction("PriorityOne", PriorityOne, 1)
  180. RegisterAlgorithmProvider(DefaultProvider, sets.NewString("PredicateOne"), sets.NewString("PriorityOne"))
  181. configData := []byte(`{
  182. "kind" : "Policy",
  183. "apiVersion" : "v1",
  184. "predicates" : [],
  185. "priorities" : []
  186. }`)
  187. var policy schedulerapi.Policy
  188. if err := runtime.DecodeInto(latestschedulerapi.Codec, configData, &policy); err != nil {
  189. t.Fatalf("Invalid configuration: %v", err)
  190. }
  191. config, err := factory.CreateFromConfig(policy)
  192. if err != nil {
  193. t.Fatalf("Failed to create scheduler from configuration: %v", err)
  194. }
  195. if len(config.Algorithm.Predicates()) != 0 {
  196. t.Error("Expected empty predicate sets")
  197. }
  198. if len(config.Algorithm.Prioritizers()) != 0 {
  199. t.Error("Expected empty priority sets")
  200. }
  201. }
  202. func PredicateOne(pod *v1.Pod, meta predicates.PredicateMetadata, nodeInfo *schedulernodeinfo.NodeInfo) (bool, []predicates.PredicateFailureReason, error) {
  203. return true, nil, nil
  204. }
  205. func PredicateTwo(pod *v1.Pod, meta predicates.PredicateMetadata, nodeInfo *schedulernodeinfo.NodeInfo) (bool, []predicates.PredicateFailureReason, error) {
  206. return true, nil, nil
  207. }
  208. func PriorityOne(pod *v1.Pod, nodeNameToInfo map[string]*schedulernodeinfo.NodeInfo, nodes []*v1.Node) (schedulerapi.HostPriorityList, error) {
  209. return []schedulerapi.HostPriority{}, nil
  210. }
  211. func PriorityTwo(pod *v1.Pod, nodeNameToInfo map[string]*schedulernodeinfo.NodeInfo, nodes []*v1.Node) (schedulerapi.HostPriorityList, error) {
  212. return []schedulerapi.HostPriority{}, nil
  213. }
  214. func TestDefaultErrorFunc(t *testing.T) {
  215. testPod := &v1.Pod{
  216. ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"},
  217. Spec: apitesting.V1DeepEqualSafePodSpec(),
  218. }
  219. client := fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testPod}})
  220. stopCh := make(chan struct{})
  221. defer close(stopCh)
  222. timestamp := time.Now()
  223. queue := internalqueue.NewPriorityQueueWithClock(nil, clock.NewFakeClock(timestamp), nil)
  224. schedulerCache := internalcache.New(30*time.Second, stopCh)
  225. errFunc := MakeDefaultErrorFunc(client, queue, schedulerCache, stopCh)
  226. // Trigger error handling again to put the pod in unschedulable queue
  227. errFunc(testPod, nil)
  228. // Try up to a minute to retrieve the error pod from priority queue
  229. foundPodFlag := false
  230. maxIterations := 10 * 60
  231. for i := 0; i < maxIterations; i++ {
  232. time.Sleep(100 * time.Millisecond)
  233. got := getPodfromPriorityQueue(queue, testPod)
  234. if got == nil {
  235. continue
  236. }
  237. testClientGetPodRequest(client, t, testPod.Namespace, testPod.Name)
  238. if e, a := testPod, got; !reflect.DeepEqual(e, a) {
  239. t.Errorf("Expected %v, got %v", e, a)
  240. }
  241. foundPodFlag = true
  242. break
  243. }
  244. if !foundPodFlag {
  245. t.Errorf("Failed to get pod from the unschedulable queue after waiting for a minute: %v", testPod)
  246. }
  247. // Remove the pod from priority queue to test putting error
  248. // pod in backoff queue.
  249. queue.Delete(testPod)
  250. // Trigger a move request
  251. queue.MoveAllToActiveQueue()
  252. // Trigger error handling again to put the pod in backoff queue
  253. errFunc(testPod, nil)
  254. foundPodFlag = false
  255. for i := 0; i < maxIterations; i++ {
  256. time.Sleep(100 * time.Millisecond)
  257. // The pod should be found from backoff queue at this time
  258. got := getPodfromPriorityQueue(queue, testPod)
  259. if got == nil {
  260. continue
  261. }
  262. testClientGetPodRequest(client, t, testPod.Namespace, testPod.Name)
  263. if e, a := testPod, got; !reflect.DeepEqual(e, a) {
  264. t.Errorf("Expected %v, got %v", e, a)
  265. }
  266. foundPodFlag = true
  267. break
  268. }
  269. if !foundPodFlag {
  270. t.Errorf("Failed to get pod from the backoff queue after waiting for a minute: %v", testPod)
  271. }
  272. }
  273. // getPodfromPriorityQueue is the function used in the TestDefaultErrorFunc test to get
  274. // the specific pod from the given priority queue. It returns the found pod in the priority queue.
  275. func getPodfromPriorityQueue(queue *internalqueue.PriorityQueue, pod *v1.Pod) *v1.Pod {
  276. podList := queue.PendingPods()
  277. if len(podList) == 0 {
  278. return nil
  279. }
  280. queryPodKey, err := cache.MetaNamespaceKeyFunc(pod)
  281. if err != nil {
  282. return nil
  283. }
  284. for _, foundPod := range podList {
  285. foundPodKey, err := cache.MetaNamespaceKeyFunc(foundPod)
  286. if err != nil {
  287. return nil
  288. }
  289. if foundPodKey == queryPodKey {
  290. return foundPod
  291. }
  292. }
  293. return nil
  294. }
  295. // testClientGetPodRequest function provides a routine used by TestDefaultErrorFunc test.
  296. // It tests whether the fake client can receive request and correctly "get" the namespace
  297. // and name of the error pod.
  298. func testClientGetPodRequest(client *fake.Clientset, t *testing.T, podNs string, podName string) {
  299. requestReceived := false
  300. actions := client.Actions()
  301. for _, a := range actions {
  302. if a.GetVerb() == "get" {
  303. getAction, ok := a.(clienttesting.GetAction)
  304. if !ok {
  305. t.Errorf("Can't cast action object to GetAction interface")
  306. break
  307. }
  308. name := getAction.GetName()
  309. ns := a.GetNamespace()
  310. if name != podName || ns != podNs {
  311. t.Errorf("Expected name %s namespace %s, got %s %s",
  312. podName, podNs, name, ns)
  313. }
  314. requestReceived = true
  315. }
  316. }
  317. if !requestReceived {
  318. t.Errorf("Get pod request not received")
  319. }
  320. }
  321. func TestBind(t *testing.T) {
  322. table := []struct {
  323. name string
  324. binding *v1.Binding
  325. }{
  326. {
  327. name: "binding can bind and validate request",
  328. binding: &v1.Binding{
  329. ObjectMeta: metav1.ObjectMeta{
  330. Namespace: metav1.NamespaceDefault,
  331. Name: "foo",
  332. },
  333. Target: v1.ObjectReference{
  334. Name: "foohost.kubernetes.mydomain.com",
  335. },
  336. },
  337. },
  338. }
  339. for _, test := range table {
  340. t.Run(test.name, func(t *testing.T) {
  341. testBind(test.binding, t)
  342. })
  343. }
  344. }
  345. func testBind(binding *v1.Binding, t *testing.T) {
  346. testPod := &v1.Pod{
  347. ObjectMeta: metav1.ObjectMeta{Name: binding.GetName(), Namespace: metav1.NamespaceDefault},
  348. Spec: apitesting.V1DeepEqualSafePodSpec(),
  349. }
  350. client := fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testPod}})
  351. b := binder{client}
  352. if err := b.Bind(binding); err != nil {
  353. t.Errorf("Unexpected error: %v", err)
  354. return
  355. }
  356. pod := client.CoreV1().Pods(metav1.NamespaceDefault).(*fakeV1.FakePods)
  357. actualBinding, err := pod.GetBinding(binding.GetName())
  358. if err != nil {
  359. t.Fatalf("Unexpected error: %v", err)
  360. return
  361. }
  362. if !reflect.DeepEqual(binding, actualBinding) {
  363. t.Errorf("Binding did not match expectation")
  364. t.Logf("Expected: %v", binding)
  365. t.Logf("Actual: %v", actualBinding)
  366. }
  367. }
  368. func TestInvalidHardPodAffinitySymmetricWeight(t *testing.T) {
  369. client := fake.NewSimpleClientset()
  370. // factory of "default-scheduler"
  371. stopCh := make(chan struct{})
  372. factory := newConfigFactory(client, -1, stopCh)
  373. defer close(stopCh)
  374. _, err := factory.Create()
  375. if err == nil {
  376. t.Errorf("expected err: invalid hardPodAffinitySymmetricWeight, got nothing")
  377. }
  378. }
  379. func TestInvalidFactoryArgs(t *testing.T) {
  380. client := fake.NewSimpleClientset()
  381. testCases := []struct {
  382. name string
  383. hardPodAffinitySymmetricWeight int32
  384. expectErr string
  385. }{
  386. {
  387. name: "symmetric weight below range",
  388. hardPodAffinitySymmetricWeight: -1,
  389. expectErr: "invalid hardPodAffinitySymmetricWeight: -1, must be in the range 0-100",
  390. },
  391. {
  392. name: "symmetric weight above range",
  393. hardPodAffinitySymmetricWeight: 101,
  394. expectErr: "invalid hardPodAffinitySymmetricWeight: 101, must be in the range 0-100",
  395. },
  396. }
  397. for _, test := range testCases {
  398. t.Run(test.name, func(t *testing.T) {
  399. stopCh := make(chan struct{})
  400. factory := newConfigFactory(client, test.hardPodAffinitySymmetricWeight, stopCh)
  401. defer close(stopCh)
  402. _, err := factory.Create()
  403. if err == nil {
  404. t.Errorf("expected err: %s, got nothing", test.expectErr)
  405. }
  406. })
  407. }
  408. }
  409. func newConfigFactory(client clientset.Interface, hardPodAffinitySymmetricWeight int32, stopCh <-chan struct{}) Configurator {
  410. informerFactory := informers.NewSharedInformerFactory(client, 0)
  411. return NewConfigFactory(&ConfigFactoryArgs{
  412. v1.DefaultSchedulerName,
  413. client,
  414. informerFactory.Core().V1().Nodes(),
  415. informerFactory.Core().V1().Pods(),
  416. informerFactory.Core().V1().PersistentVolumes(),
  417. informerFactory.Core().V1().PersistentVolumeClaims(),
  418. informerFactory.Core().V1().ReplicationControllers(),
  419. informerFactory.Apps().V1().ReplicaSets(),
  420. informerFactory.Apps().V1().StatefulSets(),
  421. informerFactory.Core().V1().Services(),
  422. informerFactory.Policy().V1beta1().PodDisruptionBudgets(),
  423. informerFactory.Storage().V1().StorageClasses(),
  424. hardPodAffinitySymmetricWeight,
  425. disablePodPreemption,
  426. schedulerapi.DefaultPercentageOfNodesToScore,
  427. bindTimeoutSeconds,
  428. stopCh,
  429. framework.NewRegistry(),
  430. nil,
  431. []config.PluginConfig{},
  432. })
  433. }
  434. type fakeExtender struct {
  435. isBinder bool
  436. interestedPodName string
  437. ignorable bool
  438. }
  439. func (f *fakeExtender) Name() string {
  440. return "fakeExtender"
  441. }
  442. func (f *fakeExtender) IsIgnorable() bool {
  443. return f.ignorable
  444. }
  445. func (f *fakeExtender) ProcessPreemption(
  446. pod *v1.Pod,
  447. nodeToVictims map[*v1.Node]*schedulerapi.Victims,
  448. nodeNameToInfo map[string]*schedulernodeinfo.NodeInfo,
  449. ) (map[*v1.Node]*schedulerapi.Victims, error) {
  450. return nil, nil
  451. }
  452. func (f *fakeExtender) SupportsPreemption() bool {
  453. return false
  454. }
  455. func (f *fakeExtender) Filter(
  456. pod *v1.Pod,
  457. nodes []*v1.Node,
  458. nodeNameToInfo map[string]*schedulernodeinfo.NodeInfo,
  459. ) (filteredNodes []*v1.Node, failedNodesMap schedulerapi.FailedNodesMap, err error) {
  460. return nil, nil, nil
  461. }
  462. func (f *fakeExtender) Prioritize(
  463. pod *v1.Pod,
  464. nodes []*v1.Node,
  465. ) (hostPriorities *schedulerapi.HostPriorityList, weight int, err error) {
  466. return nil, 0, nil
  467. }
  468. func (f *fakeExtender) Bind(binding *v1.Binding) error {
  469. if f.isBinder {
  470. return nil
  471. }
  472. return errors.New("not a binder")
  473. }
  474. func (f *fakeExtender) IsBinder() bool {
  475. return f.isBinder
  476. }
  477. func (f *fakeExtender) IsInterested(pod *v1.Pod) bool {
  478. return pod != nil && pod.Name == f.interestedPodName
  479. }
  480. func TestGetBinderFunc(t *testing.T) {
  481. table := []struct {
  482. podName string
  483. extenders []algorithm.SchedulerExtender
  484. expectedBinderType string
  485. name string
  486. }{
  487. {
  488. name: "the extender is not a binder",
  489. podName: "pod0",
  490. extenders: []algorithm.SchedulerExtender{
  491. &fakeExtender{isBinder: false, interestedPodName: "pod0"},
  492. },
  493. expectedBinderType: "*factory.binder",
  494. },
  495. {
  496. name: "one of the extenders is a binder and interested in pod",
  497. podName: "pod0",
  498. extenders: []algorithm.SchedulerExtender{
  499. &fakeExtender{isBinder: false, interestedPodName: "pod0"},
  500. &fakeExtender{isBinder: true, interestedPodName: "pod0"},
  501. },
  502. expectedBinderType: "*factory.fakeExtender",
  503. },
  504. {
  505. name: "one of the extenders is a binder, but not interested in pod",
  506. podName: "pod1",
  507. extenders: []algorithm.SchedulerExtender{
  508. &fakeExtender{isBinder: false, interestedPodName: "pod1"},
  509. &fakeExtender{isBinder: true, interestedPodName: "pod0"},
  510. },
  511. expectedBinderType: "*factory.binder",
  512. },
  513. }
  514. for _, test := range table {
  515. t.Run(test.name, func(t *testing.T) {
  516. testGetBinderFunc(test.expectedBinderType, test.podName, test.extenders, t)
  517. })
  518. }
  519. }
  520. func testGetBinderFunc(expectedBinderType, podName string, extenders []algorithm.SchedulerExtender, t *testing.T) {
  521. pod := &v1.Pod{
  522. ObjectMeta: metav1.ObjectMeta{
  523. Name: podName,
  524. },
  525. }
  526. f := &configFactory{}
  527. binderFunc := getBinderFunc(f.client, extenders)
  528. binder := binderFunc(pod)
  529. binderType := fmt.Sprintf("%s", reflect.TypeOf(binder))
  530. if binderType != expectedBinderType {
  531. t.Errorf("Expected binder %q but got %q", expectedBinderType, binderType)
  532. }
  533. }