func TestSchedulerNoPhantomPodAfterDelete(t *testing.T) { stop := make(chan struct{}) defer close(stop) queuedPodStore := clientcache.NewFIFO(clientcache.MetaNamespaceKeyFunc) scache := schedulercache.New(10*time.Minute, stop) firstPod := podWithPort("pod.Name", "", 8080) node := api.Node{ObjectMeta: api.ObjectMeta{Name: "machine1"}} scache.AddNode(&node) nodeLister := algorithm.FakeNodeLister([]*api.Node{&node}) predicateMap := map[string]algorithm.FitPredicate{"PodFitsHostPorts": predicates.PodFitsHostPorts} scheduler, bindingChan, errChan := setupTestSchedulerWithOnePodOnNode(t, queuedPodStore, scache, nodeLister, predicateMap, firstPod, &node) // We use conflicted pod ports to incur fit predicate failure. secondPod := podWithPort("bar", "", 8080) queuedPodStore.Add(secondPod) // queuedPodStore: [bar:8080] // cache: [(assumed)foo:8080] scheduler.scheduleOne() select { case err := <-errChan: expectErr := &FitError{ Pod: secondPod, FailedPredicates: FailedPredicateMap{node.Name: []algorithm.PredicateFailureReason{predicates.ErrPodNotFitsHostPorts}}, } if !reflect.DeepEqual(expectErr, err) { t.Errorf("err want=%v, get=%v", expectErr, err) } case <-time.After(wait.ForeverTestTimeout): t.Fatalf("timeout after %v", wait.ForeverTestTimeout) } // We mimic the workflow of cache behavior when a pod is removed by user. // Note: if the schedulercache timeout would be super short, the first pod would expire // and would be removed itself (without any explicit actions on schedulercache). Even in that case, // explicitly AddPod will as well correct the behavior. firstPod.Spec.NodeName = node.Name if err := scache.AddPod(firstPod); err != nil { t.Fatalf("err: %v", err) } if err := scache.RemovePod(firstPod); err != nil { t.Fatalf("err: %v", err) } queuedPodStore.Add(secondPod) scheduler.scheduleOne() select { case b := <-bindingChan: expectBinding := &api.Binding{ ObjectMeta: api.ObjectMeta{Name: "bar"}, Target: api.ObjectReference{Kind: "Node", Name: node.Name}, } if !reflect.DeepEqual(expectBinding, b) { t.Errorf("binding want=%v, get=%v", expectBinding, b) } case <-time.After(wait.ForeverTestTimeout): t.Fatalf("timeout after %v", wait.ForeverTestTimeout) } }
func TestSchedulerNoPhantomPodAfterExpire(t *testing.T) { stop := make(chan struct{}) defer close(stop) queuedPodStore := clientcache.NewFIFO(clientcache.MetaNamespaceKeyFunc) scache := schedulercache.New(100*time.Millisecond, stop) pod := podWithPort("pod.Name", "", 8080) node := api.Node{ObjectMeta: api.ObjectMeta{Name: "machine1"}} scache.AddNode(&node) nodeLister := algorithm.FakeNodeLister([]*api.Node{&node}) predicateMap := map[string]algorithm.FitPredicate{"PodFitsHostPorts": predicates.PodFitsHostPorts} scheduler, bindingChan, _ := setupTestSchedulerWithOnePodOnNode(t, queuedPodStore, scache, nodeLister, predicateMap, pod, &node) waitPodExpireChan := make(chan struct{}) timeout := make(chan struct{}) go func() { for { select { case <-timeout: return default: } pods, err := scache.List(labels.Everything()) if err != nil { t.Fatalf("cache.List failed: %v", err) } if len(pods) == 0 { close(waitPodExpireChan) return } time.Sleep(100 * time.Millisecond) } }() // waiting for the assumed pod to expire select { case <-waitPodExpireChan: case <-time.After(wait.ForeverTestTimeout): close(timeout) t.Fatalf("timeout after %v", wait.ForeverTestTimeout) } // We use conflicted pod ports to incur fit predicate failure if first pod not removed. secondPod := podWithPort("bar", "", 8080) queuedPodStore.Add(secondPod) scheduler.scheduleOne() select { case b := <-bindingChan: expectBinding := &api.Binding{ ObjectMeta: api.ObjectMeta{Name: "bar"}, Target: api.ObjectReference{Kind: "Node", Name: node.Name}, } if !reflect.DeepEqual(expectBinding, b) { t.Errorf("binding want=%v, get=%v", expectBinding, b) } case <-time.After(wait.ForeverTestTimeout): t.Fatalf("timeout after %v", wait.ForeverTestTimeout) } }
// Scheduler should preserve predicate constraint even if binding was longer // than cache ttl func TestSchedulerErrorWithLongBinding(t *testing.T) { stop := make(chan struct{}) defer close(stop) firstPod := podWithPort("foo", "", 8080) conflictPod := podWithPort("bar", "", 8080) pods := map[string]*v1.Pod{firstPod.Name: firstPod, conflictPod.Name: conflictPod} for _, test := range []struct { Expected map[string]bool CacheTTL time.Duration BindingDuration time.Duration }{ { Expected: map[string]bool{firstPod.Name: true}, CacheTTL: 100 * time.Millisecond, BindingDuration: 300 * time.Millisecond, }, { Expected: map[string]bool{firstPod.Name: true}, CacheTTL: 10 * time.Second, BindingDuration: 300 * time.Millisecond, }, } { queuedPodStore := clientcache.NewFIFO(clientcache.MetaNamespaceKeyFunc) scache := schedulercache.New(test.CacheTTL, stop) node := v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "machine1"}} scache.AddNode(&node) nodeLister := algorithm.FakeNodeLister([]*v1.Node{&node}) predicateMap := map[string]algorithm.FitPredicate{"PodFitsHostPorts": predicates.PodFitsHostPorts} scheduler, bindingChan := setupTestSchedulerLongBindingWithRetry( queuedPodStore, scache, nodeLister, predicateMap, stop, test.BindingDuration) scheduler.Run() queuedPodStore.Add(firstPod) queuedPodStore.Add(conflictPod) resultBindings := map[string]bool{} waitChan := time.After(5 * time.Second) for finished := false; !finished; { select { case b := <-bindingChan: resultBindings[b.Name] = true p := pods[b.Name] p.Spec.NodeName = b.Target.Name scache.AddPod(p) case <-waitChan: finished = true } } if !reflect.DeepEqual(resultBindings, test.Expected) { t.Errorf("Result binding are not equal to expected. %v != %v", resultBindings, test.Expected) } } }
// Initializes the factory. func NewConfigFactory(client *client.Client, schedulerName string, hardPodAffinitySymmetricWeight int, failureDomains string) *ConfigFactory { stopEverything := make(chan struct{}) schedulerCache := schedulercache.New(30*time.Second, stopEverything) c := &ConfigFactory{ Client: client, PodQueue: cache.NewFIFO(cache.MetaNamespaceKeyFunc), ScheduledPodLister: &cache.StoreToPodLister{}, // Only nodes in the "Ready" condition with status == "True" are schedulable NodeLister: &cache.StoreToNodeLister{}, PVLister: &cache.StoreToPVFetcher{Store: cache.NewStore(cache.MetaNamespaceKeyFunc)}, PVCLister: &cache.StoreToPVCFetcher{Store: cache.NewStore(cache.MetaNamespaceKeyFunc)}, ServiceLister: &cache.StoreToServiceLister{Store: cache.NewStore(cache.MetaNamespaceKeyFunc)}, ControllerLister: &cache.StoreToReplicationControllerLister{Indexer: cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})}, ReplicaSetLister: &cache.StoreToReplicaSetLister{Store: cache.NewStore(cache.MetaNamespaceKeyFunc)}, schedulerCache: schedulerCache, StopEverything: stopEverything, SchedulerName: schedulerName, HardPodAffinitySymmetricWeight: hardPodAffinitySymmetricWeight, FailureDomains: failureDomains, } c.PodLister = schedulerCache // On add/delete to the scheduled pods, remove from the assumed pods. // We construct this here instead of in CreateFromKeys because // ScheduledPodLister is something we provide to plug in functions that // they may need to call. c.ScheduledPodLister.Indexer, c.scheduledPodPopulator = framework.NewIndexerInformer( c.createAssignedNonTerminatedPodLW(), &api.Pod{}, 0, framework.ResourceEventHandlerFuncs{ AddFunc: c.addPodToCache, UpdateFunc: c.updatePodInCache, DeleteFunc: c.deletePodFromCache, }, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, ) c.NodeLister.Store, c.nodePopulator = framework.NewInformer( c.createNodeLW(), &api.Node{}, 0, framework.ResourceEventHandlerFuncs{ AddFunc: c.addNodeToCache, UpdateFunc: c.updateNodeInCache, DeleteFunc: c.deleteNodeFromCache, }, ) return c }
func TestSchedulerFailedSchedulingReasons(t *testing.T) { stop := make(chan struct{}) defer close(stop) queuedPodStore := clientcache.NewFIFO(clientcache.MetaNamespaceKeyFunc) scache := schedulercache.New(10*time.Minute, stop) node := api.Node{ ObjectMeta: api.ObjectMeta{Name: "machine1"}, Status: api.NodeStatus{ Capacity: api.ResourceList{ api.ResourceCPU: *(resource.NewQuantity(2, resource.DecimalSI)), api.ResourceMemory: *(resource.NewQuantity(100, resource.DecimalSI)), api.ResourcePods: *(resource.NewQuantity(10, resource.DecimalSI)), }, Allocatable: api.ResourceList{ api.ResourceCPU: *(resource.NewQuantity(2, resource.DecimalSI)), api.ResourceMemory: *(resource.NewQuantity(100, resource.DecimalSI)), api.ResourcePods: *(resource.NewQuantity(10, resource.DecimalSI)), }}, } scache.AddNode(&node) nodeLister := algorithm.FakeNodeLister([]*api.Node{&node}) predicateMap := map[string]algorithm.FitPredicate{ "PodFitsResources": predicates.PodFitsResources, } scheduler, _, errChan := setupTestScheduler(queuedPodStore, scache, nodeLister, predicateMap) podWithTooBigResourceRequests := podWithResources("bar", "", api.ResourceList{ api.ResourceCPU: *(resource.NewQuantity(4, resource.DecimalSI)), api.ResourceMemory: *(resource.NewQuantity(500, resource.DecimalSI)), }, api.ResourceList{ api.ResourceCPU: *(resource.NewQuantity(4, resource.DecimalSI)), api.ResourceMemory: *(resource.NewQuantity(500, resource.DecimalSI)), }) queuedPodStore.Add(podWithTooBigResourceRequests) scheduler.scheduleOne() select { case err := <-errChan: expectErr := &FitError{ Pod: podWithTooBigResourceRequests, FailedPredicates: FailedPredicateMap{node.Name: []algorithm.PredicateFailureReason{ predicates.NewInsufficientResourceError(api.ResourceCPU, 4000, 0, 2000), predicates.NewInsufficientResourceError(api.ResourceMemory, 500, 0, 100), }}, } if !reflect.DeepEqual(expectErr, err) { t.Errorf("err want=%+v, get=%+v", expectErr, err) } case <-time.After(wait.ForeverTestTimeout): t.Fatalf("timeout after %v", wait.ForeverTestTimeout) } }
func TestGenericScheduler(t *testing.T) { tests := []struct { name string predicates map[string]algorithm.FitPredicate prioritizers []algorithm.PriorityConfig nodes []string pod *api.Pod pods []*api.Pod expectedHosts sets.String expectsErr bool wErr error }{ { predicates: map[string]algorithm.FitPredicate{"false": falsePredicate}, prioritizers: []algorithm.PriorityConfig{{Map: EqualPriorityMap, Weight: 1}}, nodes: []string{"machine1", "machine2"}, expectsErr: true, pod: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "2"}}, name: "test 1", wErr: &FitError{ Pod: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "2"}}, FailedPredicates: FailedPredicateMap{ "machine1": []algorithm.PredicateFailureReason{algorithmpredicates.ErrFakePredicate}, "machine2": []algorithm.PredicateFailureReason{algorithmpredicates.ErrFakePredicate}, }}, }, { predicates: map[string]algorithm.FitPredicate{"true": truePredicate}, prioritizers: []algorithm.PriorityConfig{{Map: EqualPriorityMap, Weight: 1}}, nodes: []string{"machine1", "machine2"}, expectedHosts: sets.NewString("machine1", "machine2"), name: "test 2", wErr: nil, }, { // Fits on a machine where the pod ID matches the machine name predicates: map[string]algorithm.FitPredicate{"matches": matchesPredicate}, prioritizers: []algorithm.PriorityConfig{{Map: EqualPriorityMap, Weight: 1}}, nodes: []string{"machine1", "machine2"}, pod: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "machine2"}}, expectedHosts: sets.NewString("machine2"), name: "test 3", wErr: nil, }, { predicates: map[string]algorithm.FitPredicate{"true": truePredicate}, prioritizers: []algorithm.PriorityConfig{{Function: numericPriority, Weight: 1}}, nodes: []string{"3", "2", "1"}, expectedHosts: sets.NewString("3"), name: "test 4", wErr: nil, }, { predicates: map[string]algorithm.FitPredicate{"matches": matchesPredicate}, prioritizers: []algorithm.PriorityConfig{{Function: numericPriority, Weight: 1}}, nodes: []string{"3", "2", "1"}, pod: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "2"}}, expectedHosts: sets.NewString("2"), name: "test 5", wErr: nil, }, { predicates: map[string]algorithm.FitPredicate{"true": truePredicate}, prioritizers: []algorithm.PriorityConfig{{Function: numericPriority, Weight: 1}, {Function: reverseNumericPriority, Weight: 2}}, nodes: []string{"3", "2", "1"}, pod: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "2"}}, expectedHosts: sets.NewString("1"), name: "test 6", wErr: nil, }, { predicates: map[string]algorithm.FitPredicate{"true": truePredicate, "false": falsePredicate}, prioritizers: []algorithm.PriorityConfig{{Function: numericPriority, Weight: 1}}, nodes: []string{"3", "2", "1"}, pod: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "2"}}, expectsErr: true, name: "test 7", wErr: &FitError{ Pod: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "2"}}, FailedPredicates: FailedPredicateMap{ "3": []algorithm.PredicateFailureReason{algorithmpredicates.ErrFakePredicate}, "2": []algorithm.PredicateFailureReason{algorithmpredicates.ErrFakePredicate}, "1": []algorithm.PredicateFailureReason{algorithmpredicates.ErrFakePredicate}, }, }, }, { predicates: map[string]algorithm.FitPredicate{ "nopods": hasNoPodsPredicate, "matches": matchesPredicate, }, pods: []*api.Pod{ { ObjectMeta: api.ObjectMeta{Name: "2"}, Spec: api.PodSpec{ NodeName: "2", }, Status: api.PodStatus{ Phase: api.PodRunning, }, }, }, pod: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "2"}}, prioritizers: []algorithm.PriorityConfig{{Function: numericPriority, Weight: 1}}, nodes: []string{"1", "2"}, expectsErr: true, name: "test 8", wErr: &FitError{ Pod: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "2"}}, FailedPredicates: FailedPredicateMap{ "1": []algorithm.PredicateFailureReason{algorithmpredicates.ErrFakePredicate}, "2": []algorithm.PredicateFailureReason{algorithmpredicates.ErrFakePredicate}, }, }, }, } for _, test := range tests { cache := schedulercache.New(time.Duration(0), wait.NeverStop) for _, pod := range test.pods { cache.AddPod(pod) } for _, name := range test.nodes { cache.AddNode(&api.Node{ObjectMeta: api.ObjectMeta{Name: name}}) } scheduler := NewGenericScheduler( cache, test.predicates, algorithm.EmptyMetadataProducer, test.prioritizers, algorithm.EmptyMetadataProducer, []algorithm.SchedulerExtender{}) machine, err := scheduler.Schedule(test.pod, algorithm.FakeNodeLister(makeNodeList(test.nodes))) if !reflect.DeepEqual(err, test.wErr) { t.Errorf("Failed : %s, Unexpected error: %v, expected: %v", test.name, err, test.wErr) } if test.expectedHosts != nil && !test.expectedHosts.Has(machine) { t.Errorf("Failed : %s, Expected: %s, got: %s", test.name, test.expectedHosts, machine) } } }
// Initializes the factory. func NewConfigFactory(client *client.Client, schedulerName string) *ConfigFactory { stopEverything := make(chan struct{}) schedulerCache := schedulercache.New(30*time.Second, stopEverything) c := &ConfigFactory{ Client: client, PodQueue: cache.NewFIFO(cache.MetaNamespaceKeyFunc), ScheduledPodLister: &cache.StoreToPodLister{}, // Only nodes in the "Ready" condition with status == "True" are schedulable NodeLister: &cache.StoreToNodeLister{Store: cache.NewStore(cache.MetaNamespaceKeyFunc)}, PVLister: &cache.StoreToPVFetcher{Store: cache.NewStore(cache.MetaNamespaceKeyFunc)}, PVCLister: &cache.StoreToPVCFetcher{Store: cache.NewStore(cache.MetaNamespaceKeyFunc)}, ServiceLister: &cache.StoreToServiceLister{Store: cache.NewStore(cache.MetaNamespaceKeyFunc)}, ControllerLister: &cache.StoreToReplicationControllerLister{Store: cache.NewStore(cache.MetaNamespaceKeyFunc)}, ReplicaSetLister: &cache.StoreToReplicaSetLister{Store: cache.NewStore(cache.MetaNamespaceKeyFunc)}, schedulerCache: schedulerCache, StopEverything: stopEverything, SchedulerName: schedulerName, } c.PodLister = schedulerCache // On add/delete to the scheduled pods, remove from the assumed pods. // We construct this here instead of in CreateFromKeys because // ScheduledPodLister is something we provide to plug in functions that // they may need to call. c.ScheduledPodLister.Store, c.scheduledPodPopulator = framework.NewInformer( c.createAssignedNonTerminatedPodLW(), &api.Pod{}, 0, framework.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { pod, ok := obj.(*api.Pod) if !ok { glog.Errorf("cannot convert to *api.Pod") return } if err := schedulerCache.AddPod(pod); err != nil { glog.Errorf("scheduler cache AddPod failed: %v", err) } }, UpdateFunc: func(oldObj, newObj interface{}) { oldPod, ok := oldObj.(*api.Pod) if !ok { glog.Errorf("cannot convert to *api.Pod") return } newPod, ok := newObj.(*api.Pod) if !ok { glog.Errorf("cannot convert to *api.Pod") return } if err := schedulerCache.UpdatePod(oldPod, newPod); err != nil { glog.Errorf("scheduler cache UpdatePod failed: %v", err) } }, DeleteFunc: func(obj interface{}) { var pod *api.Pod switch t := obj.(type) { case *api.Pod: pod = t case cache.DeletedFinalStateUnknown: var ok bool pod, ok = t.Obj.(*api.Pod) if !ok { glog.Errorf("cannot convert to *api.Pod") return } default: glog.Errorf("cannot convert to *api.Pod") return } if err := schedulerCache.RemovePod(pod); err != nil { glog.Errorf("scheduler cache RemovePod failed: %v", err) } }, }, ) return c }
func TestSchedulerForgetAssumedPodAfterDelete(t *testing.T) { // Set up a channel through which we'll funnel log messages from the watcher. // This way, we can guarantee that when the test ends no thread will still be // trying to write to t.Logf (which it would if we handed t.Logf directly to // StartLogging). ch := make(chan string) done := make(chan struct{}) var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() for { select { case msg := <-ch: t.Log(msg) case <-done: return } } }() eventBroadcaster := record.NewBroadcaster() watcher := eventBroadcaster.StartLogging(func(format string, args ...interface{}) { ch <- fmt.Sprintf(format, args...) }) defer func() { watcher.Stop() close(done) wg.Wait() }() // Setup stores to test pod's workflow: // - queuedPodStore: pods queued before processing // - scheduledPodStore: pods that has a scheduling decision scheduledPodStore := cache.NewStore(cache.MetaNamespaceKeyFunc) queuedPodStore := cache.NewFIFO(cache.MetaNamespaceKeyFunc) // Port is the easiest way to cause a fit predicate failure podPort := 8080 firstPod := podWithPort("foo", "", podPort) stop := make(chan struct{}) defer close(stop) cache := schedulercache.New(1*time.Second, stop) // Create the scheduler config algo := NewGenericScheduler( cache, map[string]algorithm.FitPredicate{"PodFitsHostPorts": predicates.PodFitsHostPorts}, []algorithm.PriorityConfig{}, []algorithm.SchedulerExtender{}, rand.New(rand.NewSource(time.Now().UnixNano()))) var gotBinding *api.Binding c := &Config{ SchedulerCache: cache, NodeLister: algorithm.FakeNodeLister( api.NodeList{Items: []api.Node{{ObjectMeta: api.ObjectMeta{Name: "machine1"}}}}, ), Algorithm: algo, Binder: fakeBinder{func(b *api.Binding) error { scheduledPodStore.Add(podWithPort(b.Name, b.Target.Name, podPort)) gotBinding = b return nil }}, NextPod: func() *api.Pod { return queuedPodStore.Pop().(*api.Pod) }, Error: func(p *api.Pod, err error) { t.Errorf("Unexpected error when scheduling pod %+v: %v", p, err) }, Recorder: eventBroadcaster.NewRecorder(api.EventSource{Component: "scheduler"}), } // First scheduling pass should schedule the pod s := New(c) called := make(chan struct{}) events := eventBroadcaster.StartEventWatcher(func(e *api.Event) { if e, a := "Scheduled", e.Reason; e != a { t.Errorf("expected %v, got %v", e, a) } close(called) }) queuedPodStore.Add(firstPod) // queuedPodStore: [foo:8080] // scheduledPodStore: [] // assumedPods: [] s.scheduleOne() <-called // queuedPodStore: [] // scheduledPodStore: [foo:8080] // assumedPods: [foo:8080] pod, exists, _ := scheduledPodStore.GetByKey("foo") if !exists { t.Errorf("Expected scheduled pod store to contain pod") } pod, exists, _ = queuedPodStore.GetByKey("foo") if exists { t.Errorf("Did not expect a queued pod, found %+v", pod) } expectBind := &api.Binding{ ObjectMeta: api.ObjectMeta{Name: "foo"}, Target: api.ObjectReference{Kind: "Node", Name: "machine1"}, } if ex, ac := expectBind, gotBinding; !reflect.DeepEqual(ex, ac) { t.Errorf("Expected exact match on binding: %s", diff.ObjectDiff(ex, ac)) } events.Stop() scheduledPodStore.Delete(pod) secondPod := podWithPort("bar", "", podPort) queuedPodStore.Add(secondPod) // queuedPodStore: [bar:8080] // scheduledPodStore: [] // assumedPods: [foo:8080] var waitUntilExpired sync.WaitGroup waitUntilExpired.Add(1) // waiting for the assumed pod to expire go func() { for { pods, err := cache.List(labels.Everything()) if err != nil { t.Fatalf("cache.List failed: %v", err) } if len(pods) == 0 { waitUntilExpired.Done() return } time.Sleep(1 * time.Second) } }() waitUntilExpired.Wait() // Second scheduling pass will fail to schedule if the store hasn't expired // the deleted pod. This would normally happen with a timeout. called = make(chan struct{}) events = eventBroadcaster.StartEventWatcher(func(e *api.Event) { if e, a := "Scheduled", e.Reason; e != a { t.Errorf("expected %v, got %v", e, a) } close(called) }) s.scheduleOne() <-called expectBind = &api.Binding{ ObjectMeta: api.ObjectMeta{Name: "bar"}, Target: api.ObjectReference{Kind: "Node", Name: "machine1"}, } if ex, ac := expectBind, gotBinding; !reflect.DeepEqual(ex, ac) { t.Errorf("Expected exact match on binding: %s", diff.ObjectDiff(ex, ac)) } events.Stop() }
func TestSchedulerFailedSchedulingReasons(t *testing.T) { stop := make(chan struct{}) defer close(stop) queuedPodStore := clientcache.NewFIFO(clientcache.MetaNamespaceKeyFunc) scache := schedulercache.New(10*time.Minute, stop) // Design the baseline for the pods, and we will make nodes that dont fit it later. var cpu = int64(4) var mem = int64(500) podWithTooBigResourceRequests := podWithResources("bar", "", api.ResourceList{ api.ResourceCPU: *(resource.NewQuantity(cpu, resource.DecimalSI)), api.ResourceMemory: *(resource.NewQuantity(mem, resource.DecimalSI)), }, api.ResourceList{ api.ResourceCPU: *(resource.NewQuantity(cpu, resource.DecimalSI)), api.ResourceMemory: *(resource.NewQuantity(mem, resource.DecimalSI)), }) // create several nodes which cannot schedule the above pod nodes := []*api.Node{} for i := 0; i < 100; i++ { node := api.Node{ ObjectMeta: api.ObjectMeta{Name: fmt.Sprintf("machine%v", i)}, Status: api.NodeStatus{ Capacity: api.ResourceList{ api.ResourceCPU: *(resource.NewQuantity(cpu/2, resource.DecimalSI)), api.ResourceMemory: *(resource.NewQuantity(mem/5, resource.DecimalSI)), api.ResourcePods: *(resource.NewQuantity(10, resource.DecimalSI)), }, Allocatable: api.ResourceList{ api.ResourceCPU: *(resource.NewQuantity(cpu/2, resource.DecimalSI)), api.ResourceMemory: *(resource.NewQuantity(mem/5, resource.DecimalSI)), api.ResourcePods: *(resource.NewQuantity(10, resource.DecimalSI)), }}, } scache.AddNode(&node) nodes = append(nodes, &node) } nodeLister := algorithm.FakeNodeLister(nodes) predicateMap := map[string]algorithm.FitPredicate{ "PodFitsResources": predicates.PodFitsResources, } // Create expected failure reasons for all the nodes. Hopefully they will get rolled up into a non-spammy summary. failedPredicatesMap := FailedPredicateMap{} for _, node := range nodes { failedPredicatesMap[node.Name] = []algorithm.PredicateFailureReason{ predicates.NewInsufficientResourceError(api.ResourceCPU, 4000, 0, 2000), predicates.NewInsufficientResourceError(api.ResourceMemory, 500, 0, 100), } } scheduler, _, errChan := setupTestScheduler(queuedPodStore, scache, nodeLister, predicateMap) queuedPodStore.Add(podWithTooBigResourceRequests) scheduler.scheduleOne() select { case err := <-errChan: expectErr := &FitError{ Pod: podWithTooBigResourceRequests, FailedPredicates: failedPredicatesMap, } if len(fmt.Sprint(expectErr)) > 150 { t.Errorf("message is too spammy ! %v ", len(fmt.Sprint(expectErr))) } if !reflect.DeepEqual(expectErr, err) { t.Errorf("\n err \nWANT=%+v,\nGOT=%+v", expectErr, err) } case <-time.After(wait.ForeverTestTimeout): t.Fatalf("timeout after %v", wait.ForeverTestTimeout) } }
// Initializes the factory. func NewConfigFactory(client clientset.Interface, schedulerName string, hardPodAffinitySymmetricWeight int, failureDomains string) *ConfigFactory { stopEverything := make(chan struct{}) schedulerCache := schedulercache.New(30*time.Second, stopEverything) // TODO: pass this in as an argument... informerFactory := informers.NewSharedInformerFactory(client, nil, 0) pvcInformer := informerFactory.PersistentVolumeClaims() c := &ConfigFactory{ Client: client, PodQueue: cache.NewFIFO(cache.MetaNamespaceKeyFunc), ScheduledPodLister: &cache.StoreToPodLister{}, informerFactory: informerFactory, // Only nodes in the "Ready" condition with status == "True" are schedulable NodeLister: &cache.StoreToNodeLister{}, PVLister: &cache.StoreToPVFetcher{Store: cache.NewStore(cache.MetaNamespaceKeyFunc)}, PVCLister: pvcInformer.Lister(), pvcPopulator: pvcInformer.Informer().GetController(), ServiceLister: &cache.StoreToServiceLister{Indexer: cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})}, ControllerLister: &cache.StoreToReplicationControllerLister{Indexer: cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})}, ReplicaSetLister: &cache.StoreToReplicaSetLister{Indexer: cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})}, schedulerCache: schedulerCache, StopEverything: stopEverything, SchedulerName: schedulerName, HardPodAffinitySymmetricWeight: hardPodAffinitySymmetricWeight, FailureDomains: failureDomains, } c.PodLister = schedulerCache // On add/delete to the scheduled pods, remove from the assumed pods. // We construct this here instead of in CreateFromKeys because // ScheduledPodLister is something we provide to plug in functions that // they may need to call. c.ScheduledPodLister.Indexer, c.scheduledPodPopulator = cache.NewIndexerInformer( c.createAssignedNonTerminatedPodLW(), &v1.Pod{}, 0, cache.ResourceEventHandlerFuncs{ AddFunc: c.addPodToCache, UpdateFunc: c.updatePodInCache, DeleteFunc: c.deletePodFromCache, }, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, ) c.NodeLister.Store, c.nodePopulator = cache.NewInformer( c.createNodeLW(), &v1.Node{}, 0, cache.ResourceEventHandlerFuncs{ AddFunc: c.addNodeToCache, UpdateFunc: c.updateNodeInCache, DeleteFunc: c.deleteNodeFromCache, }, ) // TODO(harryz) need to fill all the handlers here and below for equivalence cache c.PVLister.Store, c.pvPopulator = cache.NewInformer( c.createPersistentVolumeLW(), &v1.PersistentVolume{}, 0, cache.ResourceEventHandlerFuncs{}, ) c.ServiceLister.Indexer, c.servicePopulator = cache.NewIndexerInformer( c.createServiceLW(), &v1.Service{}, 0, cache.ResourceEventHandlerFuncs{}, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, ) c.ControllerLister.Indexer, c.controllerPopulator = cache.NewIndexerInformer( c.createControllerLW(), &v1.ReplicationController{}, 0, cache.ResourceEventHandlerFuncs{}, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, ) return c }
func TestGenericSchedulerWithExtenders(t *testing.T) { tests := []struct { name string predicates map[string]algorithm.FitPredicate prioritizers []algorithm.PriorityConfig extenders []FakeExtender extenderPredicates []fitPredicate extenderPrioritizers []priorityConfig nodes []string expectedHost string expectsErr bool }{ { predicates: map[string]algorithm.FitPredicate{"true": truePredicate}, prioritizers: []algorithm.PriorityConfig{{Map: EqualPriorityMap, Weight: 1}}, extenders: []FakeExtender{ { predicates: []fitPredicate{truePredicateExtender}, }, { predicates: []fitPredicate{errorPredicateExtender}, }, }, nodes: []string{"machine1", "machine2"}, expectsErr: true, name: "test 1", }, { predicates: map[string]algorithm.FitPredicate{"true": truePredicate}, prioritizers: []algorithm.PriorityConfig{{Map: EqualPriorityMap, Weight: 1}}, extenders: []FakeExtender{ { predicates: []fitPredicate{truePredicateExtender}, }, { predicates: []fitPredicate{falsePredicateExtender}, }, }, nodes: []string{"machine1", "machine2"}, expectsErr: true, name: "test 2", }, { predicates: map[string]algorithm.FitPredicate{"true": truePredicate}, prioritizers: []algorithm.PriorityConfig{{Map: EqualPriorityMap, Weight: 1}}, extenders: []FakeExtender{ { predicates: []fitPredicate{truePredicateExtender}, }, { predicates: []fitPredicate{machine1PredicateExtender}, }, }, nodes: []string{"machine1", "machine2"}, expectedHost: "machine1", name: "test 3", }, { predicates: map[string]algorithm.FitPredicate{"true": truePredicate}, prioritizers: []algorithm.PriorityConfig{{Map: EqualPriorityMap, Weight: 1}}, extenders: []FakeExtender{ { predicates: []fitPredicate{machine2PredicateExtender}, }, { predicates: []fitPredicate{machine1PredicateExtender}, }, }, nodes: []string{"machine1", "machine2"}, expectsErr: true, name: "test 4", }, { predicates: map[string]algorithm.FitPredicate{"true": truePredicate}, prioritizers: []algorithm.PriorityConfig{{Map: EqualPriorityMap, Weight: 1}}, extenders: []FakeExtender{ { predicates: []fitPredicate{truePredicateExtender}, prioritizers: []priorityConfig{{errorPrioritizerExtender, 10}}, weight: 1, }, }, nodes: []string{"machine1"}, expectedHost: "machine1", name: "test 5", }, { predicates: map[string]algorithm.FitPredicate{"true": truePredicate}, prioritizers: []algorithm.PriorityConfig{{Map: EqualPriorityMap, Weight: 1}}, extenders: []FakeExtender{ { predicates: []fitPredicate{truePredicateExtender}, prioritizers: []priorityConfig{{machine1PrioritizerExtender, 10}}, weight: 1, }, { predicates: []fitPredicate{truePredicateExtender}, prioritizers: []priorityConfig{{machine2PrioritizerExtender, 10}}, weight: 5, }, }, nodes: []string{"machine1", "machine2"}, expectedHost: "machine2", name: "test 6", }, { predicates: map[string]algorithm.FitPredicate{"true": truePredicate}, prioritizers: []algorithm.PriorityConfig{{Function: machine2Prioritizer, Weight: 20}}, extenders: []FakeExtender{ { predicates: []fitPredicate{truePredicateExtender}, prioritizers: []priorityConfig{{machine1PrioritizerExtender, 10}}, weight: 1, }, }, nodes: []string{"machine1", "machine2"}, expectedHost: "machine2", // machine2 has higher score name: "test 7", }, } for _, test := range tests { extenders := []algorithm.SchedulerExtender{} for ii := range test.extenders { extenders = append(extenders, &test.extenders[ii]) } cache := schedulercache.New(time.Duration(0), wait.NeverStop) for _, name := range test.nodes { cache.AddNode(&v1.Node{ObjectMeta: metav1.ObjectMeta{Name: name}}) } scheduler := NewGenericScheduler( cache, test.predicates, algorithm.EmptyMetadataProducer, test.prioritizers, algorithm.EmptyMetadataProducer, extenders) podIgnored := &v1.Pod{} machine, err := scheduler.Schedule(podIgnored, algorithm.FakeNodeLister(makeNodeList(test.nodes))) if test.expectsErr { if err == nil { t.Errorf("Unexpected non-error for %s, machine %s", test.name, machine) } } else { if err != nil { t.Errorf("Unexpected error: %v", err) continue } if test.expectedHost != machine { t.Errorf("Failed : %s, Expected: %s, Saw: %s", test.name, test.expectedHost, machine) } } } }