1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801 |
- /*
- Copyright 2019 The Kubernetes Authors.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package v1alpha1
- import (
- "context"
- "fmt"
- "reflect"
- "strings"
- "testing"
- "time"
- "github.com/prometheus/client_golang/prometheus"
- dto "github.com/prometheus/client_model/go"
- v1 "k8s.io/api/core/v1"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/runtime"
- "k8s.io/apimachinery/pkg/types"
- "k8s.io/kubernetes/pkg/scheduler/apis/config"
- "k8s.io/kubernetes/pkg/scheduler/metrics"
- schedulernodeinfo "k8s.io/kubernetes/pkg/scheduler/nodeinfo"
- )
- const (
- queueSortPlugin = "no-op-queue-sort-plugin"
- scoreWithNormalizePlugin1 = "score-with-normalize-plugin-1"
- scoreWithNormalizePlugin2 = "score-with-normalize-plugin-2"
- scorePlugin1 = "score-plugin-1"
- pluginNotImplementingScore = "plugin-not-implementing-score"
- preFilterPluginName = "prefilter-plugin"
- preFilterWithExtensionsPluginName = "prefilter-with-extensions-plugin"
- duplicatePluginName = "duplicate-plugin"
- testPlugin = "test-plugin"
- permitPlugin = "permit-plugin"
- bindPlugin = "bind-plugin"
- )
- // TestScoreWithNormalizePlugin implements ScoreWithNormalizePlugin interface.
- // TestScorePlugin only implements ScorePlugin interface.
- var _ ScorePlugin = &TestScoreWithNormalizePlugin{}
- var _ ScorePlugin = &TestScorePlugin{}
- func newScoreWithNormalizePlugin1(injArgs *runtime.Unknown, f FrameworkHandle) (Plugin, error) {
- var inj injectedResult
- if err := DecodeInto(injArgs, &inj); err != nil {
- return nil, err
- }
- return &TestScoreWithNormalizePlugin{scoreWithNormalizePlugin1, inj}, nil
- }
- func newScoreWithNormalizePlugin2(injArgs *runtime.Unknown, f FrameworkHandle) (Plugin, error) {
- var inj injectedResult
- if err := DecodeInto(injArgs, &inj); err != nil {
- return nil, err
- }
- return &TestScoreWithNormalizePlugin{scoreWithNormalizePlugin2, inj}, nil
- }
- func newScorePlugin1(injArgs *runtime.Unknown, f FrameworkHandle) (Plugin, error) {
- var inj injectedResult
- if err := DecodeInto(injArgs, &inj); err != nil {
- return nil, err
- }
- return &TestScorePlugin{scorePlugin1, inj}, nil
- }
- func newPluginNotImplementingScore(_ *runtime.Unknown, _ FrameworkHandle) (Plugin, error) {
- return &PluginNotImplementingScore{}, nil
- }
- type TestScoreWithNormalizePlugin struct {
- name string
- inj injectedResult
- }
- func (pl *TestScoreWithNormalizePlugin) Name() string {
- return pl.name
- }
- func (pl *TestScoreWithNormalizePlugin) NormalizeScore(ctx context.Context, state *CycleState, pod *v1.Pod, scores NodeScoreList) *Status {
- return injectNormalizeRes(pl.inj, scores)
- }
- func (pl *TestScoreWithNormalizePlugin) Score(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string) (int64, *Status) {
- return setScoreRes(pl.inj)
- }
- func (pl *TestScoreWithNormalizePlugin) ScoreExtensions() ScoreExtensions {
- return pl
- }
- // TestScorePlugin only implements ScorePlugin interface.
- type TestScorePlugin struct {
- name string
- inj injectedResult
- }
- func (pl *TestScorePlugin) Name() string {
- return pl.name
- }
- func (pl *TestScorePlugin) Score(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string) (int64, *Status) {
- return setScoreRes(pl.inj)
- }
- func (pl *TestScorePlugin) ScoreExtensions() ScoreExtensions {
- return nil
- }
- // PluginNotImplementingScore doesn't implement the ScorePlugin interface.
- type PluginNotImplementingScore struct{}
- func (pl *PluginNotImplementingScore) Name() string {
- return pluginNotImplementingScore
- }
- // TestPlugin implements all Plugin interfaces.
- type TestPlugin struct {
- name string
- inj injectedResult
- }
- type TestPluginPreFilterExtension struct {
- inj injectedResult
- }
- func (e *TestPluginPreFilterExtension) AddPod(ctx context.Context, state *CycleState, podToSchedule *v1.Pod, podToAdd *v1.Pod, nodeInfo *schedulernodeinfo.NodeInfo) *Status {
- return NewStatus(Code(e.inj.PreFilterAddPodStatus), "injected status")
- }
- func (e *TestPluginPreFilterExtension) RemovePod(ctx context.Context, state *CycleState, podToSchedule *v1.Pod, podToRemove *v1.Pod, nodeInfo *schedulernodeinfo.NodeInfo) *Status {
- return NewStatus(Code(e.inj.PreFilterRemovePodStatus), "injected status")
- }
- func (pl *TestPlugin) Name() string {
- return pl.name
- }
- func (pl *TestPlugin) Score(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string) (int64, *Status) {
- return 0, NewStatus(Code(pl.inj.ScoreStatus), "injected status")
- }
- func (pl *TestPlugin) ScoreExtensions() ScoreExtensions {
- return nil
- }
- func (pl *TestPlugin) PreFilter(ctx context.Context, state *CycleState, p *v1.Pod) *Status {
- return NewStatus(Code(pl.inj.PreFilterStatus), "injected status")
- }
- func (pl *TestPlugin) PreFilterExtensions() PreFilterExtensions {
- return &TestPluginPreFilterExtension{inj: pl.inj}
- }
- func (pl *TestPlugin) Filter(ctx context.Context, state *CycleState, pod *v1.Pod, nodeInfo *schedulernodeinfo.NodeInfo) *Status {
- return NewStatus(Code(pl.inj.FilterStatus), "injected filter status")
- }
- func (pl *TestPlugin) PostFilter(ctx context.Context, state *CycleState, pod *v1.Pod, nodes []*v1.Node, filteredNodesStatuses NodeToStatusMap) *Status {
- return NewStatus(Code(pl.inj.PostFilterStatus), "injected status")
- }
- func (pl *TestPlugin) Reserve(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string) *Status {
- return NewStatus(Code(pl.inj.ReserveStatus), "injected status")
- }
- func (pl *TestPlugin) PreBind(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string) *Status {
- return NewStatus(Code(pl.inj.PreBindStatus), "injected status")
- }
- func (pl *TestPlugin) PostBind(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string) {}
- func (pl *TestPlugin) Unreserve(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string) {}
- func (pl *TestPlugin) Permit(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string) (*Status, time.Duration) {
- return NewStatus(Code(pl.inj.PermitStatus), "injected status"), time.Duration(0)
- }
- func (pl *TestPlugin) Bind(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string) *Status {
- return NewStatus(Code(pl.inj.BindStatus), "injected status")
- }
- // TestPreFilterPlugin only implements PreFilterPlugin interface.
- type TestPreFilterPlugin struct {
- PreFilterCalled int
- }
- func (pl *TestPreFilterPlugin) Name() string {
- return preFilterPluginName
- }
- func (pl *TestPreFilterPlugin) PreFilter(ctx context.Context, state *CycleState, p *v1.Pod) *Status {
- pl.PreFilterCalled++
- return nil
- }
- func (pl *TestPreFilterPlugin) PreFilterExtensions() PreFilterExtensions {
- return nil
- }
- // TestPreFilterWithExtensionsPlugin implements Add/Remove interfaces.
- type TestPreFilterWithExtensionsPlugin struct {
- PreFilterCalled int
- AddCalled int
- RemoveCalled int
- }
- func (pl *TestPreFilterWithExtensionsPlugin) Name() string {
- return preFilterWithExtensionsPluginName
- }
- func (pl *TestPreFilterWithExtensionsPlugin) PreFilter(ctx context.Context, state *CycleState, p *v1.Pod) *Status {
- pl.PreFilterCalled++
- return nil
- }
- func (pl *TestPreFilterWithExtensionsPlugin) AddPod(ctx context.Context, state *CycleState, podToSchedule *v1.Pod,
- podToAdd *v1.Pod, nodeInfo *schedulernodeinfo.NodeInfo) *Status {
- pl.AddCalled++
- return nil
- }
- func (pl *TestPreFilterWithExtensionsPlugin) RemovePod(ctx context.Context, state *CycleState, podToSchedule *v1.Pod,
- podToRemove *v1.Pod, nodeInfo *schedulernodeinfo.NodeInfo) *Status {
- pl.RemoveCalled++
- return nil
- }
- func (pl *TestPreFilterWithExtensionsPlugin) PreFilterExtensions() PreFilterExtensions {
- return pl
- }
- type TestDuplicatePlugin struct {
- }
- func (dp *TestDuplicatePlugin) Name() string {
- return duplicatePluginName
- }
- func (dp *TestDuplicatePlugin) PreFilter(ctx context.Context, state *CycleState, p *v1.Pod) *Status {
- return nil
- }
- func (dp *TestDuplicatePlugin) PreFilterExtensions() PreFilterExtensions {
- return nil
- }
- var _ PreFilterPlugin = &TestDuplicatePlugin{}
- func newDuplicatePlugin(_ *runtime.Unknown, _ FrameworkHandle) (Plugin, error) {
- return &TestDuplicatePlugin{}, nil
- }
- // TestPermitPlugin only implements PermitPlugin interface.
- type TestPermitPlugin struct {
- PreFilterCalled int
- }
- func (pp *TestPermitPlugin) Name() string {
- return permitPlugin
- }
- func (pp *TestPermitPlugin) Permit(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string) (*Status, time.Duration) {
- return NewStatus(Wait, ""), time.Duration(10 * time.Second)
- }
- var _ QueueSortPlugin = &TestQueueSortPlugin{}
- func newQueueSortPlugin(_ *runtime.Unknown, _ FrameworkHandle) (Plugin, error) {
- return &TestQueueSortPlugin{}, nil
- }
- // TestQueueSortPlugin is a no-op implementation for QueueSort extension point.
- type TestQueueSortPlugin struct{}
- func (pl *TestQueueSortPlugin) Name() string {
- return queueSortPlugin
- }
- func (pl *TestQueueSortPlugin) Less(_, _ *PodInfo) bool {
- return false
- }
- var _ BindPlugin = &TestBindPlugin{}
- func newBindPlugin(_ *runtime.Unknown, _ FrameworkHandle) (Plugin, error) {
- return &TestBindPlugin{}, nil
- }
- // TestBindPlugin is a no-op implementation for Bind extension point.
- type TestBindPlugin struct{}
- func (t TestBindPlugin) Name() string {
- return bindPlugin
- }
- func (t TestBindPlugin) Bind(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string) *Status {
- return nil
- }
- var registry = func() Registry {
- r := make(Registry)
- r.Register(scoreWithNormalizePlugin1, newScoreWithNormalizePlugin1)
- r.Register(scoreWithNormalizePlugin2, newScoreWithNormalizePlugin2)
- r.Register(scorePlugin1, newScorePlugin1)
- r.Register(pluginNotImplementingScore, newPluginNotImplementingScore)
- r.Register(duplicatePluginName, newDuplicatePlugin)
- return r
- }()
- var defaultWeights = map[string]int32{
- scoreWithNormalizePlugin1: 1,
- scoreWithNormalizePlugin2: 2,
- scorePlugin1: 1,
- }
- var emptyArgs = make([]config.PluginConfig, 0)
- var state = &CycleState{}
- // Pod is only used for logging errors.
- var pod = &v1.Pod{}
- var nodes = []*v1.Node{
- {ObjectMeta: metav1.ObjectMeta{Name: "node1"}},
- {ObjectMeta: metav1.ObjectMeta{Name: "node2"}},
- }
- func newFrameworkWithQueueSortAndBind(r Registry, pl *config.Plugins, plc []config.PluginConfig, opts ...Option) (Framework, error) {
- if _, ok := r[queueSortPlugin]; !ok {
- r[queueSortPlugin] = newQueueSortPlugin
- }
- if _, ok := r[bindPlugin]; !ok {
- r[bindPlugin] = newBindPlugin
- }
- plugins := &config.Plugins{}
- plugins.Append(pl)
- if plugins.QueueSort == nil || len(plugins.QueueSort.Enabled) == 0 {
- plugins.Append(&config.Plugins{
- QueueSort: &config.PluginSet{
- Enabled: []config.Plugin{{Name: queueSortPlugin}},
- },
- })
- }
- if plugins.Bind == nil || len(plugins.Bind.Enabled) == 0 {
- plugins.Append(&config.Plugins{
- Bind: &config.PluginSet{
- Enabled: []config.Plugin{{Name: bindPlugin}},
- },
- })
- }
- return NewFramework(r, plugins, plc, opts...)
- }
- func TestInitFrameworkWithScorePlugins(t *testing.T) {
- tests := []struct {
- name string
- plugins *config.Plugins
- // If initErr is true, we expect framework initialization to fail.
- initErr bool
- }{
- {
- name: "enabled Score plugin doesn't exist in registry",
- plugins: buildScoreConfigDefaultWeights("notExist"),
- initErr: true,
- },
- {
- name: "enabled Score plugin doesn't extend the ScorePlugin interface",
- plugins: buildScoreConfigDefaultWeights(pluginNotImplementingScore),
- initErr: true,
- },
- {
- name: "Score plugins are nil",
- plugins: &config.Plugins{Score: nil},
- },
- {
- name: "enabled Score plugin list is empty",
- plugins: buildScoreConfigDefaultWeights(),
- },
- {
- name: "enabled plugin only implements ScorePlugin interface",
- plugins: buildScoreConfigDefaultWeights(scorePlugin1),
- },
- {
- name: "enabled plugin implements ScoreWithNormalizePlugin interface",
- plugins: buildScoreConfigDefaultWeights(scoreWithNormalizePlugin1),
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- _, err := newFrameworkWithQueueSortAndBind(registry, tt.plugins, emptyArgs)
- if tt.initErr && err == nil {
- t.Fatal("Framework initialization should fail")
- }
- if !tt.initErr && err != nil {
- t.Fatalf("Failed to create framework for testing: %v", err)
- }
- })
- }
- }
- func TestRegisterDuplicatePluginWouldFail(t *testing.T) {
- plugin := config.Plugin{Name: duplicatePluginName, Weight: 1}
- pluginSet := config.PluginSet{
- Enabled: []config.Plugin{
- plugin,
- plugin,
- },
- }
- plugins := config.Plugins{}
- plugins.PreFilter = &pluginSet
- _, err := NewFramework(registry, &plugins, emptyArgs)
- if err == nil {
- t.Fatal("Framework initialization should fail")
- }
- if err != nil && !strings.Contains(err.Error(), "already registered") {
- t.Fatalf("Unexpected error, got %s, expect: plugin already registered", err.Error())
- }
- }
- func TestRunScorePlugins(t *testing.T) {
- tests := []struct {
- name string
- registry Registry
- plugins *config.Plugins
- pluginConfigs []config.PluginConfig
- want PluginToNodeScores
- // If err is true, we expect RunScorePlugin to fail.
- err bool
- }{
- {
- name: "no Score plugins",
- plugins: buildScoreConfigDefaultWeights(),
- want: PluginToNodeScores{},
- },
- {
- name: "single Score plugin",
- plugins: buildScoreConfigDefaultWeights(scorePlugin1),
- pluginConfigs: []config.PluginConfig{
- {
- Name: scorePlugin1,
- Args: runtime.Unknown{
- Raw: []byte(`{ "scoreRes": 1 }`),
- },
- },
- },
- // scorePlugin1 Score returns 1, weight=1, so want=1.
- want: PluginToNodeScores{
- scorePlugin1: {{Name: "node1", Score: 1}, {Name: "node2", Score: 1}},
- },
- },
- {
- name: "single ScoreWithNormalize plugin",
- //registry: registry,
- plugins: buildScoreConfigDefaultWeights(scoreWithNormalizePlugin1),
- pluginConfigs: []config.PluginConfig{
- {
- Name: scoreWithNormalizePlugin1,
- Args: runtime.Unknown{
- Raw: []byte(`{ "scoreRes": 10, "normalizeRes": 5 }`),
- },
- },
- },
- // scoreWithNormalizePlugin1 Score returns 10, but NormalizeScore overrides to 5, weight=1, so want=5
- want: PluginToNodeScores{
- scoreWithNormalizePlugin1: {{Name: "node1", Score: 5}, {Name: "node2", Score: 5}},
- },
- },
- {
- name: "2 Score plugins, 2 NormalizeScore plugins",
- plugins: buildScoreConfigDefaultWeights(scorePlugin1, scoreWithNormalizePlugin1, scoreWithNormalizePlugin2),
- pluginConfigs: []config.PluginConfig{
- {
- Name: scorePlugin1,
- Args: runtime.Unknown{
- Raw: []byte(`{ "scoreRes": 1 }`),
- },
- },
- {
- Name: scoreWithNormalizePlugin1,
- Args: runtime.Unknown{
- Raw: []byte(`{ "scoreRes": 3, "normalizeRes": 4}`),
- },
- },
- {
- Name: scoreWithNormalizePlugin2,
- Args: runtime.Unknown{
- Raw: []byte(`{ "scoreRes": 4, "normalizeRes": 5}`),
- },
- },
- },
- // scorePlugin1 Score returns 1, weight =1, so want=1.
- // scoreWithNormalizePlugin1 Score returns 3, but NormalizeScore overrides to 4, weight=1, so want=4.
- // scoreWithNormalizePlugin2 Score returns 4, but NormalizeScore overrides to 5, weight=2, so want=10.
- want: PluginToNodeScores{
- scorePlugin1: {{Name: "node1", Score: 1}, {Name: "node2", Score: 1}},
- scoreWithNormalizePlugin1: {{Name: "node1", Score: 4}, {Name: "node2", Score: 4}},
- scoreWithNormalizePlugin2: {{Name: "node1", Score: 10}, {Name: "node2", Score: 10}},
- },
- },
- {
- name: "score fails",
- pluginConfigs: []config.PluginConfig{
- {
- Name: scoreWithNormalizePlugin1,
- Args: runtime.Unknown{
- Raw: []byte(`{ "scoreStatus": 1 }`),
- },
- },
- },
- plugins: buildScoreConfigDefaultWeights(scorePlugin1, scoreWithNormalizePlugin1),
- err: true,
- },
- {
- name: "normalize fails",
- pluginConfigs: []config.PluginConfig{
- {
- Name: scoreWithNormalizePlugin1,
- Args: runtime.Unknown{
- Raw: []byte(`{ "normalizeStatus": 1 }`),
- },
- },
- },
- plugins: buildScoreConfigDefaultWeights(scorePlugin1, scoreWithNormalizePlugin1),
- err: true,
- },
- {
- name: "Score plugin return score greater than MaxNodeScore",
- plugins: buildScoreConfigDefaultWeights(scorePlugin1),
- pluginConfigs: []config.PluginConfig{
- {
- Name: scorePlugin1,
- Args: runtime.Unknown{
- Raw: []byte(fmt.Sprintf(`{ "scoreRes": %d }`, MaxNodeScore+1)),
- },
- },
- },
- err: true,
- },
- {
- name: "Score plugin return score less than MinNodeScore",
- plugins: buildScoreConfigDefaultWeights(scorePlugin1),
- pluginConfigs: []config.PluginConfig{
- {
- Name: scorePlugin1,
- Args: runtime.Unknown{
- Raw: []byte(fmt.Sprintf(`{ "scoreRes": %d }`, MinNodeScore-1)),
- },
- },
- },
- err: true,
- },
- {
- name: "ScoreWithNormalize plugin return score greater than MaxNodeScore",
- plugins: buildScoreConfigDefaultWeights(scoreWithNormalizePlugin1),
- pluginConfigs: []config.PluginConfig{
- {
- Name: scoreWithNormalizePlugin1,
- Args: runtime.Unknown{
- Raw: []byte(fmt.Sprintf(`{ "normalizeRes": %d }`, MaxNodeScore+1)),
- },
- },
- },
- err: true,
- },
- {
- name: "ScoreWithNormalize plugin return score less than MinNodeScore",
- plugins: buildScoreConfigDefaultWeights(scoreWithNormalizePlugin1),
- pluginConfigs: []config.PluginConfig{
- {
- Name: scoreWithNormalizePlugin1,
- Args: runtime.Unknown{
- Raw: []byte(fmt.Sprintf(`{ "normalizeRes": %d }`, MinNodeScore-1)),
- },
- },
- },
- err: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- // Inject the results via Args in PluginConfig.
- f, err := newFrameworkWithQueueSortAndBind(registry, tt.plugins, tt.pluginConfigs)
- if err != nil {
- t.Fatalf("Failed to create framework for testing: %v", err)
- }
- res, status := f.RunScorePlugins(context.Background(), state, pod, nodes)
- if tt.err {
- if status.IsSuccess() {
- t.Errorf("Expected status to be non-success. got: %v", status.Code().String())
- }
- return
- }
- if !status.IsSuccess() {
- t.Errorf("Expected status to be success.")
- }
- if !reflect.DeepEqual(res, tt.want) {
- t.Errorf("Score map after RunScorePlugin: %+v, want: %+v.", res, tt.want)
- }
- })
- }
- }
- func TestPreFilterPlugins(t *testing.T) {
- preFilter1 := &TestPreFilterPlugin{}
- preFilter2 := &TestPreFilterWithExtensionsPlugin{}
- r := make(Registry)
- r.Register(preFilterPluginName,
- func(_ *runtime.Unknown, fh FrameworkHandle) (Plugin, error) {
- return preFilter1, nil
- })
- r.Register(preFilterWithExtensionsPluginName,
- func(_ *runtime.Unknown, fh FrameworkHandle) (Plugin, error) {
- return preFilter2, nil
- })
- plugins := &config.Plugins{PreFilter: &config.PluginSet{Enabled: []config.Plugin{{Name: preFilterWithExtensionsPluginName}, {Name: preFilterPluginName}}}}
- t.Run("TestPreFilterPlugin", func(t *testing.T) {
- f, err := newFrameworkWithQueueSortAndBind(r, plugins, emptyArgs)
- if err != nil {
- t.Fatalf("Failed to create framework for testing: %v", err)
- }
- f.RunPreFilterPlugins(context.Background(), nil, nil)
- f.RunPreFilterExtensionAddPod(context.Background(), nil, nil, nil, nil)
- f.RunPreFilterExtensionRemovePod(context.Background(), nil, nil, nil, nil)
- if preFilter1.PreFilterCalled != 1 {
- t.Errorf("preFilter1 called %v, expected: 1", preFilter1.PreFilterCalled)
- }
- if preFilter2.PreFilterCalled != 1 {
- t.Errorf("preFilter2 called %v, expected: 1", preFilter2.PreFilterCalled)
- }
- if preFilter2.AddCalled != 1 {
- t.Errorf("AddPod called %v, expected: 1", preFilter2.AddCalled)
- }
- if preFilter2.RemoveCalled != 1 {
- t.Errorf("AddPod called %v, expected: 1", preFilter2.RemoveCalled)
- }
- })
- }
- func TestFilterPlugins(t *testing.T) {
- tests := []struct {
- name string
- plugins []*TestPlugin
- wantStatus *Status
- wantStatusMap PluginToStatus
- runAllFilters bool
- }{
- {
- name: "SuccessFilter",
- plugins: []*TestPlugin{
- {
- name: "TestPlugin",
- inj: injectedResult{FilterStatus: int(Success)},
- },
- },
- wantStatus: nil,
- wantStatusMap: PluginToStatus{},
- },
- {
- name: "ErrorFilter",
- plugins: []*TestPlugin{
- {
- name: "TestPlugin",
- inj: injectedResult{FilterStatus: int(Error)},
- },
- },
- wantStatus: NewStatus(Error, `running "TestPlugin" filter plugin for pod "": injected filter status`),
- wantStatusMap: PluginToStatus{"TestPlugin": NewStatus(Error, `running "TestPlugin" filter plugin for pod "": injected filter status`)},
- },
- {
- name: "UnschedulableFilter",
- plugins: []*TestPlugin{
- {
- name: "TestPlugin",
- inj: injectedResult{FilterStatus: int(Unschedulable)},
- },
- },
- wantStatus: NewStatus(Unschedulable, "injected filter status"),
- wantStatusMap: PluginToStatus{"TestPlugin": NewStatus(Unschedulable, "injected filter status")},
- },
- {
- name: "UnschedulableAndUnresolvableFilter",
- plugins: []*TestPlugin{
- {
- name: "TestPlugin",
- inj: injectedResult{
- FilterStatus: int(UnschedulableAndUnresolvable)},
- },
- },
- wantStatus: NewStatus(UnschedulableAndUnresolvable, "injected filter status"),
- wantStatusMap: PluginToStatus{"TestPlugin": NewStatus(UnschedulableAndUnresolvable, "injected filter status")},
- },
- // followings tests cover multiple-plugins scenarios
- {
- name: "ErrorAndErrorFilters",
- plugins: []*TestPlugin{
- {
- name: "TestPlugin1",
- inj: injectedResult{FilterStatus: int(Error)},
- },
- {
- name: "TestPlugin2",
- inj: injectedResult{FilterStatus: int(Error)},
- },
- },
- wantStatus: NewStatus(Error, `running "TestPlugin1" filter plugin for pod "": injected filter status`),
- wantStatusMap: PluginToStatus{"TestPlugin1": NewStatus(Error, `running "TestPlugin1" filter plugin for pod "": injected filter status`)},
- },
- {
- name: "SuccessAndSuccessFilters",
- plugins: []*TestPlugin{
- {
- name: "TestPlugin1",
- inj: injectedResult{FilterStatus: int(Success)},
- },
- {
- name: "TestPlugin2",
- inj: injectedResult{FilterStatus: int(Success)},
- },
- },
- wantStatus: nil,
- wantStatusMap: PluginToStatus{},
- },
- {
- name: "ErrorAndSuccessFilters",
- plugins: []*TestPlugin{
- {
- name: "TestPlugin1",
- inj: injectedResult{FilterStatus: int(Error)},
- },
- {
- name: "TestPlugin2",
- inj: injectedResult{FilterStatus: int(Success)},
- },
- },
- wantStatus: NewStatus(Error, `running "TestPlugin1" filter plugin for pod "": injected filter status`),
- wantStatusMap: PluginToStatus{"TestPlugin1": NewStatus(Error, `running "TestPlugin1" filter plugin for pod "": injected filter status`)},
- },
- {
- name: "SuccessAndErrorFilters",
- plugins: []*TestPlugin{
- {
- name: "TestPlugin1",
- inj: injectedResult{FilterStatus: int(Success)},
- },
- {
- name: "TestPlugin2",
- inj: injectedResult{FilterStatus: int(Error)},
- },
- },
- wantStatus: NewStatus(Error, `running "TestPlugin2" filter plugin for pod "": injected filter status`),
- wantStatusMap: PluginToStatus{"TestPlugin2": NewStatus(Error, `running "TestPlugin2" filter plugin for pod "": injected filter status`)},
- },
- {
- name: "SuccessAndUnschedulableFilters",
- plugins: []*TestPlugin{
- {
- name: "TestPlugin1",
- inj: injectedResult{FilterStatus: int(Success)},
- },
- {
- name: "TestPlugin2",
- inj: injectedResult{FilterStatus: int(Unschedulable)},
- },
- },
- wantStatus: NewStatus(Unschedulable, "injected filter status"),
- wantStatusMap: PluginToStatus{"TestPlugin2": NewStatus(Unschedulable, "injected filter status")},
- },
- {
- name: "SuccessFilterWithRunAllFilters",
- plugins: []*TestPlugin{
- {
- name: "TestPlugin",
- inj: injectedResult{FilterStatus: int(Success)},
- },
- },
- runAllFilters: true,
- wantStatus: nil,
- wantStatusMap: PluginToStatus{},
- },
- {
- name: "ErrorAndErrorFilters",
- plugins: []*TestPlugin{
- {
- name: "TestPlugin1",
- inj: injectedResult{FilterStatus: int(Error)},
- },
- {
- name: "TestPlugin2",
- inj: injectedResult{FilterStatus: int(Error)},
- },
- },
- runAllFilters: true,
- wantStatus: NewStatus(Error, `running "TestPlugin1" filter plugin for pod "": injected filter status`),
- wantStatusMap: PluginToStatus{"TestPlugin1": NewStatus(Error, `running "TestPlugin1" filter plugin for pod "": injected filter status`)},
- },
- {
- name: "ErrorAndErrorFilters",
- plugins: []*TestPlugin{
- {
- name: "TestPlugin1",
- inj: injectedResult{FilterStatus: int(UnschedulableAndUnresolvable)},
- },
- {
- name: "TestPlugin2",
- inj: injectedResult{FilterStatus: int(Unschedulable)},
- },
- },
- runAllFilters: true,
- wantStatus: NewStatus(UnschedulableAndUnresolvable, "injected filter status", "injected filter status"),
- wantStatusMap: PluginToStatus{
- "TestPlugin1": NewStatus(UnschedulableAndUnresolvable, "injected filter status"),
- "TestPlugin2": NewStatus(Unschedulable, "injected filter status"),
- },
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- registry := Registry{}
- cfgPls := &config.Plugins{Filter: &config.PluginSet{}}
- for _, pl := range tt.plugins {
- // register all plugins
- tmpPl := pl
- if err := registry.Register(pl.name,
- func(_ *runtime.Unknown, _ FrameworkHandle) (Plugin, error) {
- return tmpPl, nil
- }); err != nil {
- t.Fatalf("fail to register filter plugin (%s)", pl.name)
- }
- // append plugins to filter pluginset
- cfgPls.Filter.Enabled = append(
- cfgPls.Filter.Enabled,
- config.Plugin{Name: pl.name})
- }
- f, err := newFrameworkWithQueueSortAndBind(registry, cfgPls, emptyArgs, WithRunAllFilters(tt.runAllFilters))
- if err != nil {
- t.Fatalf("fail to create framework: %s", err)
- }
- gotStatusMap := f.RunFilterPlugins(context.TODO(), nil, pod, nil)
- gotStatus := gotStatusMap.Merge()
- if !reflect.DeepEqual(gotStatus, tt.wantStatus) {
- t.Errorf("wrong status code. got: %v, want:%v", gotStatus, tt.wantStatus)
- }
- if !reflect.DeepEqual(gotStatusMap, tt.wantStatusMap) {
- t.Errorf("wrong status map. got: %+v, want: %+v", gotStatusMap, tt.wantStatusMap)
- }
- })
- }
- }
- func TestPreBindPlugins(t *testing.T) {
- tests := []struct {
- name string
- plugins []*TestPlugin
- wantStatus *Status
- }{
- {
- name: "NoPreBindPlugin",
- plugins: []*TestPlugin{},
- wantStatus: nil,
- },
- {
- name: "SuccessPreBindPlugins",
- plugins: []*TestPlugin{
- {
- name: "TestPlugin",
- inj: injectedResult{PreBindStatus: int(Success)},
- },
- },
- wantStatus: nil,
- },
- {
- name: "UnshedulablePreBindPlugin",
- plugins: []*TestPlugin{
- {
- name: "TestPlugin",
- inj: injectedResult{PreBindStatus: int(Unschedulable)},
- },
- },
- wantStatus: NewStatus(Error, `error while running "TestPlugin" prebind plugin for pod "": injected status`),
- },
- {
- name: "ErrorPreBindPlugin",
- plugins: []*TestPlugin{
- {
- name: "TestPlugin",
- inj: injectedResult{PreBindStatus: int(Error)},
- },
- },
- wantStatus: NewStatus(Error, `error while running "TestPlugin" prebind plugin for pod "": injected status`),
- },
- {
- name: "UnschedulablePreBindPlugin",
- plugins: []*TestPlugin{
- {
- name: "TestPlugin",
- inj: injectedResult{PreBindStatus: int(UnschedulableAndUnresolvable)},
- },
- },
- wantStatus: NewStatus(Error, `error while running "TestPlugin" prebind plugin for pod "": injected status`),
- },
- {
- name: "SuccessErrorPreBindPlugins",
- plugins: []*TestPlugin{
- {
- name: "TestPlugin",
- inj: injectedResult{PreBindStatus: int(Success)},
- },
- {
- name: "TestPlugin 1",
- inj: injectedResult{PreBindStatus: int(Error)},
- },
- },
- wantStatus: NewStatus(Error, `error while running "TestPlugin 1" prebind plugin for pod "": injected status`),
- },
- {
- name: "ErrorSuccessPreBindPlugin",
- plugins: []*TestPlugin{
- {
- name: "TestPlugin",
- inj: injectedResult{PreBindStatus: int(Error)},
- },
- {
- name: "TestPlugin 1",
- inj: injectedResult{PreBindStatus: int(Success)},
- },
- },
- wantStatus: NewStatus(Error, `error while running "TestPlugin" prebind plugin for pod "": injected status`),
- },
- {
- name: "SuccessSuccessPreBindPlugin",
- plugins: []*TestPlugin{
- {
- name: "TestPlugin",
- inj: injectedResult{PreBindStatus: int(Success)},
- },
- {
- name: "TestPlugin 1",
- inj: injectedResult{PreBindStatus: int(Success)},
- },
- },
- wantStatus: nil,
- },
- {
- name: "ErrorAndErrorPlugins",
- plugins: []*TestPlugin{
- {
- name: "TestPlugin",
- inj: injectedResult{PreBindStatus: int(Error)},
- },
- {
- name: "TestPlugin 1",
- inj: injectedResult{PreBindStatus: int(Error)},
- },
- },
- wantStatus: NewStatus(Error, `error while running "TestPlugin" prebind plugin for pod "": injected status`),
- },
- {
- name: "UnschedulableAndSuccessPreBindPlugin",
- plugins: []*TestPlugin{
- {
- name: "TestPlugin",
- inj: injectedResult{PreBindStatus: int(Unschedulable)},
- },
- {
- name: "TestPlugin 1",
- inj: injectedResult{PreBindStatus: int(Success)},
- },
- },
- wantStatus: NewStatus(Error, `error while running "TestPlugin" prebind plugin for pod "": injected status`),
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- registry := Registry{}
- configPlugins := &config.Plugins{PreBind: &config.PluginSet{}}
- for _, pl := range tt.plugins {
- tmpPl := pl
- if err := registry.Register(pl.name, func(_ *runtime.Unknown, _ FrameworkHandle) (Plugin, error) {
- return tmpPl, nil
- }); err != nil {
- t.Fatalf("Unable to register pre bind plugins: %s", pl.name)
- }
- configPlugins.PreBind.Enabled = append(
- configPlugins.PreBind.Enabled,
- config.Plugin{Name: pl.name},
- )
- }
- f, err := newFrameworkWithQueueSortAndBind(registry, configPlugins, emptyArgs)
- if err != nil {
- t.Fatalf("fail to create framework: %s", err)
- }
- status := f.RunPreBindPlugins(context.TODO(), nil, pod, "")
- if !reflect.DeepEqual(status, tt.wantStatus) {
- t.Errorf("wrong status code. got %v, want %v", status, tt.wantStatus)
- }
- })
- }
- }
- func TestReservePlugins(t *testing.T) {
- tests := []struct {
- name string
- plugins []*TestPlugin
- wantStatus *Status
- }{
- {
- name: "NoReservePlugin",
- plugins: []*TestPlugin{},
- wantStatus: nil,
- },
- {
- name: "SuccessReservePlugins",
- plugins: []*TestPlugin{
- {
- name: "TestPlugin",
- inj: injectedResult{ReserveStatus: int(Success)},
- },
- },
- wantStatus: nil,
- },
- {
- name: "UnshedulableReservePlugin",
- plugins: []*TestPlugin{
- {
- name: "TestPlugin",
- inj: injectedResult{ReserveStatus: int(Unschedulable)},
- },
- },
- wantStatus: NewStatus(Error, `error while running "TestPlugin" reserve plugin for pod "": injected status`),
- },
- {
- name: "ErrorReservePlugin",
- plugins: []*TestPlugin{
- {
- name: "TestPlugin",
- inj: injectedResult{ReserveStatus: int(Error)},
- },
- },
- wantStatus: NewStatus(Error, `error while running "TestPlugin" reserve plugin for pod "": injected status`),
- },
- {
- name: "UnschedulableReservePlugin",
- plugins: []*TestPlugin{
- {
- name: "TestPlugin",
- inj: injectedResult{ReserveStatus: int(UnschedulableAndUnresolvable)},
- },
- },
- wantStatus: NewStatus(Error, `error while running "TestPlugin" reserve plugin for pod "": injected status`),
- },
- {
- name: "SuccessSuccessReservePlugins",
- plugins: []*TestPlugin{
- {
- name: "TestPlugin",
- inj: injectedResult{ReserveStatus: int(Success)},
- },
- {
- name: "TestPlugin 1",
- inj: injectedResult{ReserveStatus: int(Success)},
- },
- },
- wantStatus: nil,
- },
- {
- name: "ErrorErrorReservePlugins",
- plugins: []*TestPlugin{
- {
- name: "TestPlugin",
- inj: injectedResult{ReserveStatus: int(Error)},
- },
- {
- name: "TestPlugin 1",
- inj: injectedResult{ReserveStatus: int(Error)},
- },
- },
- wantStatus: NewStatus(Error, `error while running "TestPlugin" reserve plugin for pod "": injected status`),
- },
- {
- name: "SuccessErrorReservePlugins",
- plugins: []*TestPlugin{
- {
- name: "TestPlugin",
- inj: injectedResult{ReserveStatus: int(Success)},
- },
- {
- name: "TestPlugin 1",
- inj: injectedResult{ReserveStatus: int(Error)},
- },
- },
- wantStatus: NewStatus(Error, `error while running "TestPlugin 1" reserve plugin for pod "": injected status`),
- },
- {
- name: "ErrorSuccessReservePlugin",
- plugins: []*TestPlugin{
- {
- name: "TestPlugin",
- inj: injectedResult{ReserveStatus: int(Error)},
- },
- {
- name: "TestPlugin 1",
- inj: injectedResult{ReserveStatus: int(Success)},
- },
- },
- wantStatus: NewStatus(Error, `error while running "TestPlugin" reserve plugin for pod "": injected status`),
- },
- {
- name: "UnschedulableAndSuccessReservePlugin",
- plugins: []*TestPlugin{
- {
- name: "TestPlugin",
- inj: injectedResult{ReserveStatus: int(Unschedulable)},
- },
- {
- name: "TestPlugin 1",
- inj: injectedResult{ReserveStatus: int(Success)},
- },
- },
- wantStatus: NewStatus(Error, `error while running "TestPlugin" reserve plugin for pod "": injected status`),
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- registry := Registry{}
- configPlugins := &config.Plugins{Reserve: &config.PluginSet{}}
- for _, pl := range tt.plugins {
- tmpPl := pl
- if err := registry.Register(pl.name, func(_ *runtime.Unknown, _ FrameworkHandle) (Plugin, error) {
- return tmpPl, nil
- }); err != nil {
- t.Fatalf("Unable to register pre bind plugins: %s", pl.name)
- }
- configPlugins.Reserve.Enabled = append(
- configPlugins.Reserve.Enabled,
- config.Plugin{Name: pl.name},
- )
- }
- f, err := newFrameworkWithQueueSortAndBind(registry, configPlugins, emptyArgs)
- if err != nil {
- t.Fatalf("fail to create framework: %s", err)
- }
- status := f.RunReservePlugins(context.TODO(), nil, pod, "")
- if !reflect.DeepEqual(status, tt.wantStatus) {
- t.Errorf("wrong status code. got %v, want %v", status, tt.wantStatus)
- }
- })
- }
- }
- func TestPermitPlugins(t *testing.T) {
- tests := []struct {
- name string
- plugins []*TestPlugin
- want *Status
- }{
- {
- name: "NilPermitPlugin",
- plugins: []*TestPlugin{},
- want: nil,
- },
- {
- name: "SuccessPermitPlugin",
- plugins: []*TestPlugin{
- {
- name: "TestPlugin",
- inj: injectedResult{PermitStatus: int(Success)},
- },
- },
- want: nil,
- },
- {
- name: "UnschedulablePermitPlugin",
- plugins: []*TestPlugin{
- {
- name: "TestPlugin",
- inj: injectedResult{PermitStatus: int(Unschedulable)},
- },
- },
- want: NewStatus(Unschedulable, `rejected by "TestPlugin" at permit: injected status`),
- },
- {
- name: "ErrorPermitPlugin",
- plugins: []*TestPlugin{
- {
- name: "TestPlugin",
- inj: injectedResult{PermitStatus: int(Error)},
- },
- },
- want: NewStatus(Error, `error while running "TestPlugin" permit plugin for pod "": injected status`),
- },
- {
- name: "UnschedulableAndUnresolvablePermitPlugin",
- plugins: []*TestPlugin{
- {
- name: "TestPlugin",
- inj: injectedResult{PermitStatus: int(UnschedulableAndUnresolvable)},
- },
- },
- want: NewStatus(UnschedulableAndUnresolvable, `rejected by "TestPlugin" at permit: injected status`),
- },
- {
- name: "WaitPermitPlugin",
- plugins: []*TestPlugin{
- {
- name: "TestPlugin",
- inj: injectedResult{PermitStatus: int(Wait)},
- },
- },
- want: NewStatus(Unschedulable, `pod "" rejected while waiting at permit: rejected due to timeout after waiting 0s at plugin TestPlugin`),
- },
- {
- name: "SuccessSuccessPermitPlugin",
- plugins: []*TestPlugin{
- {
- name: "TestPlugin",
- inj: injectedResult{PermitStatus: int(Success)},
- },
- {
- name: "TestPlugin 1",
- inj: injectedResult{PermitStatus: int(Success)},
- },
- },
- want: nil,
- },
- {
- name: "ErrorAndErrorPlugins",
- plugins: []*TestPlugin{
- {
- name: "TestPlugin",
- inj: injectedResult{PermitStatus: int(Error)},
- },
- {
- name: "TestPlugin 1",
- inj: injectedResult{PermitStatus: int(Error)},
- },
- },
- want: NewStatus(Error, `error while running "TestPlugin" permit plugin for pod "": injected status`),
- },
- }
- for _, tt := range tests {
- registry := Registry{}
- configPlugins := &config.Plugins{Permit: &config.PluginSet{}}
- for _, pl := range tt.plugins {
- tmpPl := pl
- if err := registry.Register(pl.name, func(_ *runtime.Unknown, _ FrameworkHandle) (Plugin, error) {
- return tmpPl, nil
- }); err != nil {
- t.Fatalf("Unable to register Permit plugin: %s", pl.name)
- }
- configPlugins.Permit.Enabled = append(
- configPlugins.Permit.Enabled,
- config.Plugin{Name: pl.name},
- )
- }
- f, err := newFrameworkWithQueueSortAndBind(registry, configPlugins, emptyArgs)
- if err != nil {
- t.Fatalf("fail to create framework: %s", err)
- }
- status := f.RunPermitPlugins(context.TODO(), nil, pod, "")
- if !reflect.DeepEqual(status, tt.want) {
- t.Errorf("wrong status code. got %v, want %v", status, tt.want)
- }
- }
- }
- func TestRecordingMetrics(t *testing.T) {
- state := &CycleState{
- recordPluginMetrics: true,
- }
- tests := []struct {
- name string
- action func(f Framework)
- inject injectedResult
- wantExtensionPoint string
- wantStatus Code
- }{
- {
- name: "PreFilter - Success",
- action: func(f Framework) { f.RunPreFilterPlugins(context.Background(), state, pod) },
- wantExtensionPoint: "PreFilter",
- wantStatus: Success,
- },
- {
- name: "PostFilter - Success",
- action: func(f Framework) { f.RunPostFilterPlugins(context.Background(), state, pod, nil, nil) },
- wantExtensionPoint: "PostFilter",
- wantStatus: Success,
- },
- {
- name: "Score - Success",
- action: func(f Framework) { f.RunScorePlugins(context.Background(), state, pod, nodes) },
- wantExtensionPoint: "Score",
- wantStatus: Success,
- },
- {
- name: "Reserve - Success",
- action: func(f Framework) { f.RunReservePlugins(context.Background(), state, pod, "") },
- wantExtensionPoint: "Reserve",
- wantStatus: Success,
- },
- {
- name: "Unreserve - Success",
- action: func(f Framework) { f.RunUnreservePlugins(context.Background(), state, pod, "") },
- wantExtensionPoint: "Unreserve",
- wantStatus: Success,
- },
- {
- name: "PreBind - Success",
- action: func(f Framework) { f.RunPreBindPlugins(context.Background(), state, pod, "") },
- wantExtensionPoint: "PreBind",
- wantStatus: Success,
- },
- {
- name: "Bind - Success",
- action: func(f Framework) { f.RunBindPlugins(context.Background(), state, pod, "") },
- wantExtensionPoint: "Bind",
- wantStatus: Success,
- },
- {
- name: "PostBind - Success",
- action: func(f Framework) { f.RunPostBindPlugins(context.Background(), state, pod, "") },
- wantExtensionPoint: "PostBind",
- wantStatus: Success,
- },
- {
- name: "Permit - Success",
- action: func(f Framework) { f.RunPermitPlugins(context.Background(), state, pod, "") },
- wantExtensionPoint: "Permit",
- wantStatus: Success,
- },
- {
- name: "PreFilter - Error",
- action: func(f Framework) { f.RunPreFilterPlugins(context.Background(), state, pod) },
- inject: injectedResult{PreFilterStatus: int(Error)},
- wantExtensionPoint: "PreFilter",
- wantStatus: Error,
- },
- {
- name: "PostFilter - Error",
- action: func(f Framework) { f.RunPostFilterPlugins(context.Background(), state, pod, nil, nil) },
- inject: injectedResult{PostFilterStatus: int(Error)},
- wantExtensionPoint: "PostFilter",
- wantStatus: Error,
- },
- {
- name: "Score - Error",
- action: func(f Framework) { f.RunScorePlugins(context.Background(), state, pod, nodes) },
- inject: injectedResult{ScoreStatus: int(Error)},
- wantExtensionPoint: "Score",
- wantStatus: Error,
- },
- {
- name: "Reserve - Error",
- action: func(f Framework) { f.RunReservePlugins(context.Background(), state, pod, "") },
- inject: injectedResult{ReserveStatus: int(Error)},
- wantExtensionPoint: "Reserve",
- wantStatus: Error,
- },
- {
- name: "PreBind - Error",
- action: func(f Framework) { f.RunPreBindPlugins(context.Background(), state, pod, "") },
- inject: injectedResult{PreBindStatus: int(Error)},
- wantExtensionPoint: "PreBind",
- wantStatus: Error,
- },
- {
- name: "Bind - Error",
- action: func(f Framework) { f.RunBindPlugins(context.Background(), state, pod, "") },
- inject: injectedResult{BindStatus: int(Error)},
- wantExtensionPoint: "Bind",
- wantStatus: Error,
- },
- {
- name: "Permit - Error",
- action: func(f Framework) { f.RunPermitPlugins(context.Background(), state, pod, "") },
- inject: injectedResult{PermitStatus: int(Error)},
- wantExtensionPoint: "Permit",
- wantStatus: Error,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- metrics.Register()
- metrics.FrameworkExtensionPointDuration.Reset()
- metrics.PluginExecutionDuration.Reset()
- plugin := &TestPlugin{name: testPlugin, inj: tt.inject}
- r := make(Registry)
- r.Register(testPlugin,
- func(_ *runtime.Unknown, fh FrameworkHandle) (Plugin, error) {
- return plugin, nil
- })
- pluginSet := &config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin, Weight: 1}}}
- plugins := &config.Plugins{
- Score: pluginSet,
- PreFilter: pluginSet,
- Filter: pluginSet,
- PostFilter: pluginSet,
- Reserve: pluginSet,
- Permit: pluginSet,
- PreBind: pluginSet,
- Bind: pluginSet,
- PostBind: pluginSet,
- Unreserve: pluginSet,
- }
- recorder := newMetricsRecorder(100, time.Nanosecond)
- f, err := newFrameworkWithQueueSortAndBind(r, plugins, emptyArgs, withMetricsRecorder(recorder))
- if err != nil {
- t.Fatalf("Failed to create framework for testing: %v", err)
- }
- tt.action(f)
- // Stop the goroutine which records metrics and ensure it's stopped.
- close(recorder.stopCh)
- <-recorder.isStoppedCh
- // Try to clean up the metrics buffer again in case it's not empty.
- recorder.flushMetrics()
- collectAndCompareFrameworkMetrics(t, tt.wantExtensionPoint, tt.wantStatus)
- collectAndComparePluginMetrics(t, tt.wantExtensionPoint, testPlugin, tt.wantStatus)
- })
- }
- }
- func TestRunBindPlugins(t *testing.T) {
- tests := []struct {
- name string
- injects []Code
- wantStatus Code
- }{
- {
- name: "simple success",
- injects: []Code{Success},
- wantStatus: Success,
- },
- {
- name: "error on second",
- injects: []Code{Skip, Error, Success},
- wantStatus: Error,
- },
- {
- name: "all skip",
- injects: []Code{Skip, Skip, Skip},
- wantStatus: Skip,
- },
- {
- name: "error on third, but not reached",
- injects: []Code{Skip, Success, Error},
- wantStatus: Success,
- },
- {
- name: "no bind plugin, returns default binder",
- injects: []Code{},
- wantStatus: Success,
- },
- {
- name: "invalid status",
- injects: []Code{Unschedulable},
- wantStatus: Error,
- },
- {
- name: "simple error",
- injects: []Code{Error},
- wantStatus: Error,
- },
- {
- name: "success on second, returns success",
- injects: []Code{Skip, Success},
- wantStatus: Success,
- },
- {
- name: "invalid status, returns error",
- injects: []Code{Skip, UnschedulableAndUnresolvable},
- wantStatus: Error,
- },
- {
- name: "error after success status, returns success",
- injects: []Code{Success, Error},
- wantStatus: Success,
- },
- {
- name: "success before invalid status, returns success",
- injects: []Code{Success, Error},
- wantStatus: Success,
- },
- {
- name: "success after error status, returns error",
- injects: []Code{Error, Success},
- wantStatus: Error,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- metrics.Register()
- metrics.FrameworkExtensionPointDuration.Reset()
- metrics.PluginExecutionDuration.Reset()
- pluginSet := &config.PluginSet{}
- r := make(Registry)
- for i, inj := range tt.injects {
- name := fmt.Sprintf("bind-%d", i)
- plugin := &TestPlugin{name: name, inj: injectedResult{BindStatus: int(inj)}}
- r.Register(name,
- func(_ *runtime.Unknown, fh FrameworkHandle) (Plugin, error) {
- return plugin, nil
- })
- pluginSet.Enabled = append(pluginSet.Enabled, config.Plugin{Name: name})
- }
- plugins := &config.Plugins{Bind: pluginSet}
- recorder := newMetricsRecorder(100, time.Nanosecond)
- fwk, err := newFrameworkWithQueueSortAndBind(r, plugins, emptyArgs, withMetricsRecorder(recorder))
- if err != nil {
- t.Fatal(err)
- }
- st := fwk.RunBindPlugins(context.Background(), state, pod, "")
- if st.Code() != tt.wantStatus {
- t.Errorf("got status code %s, want %s", st.Code(), tt.wantStatus)
- }
- // Stop the goroutine which records metrics and ensure it's stopped.
- close(recorder.stopCh)
- <-recorder.isStoppedCh
- // Try to clean up the metrics buffer again in case it's not empty.
- recorder.flushMetrics()
- collectAndCompareFrameworkMetrics(t, "Bind", tt.wantStatus)
- })
- }
- }
- func TestPermitWaitingMetric(t *testing.T) {
- tests := []struct {
- name string
- inject injectedResult
- wantRes string
- }{
- {
- name: "Permit - Success",
- },
- {
- name: "Permit - Wait Timeout",
- inject: injectedResult{PermitStatus: int(Wait)},
- wantRes: "Unschedulable",
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- metrics.Register()
- metrics.PermitWaitDuration.Reset()
- plugin := &TestPlugin{name: testPlugin, inj: tt.inject}
- r := make(Registry)
- err := r.Register(testPlugin,
- func(_ *runtime.Unknown, fh FrameworkHandle) (Plugin, error) {
- return plugin, nil
- })
- if err != nil {
- t.Fatal(err)
- }
- plugins := &config.Plugins{
- Permit: &config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin, Weight: 1}}},
- }
- f, err := newFrameworkWithQueueSortAndBind(r, plugins, emptyArgs)
- if err != nil {
- t.Fatalf("Failed to create framework for testing: %v", err)
- }
- f.RunPermitPlugins(context.TODO(), nil, pod, "")
- collectAndComparePermitWaitDuration(t, tt.wantRes)
- })
- }
- }
- func TestRejectWaitingPod(t *testing.T) {
- pod := &v1.Pod{
- ObjectMeta: metav1.ObjectMeta{
- Name: "pod",
- UID: types.UID("pod"),
- },
- }
- testPermitPlugin := &TestPermitPlugin{}
- r := make(Registry)
- r.Register(permitPlugin,
- func(_ *runtime.Unknown, fh FrameworkHandle) (Plugin, error) {
- return testPermitPlugin, nil
- })
- plugins := &config.Plugins{
- Permit: &config.PluginSet{Enabled: []config.Plugin{{Name: permitPlugin, Weight: 1}}},
- }
- f, err := newFrameworkWithQueueSortAndBind(r, plugins, emptyArgs)
- if err != nil {
- t.Fatalf("Failed to create framework for testing: %v", err)
- }
- go func() {
- for {
- waitingPod := f.GetWaitingPod(pod.UID)
- if waitingPod != nil {
- break
- }
- }
- f.RejectWaitingPod(pod.UID)
- }()
- permitStatus := f.RunPermitPlugins(context.Background(), nil, pod, "")
- if permitStatus.Message() != "pod \"pod\" rejected while waiting at permit: removed" {
- t.Fatalf("RejectWaitingPod failed, permitStatus: %v", permitStatus)
- }
- }
- func buildScoreConfigDefaultWeights(ps ...string) *config.Plugins {
- return buildScoreConfigWithWeights(defaultWeights, ps...)
- }
- func buildScoreConfigWithWeights(weights map[string]int32, ps ...string) *config.Plugins {
- var plugins []config.Plugin
- for _, p := range ps {
- plugins = append(plugins, config.Plugin{Name: p, Weight: weights[p]})
- }
- return &config.Plugins{Score: &config.PluginSet{Enabled: plugins}}
- }
- type injectedResult struct {
- ScoreRes int64 `json:"scoreRes,omitempty"`
- NormalizeRes int64 `json:"normalizeRes,omitempty"`
- ScoreStatus int `json:"scoreStatus,omitempty"`
- NormalizeStatus int `json:"normalizeStatus,omitempty"`
- PreFilterStatus int `json:"preFilterStatus,omitempty"`
- PreFilterAddPodStatus int `json:"preFilterAddPodStatus,omitempty"`
- PreFilterRemovePodStatus int `json:"preFilterRemovePodStatus,omitempty"`
- FilterStatus int `json:"filterStatus,omitempty"`
- PostFilterStatus int `json:"postFilterStatus,omitempty"`
- ReserveStatus int `json:"reserveStatus,omitempty"`
- PreBindStatus int `json:"preBindStatus,omitempty"`
- BindStatus int `json:"bindStatus,omitempty"`
- PermitStatus int `json:"permitStatus,omitempty"`
- }
- func setScoreRes(inj injectedResult) (int64, *Status) {
- if Code(inj.ScoreStatus) != Success {
- return 0, NewStatus(Code(inj.ScoreStatus), "injecting failure.")
- }
- return inj.ScoreRes, nil
- }
- func injectNormalizeRes(inj injectedResult, scores NodeScoreList) *Status {
- if Code(inj.NormalizeStatus) != Success {
- return NewStatus(Code(inj.NormalizeStatus), "injecting failure.")
- }
- for i := range scores {
- scores[i].Score = inj.NormalizeRes
- }
- return nil
- }
- func collectAndComparePluginMetrics(t *testing.T, wantExtensionPoint, wantPlugin string, wantStatus Code) {
- t.Helper()
- m := collectHistogramMetric(metrics.PluginExecutionDuration)
- if len(m.Label) != 3 {
- t.Fatalf("Unexpected number of label pairs, got: %v, want: 2", len(m.Label))
- }
- if *m.Label[0].Value != wantExtensionPoint {
- t.Errorf("Unexpected extension point label, got: %q, want %q", *m.Label[0].Value, wantExtensionPoint)
- }
- if *m.Label[1].Value != wantPlugin {
- t.Errorf("Unexpected plugin label, got: %q, want %q", *m.Label[1].Value, wantPlugin)
- }
- if *m.Label[2].Value != wantStatus.String() {
- t.Errorf("Unexpected status code label, got: %q, want %q", *m.Label[2].Value, wantStatus)
- }
- if *m.Histogram.SampleCount == 0 {
- t.Error("Expect at least 1 sample")
- }
- if *m.Histogram.SampleSum <= 0 {
- t.Errorf("Expect latency to be greater than 0, got: %v", *m.Histogram.SampleSum)
- }
- }
- func collectAndCompareFrameworkMetrics(t *testing.T, wantExtensionPoint string, wantStatus Code) {
- t.Helper()
- m := collectHistogramMetric(metrics.FrameworkExtensionPointDuration)
- if len(m.Label) != 2 {
- t.Fatalf("Unexpected number of label pairs, got: %v, want: 2", len(m.Label))
- }
- if *m.Label[0].Value != wantExtensionPoint {
- t.Errorf("Unexpected extension point label, got: %q, want %q", *m.Label[0].Value, wantExtensionPoint)
- }
- if *m.Label[1].Value != wantStatus.String() {
- t.Errorf("Unexpected status code label, got: %q, want %q", *m.Label[1].Value, wantStatus)
- }
- if *m.Histogram.SampleCount != 1 {
- t.Errorf("Expect 1 sample, got: %v", *m.Histogram.SampleCount)
- }
- if *m.Histogram.SampleSum <= 0 {
- t.Errorf("Expect latency to be greater than 0, got: %v", *m.Histogram.SampleSum)
- }
- }
- func collectAndComparePermitWaitDuration(t *testing.T, wantRes string) {
- m := collectHistogramMetric(metrics.PermitWaitDuration)
- if wantRes == "" {
- if m != nil {
- t.Errorf("PermitWaitDuration shouldn't be recorded but got %+v", m)
- }
- return
- }
- if wantRes != "" {
- if len(m.Label) != 1 {
- t.Fatalf("Unexpected number of label pairs, got: %v, want: 1", len(m.Label))
- }
- if *m.Label[0].Value != wantRes {
- t.Errorf("Unexpected result label, got: %q, want %q", *m.Label[0].Value, wantRes)
- }
- if *m.Histogram.SampleCount != 1 {
- t.Errorf("Expect 1 sample, got: %v", *m.Histogram.SampleCount)
- }
- if *m.Histogram.SampleSum <= 0 {
- t.Errorf("Expect latency to be greater than 0, got: %v", *m.Histogram.SampleSum)
- }
- }
- }
- func collectHistogramMetric(metric prometheus.Collector) *dto.Metric {
- ch := make(chan prometheus.Metric, 100)
- metric.Collect(ch)
- select {
- case got := <-ch:
- m := &dto.Metric{}
- got.Write(m)
- return m
- default:
- return nil
- }
- }
|