scheduler_test.go 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763
  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 scheduler
  14. // This file tests the scheduler.
  15. import (
  16. "context"
  17. "fmt"
  18. "testing"
  19. "time"
  20. "github.com/google/go-cmp/cmp"
  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/schema"
  25. "k8s.io/apimachinery/pkg/util/wait"
  26. "k8s.io/client-go/informers"
  27. clientset "k8s.io/client-go/kubernetes"
  28. corelisters "k8s.io/client-go/listers/core/v1"
  29. restclient "k8s.io/client-go/rest"
  30. "k8s.io/client-go/tools/cache"
  31. "k8s.io/client-go/tools/events"
  32. "k8s.io/kubernetes/pkg/api/legacyscheme"
  33. "k8s.io/kubernetes/pkg/scheduler"
  34. kubeschedulerconfig "k8s.io/kubernetes/pkg/scheduler/apis/config"
  35. "k8s.io/kubernetes/test/integration/framework"
  36. )
  37. type nodeMutationFunc func(t *testing.T, n *v1.Node, nodeLister corelisters.NodeLister, c clientset.Interface)
  38. type nodeStateManager struct {
  39. makeSchedulable nodeMutationFunc
  40. makeUnSchedulable nodeMutationFunc
  41. }
  42. // TestSchedulerCreationFromConfigMap verifies that scheduler can be created
  43. // from configurations provided by a ConfigMap object and then verifies that the
  44. // configuration is applied correctly.
  45. func TestSchedulerCreationFromConfigMap(t *testing.T) {
  46. _, s, closeFn := framework.RunAMaster(nil)
  47. defer closeFn()
  48. ns := framework.CreateTestingNamespace("configmap", s, t)
  49. defer framework.DeleteTestingNamespace(ns, s, t)
  50. clientSet := clientset.NewForConfigOrDie(&restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}})
  51. defer clientSet.CoreV1().Nodes().DeleteCollection(context.TODO(), nil, metav1.ListOptions{})
  52. informerFactory := informers.NewSharedInformerFactory(clientSet, 0)
  53. for i, test := range []struct {
  54. policy string
  55. expectedPlugins map[string][]kubeschedulerconfig.Plugin
  56. }{
  57. {
  58. policy: `{
  59. "kind" : "Policy",
  60. "apiVersion" : "v1",
  61. "predicates" : [
  62. {"name" : "PodFitsResources"}
  63. ],
  64. "priorities" : [
  65. {"name" : "ImageLocalityPriority", "weight" : 1}
  66. ]
  67. }`,
  68. expectedPlugins: map[string][]kubeschedulerconfig.Plugin{
  69. "QueueSortPlugin": {{Name: "PrioritySort"}},
  70. "PreFilterPlugin": {
  71. {Name: "NodeResourcesFit"},
  72. },
  73. "FilterPlugin": {
  74. {Name: "NodeUnschedulable"},
  75. {Name: "NodeResourcesFit"},
  76. {Name: "TaintToleration"},
  77. },
  78. "ScorePlugin": {
  79. {Name: "ImageLocality", Weight: 1},
  80. },
  81. "BindPlugin": {{Name: "DefaultBinder"}},
  82. },
  83. },
  84. {
  85. policy: `{
  86. "kind" : "Policy",
  87. "apiVersion" : "v1"
  88. }`,
  89. expectedPlugins: map[string][]kubeschedulerconfig.Plugin{
  90. "QueueSortPlugin": {{Name: "PrioritySort"}},
  91. "PreFilterPlugin": {
  92. {Name: "NodeResourcesFit"},
  93. {Name: "NodePorts"},
  94. {Name: "PodTopologySpread"},
  95. {Name: "InterPodAffinity"},
  96. },
  97. "FilterPlugin": {
  98. {Name: "NodeUnschedulable"},
  99. {Name: "NodeResourcesFit"},
  100. {Name: "NodeName"},
  101. {Name: "NodePorts"},
  102. {Name: "NodeAffinity"},
  103. {Name: "VolumeRestrictions"},
  104. {Name: "TaintToleration"},
  105. {Name: "EBSLimits"},
  106. {Name: "GCEPDLimits"},
  107. {Name: "NodeVolumeLimits"},
  108. {Name: "AzureDiskLimits"},
  109. {Name: "VolumeBinding"},
  110. {Name: "VolumeZone"},
  111. {Name: "PodTopologySpread"},
  112. {Name: "InterPodAffinity"},
  113. },
  114. "PreScorePlugin": {
  115. {Name: "PodTopologySpread"},
  116. {Name: "InterPodAffinity"},
  117. {Name: "DefaultPodTopologySpread"},
  118. {Name: "TaintToleration"},
  119. },
  120. "ScorePlugin": {
  121. {Name: "NodeResourcesBalancedAllocation", Weight: 1},
  122. {Name: "PodTopologySpread", Weight: 1},
  123. {Name: "ImageLocality", Weight: 1},
  124. {Name: "InterPodAffinity", Weight: 1},
  125. {Name: "NodeResourcesLeastAllocated", Weight: 1},
  126. {Name: "NodeAffinity", Weight: 1},
  127. {Name: "NodePreferAvoidPods", Weight: 10000},
  128. {Name: "DefaultPodTopologySpread", Weight: 1},
  129. {Name: "TaintToleration", Weight: 1},
  130. },
  131. "BindPlugin": {{Name: "DefaultBinder"}},
  132. },
  133. },
  134. {
  135. policy: `{
  136. "kind" : "Policy",
  137. "apiVersion" : "v1",
  138. "predicates" : [],
  139. "priorities" : []
  140. }`,
  141. expectedPlugins: map[string][]kubeschedulerconfig.Plugin{
  142. "QueueSortPlugin": {{Name: "PrioritySort"}},
  143. "FilterPlugin": {
  144. {Name: "NodeUnschedulable"},
  145. {Name: "TaintToleration"},
  146. },
  147. "BindPlugin": {{Name: "DefaultBinder"}},
  148. },
  149. },
  150. {
  151. policy: `apiVersion: v1
  152. kind: Policy
  153. predicates:
  154. - name: PodFitsResources
  155. priorities:
  156. - name: ImageLocalityPriority
  157. weight: 1
  158. `,
  159. expectedPlugins: map[string][]kubeschedulerconfig.Plugin{
  160. "QueueSortPlugin": {{Name: "PrioritySort"}},
  161. "PreFilterPlugin": {
  162. {Name: "NodeResourcesFit"},
  163. },
  164. "FilterPlugin": {
  165. {Name: "NodeUnschedulable"},
  166. {Name: "NodeResourcesFit"},
  167. {Name: "TaintToleration"},
  168. },
  169. "ScorePlugin": {
  170. {Name: "ImageLocality", Weight: 1},
  171. },
  172. "BindPlugin": {{Name: "DefaultBinder"}},
  173. },
  174. },
  175. {
  176. policy: `apiVersion: v1
  177. kind: Policy
  178. `,
  179. expectedPlugins: map[string][]kubeschedulerconfig.Plugin{
  180. "QueueSortPlugin": {{Name: "PrioritySort"}},
  181. "PreFilterPlugin": {
  182. {Name: "NodeResourcesFit"},
  183. {Name: "NodePorts"},
  184. {Name: "PodTopologySpread"},
  185. {Name: "InterPodAffinity"},
  186. },
  187. "FilterPlugin": {
  188. {Name: "NodeUnschedulable"},
  189. {Name: "NodeResourcesFit"},
  190. {Name: "NodeName"},
  191. {Name: "NodePorts"},
  192. {Name: "NodeAffinity"},
  193. {Name: "VolumeRestrictions"},
  194. {Name: "TaintToleration"},
  195. {Name: "EBSLimits"},
  196. {Name: "GCEPDLimits"},
  197. {Name: "NodeVolumeLimits"},
  198. {Name: "AzureDiskLimits"},
  199. {Name: "VolumeBinding"},
  200. {Name: "VolumeZone"},
  201. {Name: "PodTopologySpread"},
  202. {Name: "InterPodAffinity"},
  203. },
  204. "PreScorePlugin": {
  205. {Name: "PodTopologySpread"},
  206. {Name: "InterPodAffinity"},
  207. {Name: "DefaultPodTopologySpread"},
  208. {Name: "TaintToleration"},
  209. },
  210. "ScorePlugin": {
  211. {Name: "NodeResourcesBalancedAllocation", Weight: 1},
  212. {Name: "PodTopologySpread", Weight: 1},
  213. {Name: "ImageLocality", Weight: 1},
  214. {Name: "InterPodAffinity", Weight: 1},
  215. {Name: "NodeResourcesLeastAllocated", Weight: 1},
  216. {Name: "NodeAffinity", Weight: 1},
  217. {Name: "NodePreferAvoidPods", Weight: 10000},
  218. {Name: "DefaultPodTopologySpread", Weight: 1},
  219. {Name: "TaintToleration", Weight: 1},
  220. },
  221. "BindPlugin": {{Name: "DefaultBinder"}},
  222. },
  223. },
  224. {
  225. policy: `apiVersion: v1
  226. kind: Policy
  227. predicates: []
  228. priorities: []
  229. `,
  230. expectedPlugins: map[string][]kubeschedulerconfig.Plugin{
  231. "QueueSortPlugin": {{Name: "PrioritySort"}},
  232. "FilterPlugin": {
  233. {Name: "NodeUnschedulable"},
  234. {Name: "TaintToleration"},
  235. },
  236. "BindPlugin": {{Name: "DefaultBinder"}},
  237. },
  238. },
  239. } {
  240. // Add a ConfigMap object.
  241. configPolicyName := fmt.Sprintf("scheduler-custom-policy-config-%d", i)
  242. policyConfigMap := v1.ConfigMap{
  243. ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: configPolicyName},
  244. Data: map[string]string{kubeschedulerconfig.SchedulerPolicyConfigMapKey: test.policy},
  245. }
  246. policyConfigMap.APIVersion = "v1"
  247. clientSet.CoreV1().ConfigMaps(metav1.NamespaceSystem).Create(context.TODO(), &policyConfigMap, metav1.CreateOptions{})
  248. eventBroadcaster := events.NewBroadcaster(&events.EventSinkImpl{Interface: clientSet.EventsV1beta1().Events("")})
  249. stopCh := make(chan struct{})
  250. eventBroadcaster.StartRecordingToSink(stopCh)
  251. defaultBindTimeout := int64(30)
  252. sched, err := scheduler.New(clientSet,
  253. informerFactory,
  254. scheduler.NewPodInformer(clientSet, 0),
  255. eventBroadcaster.NewRecorder(legacyscheme.Scheme, v1.DefaultSchedulerName),
  256. nil,
  257. scheduler.WithName(v1.DefaultSchedulerName),
  258. scheduler.WithAlgorithmSource(kubeschedulerconfig.SchedulerAlgorithmSource{
  259. Policy: &kubeschedulerconfig.SchedulerPolicySource{
  260. ConfigMap: &kubeschedulerconfig.SchedulerPolicyConfigMapSource{
  261. Namespace: policyConfigMap.Namespace,
  262. Name: policyConfigMap.Name,
  263. },
  264. },
  265. }),
  266. scheduler.WithBindTimeoutSeconds(defaultBindTimeout),
  267. )
  268. if err != nil {
  269. t.Fatalf("couldn't make scheduler config for test %d: %v", i, err)
  270. }
  271. schedPlugins := sched.Framework.ListPlugins()
  272. if diff := cmp.Diff(test.expectedPlugins, schedPlugins); diff != "" {
  273. t.Errorf("unexpected plugins diff (-want, +got): %s", diff)
  274. }
  275. }
  276. }
  277. // TestSchedulerCreationFromNonExistentConfigMap ensures that creation of the
  278. // scheduler from a non-existent ConfigMap fails.
  279. func TestSchedulerCreationFromNonExistentConfigMap(t *testing.T) {
  280. _, s, closeFn := framework.RunAMaster(nil)
  281. defer closeFn()
  282. ns := framework.CreateTestingNamespace("configmap", s, t)
  283. defer framework.DeleteTestingNamespace(ns, s, t)
  284. clientSet := clientset.NewForConfigOrDie(&restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}})
  285. defer clientSet.CoreV1().Nodes().DeleteCollection(context.TODO(), nil, metav1.ListOptions{})
  286. informerFactory := informers.NewSharedInformerFactory(clientSet, 0)
  287. eventBroadcaster := events.NewBroadcaster(&events.EventSinkImpl{Interface: clientSet.EventsV1beta1().Events("")})
  288. stopCh := make(chan struct{})
  289. eventBroadcaster.StartRecordingToSink(stopCh)
  290. defaultBindTimeout := int64(30)
  291. _, err := scheduler.New(clientSet,
  292. informerFactory,
  293. scheduler.NewPodInformer(clientSet, 0),
  294. eventBroadcaster.NewRecorder(legacyscheme.Scheme, v1.DefaultSchedulerName),
  295. nil,
  296. scheduler.WithName(v1.DefaultSchedulerName),
  297. scheduler.WithAlgorithmSource(kubeschedulerconfig.SchedulerAlgorithmSource{
  298. Policy: &kubeschedulerconfig.SchedulerPolicySource{
  299. ConfigMap: &kubeschedulerconfig.SchedulerPolicyConfigMapSource{
  300. Namespace: "non-existent-config",
  301. Name: "non-existent-config",
  302. },
  303. },
  304. }),
  305. scheduler.WithBindTimeoutSeconds(defaultBindTimeout))
  306. if err == nil {
  307. t.Fatalf("Creation of scheduler didn't fail while the policy ConfigMap didn't exist.")
  308. }
  309. }
  310. func TestUnschedulableNodes(t *testing.T) {
  311. testCtx := initTest(t, "unschedulable-nodes")
  312. defer cleanupTest(t, testCtx)
  313. nodeLister := testCtx.informerFactory.Core().V1().Nodes().Lister()
  314. // NOTE: This test cannot run in parallel, because it is creating and deleting
  315. // non-namespaced objects (Nodes).
  316. defer testCtx.clientSet.CoreV1().Nodes().DeleteCollection(context.TODO(), nil, metav1.ListOptions{})
  317. goodCondition := v1.NodeCondition{
  318. Type: v1.NodeReady,
  319. Status: v1.ConditionTrue,
  320. Reason: fmt.Sprintf("schedulable condition"),
  321. LastHeartbeatTime: metav1.Time{Time: time.Now()},
  322. }
  323. // Create a new schedulable node, since we're first going to apply
  324. // the unschedulable condition and verify that pods aren't scheduled.
  325. node := &v1.Node{
  326. ObjectMeta: metav1.ObjectMeta{Name: "node-scheduling-test-node"},
  327. Spec: v1.NodeSpec{Unschedulable: false},
  328. Status: v1.NodeStatus{
  329. Capacity: v1.ResourceList{
  330. v1.ResourcePods: *resource.NewQuantity(32, resource.DecimalSI),
  331. },
  332. Conditions: []v1.NodeCondition{goodCondition},
  333. },
  334. }
  335. nodeKey, err := cache.MetaNamespaceKeyFunc(node)
  336. if err != nil {
  337. t.Fatalf("Couldn't retrieve key for node %v", node.Name)
  338. }
  339. // The test does the following for each nodeStateManager in this list:
  340. // 1. Create a new node
  341. // 2. Apply the makeUnSchedulable function
  342. // 3. Create a new pod
  343. // 4. Check that the pod doesn't get assigned to the node
  344. // 5. Apply the schedulable function
  345. // 6. Check that the pod *does* get assigned to the node
  346. // 7. Delete the pod and node.
  347. nodeModifications := []nodeStateManager{
  348. // Test node.Spec.Unschedulable=true/false
  349. {
  350. makeUnSchedulable: func(t *testing.T, n *v1.Node, nodeLister corelisters.NodeLister, c clientset.Interface) {
  351. n.Spec.Unschedulable = true
  352. if _, err := c.CoreV1().Nodes().Update(context.TODO(), n, metav1.UpdateOptions{}); err != nil {
  353. t.Fatalf("Failed to update node with unschedulable=true: %v", err)
  354. }
  355. err = waitForReflection(t, nodeLister, nodeKey, func(node interface{}) bool {
  356. // An unschedulable node should still be present in the store
  357. // Nodes that are unschedulable or that are not ready or
  358. // have their disk full (Node.Spec.Conditions) are excluded
  359. // based on NodeConditionPredicate, a separate check
  360. return node != nil && node.(*v1.Node).Spec.Unschedulable == true
  361. })
  362. if err != nil {
  363. t.Fatalf("Failed to observe reflected update for setting unschedulable=true: %v", err)
  364. }
  365. },
  366. makeSchedulable: func(t *testing.T, n *v1.Node, nodeLister corelisters.NodeLister, c clientset.Interface) {
  367. n.Spec.Unschedulable = false
  368. if _, err := c.CoreV1().Nodes().Update(context.TODO(), n, metav1.UpdateOptions{}); err != nil {
  369. t.Fatalf("Failed to update node with unschedulable=false: %v", err)
  370. }
  371. err = waitForReflection(t, nodeLister, nodeKey, func(node interface{}) bool {
  372. return node != nil && node.(*v1.Node).Spec.Unschedulable == false
  373. })
  374. if err != nil {
  375. t.Fatalf("Failed to observe reflected update for setting unschedulable=false: %v", err)
  376. }
  377. },
  378. },
  379. }
  380. for i, mod := range nodeModifications {
  381. unSchedNode, err := testCtx.clientSet.CoreV1().Nodes().Create(context.TODO(), node, metav1.CreateOptions{})
  382. if err != nil {
  383. t.Fatalf("Failed to create node: %v", err)
  384. }
  385. // Apply the unschedulable modification to the node, and wait for the reflection
  386. mod.makeUnSchedulable(t, unSchedNode, nodeLister, testCtx.clientSet)
  387. // Create the new pod, note that this needs to happen post unschedulable
  388. // modification or we have a race in the test.
  389. myPod, err := createPausePodWithResource(testCtx.clientSet, "node-scheduling-test-pod", testCtx.ns.Name, nil)
  390. if err != nil {
  391. t.Fatalf("Failed to create pod: %v", err)
  392. }
  393. // There are no schedulable nodes - the pod shouldn't be scheduled.
  394. err = waitForPodToScheduleWithTimeout(testCtx.clientSet, myPod, 2*time.Second)
  395. if err == nil {
  396. t.Errorf("Test %d: Pod scheduled successfully on unschedulable nodes", i)
  397. }
  398. if err != wait.ErrWaitTimeout {
  399. t.Errorf("Test %d: failed while trying to confirm the pod does not get scheduled on the node: %v", i, err)
  400. } else {
  401. t.Logf("Test %d: Pod did not get scheduled on an unschedulable node", i)
  402. }
  403. // Apply the schedulable modification to the node, and wait for the reflection
  404. schedNode, err := testCtx.clientSet.CoreV1().Nodes().Get(context.TODO(), unSchedNode.Name, metav1.GetOptions{})
  405. if err != nil {
  406. t.Fatalf("Failed to get node: %v", err)
  407. }
  408. mod.makeSchedulable(t, schedNode, nodeLister, testCtx.clientSet)
  409. // Wait until the pod is scheduled.
  410. if err := waitForPodToSchedule(testCtx.clientSet, myPod); err != nil {
  411. t.Errorf("Test %d: failed to schedule a pod: %v", i, err)
  412. } else {
  413. t.Logf("Test %d: Pod got scheduled on a schedulable node", i)
  414. }
  415. // Clean up.
  416. if err := deletePod(testCtx.clientSet, myPod.Name, myPod.Namespace); err != nil {
  417. t.Errorf("Failed to delete pod: %v", err)
  418. }
  419. err = testCtx.clientSet.CoreV1().Nodes().Delete(context.TODO(), schedNode.Name, nil)
  420. if err != nil {
  421. t.Errorf("Failed to delete node: %v", err)
  422. }
  423. }
  424. }
  425. func TestMultiScheduler(t *testing.T) {
  426. // This integration tests the multi-scheduler feature in the following way:
  427. // 1. create a default scheduler
  428. // 2. create a node
  429. // 3. create 3 pods: testPodNoAnnotation, testPodWithAnnotationFitsDefault and testPodWithAnnotationFitsFoo
  430. // - note: the first two should be picked and scheduled by default scheduler while the last one should be
  431. // picked by scheduler of name "foo-scheduler" which does not exist yet.
  432. // 4. **check point-1**:
  433. // - testPodNoAnnotation, testPodWithAnnotationFitsDefault should be scheduled
  434. // - testPodWithAnnotationFitsFoo should NOT be scheduled
  435. // 5. create a scheduler with name "foo-scheduler"
  436. // 6. **check point-2**:
  437. // - testPodWithAnnotationFitsFoo should be scheduled
  438. // 7. stop default scheduler
  439. // 8. create 2 pods: testPodNoAnnotation2 and testPodWithAnnotationFitsDefault2
  440. // - note: these two pods belong to default scheduler which no longer exists
  441. // 9. **check point-3**:
  442. // - testPodNoAnnotation2 and testPodWithAnnotationFitsDefault2 should NOT be scheduled
  443. // 1. create and start default-scheduler
  444. testCtx := initTest(t, "multi-scheduler")
  445. defer cleanupTest(t, testCtx)
  446. // 2. create a node
  447. node := &v1.Node{
  448. ObjectMeta: metav1.ObjectMeta{Name: "node-multi-scheduler-test-node"},
  449. Spec: v1.NodeSpec{Unschedulable: false},
  450. Status: v1.NodeStatus{
  451. Capacity: v1.ResourceList{
  452. v1.ResourcePods: *resource.NewQuantity(32, resource.DecimalSI),
  453. },
  454. },
  455. }
  456. testCtx.clientSet.CoreV1().Nodes().Create(context.TODO(), node, metav1.CreateOptions{})
  457. // 3. create 3 pods for testing
  458. t.Logf("create 3 pods for testing")
  459. testPod, err := createPausePodWithResource(testCtx.clientSet, "pod-without-scheduler-name", testCtx.ns.Name, nil)
  460. if err != nil {
  461. t.Fatalf("Failed to create pod: %v", err)
  462. }
  463. defaultScheduler := "default-scheduler"
  464. testPodFitsDefault, err := createPausePod(testCtx.clientSet, initPausePod(testCtx.clientSet, &pausePodConfig{Name: "pod-fits-default", Namespace: testCtx.ns.Name, SchedulerName: defaultScheduler}))
  465. if err != nil {
  466. t.Fatalf("Failed to create pod: %v", err)
  467. }
  468. fooScheduler := "foo-scheduler"
  469. testPodFitsFoo, err := createPausePod(testCtx.clientSet, initPausePod(testCtx.clientSet, &pausePodConfig{Name: "pod-fits-foo", Namespace: testCtx.ns.Name, SchedulerName: fooScheduler}))
  470. if err != nil {
  471. t.Fatalf("Failed to create pod: %v", err)
  472. }
  473. // 4. **check point-1**:
  474. // - testPod, testPodFitsDefault should be scheduled
  475. // - testPodFitsFoo should NOT be scheduled
  476. t.Logf("wait for pods scheduled")
  477. if err := waitForPodToSchedule(testCtx.clientSet, testPod); err != nil {
  478. t.Errorf("Test MultiScheduler: %s Pod not scheduled: %v", testPod.Name, err)
  479. } else {
  480. t.Logf("Test MultiScheduler: %s Pod scheduled", testPod.Name)
  481. }
  482. if err := waitForPodToSchedule(testCtx.clientSet, testPodFitsDefault); err != nil {
  483. t.Errorf("Test MultiScheduler: %s Pod not scheduled: %v", testPodFitsDefault.Name, err)
  484. } else {
  485. t.Logf("Test MultiScheduler: %s Pod scheduled", testPodFitsDefault.Name)
  486. }
  487. if err := waitForPodToScheduleWithTimeout(testCtx.clientSet, testPodFitsFoo, time.Second*5); err == nil {
  488. t.Errorf("Test MultiScheduler: %s Pod got scheduled, %v", testPodFitsFoo.Name, err)
  489. } else {
  490. t.Logf("Test MultiScheduler: %s Pod not scheduled", testPodFitsFoo.Name)
  491. }
  492. // 5. create and start a scheduler with name "foo-scheduler"
  493. testCtx = initTestSchedulerWithOptions(t, testCtx, true, nil, time.Second, scheduler.WithName(fooScheduler))
  494. // 6. **check point-2**:
  495. // - testPodWithAnnotationFitsFoo should be scheduled
  496. err = waitForPodToSchedule(testCtx.clientSet, testPodFitsFoo)
  497. if err != nil {
  498. t.Errorf("Test MultiScheduler: %s Pod not scheduled, %v", testPodFitsFoo.Name, err)
  499. } else {
  500. t.Logf("Test MultiScheduler: %s Pod scheduled", testPodFitsFoo.Name)
  501. }
  502. // 7. delete the pods that were scheduled by the default scheduler, and stop the default scheduler
  503. if err := deletePod(testCtx.clientSet, testPod.Name, testCtx.ns.Name); err != nil {
  504. t.Errorf("Failed to delete pod: %v", err)
  505. }
  506. if err := deletePod(testCtx.clientSet, testPodFitsDefault.Name, testCtx.ns.Name); err != nil {
  507. t.Errorf("Failed to delete pod: %v", err)
  508. }
  509. // The rest of this test assumes that closing StopEverything will cause the
  510. // scheduler thread to stop immediately. It won't, and in fact it will often
  511. // schedule 1 more pod before finally exiting. Comment out until we fix that.
  512. //
  513. // See https://github.com/kubernetes/kubernetes/issues/23715 for more details.
  514. /*
  515. close(schedulerConfig.StopEverything)
  516. // 8. create 2 pods: testPodNoAnnotation2 and testPodWithAnnotationFitsDefault2
  517. // - note: these two pods belong to default scheduler which no longer exists
  518. podWithNoAnnotation2 := createPod("pod-with-no-annotation2", nil)
  519. podWithAnnotationFitsDefault2 := createPod("pod-with-annotation-fits-default2", schedulerAnnotationFitsDefault)
  520. testPodNoAnnotation2, err := clientSet.CoreV1().Pods(ns.Name).Create(podWithNoAnnotation2)
  521. if err != nil {
  522. t.Fatalf("Failed to create pod: %v", err)
  523. }
  524. testPodWithAnnotationFitsDefault2, err := clientSet.CoreV1().Pods(ns.Name).Create(podWithAnnotationFitsDefault2)
  525. if err != nil {
  526. t.Fatalf("Failed to create pod: %v", err)
  527. }
  528. // 9. **check point-3**:
  529. // - testPodNoAnnotation2 and testPodWithAnnotationFitsDefault2 should NOT be scheduled
  530. err = wait.Poll(time.Second, time.Second*5, podScheduled(clientSet, testPodNoAnnotation2.Namespace, testPodNoAnnotation2.Name))
  531. if err == nil {
  532. t.Errorf("Test MultiScheduler: %s Pod got scheduled, %v", testPodNoAnnotation2.Name, err)
  533. } else {
  534. t.Logf("Test MultiScheduler: %s Pod not scheduled", testPodNoAnnotation2.Name)
  535. }
  536. err = wait.Poll(time.Second, time.Second*5, podScheduled(clientSet, testPodWithAnnotationFitsDefault2.Namespace, testPodWithAnnotationFitsDefault2.Name))
  537. if err == nil {
  538. t.Errorf("Test MultiScheduler: %s Pod got scheduled, %v", testPodWithAnnotationFitsDefault2.Name, err)
  539. } else {
  540. t.Logf("Test MultiScheduler: %s Pod scheduled", testPodWithAnnotationFitsDefault2.Name)
  541. }
  542. */
  543. }
  544. // This test will verify scheduler can work well regardless of whether kubelet is allocatable aware or not.
  545. func TestAllocatable(t *testing.T) {
  546. testCtx := initTest(t, "allocatable")
  547. defer cleanupTest(t, testCtx)
  548. // 2. create a node without allocatable awareness
  549. nodeRes := &v1.ResourceList{
  550. v1.ResourcePods: *resource.NewQuantity(32, resource.DecimalSI),
  551. v1.ResourceCPU: *resource.NewMilliQuantity(30, resource.DecimalSI),
  552. v1.ResourceMemory: *resource.NewQuantity(30, resource.BinarySI),
  553. }
  554. allocNode, err := createNode(testCtx.clientSet, "node-allocatable-scheduler-test-node", nodeRes)
  555. if err != nil {
  556. t.Fatalf("Failed to create node: %v", err)
  557. }
  558. // 3. create resource pod which requires less than Capacity
  559. podName := "pod-test-allocatable"
  560. podRes := &v1.ResourceList{
  561. v1.ResourceCPU: *resource.NewMilliQuantity(20, resource.DecimalSI),
  562. v1.ResourceMemory: *resource.NewQuantity(20, resource.BinarySI),
  563. }
  564. testAllocPod, err := createPausePodWithResource(testCtx.clientSet, podName, testCtx.ns.Name, podRes)
  565. if err != nil {
  566. t.Fatalf("Test allocatable unawareness failed to create pod: %v", err)
  567. }
  568. // 4. Test: this test pod should be scheduled since api-server will use Capacity as Allocatable
  569. err = waitForPodToScheduleWithTimeout(testCtx.clientSet, testAllocPod, time.Second*5)
  570. if err != nil {
  571. t.Errorf("Test allocatable unawareness: %s Pod not scheduled: %v", testAllocPod.Name, err)
  572. } else {
  573. t.Logf("Test allocatable unawareness: %s Pod scheduled", testAllocPod.Name)
  574. }
  575. // 5. Change the node status to allocatable aware, note that Allocatable is less than Pod's requirement
  576. allocNode.Status = v1.NodeStatus{
  577. Capacity: v1.ResourceList{
  578. v1.ResourcePods: *resource.NewQuantity(32, resource.DecimalSI),
  579. v1.ResourceCPU: *resource.NewMilliQuantity(30, resource.DecimalSI),
  580. v1.ResourceMemory: *resource.NewQuantity(30, resource.BinarySI),
  581. },
  582. Allocatable: v1.ResourceList{
  583. v1.ResourcePods: *resource.NewQuantity(32, resource.DecimalSI),
  584. v1.ResourceCPU: *resource.NewMilliQuantity(10, resource.DecimalSI),
  585. v1.ResourceMemory: *resource.NewQuantity(10, resource.BinarySI),
  586. },
  587. }
  588. if _, err := testCtx.clientSet.CoreV1().Nodes().UpdateStatus(context.TODO(), allocNode, metav1.UpdateOptions{}); err != nil {
  589. t.Fatalf("Failed to update node with Status.Allocatable: %v", err)
  590. }
  591. if err := deletePod(testCtx.clientSet, testAllocPod.Name, testCtx.ns.Name); err != nil {
  592. t.Fatalf("Failed to remove the first pod: %v", err)
  593. }
  594. // 6. Make another pod with different name, same resource request
  595. podName2 := "pod-test-allocatable2"
  596. testAllocPod2, err := createPausePodWithResource(testCtx.clientSet, podName2, testCtx.ns.Name, podRes)
  597. if err != nil {
  598. t.Fatalf("Test allocatable awareness failed to create pod: %v", err)
  599. }
  600. // 7. Test: this test pod should not be scheduled since it request more than Allocatable
  601. if err := waitForPodToScheduleWithTimeout(testCtx.clientSet, testAllocPod2, time.Second*5); err == nil {
  602. t.Errorf("Test allocatable awareness: %s Pod got scheduled unexpectedly, %v", testAllocPod2.Name, err)
  603. } else {
  604. t.Logf("Test allocatable awareness: %s Pod not scheduled as expected", testAllocPod2.Name)
  605. }
  606. }
  607. // TestSchedulerInformers tests that scheduler receives informer events and updates its cache when
  608. // pods are scheduled by other schedulers.
  609. func TestSchedulerInformers(t *testing.T) {
  610. // Initialize scheduler.
  611. testCtx := initTest(t, "scheduler-informer")
  612. defer cleanupTest(t, testCtx)
  613. cs := testCtx.clientSet
  614. defaultPodRes := &v1.ResourceRequirements{Requests: v1.ResourceList{
  615. v1.ResourceCPU: *resource.NewMilliQuantity(200, resource.DecimalSI),
  616. v1.ResourceMemory: *resource.NewQuantity(200, resource.BinarySI)},
  617. }
  618. defaultNodeRes := &v1.ResourceList{
  619. v1.ResourcePods: *resource.NewQuantity(32, resource.DecimalSI),
  620. v1.ResourceCPU: *resource.NewMilliQuantity(500, resource.DecimalSI),
  621. v1.ResourceMemory: *resource.NewQuantity(500, resource.BinarySI),
  622. }
  623. type nodeConfig struct {
  624. name string
  625. res *v1.ResourceList
  626. }
  627. tests := []struct {
  628. description string
  629. nodes []*nodeConfig
  630. existingPods []*v1.Pod
  631. pod *v1.Pod
  632. preemptedPodIndexes map[int]struct{}
  633. }{
  634. {
  635. description: "Pod cannot be scheduled when node is occupied by pods scheduled by other schedulers",
  636. nodes: []*nodeConfig{{name: "node-1", res: defaultNodeRes}},
  637. existingPods: []*v1.Pod{
  638. initPausePod(testCtx.clientSet, &pausePodConfig{
  639. Name: "pod1",
  640. Namespace: testCtx.ns.Name,
  641. Resources: defaultPodRes,
  642. Labels: map[string]string{"foo": "bar"},
  643. NodeName: "node-1",
  644. SchedulerName: "foo-scheduler",
  645. }),
  646. initPausePod(testCtx.clientSet, &pausePodConfig{
  647. Name: "pod2",
  648. Namespace: testCtx.ns.Name,
  649. Resources: defaultPodRes,
  650. Labels: map[string]string{"foo": "bar"},
  651. NodeName: "node-1",
  652. SchedulerName: "bar-scheduler",
  653. }),
  654. },
  655. pod: initPausePod(cs, &pausePodConfig{
  656. Name: "unschedulable-pod",
  657. Namespace: testCtx.ns.Name,
  658. Resources: defaultPodRes,
  659. }),
  660. preemptedPodIndexes: map[int]struct{}{2: {}},
  661. },
  662. }
  663. for _, test := range tests {
  664. for _, nodeConf := range test.nodes {
  665. _, err := createNode(cs, nodeConf.name, nodeConf.res)
  666. if err != nil {
  667. t.Fatalf("Error creating node %v: %v", nodeConf.name, err)
  668. }
  669. }
  670. pods := make([]*v1.Pod, len(test.existingPods))
  671. var err error
  672. // Create and run existingPods.
  673. for i, p := range test.existingPods {
  674. if pods[i], err = runPausePod(cs, p); err != nil {
  675. t.Fatalf("Test [%v]: Error running pause pod: %v", test.description, err)
  676. }
  677. }
  678. // Create the new "pod".
  679. unschedulable, err := createPausePod(cs, test.pod)
  680. if err != nil {
  681. t.Errorf("Error while creating new pod: %v", err)
  682. }
  683. if err := waitForPodUnschedulable(cs, unschedulable); err != nil {
  684. t.Errorf("Pod %v got scheduled: %v", unschedulable.Name, err)
  685. }
  686. // Cleanup
  687. pods = append(pods, unschedulable)
  688. cleanupPods(cs, t, pods)
  689. cs.PolicyV1beta1().PodDisruptionBudgets(testCtx.ns.Name).DeleteCollection(context.TODO(), nil, metav1.ListOptions{})
  690. cs.CoreV1().Nodes().DeleteCollection(context.TODO(), nil, metav1.ListOptions{})
  691. }
  692. }