framework_test.go 53 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841
  1. /*
  2. Copyright 2019 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 v1alpha1
  14. import (
  15. "context"
  16. "fmt"
  17. "reflect"
  18. "strings"
  19. "testing"
  20. "time"
  21. "github.com/prometheus/client_golang/prometheus"
  22. dto "github.com/prometheus/client_model/go"
  23. v1 "k8s.io/api/core/v1"
  24. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  25. "k8s.io/apimachinery/pkg/runtime"
  26. "k8s.io/apimachinery/pkg/types"
  27. "k8s.io/kubernetes/pkg/scheduler/apis/config"
  28. "k8s.io/kubernetes/pkg/scheduler/metrics"
  29. schedulernodeinfo "k8s.io/kubernetes/pkg/scheduler/nodeinfo"
  30. )
  31. const (
  32. queueSortPlugin = "no-op-queue-sort-plugin"
  33. scoreWithNormalizePlugin1 = "score-with-normalize-plugin-1"
  34. scoreWithNormalizePlugin2 = "score-with-normalize-plugin-2"
  35. scorePlugin1 = "score-plugin-1"
  36. pluginNotImplementingScore = "plugin-not-implementing-score"
  37. preFilterPluginName = "prefilter-plugin"
  38. preFilterWithExtensionsPluginName = "prefilter-with-extensions-plugin"
  39. duplicatePluginName = "duplicate-plugin"
  40. testPlugin = "test-plugin"
  41. permitPlugin = "permit-plugin"
  42. bindPlugin = "bind-plugin"
  43. )
  44. // TestScoreWithNormalizePlugin implements ScoreWithNormalizePlugin interface.
  45. // TestScorePlugin only implements ScorePlugin interface.
  46. var _ ScorePlugin = &TestScoreWithNormalizePlugin{}
  47. var _ ScorePlugin = &TestScorePlugin{}
  48. func newScoreWithNormalizePlugin1(injArgs *runtime.Unknown, f FrameworkHandle) (Plugin, error) {
  49. var inj injectedResult
  50. if err := DecodeInto(injArgs, &inj); err != nil {
  51. return nil, err
  52. }
  53. return &TestScoreWithNormalizePlugin{scoreWithNormalizePlugin1, inj}, nil
  54. }
  55. func newScoreWithNormalizePlugin2(injArgs *runtime.Unknown, f FrameworkHandle) (Plugin, error) {
  56. var inj injectedResult
  57. if err := DecodeInto(injArgs, &inj); err != nil {
  58. return nil, err
  59. }
  60. return &TestScoreWithNormalizePlugin{scoreWithNormalizePlugin2, inj}, nil
  61. }
  62. func newScorePlugin1(injArgs *runtime.Unknown, f FrameworkHandle) (Plugin, error) {
  63. var inj injectedResult
  64. if err := DecodeInto(injArgs, &inj); err != nil {
  65. return nil, err
  66. }
  67. return &TestScorePlugin{scorePlugin1, inj}, nil
  68. }
  69. func newPluginNotImplementingScore(_ *runtime.Unknown, _ FrameworkHandle) (Plugin, error) {
  70. return &PluginNotImplementingScore{}, nil
  71. }
  72. type TestScoreWithNormalizePlugin struct {
  73. name string
  74. inj injectedResult
  75. }
  76. func (pl *TestScoreWithNormalizePlugin) Name() string {
  77. return pl.name
  78. }
  79. func (pl *TestScoreWithNormalizePlugin) NormalizeScore(ctx context.Context, state *CycleState, pod *v1.Pod, scores NodeScoreList) *Status {
  80. return injectNormalizeRes(pl.inj, scores)
  81. }
  82. func (pl *TestScoreWithNormalizePlugin) Score(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string) (int64, *Status) {
  83. return setScoreRes(pl.inj)
  84. }
  85. func (pl *TestScoreWithNormalizePlugin) ScoreExtensions() ScoreExtensions {
  86. return pl
  87. }
  88. // TestScorePlugin only implements ScorePlugin interface.
  89. type TestScorePlugin struct {
  90. name string
  91. inj injectedResult
  92. }
  93. func (pl *TestScorePlugin) Name() string {
  94. return pl.name
  95. }
  96. func (pl *TestScorePlugin) Score(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string) (int64, *Status) {
  97. return setScoreRes(pl.inj)
  98. }
  99. func (pl *TestScorePlugin) ScoreExtensions() ScoreExtensions {
  100. return nil
  101. }
  102. // PluginNotImplementingScore doesn't implement the ScorePlugin interface.
  103. type PluginNotImplementingScore struct{}
  104. func (pl *PluginNotImplementingScore) Name() string {
  105. return pluginNotImplementingScore
  106. }
  107. // TestPlugin implements all Plugin interfaces.
  108. type TestPlugin struct {
  109. name string
  110. inj injectedResult
  111. }
  112. type TestPluginPreFilterExtension struct {
  113. inj injectedResult
  114. }
  115. func (e *TestPluginPreFilterExtension) AddPod(ctx context.Context, state *CycleState, podToSchedule *v1.Pod, podToAdd *v1.Pod, nodeInfo *schedulernodeinfo.NodeInfo) *Status {
  116. return NewStatus(Code(e.inj.PreFilterAddPodStatus), "injected status")
  117. }
  118. func (e *TestPluginPreFilterExtension) RemovePod(ctx context.Context, state *CycleState, podToSchedule *v1.Pod, podToRemove *v1.Pod, nodeInfo *schedulernodeinfo.NodeInfo) *Status {
  119. return NewStatus(Code(e.inj.PreFilterRemovePodStatus), "injected status")
  120. }
  121. func (pl *TestPlugin) Name() string {
  122. return pl.name
  123. }
  124. func (pl *TestPlugin) Score(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string) (int64, *Status) {
  125. return 0, NewStatus(Code(pl.inj.ScoreStatus), "injected status")
  126. }
  127. func (pl *TestPlugin) ScoreExtensions() ScoreExtensions {
  128. return nil
  129. }
  130. func (pl *TestPlugin) PreFilter(ctx context.Context, state *CycleState, p *v1.Pod) *Status {
  131. return NewStatus(Code(pl.inj.PreFilterStatus), "injected status")
  132. }
  133. func (pl *TestPlugin) PreFilterExtensions() PreFilterExtensions {
  134. return &TestPluginPreFilterExtension{inj: pl.inj}
  135. }
  136. func (pl *TestPlugin) Filter(ctx context.Context, state *CycleState, pod *v1.Pod, nodeInfo *schedulernodeinfo.NodeInfo) *Status {
  137. return NewStatus(Code(pl.inj.FilterStatus), "injected filter status")
  138. }
  139. func (pl *TestPlugin) PreScore(ctx context.Context, state *CycleState, pod *v1.Pod, nodes []*v1.Node) *Status {
  140. return NewStatus(Code(pl.inj.PreScoreStatus), "injected status")
  141. }
  142. func (pl *TestPlugin) Reserve(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string) *Status {
  143. return NewStatus(Code(pl.inj.ReserveStatus), "injected status")
  144. }
  145. func (pl *TestPlugin) PreBind(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string) *Status {
  146. return NewStatus(Code(pl.inj.PreBindStatus), "injected status")
  147. }
  148. func (pl *TestPlugin) PostBind(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string) {}
  149. func (pl *TestPlugin) Unreserve(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string) {}
  150. func (pl *TestPlugin) Permit(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string) (*Status, time.Duration) {
  151. return NewStatus(Code(pl.inj.PermitStatus), "injected status"), time.Duration(0)
  152. }
  153. func (pl *TestPlugin) Bind(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string) *Status {
  154. return NewStatus(Code(pl.inj.BindStatus), "injected status")
  155. }
  156. // TestPreFilterPlugin only implements PreFilterPlugin interface.
  157. type TestPreFilterPlugin struct {
  158. PreFilterCalled int
  159. }
  160. func (pl *TestPreFilterPlugin) Name() string {
  161. return preFilterPluginName
  162. }
  163. func (pl *TestPreFilterPlugin) PreFilter(ctx context.Context, state *CycleState, p *v1.Pod) *Status {
  164. pl.PreFilterCalled++
  165. return nil
  166. }
  167. func (pl *TestPreFilterPlugin) PreFilterExtensions() PreFilterExtensions {
  168. return nil
  169. }
  170. // TestPreFilterWithExtensionsPlugin implements Add/Remove interfaces.
  171. type TestPreFilterWithExtensionsPlugin struct {
  172. PreFilterCalled int
  173. AddCalled int
  174. RemoveCalled int
  175. }
  176. func (pl *TestPreFilterWithExtensionsPlugin) Name() string {
  177. return preFilterWithExtensionsPluginName
  178. }
  179. func (pl *TestPreFilterWithExtensionsPlugin) PreFilter(ctx context.Context, state *CycleState, p *v1.Pod) *Status {
  180. pl.PreFilterCalled++
  181. return nil
  182. }
  183. func (pl *TestPreFilterWithExtensionsPlugin) AddPod(ctx context.Context, state *CycleState, podToSchedule *v1.Pod,
  184. podToAdd *v1.Pod, nodeInfo *schedulernodeinfo.NodeInfo) *Status {
  185. pl.AddCalled++
  186. return nil
  187. }
  188. func (pl *TestPreFilterWithExtensionsPlugin) RemovePod(ctx context.Context, state *CycleState, podToSchedule *v1.Pod,
  189. podToRemove *v1.Pod, nodeInfo *schedulernodeinfo.NodeInfo) *Status {
  190. pl.RemoveCalled++
  191. return nil
  192. }
  193. func (pl *TestPreFilterWithExtensionsPlugin) PreFilterExtensions() PreFilterExtensions {
  194. return pl
  195. }
  196. type TestDuplicatePlugin struct {
  197. }
  198. func (dp *TestDuplicatePlugin) Name() string {
  199. return duplicatePluginName
  200. }
  201. func (dp *TestDuplicatePlugin) PreFilter(ctx context.Context, state *CycleState, p *v1.Pod) *Status {
  202. return nil
  203. }
  204. func (dp *TestDuplicatePlugin) PreFilterExtensions() PreFilterExtensions {
  205. return nil
  206. }
  207. var _ PreFilterPlugin = &TestDuplicatePlugin{}
  208. func newDuplicatePlugin(_ *runtime.Unknown, _ FrameworkHandle) (Plugin, error) {
  209. return &TestDuplicatePlugin{}, nil
  210. }
  211. // TestPermitPlugin only implements PermitPlugin interface.
  212. type TestPermitPlugin struct {
  213. PreFilterCalled int
  214. }
  215. func (pp *TestPermitPlugin) Name() string {
  216. return permitPlugin
  217. }
  218. func (pp *TestPermitPlugin) Permit(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string) (*Status, time.Duration) {
  219. return NewStatus(Wait, ""), time.Duration(10 * time.Second)
  220. }
  221. var _ QueueSortPlugin = &TestQueueSortPlugin{}
  222. func newQueueSortPlugin(_ *runtime.Unknown, _ FrameworkHandle) (Plugin, error) {
  223. return &TestQueueSortPlugin{}, nil
  224. }
  225. // TestQueueSortPlugin is a no-op implementation for QueueSort extension point.
  226. type TestQueueSortPlugin struct{}
  227. func (pl *TestQueueSortPlugin) Name() string {
  228. return queueSortPlugin
  229. }
  230. func (pl *TestQueueSortPlugin) Less(_, _ *PodInfo) bool {
  231. return false
  232. }
  233. var _ BindPlugin = &TestBindPlugin{}
  234. func newBindPlugin(_ *runtime.Unknown, _ FrameworkHandle) (Plugin, error) {
  235. return &TestBindPlugin{}, nil
  236. }
  237. // TestBindPlugin is a no-op implementation for Bind extension point.
  238. type TestBindPlugin struct{}
  239. func (t TestBindPlugin) Name() string {
  240. return bindPlugin
  241. }
  242. func (t TestBindPlugin) Bind(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string) *Status {
  243. return nil
  244. }
  245. var registry = func() Registry {
  246. r := make(Registry)
  247. r.Register(scoreWithNormalizePlugin1, newScoreWithNormalizePlugin1)
  248. r.Register(scoreWithNormalizePlugin2, newScoreWithNormalizePlugin2)
  249. r.Register(scorePlugin1, newScorePlugin1)
  250. r.Register(pluginNotImplementingScore, newPluginNotImplementingScore)
  251. r.Register(duplicatePluginName, newDuplicatePlugin)
  252. return r
  253. }()
  254. var defaultWeights = map[string]int32{
  255. scoreWithNormalizePlugin1: 1,
  256. scoreWithNormalizePlugin2: 2,
  257. scorePlugin1: 1,
  258. }
  259. var emptyArgs = make([]config.PluginConfig, 0)
  260. var state = &CycleState{}
  261. // Pod is only used for logging errors.
  262. var pod = &v1.Pod{}
  263. var nodes = []*v1.Node{
  264. {ObjectMeta: metav1.ObjectMeta{Name: "node1"}},
  265. {ObjectMeta: metav1.ObjectMeta{Name: "node2"}},
  266. }
  267. func newFrameworkWithQueueSortAndBind(r Registry, pl *config.Plugins, plc []config.PluginConfig, opts ...Option) (Framework, error) {
  268. if _, ok := r[queueSortPlugin]; !ok {
  269. r[queueSortPlugin] = newQueueSortPlugin
  270. }
  271. if _, ok := r[bindPlugin]; !ok {
  272. r[bindPlugin] = newBindPlugin
  273. }
  274. plugins := &config.Plugins{}
  275. plugins.Append(pl)
  276. if plugins.QueueSort == nil || len(plugins.QueueSort.Enabled) == 0 {
  277. plugins.Append(&config.Plugins{
  278. QueueSort: &config.PluginSet{
  279. Enabled: []config.Plugin{{Name: queueSortPlugin}},
  280. },
  281. })
  282. }
  283. if plugins.Bind == nil || len(plugins.Bind.Enabled) == 0 {
  284. plugins.Append(&config.Plugins{
  285. Bind: &config.PluginSet{
  286. Enabled: []config.Plugin{{Name: bindPlugin}},
  287. },
  288. })
  289. }
  290. return NewFramework(r, plugins, plc, opts...)
  291. }
  292. func TestInitFrameworkWithScorePlugins(t *testing.T) {
  293. tests := []struct {
  294. name string
  295. plugins *config.Plugins
  296. // If initErr is true, we expect framework initialization to fail.
  297. initErr bool
  298. }{
  299. {
  300. name: "enabled Score plugin doesn't exist in registry",
  301. plugins: buildScoreConfigDefaultWeights("notExist"),
  302. initErr: true,
  303. },
  304. {
  305. name: "enabled Score plugin doesn't extend the ScorePlugin interface",
  306. plugins: buildScoreConfigDefaultWeights(pluginNotImplementingScore),
  307. initErr: true,
  308. },
  309. {
  310. name: "Score plugins are nil",
  311. plugins: &config.Plugins{Score: nil},
  312. },
  313. {
  314. name: "enabled Score plugin list is empty",
  315. plugins: buildScoreConfigDefaultWeights(),
  316. },
  317. {
  318. name: "enabled plugin only implements ScorePlugin interface",
  319. plugins: buildScoreConfigDefaultWeights(scorePlugin1),
  320. },
  321. {
  322. name: "enabled plugin implements ScoreWithNormalizePlugin interface",
  323. plugins: buildScoreConfigDefaultWeights(scoreWithNormalizePlugin1),
  324. },
  325. }
  326. for _, tt := range tests {
  327. t.Run(tt.name, func(t *testing.T) {
  328. _, err := newFrameworkWithQueueSortAndBind(registry, tt.plugins, emptyArgs)
  329. if tt.initErr && err == nil {
  330. t.Fatal("Framework initialization should fail")
  331. }
  332. if !tt.initErr && err != nil {
  333. t.Fatalf("Failed to create framework for testing: %v", err)
  334. }
  335. })
  336. }
  337. }
  338. func TestRegisterDuplicatePluginWouldFail(t *testing.T) {
  339. plugin := config.Plugin{Name: duplicatePluginName, Weight: 1}
  340. pluginSet := config.PluginSet{
  341. Enabled: []config.Plugin{
  342. plugin,
  343. plugin,
  344. },
  345. }
  346. plugins := config.Plugins{}
  347. plugins.PreFilter = &pluginSet
  348. _, err := NewFramework(registry, &plugins, emptyArgs)
  349. if err == nil {
  350. t.Fatal("Framework initialization should fail")
  351. }
  352. if err != nil && !strings.Contains(err.Error(), "already registered") {
  353. t.Fatalf("Unexpected error, got %s, expect: plugin already registered", err.Error())
  354. }
  355. }
  356. func TestRunScorePlugins(t *testing.T) {
  357. tests := []struct {
  358. name string
  359. registry Registry
  360. plugins *config.Plugins
  361. pluginConfigs []config.PluginConfig
  362. want PluginToNodeScores
  363. // If err is true, we expect RunScorePlugin to fail.
  364. err bool
  365. }{
  366. {
  367. name: "no Score plugins",
  368. plugins: buildScoreConfigDefaultWeights(),
  369. want: PluginToNodeScores{},
  370. },
  371. {
  372. name: "single Score plugin",
  373. plugins: buildScoreConfigDefaultWeights(scorePlugin1),
  374. pluginConfigs: []config.PluginConfig{
  375. {
  376. Name: scorePlugin1,
  377. Args: runtime.Unknown{
  378. Raw: []byte(`{ "scoreRes": 1 }`),
  379. },
  380. },
  381. },
  382. // scorePlugin1 Score returns 1, weight=1, so want=1.
  383. want: PluginToNodeScores{
  384. scorePlugin1: {{Name: "node1", Score: 1}, {Name: "node2", Score: 1}},
  385. },
  386. },
  387. {
  388. name: "single ScoreWithNormalize plugin",
  389. //registry: registry,
  390. plugins: buildScoreConfigDefaultWeights(scoreWithNormalizePlugin1),
  391. pluginConfigs: []config.PluginConfig{
  392. {
  393. Name: scoreWithNormalizePlugin1,
  394. Args: runtime.Unknown{
  395. Raw: []byte(`{ "scoreRes": 10, "normalizeRes": 5 }`),
  396. },
  397. },
  398. },
  399. // scoreWithNormalizePlugin1 Score returns 10, but NormalizeScore overrides to 5, weight=1, so want=5
  400. want: PluginToNodeScores{
  401. scoreWithNormalizePlugin1: {{Name: "node1", Score: 5}, {Name: "node2", Score: 5}},
  402. },
  403. },
  404. {
  405. name: "2 Score plugins, 2 NormalizeScore plugins",
  406. plugins: buildScoreConfigDefaultWeights(scorePlugin1, scoreWithNormalizePlugin1, scoreWithNormalizePlugin2),
  407. pluginConfigs: []config.PluginConfig{
  408. {
  409. Name: scorePlugin1,
  410. Args: runtime.Unknown{
  411. Raw: []byte(`{ "scoreRes": 1 }`),
  412. },
  413. },
  414. {
  415. Name: scoreWithNormalizePlugin1,
  416. Args: runtime.Unknown{
  417. Raw: []byte(`{ "scoreRes": 3, "normalizeRes": 4}`),
  418. },
  419. },
  420. {
  421. Name: scoreWithNormalizePlugin2,
  422. Args: runtime.Unknown{
  423. Raw: []byte(`{ "scoreRes": 4, "normalizeRes": 5}`),
  424. },
  425. },
  426. },
  427. // scorePlugin1 Score returns 1, weight =1, so want=1.
  428. // scoreWithNormalizePlugin1 Score returns 3, but NormalizeScore overrides to 4, weight=1, so want=4.
  429. // scoreWithNormalizePlugin2 Score returns 4, but NormalizeScore overrides to 5, weight=2, so want=10.
  430. want: PluginToNodeScores{
  431. scorePlugin1: {{Name: "node1", Score: 1}, {Name: "node2", Score: 1}},
  432. scoreWithNormalizePlugin1: {{Name: "node1", Score: 4}, {Name: "node2", Score: 4}},
  433. scoreWithNormalizePlugin2: {{Name: "node1", Score: 10}, {Name: "node2", Score: 10}},
  434. },
  435. },
  436. {
  437. name: "score fails",
  438. pluginConfigs: []config.PluginConfig{
  439. {
  440. Name: scoreWithNormalizePlugin1,
  441. Args: runtime.Unknown{
  442. Raw: []byte(`{ "scoreStatus": 1 }`),
  443. },
  444. },
  445. },
  446. plugins: buildScoreConfigDefaultWeights(scorePlugin1, scoreWithNormalizePlugin1),
  447. err: true,
  448. },
  449. {
  450. name: "normalize fails",
  451. pluginConfigs: []config.PluginConfig{
  452. {
  453. Name: scoreWithNormalizePlugin1,
  454. Args: runtime.Unknown{
  455. Raw: []byte(`{ "normalizeStatus": 1 }`),
  456. },
  457. },
  458. },
  459. plugins: buildScoreConfigDefaultWeights(scorePlugin1, scoreWithNormalizePlugin1),
  460. err: true,
  461. },
  462. {
  463. name: "Score plugin return score greater than MaxNodeScore",
  464. plugins: buildScoreConfigDefaultWeights(scorePlugin1),
  465. pluginConfigs: []config.PluginConfig{
  466. {
  467. Name: scorePlugin1,
  468. Args: runtime.Unknown{
  469. Raw: []byte(fmt.Sprintf(`{ "scoreRes": %d }`, MaxNodeScore+1)),
  470. },
  471. },
  472. },
  473. err: true,
  474. },
  475. {
  476. name: "Score plugin return score less than MinNodeScore",
  477. plugins: buildScoreConfigDefaultWeights(scorePlugin1),
  478. pluginConfigs: []config.PluginConfig{
  479. {
  480. Name: scorePlugin1,
  481. Args: runtime.Unknown{
  482. Raw: []byte(fmt.Sprintf(`{ "scoreRes": %d }`, MinNodeScore-1)),
  483. },
  484. },
  485. },
  486. err: true,
  487. },
  488. {
  489. name: "ScoreWithNormalize plugin return score greater than MaxNodeScore",
  490. plugins: buildScoreConfigDefaultWeights(scoreWithNormalizePlugin1),
  491. pluginConfigs: []config.PluginConfig{
  492. {
  493. Name: scoreWithNormalizePlugin1,
  494. Args: runtime.Unknown{
  495. Raw: []byte(fmt.Sprintf(`{ "normalizeRes": %d }`, MaxNodeScore+1)),
  496. },
  497. },
  498. },
  499. err: true,
  500. },
  501. {
  502. name: "ScoreWithNormalize plugin return score less than MinNodeScore",
  503. plugins: buildScoreConfigDefaultWeights(scoreWithNormalizePlugin1),
  504. pluginConfigs: []config.PluginConfig{
  505. {
  506. Name: scoreWithNormalizePlugin1,
  507. Args: runtime.Unknown{
  508. Raw: []byte(fmt.Sprintf(`{ "normalizeRes": %d }`, MinNodeScore-1)),
  509. },
  510. },
  511. },
  512. err: true,
  513. },
  514. }
  515. for _, tt := range tests {
  516. t.Run(tt.name, func(t *testing.T) {
  517. // Inject the results via Args in PluginConfig.
  518. f, err := newFrameworkWithQueueSortAndBind(registry, tt.plugins, tt.pluginConfigs)
  519. if err != nil {
  520. t.Fatalf("Failed to create framework for testing: %v", err)
  521. }
  522. res, status := f.RunScorePlugins(context.Background(), state, pod, nodes)
  523. if tt.err {
  524. if status.IsSuccess() {
  525. t.Errorf("Expected status to be non-success. got: %v", status.Code().String())
  526. }
  527. return
  528. }
  529. if !status.IsSuccess() {
  530. t.Errorf("Expected status to be success.")
  531. }
  532. if !reflect.DeepEqual(res, tt.want) {
  533. t.Errorf("Score map after RunScorePlugin: %+v, want: %+v.", res, tt.want)
  534. }
  535. })
  536. }
  537. }
  538. func TestPreFilterPlugins(t *testing.T) {
  539. preFilter1 := &TestPreFilterPlugin{}
  540. preFilter2 := &TestPreFilterWithExtensionsPlugin{}
  541. r := make(Registry)
  542. r.Register(preFilterPluginName,
  543. func(_ *runtime.Unknown, fh FrameworkHandle) (Plugin, error) {
  544. return preFilter1, nil
  545. })
  546. r.Register(preFilterWithExtensionsPluginName,
  547. func(_ *runtime.Unknown, fh FrameworkHandle) (Plugin, error) {
  548. return preFilter2, nil
  549. })
  550. plugins := &config.Plugins{PreFilter: &config.PluginSet{Enabled: []config.Plugin{{Name: preFilterWithExtensionsPluginName}, {Name: preFilterPluginName}}}}
  551. t.Run("TestPreFilterPlugin", func(t *testing.T) {
  552. f, err := newFrameworkWithQueueSortAndBind(r, plugins, emptyArgs)
  553. if err != nil {
  554. t.Fatalf("Failed to create framework for testing: %v", err)
  555. }
  556. f.RunPreFilterPlugins(context.Background(), nil, nil)
  557. f.RunPreFilterExtensionAddPod(context.Background(), nil, nil, nil, nil)
  558. f.RunPreFilterExtensionRemovePod(context.Background(), nil, nil, nil, nil)
  559. if preFilter1.PreFilterCalled != 1 {
  560. t.Errorf("preFilter1 called %v, expected: 1", preFilter1.PreFilterCalled)
  561. }
  562. if preFilter2.PreFilterCalled != 1 {
  563. t.Errorf("preFilter2 called %v, expected: 1", preFilter2.PreFilterCalled)
  564. }
  565. if preFilter2.AddCalled != 1 {
  566. t.Errorf("AddPod called %v, expected: 1", preFilter2.AddCalled)
  567. }
  568. if preFilter2.RemoveCalled != 1 {
  569. t.Errorf("AddPod called %v, expected: 1", preFilter2.RemoveCalled)
  570. }
  571. })
  572. }
  573. func TestFilterPlugins(t *testing.T) {
  574. tests := []struct {
  575. name string
  576. plugins []*TestPlugin
  577. wantStatus *Status
  578. wantStatusMap PluginToStatus
  579. runAllFilters bool
  580. }{
  581. {
  582. name: "SuccessFilter",
  583. plugins: []*TestPlugin{
  584. {
  585. name: "TestPlugin",
  586. inj: injectedResult{FilterStatus: int(Success)},
  587. },
  588. },
  589. wantStatus: nil,
  590. wantStatusMap: PluginToStatus{},
  591. },
  592. {
  593. name: "ErrorFilter",
  594. plugins: []*TestPlugin{
  595. {
  596. name: "TestPlugin",
  597. inj: injectedResult{FilterStatus: int(Error)},
  598. },
  599. },
  600. wantStatus: NewStatus(Error, `running "TestPlugin" filter plugin for pod "": injected filter status`),
  601. wantStatusMap: PluginToStatus{"TestPlugin": NewStatus(Error, `running "TestPlugin" filter plugin for pod "": injected filter status`)},
  602. },
  603. {
  604. name: "UnschedulableFilter",
  605. plugins: []*TestPlugin{
  606. {
  607. name: "TestPlugin",
  608. inj: injectedResult{FilterStatus: int(Unschedulable)},
  609. },
  610. },
  611. wantStatus: NewStatus(Unschedulable, "injected filter status"),
  612. wantStatusMap: PluginToStatus{"TestPlugin": NewStatus(Unschedulable, "injected filter status")},
  613. },
  614. {
  615. name: "UnschedulableAndUnresolvableFilter",
  616. plugins: []*TestPlugin{
  617. {
  618. name: "TestPlugin",
  619. inj: injectedResult{
  620. FilterStatus: int(UnschedulableAndUnresolvable)},
  621. },
  622. },
  623. wantStatus: NewStatus(UnschedulableAndUnresolvable, "injected filter status"),
  624. wantStatusMap: PluginToStatus{"TestPlugin": NewStatus(UnschedulableAndUnresolvable, "injected filter status")},
  625. },
  626. // followings tests cover multiple-plugins scenarios
  627. {
  628. name: "ErrorAndErrorFilters",
  629. plugins: []*TestPlugin{
  630. {
  631. name: "TestPlugin1",
  632. inj: injectedResult{FilterStatus: int(Error)},
  633. },
  634. {
  635. name: "TestPlugin2",
  636. inj: injectedResult{FilterStatus: int(Error)},
  637. },
  638. },
  639. wantStatus: NewStatus(Error, `running "TestPlugin1" filter plugin for pod "": injected filter status`),
  640. wantStatusMap: PluginToStatus{"TestPlugin1": NewStatus(Error, `running "TestPlugin1" filter plugin for pod "": injected filter status`)},
  641. },
  642. {
  643. name: "SuccessAndSuccessFilters",
  644. plugins: []*TestPlugin{
  645. {
  646. name: "TestPlugin1",
  647. inj: injectedResult{FilterStatus: int(Success)},
  648. },
  649. {
  650. name: "TestPlugin2",
  651. inj: injectedResult{FilterStatus: int(Success)},
  652. },
  653. },
  654. wantStatus: nil,
  655. wantStatusMap: PluginToStatus{},
  656. },
  657. {
  658. name: "ErrorAndSuccessFilters",
  659. plugins: []*TestPlugin{
  660. {
  661. name: "TestPlugin1",
  662. inj: injectedResult{FilterStatus: int(Error)},
  663. },
  664. {
  665. name: "TestPlugin2",
  666. inj: injectedResult{FilterStatus: int(Success)},
  667. },
  668. },
  669. wantStatus: NewStatus(Error, `running "TestPlugin1" filter plugin for pod "": injected filter status`),
  670. wantStatusMap: PluginToStatus{"TestPlugin1": NewStatus(Error, `running "TestPlugin1" filter plugin for pod "": injected filter status`)},
  671. },
  672. {
  673. name: "SuccessAndErrorFilters",
  674. plugins: []*TestPlugin{
  675. {
  676. name: "TestPlugin1",
  677. inj: injectedResult{FilterStatus: int(Success)},
  678. },
  679. {
  680. name: "TestPlugin2",
  681. inj: injectedResult{FilterStatus: int(Error)},
  682. },
  683. },
  684. wantStatus: NewStatus(Error, `running "TestPlugin2" filter plugin for pod "": injected filter status`),
  685. wantStatusMap: PluginToStatus{"TestPlugin2": NewStatus(Error, `running "TestPlugin2" filter plugin for pod "": injected filter status`)},
  686. },
  687. {
  688. name: "SuccessAndUnschedulableFilters",
  689. plugins: []*TestPlugin{
  690. {
  691. name: "TestPlugin1",
  692. inj: injectedResult{FilterStatus: int(Success)},
  693. },
  694. {
  695. name: "TestPlugin2",
  696. inj: injectedResult{FilterStatus: int(Unschedulable)},
  697. },
  698. },
  699. wantStatus: NewStatus(Unschedulable, "injected filter status"),
  700. wantStatusMap: PluginToStatus{"TestPlugin2": NewStatus(Unschedulable, "injected filter status")},
  701. },
  702. {
  703. name: "SuccessFilterWithRunAllFilters",
  704. plugins: []*TestPlugin{
  705. {
  706. name: "TestPlugin",
  707. inj: injectedResult{FilterStatus: int(Success)},
  708. },
  709. },
  710. runAllFilters: true,
  711. wantStatus: nil,
  712. wantStatusMap: PluginToStatus{},
  713. },
  714. {
  715. name: "ErrorAndErrorFilters",
  716. plugins: []*TestPlugin{
  717. {
  718. name: "TestPlugin1",
  719. inj: injectedResult{FilterStatus: int(Error)},
  720. },
  721. {
  722. name: "TestPlugin2",
  723. inj: injectedResult{FilterStatus: int(Error)},
  724. },
  725. },
  726. runAllFilters: true,
  727. wantStatus: NewStatus(Error, `running "TestPlugin1" filter plugin for pod "": injected filter status`),
  728. wantStatusMap: PluginToStatus{"TestPlugin1": NewStatus(Error, `running "TestPlugin1" filter plugin for pod "": injected filter status`)},
  729. },
  730. {
  731. name: "ErrorAndErrorFilters",
  732. plugins: []*TestPlugin{
  733. {
  734. name: "TestPlugin1",
  735. inj: injectedResult{FilterStatus: int(UnschedulableAndUnresolvable)},
  736. },
  737. {
  738. name: "TestPlugin2",
  739. inj: injectedResult{FilterStatus: int(Unschedulable)},
  740. },
  741. },
  742. runAllFilters: true,
  743. wantStatus: NewStatus(UnschedulableAndUnresolvable, "injected filter status", "injected filter status"),
  744. wantStatusMap: PluginToStatus{
  745. "TestPlugin1": NewStatus(UnschedulableAndUnresolvable, "injected filter status"),
  746. "TestPlugin2": NewStatus(Unschedulable, "injected filter status"),
  747. },
  748. },
  749. }
  750. for _, tt := range tests {
  751. t.Run(tt.name, func(t *testing.T) {
  752. registry := Registry{}
  753. cfgPls := &config.Plugins{Filter: &config.PluginSet{}}
  754. for _, pl := range tt.plugins {
  755. // register all plugins
  756. tmpPl := pl
  757. if err := registry.Register(pl.name,
  758. func(_ *runtime.Unknown, _ FrameworkHandle) (Plugin, error) {
  759. return tmpPl, nil
  760. }); err != nil {
  761. t.Fatalf("fail to register filter plugin (%s)", pl.name)
  762. }
  763. // append plugins to filter pluginset
  764. cfgPls.Filter.Enabled = append(
  765. cfgPls.Filter.Enabled,
  766. config.Plugin{Name: pl.name})
  767. }
  768. f, err := newFrameworkWithQueueSortAndBind(registry, cfgPls, emptyArgs, WithRunAllFilters(tt.runAllFilters))
  769. if err != nil {
  770. t.Fatalf("fail to create framework: %s", err)
  771. }
  772. gotStatusMap := f.RunFilterPlugins(context.TODO(), nil, pod, nil)
  773. gotStatus := gotStatusMap.Merge()
  774. if !reflect.DeepEqual(gotStatus, tt.wantStatus) {
  775. t.Errorf("wrong status code. got: %v, want:%v", gotStatus, tt.wantStatus)
  776. }
  777. if !reflect.DeepEqual(gotStatusMap, tt.wantStatusMap) {
  778. t.Errorf("wrong status map. got: %+v, want: %+v", gotStatusMap, tt.wantStatusMap)
  779. }
  780. })
  781. }
  782. }
  783. func TestPreBindPlugins(t *testing.T) {
  784. tests := []struct {
  785. name string
  786. plugins []*TestPlugin
  787. wantStatus *Status
  788. }{
  789. {
  790. name: "NoPreBindPlugin",
  791. plugins: []*TestPlugin{},
  792. wantStatus: nil,
  793. },
  794. {
  795. name: "SuccessPreBindPlugins",
  796. plugins: []*TestPlugin{
  797. {
  798. name: "TestPlugin",
  799. inj: injectedResult{PreBindStatus: int(Success)},
  800. },
  801. },
  802. wantStatus: nil,
  803. },
  804. {
  805. name: "UnshedulablePreBindPlugin",
  806. plugins: []*TestPlugin{
  807. {
  808. name: "TestPlugin",
  809. inj: injectedResult{PreBindStatus: int(Unschedulable)},
  810. },
  811. },
  812. wantStatus: NewStatus(Error, `error while running "TestPlugin" prebind plugin for pod "": injected status`),
  813. },
  814. {
  815. name: "ErrorPreBindPlugin",
  816. plugins: []*TestPlugin{
  817. {
  818. name: "TestPlugin",
  819. inj: injectedResult{PreBindStatus: int(Error)},
  820. },
  821. },
  822. wantStatus: NewStatus(Error, `error while running "TestPlugin" prebind plugin for pod "": injected status`),
  823. },
  824. {
  825. name: "UnschedulablePreBindPlugin",
  826. plugins: []*TestPlugin{
  827. {
  828. name: "TestPlugin",
  829. inj: injectedResult{PreBindStatus: int(UnschedulableAndUnresolvable)},
  830. },
  831. },
  832. wantStatus: NewStatus(Error, `error while running "TestPlugin" prebind plugin for pod "": injected status`),
  833. },
  834. {
  835. name: "SuccessErrorPreBindPlugins",
  836. plugins: []*TestPlugin{
  837. {
  838. name: "TestPlugin",
  839. inj: injectedResult{PreBindStatus: int(Success)},
  840. },
  841. {
  842. name: "TestPlugin 1",
  843. inj: injectedResult{PreBindStatus: int(Error)},
  844. },
  845. },
  846. wantStatus: NewStatus(Error, `error while running "TestPlugin 1" prebind plugin for pod "": injected status`),
  847. },
  848. {
  849. name: "ErrorSuccessPreBindPlugin",
  850. plugins: []*TestPlugin{
  851. {
  852. name: "TestPlugin",
  853. inj: injectedResult{PreBindStatus: int(Error)},
  854. },
  855. {
  856. name: "TestPlugin 1",
  857. inj: injectedResult{PreBindStatus: int(Success)},
  858. },
  859. },
  860. wantStatus: NewStatus(Error, `error while running "TestPlugin" prebind plugin for pod "": injected status`),
  861. },
  862. {
  863. name: "SuccessSuccessPreBindPlugin",
  864. plugins: []*TestPlugin{
  865. {
  866. name: "TestPlugin",
  867. inj: injectedResult{PreBindStatus: int(Success)},
  868. },
  869. {
  870. name: "TestPlugin 1",
  871. inj: injectedResult{PreBindStatus: int(Success)},
  872. },
  873. },
  874. wantStatus: nil,
  875. },
  876. {
  877. name: "ErrorAndErrorPlugins",
  878. plugins: []*TestPlugin{
  879. {
  880. name: "TestPlugin",
  881. inj: injectedResult{PreBindStatus: int(Error)},
  882. },
  883. {
  884. name: "TestPlugin 1",
  885. inj: injectedResult{PreBindStatus: int(Error)},
  886. },
  887. },
  888. wantStatus: NewStatus(Error, `error while running "TestPlugin" prebind plugin for pod "": injected status`),
  889. },
  890. {
  891. name: "UnschedulableAndSuccessPreBindPlugin",
  892. plugins: []*TestPlugin{
  893. {
  894. name: "TestPlugin",
  895. inj: injectedResult{PreBindStatus: int(Unschedulable)},
  896. },
  897. {
  898. name: "TestPlugin 1",
  899. inj: injectedResult{PreBindStatus: int(Success)},
  900. },
  901. },
  902. wantStatus: NewStatus(Error, `error while running "TestPlugin" prebind plugin for pod "": injected status`),
  903. },
  904. }
  905. for _, tt := range tests {
  906. t.Run(tt.name, func(t *testing.T) {
  907. registry := Registry{}
  908. configPlugins := &config.Plugins{PreBind: &config.PluginSet{}}
  909. for _, pl := range tt.plugins {
  910. tmpPl := pl
  911. if err := registry.Register(pl.name, func(_ *runtime.Unknown, _ FrameworkHandle) (Plugin, error) {
  912. return tmpPl, nil
  913. }); err != nil {
  914. t.Fatalf("Unable to register pre bind plugins: %s", pl.name)
  915. }
  916. configPlugins.PreBind.Enabled = append(
  917. configPlugins.PreBind.Enabled,
  918. config.Plugin{Name: pl.name},
  919. )
  920. }
  921. f, err := newFrameworkWithQueueSortAndBind(registry, configPlugins, emptyArgs)
  922. if err != nil {
  923. t.Fatalf("fail to create framework: %s", err)
  924. }
  925. status := f.RunPreBindPlugins(context.TODO(), nil, pod, "")
  926. if !reflect.DeepEqual(status, tt.wantStatus) {
  927. t.Errorf("wrong status code. got %v, want %v", status, tt.wantStatus)
  928. }
  929. })
  930. }
  931. }
  932. func TestReservePlugins(t *testing.T) {
  933. tests := []struct {
  934. name string
  935. plugins []*TestPlugin
  936. wantStatus *Status
  937. }{
  938. {
  939. name: "NoReservePlugin",
  940. plugins: []*TestPlugin{},
  941. wantStatus: nil,
  942. },
  943. {
  944. name: "SuccessReservePlugins",
  945. plugins: []*TestPlugin{
  946. {
  947. name: "TestPlugin",
  948. inj: injectedResult{ReserveStatus: int(Success)},
  949. },
  950. },
  951. wantStatus: nil,
  952. },
  953. {
  954. name: "UnshedulableReservePlugin",
  955. plugins: []*TestPlugin{
  956. {
  957. name: "TestPlugin",
  958. inj: injectedResult{ReserveStatus: int(Unschedulable)},
  959. },
  960. },
  961. wantStatus: NewStatus(Error, `error while running "TestPlugin" reserve plugin for pod "": injected status`),
  962. },
  963. {
  964. name: "ErrorReservePlugin",
  965. plugins: []*TestPlugin{
  966. {
  967. name: "TestPlugin",
  968. inj: injectedResult{ReserveStatus: int(Error)},
  969. },
  970. },
  971. wantStatus: NewStatus(Error, `error while running "TestPlugin" reserve plugin for pod "": injected status`),
  972. },
  973. {
  974. name: "UnschedulableReservePlugin",
  975. plugins: []*TestPlugin{
  976. {
  977. name: "TestPlugin",
  978. inj: injectedResult{ReserveStatus: int(UnschedulableAndUnresolvable)},
  979. },
  980. },
  981. wantStatus: NewStatus(Error, `error while running "TestPlugin" reserve plugin for pod "": injected status`),
  982. },
  983. {
  984. name: "SuccessSuccessReservePlugins",
  985. plugins: []*TestPlugin{
  986. {
  987. name: "TestPlugin",
  988. inj: injectedResult{ReserveStatus: int(Success)},
  989. },
  990. {
  991. name: "TestPlugin 1",
  992. inj: injectedResult{ReserveStatus: int(Success)},
  993. },
  994. },
  995. wantStatus: nil,
  996. },
  997. {
  998. name: "ErrorErrorReservePlugins",
  999. plugins: []*TestPlugin{
  1000. {
  1001. name: "TestPlugin",
  1002. inj: injectedResult{ReserveStatus: int(Error)},
  1003. },
  1004. {
  1005. name: "TestPlugin 1",
  1006. inj: injectedResult{ReserveStatus: int(Error)},
  1007. },
  1008. },
  1009. wantStatus: NewStatus(Error, `error while running "TestPlugin" reserve plugin for pod "": injected status`),
  1010. },
  1011. {
  1012. name: "SuccessErrorReservePlugins",
  1013. plugins: []*TestPlugin{
  1014. {
  1015. name: "TestPlugin",
  1016. inj: injectedResult{ReserveStatus: int(Success)},
  1017. },
  1018. {
  1019. name: "TestPlugin 1",
  1020. inj: injectedResult{ReserveStatus: int(Error)},
  1021. },
  1022. },
  1023. wantStatus: NewStatus(Error, `error while running "TestPlugin 1" reserve plugin for pod "": injected status`),
  1024. },
  1025. {
  1026. name: "ErrorSuccessReservePlugin",
  1027. plugins: []*TestPlugin{
  1028. {
  1029. name: "TestPlugin",
  1030. inj: injectedResult{ReserveStatus: int(Error)},
  1031. },
  1032. {
  1033. name: "TestPlugin 1",
  1034. inj: injectedResult{ReserveStatus: int(Success)},
  1035. },
  1036. },
  1037. wantStatus: NewStatus(Error, `error while running "TestPlugin" reserve plugin for pod "": injected status`),
  1038. },
  1039. {
  1040. name: "UnschedulableAndSuccessReservePlugin",
  1041. plugins: []*TestPlugin{
  1042. {
  1043. name: "TestPlugin",
  1044. inj: injectedResult{ReserveStatus: int(Unschedulable)},
  1045. },
  1046. {
  1047. name: "TestPlugin 1",
  1048. inj: injectedResult{ReserveStatus: int(Success)},
  1049. },
  1050. },
  1051. wantStatus: NewStatus(Error, `error while running "TestPlugin" reserve plugin for pod "": injected status`),
  1052. },
  1053. }
  1054. for _, tt := range tests {
  1055. t.Run(tt.name, func(t *testing.T) {
  1056. registry := Registry{}
  1057. configPlugins := &config.Plugins{Reserve: &config.PluginSet{}}
  1058. for _, pl := range tt.plugins {
  1059. tmpPl := pl
  1060. if err := registry.Register(pl.name, func(_ *runtime.Unknown, _ FrameworkHandle) (Plugin, error) {
  1061. return tmpPl, nil
  1062. }); err != nil {
  1063. t.Fatalf("Unable to register pre bind plugins: %s", pl.name)
  1064. }
  1065. configPlugins.Reserve.Enabled = append(
  1066. configPlugins.Reserve.Enabled,
  1067. config.Plugin{Name: pl.name},
  1068. )
  1069. }
  1070. f, err := newFrameworkWithQueueSortAndBind(registry, configPlugins, emptyArgs)
  1071. if err != nil {
  1072. t.Fatalf("fail to create framework: %s", err)
  1073. }
  1074. status := f.RunReservePlugins(context.TODO(), nil, pod, "")
  1075. if !reflect.DeepEqual(status, tt.wantStatus) {
  1076. t.Errorf("wrong status code. got %v, want %v", status, tt.wantStatus)
  1077. }
  1078. })
  1079. }
  1080. }
  1081. func TestPermitPlugins(t *testing.T) {
  1082. tests := []struct {
  1083. name string
  1084. plugins []*TestPlugin
  1085. want *Status
  1086. }{
  1087. {
  1088. name: "NilPermitPlugin",
  1089. plugins: []*TestPlugin{},
  1090. want: nil,
  1091. },
  1092. {
  1093. name: "SuccessPermitPlugin",
  1094. plugins: []*TestPlugin{
  1095. {
  1096. name: "TestPlugin",
  1097. inj: injectedResult{PermitStatus: int(Success)},
  1098. },
  1099. },
  1100. want: nil,
  1101. },
  1102. {
  1103. name: "UnschedulablePermitPlugin",
  1104. plugins: []*TestPlugin{
  1105. {
  1106. name: "TestPlugin",
  1107. inj: injectedResult{PermitStatus: int(Unschedulable)},
  1108. },
  1109. },
  1110. want: NewStatus(Unschedulable, `rejected pod "" by permit plugin "TestPlugin": injected status`),
  1111. },
  1112. {
  1113. name: "ErrorPermitPlugin",
  1114. plugins: []*TestPlugin{
  1115. {
  1116. name: "TestPlugin",
  1117. inj: injectedResult{PermitStatus: int(Error)},
  1118. },
  1119. },
  1120. want: NewStatus(Error, `error while running "TestPlugin" permit plugin for pod "": injected status`),
  1121. },
  1122. {
  1123. name: "UnschedulableAndUnresolvablePermitPlugin",
  1124. plugins: []*TestPlugin{
  1125. {
  1126. name: "TestPlugin",
  1127. inj: injectedResult{PermitStatus: int(UnschedulableAndUnresolvable)},
  1128. },
  1129. },
  1130. want: NewStatus(UnschedulableAndUnresolvable, `rejected pod "" by permit plugin "TestPlugin": injected status`),
  1131. },
  1132. {
  1133. name: "WaitPermitPlugin",
  1134. plugins: []*TestPlugin{
  1135. {
  1136. name: "TestPlugin",
  1137. inj: injectedResult{PermitStatus: int(Wait)},
  1138. },
  1139. },
  1140. want: NewStatus(Wait, `one or more plugins asked to wait and no plugin rejected pod ""`),
  1141. },
  1142. {
  1143. name: "SuccessSuccessPermitPlugin",
  1144. plugins: []*TestPlugin{
  1145. {
  1146. name: "TestPlugin",
  1147. inj: injectedResult{PermitStatus: int(Success)},
  1148. },
  1149. {
  1150. name: "TestPlugin 1",
  1151. inj: injectedResult{PermitStatus: int(Success)},
  1152. },
  1153. },
  1154. want: nil,
  1155. },
  1156. {
  1157. name: "ErrorAndErrorPlugins",
  1158. plugins: []*TestPlugin{
  1159. {
  1160. name: "TestPlugin",
  1161. inj: injectedResult{PermitStatus: int(Error)},
  1162. },
  1163. {
  1164. name: "TestPlugin 1",
  1165. inj: injectedResult{PermitStatus: int(Error)},
  1166. },
  1167. },
  1168. want: NewStatus(Error, `error while running "TestPlugin" permit plugin for pod "": injected status`),
  1169. },
  1170. }
  1171. for _, tt := range tests {
  1172. registry := Registry{}
  1173. configPlugins := &config.Plugins{Permit: &config.PluginSet{}}
  1174. for _, pl := range tt.plugins {
  1175. tmpPl := pl
  1176. if err := registry.Register(pl.name, func(_ *runtime.Unknown, _ FrameworkHandle) (Plugin, error) {
  1177. return tmpPl, nil
  1178. }); err != nil {
  1179. t.Fatalf("Unable to register Permit plugin: %s", pl.name)
  1180. }
  1181. configPlugins.Permit.Enabled = append(
  1182. configPlugins.Permit.Enabled,
  1183. config.Plugin{Name: pl.name},
  1184. )
  1185. }
  1186. f, err := newFrameworkWithQueueSortAndBind(registry, configPlugins, emptyArgs)
  1187. if err != nil {
  1188. t.Fatalf("fail to create framework: %s", err)
  1189. }
  1190. status := f.RunPermitPlugins(context.TODO(), nil, pod, "")
  1191. if !reflect.DeepEqual(status, tt.want) {
  1192. t.Errorf("wrong status code. got %v, want %v", status, tt.want)
  1193. }
  1194. }
  1195. }
  1196. func TestRecordingMetrics(t *testing.T) {
  1197. state := &CycleState{
  1198. recordPluginMetrics: true,
  1199. }
  1200. tests := []struct {
  1201. name string
  1202. action func(f Framework)
  1203. inject injectedResult
  1204. wantExtensionPoint string
  1205. wantStatus Code
  1206. }{
  1207. {
  1208. name: "PreFilter - Success",
  1209. action: func(f Framework) { f.RunPreFilterPlugins(context.Background(), state, pod) },
  1210. wantExtensionPoint: "PreFilter",
  1211. wantStatus: Success,
  1212. },
  1213. {
  1214. name: "PreScore - Success",
  1215. action: func(f Framework) { f.RunPreScorePlugins(context.Background(), state, pod, nil) },
  1216. wantExtensionPoint: "PreScore",
  1217. wantStatus: Success,
  1218. },
  1219. {
  1220. name: "Score - Success",
  1221. action: func(f Framework) { f.RunScorePlugins(context.Background(), state, pod, nodes) },
  1222. wantExtensionPoint: "Score",
  1223. wantStatus: Success,
  1224. },
  1225. {
  1226. name: "Reserve - Success",
  1227. action: func(f Framework) { f.RunReservePlugins(context.Background(), state, pod, "") },
  1228. wantExtensionPoint: "Reserve",
  1229. wantStatus: Success,
  1230. },
  1231. {
  1232. name: "Unreserve - Success",
  1233. action: func(f Framework) { f.RunUnreservePlugins(context.Background(), state, pod, "") },
  1234. wantExtensionPoint: "Unreserve",
  1235. wantStatus: Success,
  1236. },
  1237. {
  1238. name: "PreBind - Success",
  1239. action: func(f Framework) { f.RunPreBindPlugins(context.Background(), state, pod, "") },
  1240. wantExtensionPoint: "PreBind",
  1241. wantStatus: Success,
  1242. },
  1243. {
  1244. name: "Bind - Success",
  1245. action: func(f Framework) { f.RunBindPlugins(context.Background(), state, pod, "") },
  1246. wantExtensionPoint: "Bind",
  1247. wantStatus: Success,
  1248. },
  1249. {
  1250. name: "PostBind - Success",
  1251. action: func(f Framework) { f.RunPostBindPlugins(context.Background(), state, pod, "") },
  1252. wantExtensionPoint: "PostBind",
  1253. wantStatus: Success,
  1254. },
  1255. {
  1256. name: "Permit - Success",
  1257. action: func(f Framework) { f.RunPermitPlugins(context.Background(), state, pod, "") },
  1258. wantExtensionPoint: "Permit",
  1259. wantStatus: Success,
  1260. },
  1261. {
  1262. name: "PreFilter - Error",
  1263. action: func(f Framework) { f.RunPreFilterPlugins(context.Background(), state, pod) },
  1264. inject: injectedResult{PreFilterStatus: int(Error)},
  1265. wantExtensionPoint: "PreFilter",
  1266. wantStatus: Error,
  1267. },
  1268. {
  1269. name: "PreScore - Error",
  1270. action: func(f Framework) { f.RunPreScorePlugins(context.Background(), state, pod, nil) },
  1271. inject: injectedResult{PreScoreStatus: int(Error)},
  1272. wantExtensionPoint: "PreScore",
  1273. wantStatus: Error,
  1274. },
  1275. {
  1276. name: "Score - Error",
  1277. action: func(f Framework) { f.RunScorePlugins(context.Background(), state, pod, nodes) },
  1278. inject: injectedResult{ScoreStatus: int(Error)},
  1279. wantExtensionPoint: "Score",
  1280. wantStatus: Error,
  1281. },
  1282. {
  1283. name: "Reserve - Error",
  1284. action: func(f Framework) { f.RunReservePlugins(context.Background(), state, pod, "") },
  1285. inject: injectedResult{ReserveStatus: int(Error)},
  1286. wantExtensionPoint: "Reserve",
  1287. wantStatus: Error,
  1288. },
  1289. {
  1290. name: "PreBind - Error",
  1291. action: func(f Framework) { f.RunPreBindPlugins(context.Background(), state, pod, "") },
  1292. inject: injectedResult{PreBindStatus: int(Error)},
  1293. wantExtensionPoint: "PreBind",
  1294. wantStatus: Error,
  1295. },
  1296. {
  1297. name: "Bind - Error",
  1298. action: func(f Framework) { f.RunBindPlugins(context.Background(), state, pod, "") },
  1299. inject: injectedResult{BindStatus: int(Error)},
  1300. wantExtensionPoint: "Bind",
  1301. wantStatus: Error,
  1302. },
  1303. {
  1304. name: "Permit - Error",
  1305. action: func(f Framework) { f.RunPermitPlugins(context.Background(), state, pod, "") },
  1306. inject: injectedResult{PermitStatus: int(Error)},
  1307. wantExtensionPoint: "Permit",
  1308. wantStatus: Error,
  1309. },
  1310. {
  1311. name: "Permit - Wait",
  1312. action: func(f Framework) { f.RunPermitPlugins(context.Background(), state, pod, "") },
  1313. inject: injectedResult{PermitStatus: int(Wait)},
  1314. wantExtensionPoint: "Permit",
  1315. wantStatus: Wait,
  1316. },
  1317. }
  1318. for _, tt := range tests {
  1319. t.Run(tt.name, func(t *testing.T) {
  1320. metrics.Register()
  1321. metrics.FrameworkExtensionPointDuration.Reset()
  1322. metrics.PluginExecutionDuration.Reset()
  1323. plugin := &TestPlugin{name: testPlugin, inj: tt.inject}
  1324. r := make(Registry)
  1325. r.Register(testPlugin,
  1326. func(_ *runtime.Unknown, fh FrameworkHandle) (Plugin, error) {
  1327. return plugin, nil
  1328. })
  1329. pluginSet := &config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin, Weight: 1}}}
  1330. plugins := &config.Plugins{
  1331. Score: pluginSet,
  1332. PreFilter: pluginSet,
  1333. Filter: pluginSet,
  1334. PreScore: pluginSet,
  1335. Reserve: pluginSet,
  1336. Permit: pluginSet,
  1337. PreBind: pluginSet,
  1338. Bind: pluginSet,
  1339. PostBind: pluginSet,
  1340. Unreserve: pluginSet,
  1341. }
  1342. recorder := newMetricsRecorder(100, time.Nanosecond)
  1343. f, err := newFrameworkWithQueueSortAndBind(r, plugins, emptyArgs, withMetricsRecorder(recorder))
  1344. if err != nil {
  1345. t.Fatalf("Failed to create framework for testing: %v", err)
  1346. }
  1347. tt.action(f)
  1348. // Stop the goroutine which records metrics and ensure it's stopped.
  1349. close(recorder.stopCh)
  1350. <-recorder.isStoppedCh
  1351. // Try to clean up the metrics buffer again in case it's not empty.
  1352. recorder.flushMetrics()
  1353. collectAndCompareFrameworkMetrics(t, tt.wantExtensionPoint, tt.wantStatus)
  1354. collectAndComparePluginMetrics(t, tt.wantExtensionPoint, testPlugin, tt.wantStatus)
  1355. })
  1356. }
  1357. }
  1358. func TestRunBindPlugins(t *testing.T) {
  1359. tests := []struct {
  1360. name string
  1361. injects []Code
  1362. wantStatus Code
  1363. }{
  1364. {
  1365. name: "simple success",
  1366. injects: []Code{Success},
  1367. wantStatus: Success,
  1368. },
  1369. {
  1370. name: "error on second",
  1371. injects: []Code{Skip, Error, Success},
  1372. wantStatus: Error,
  1373. },
  1374. {
  1375. name: "all skip",
  1376. injects: []Code{Skip, Skip, Skip},
  1377. wantStatus: Skip,
  1378. },
  1379. {
  1380. name: "error on third, but not reached",
  1381. injects: []Code{Skip, Success, Error},
  1382. wantStatus: Success,
  1383. },
  1384. {
  1385. name: "no bind plugin, returns default binder",
  1386. injects: []Code{},
  1387. wantStatus: Success,
  1388. },
  1389. {
  1390. name: "invalid status",
  1391. injects: []Code{Unschedulable},
  1392. wantStatus: Error,
  1393. },
  1394. {
  1395. name: "simple error",
  1396. injects: []Code{Error},
  1397. wantStatus: Error,
  1398. },
  1399. {
  1400. name: "success on second, returns success",
  1401. injects: []Code{Skip, Success},
  1402. wantStatus: Success,
  1403. },
  1404. {
  1405. name: "invalid status, returns error",
  1406. injects: []Code{Skip, UnschedulableAndUnresolvable},
  1407. wantStatus: Error,
  1408. },
  1409. {
  1410. name: "error after success status, returns success",
  1411. injects: []Code{Success, Error},
  1412. wantStatus: Success,
  1413. },
  1414. {
  1415. name: "success before invalid status, returns success",
  1416. injects: []Code{Success, Error},
  1417. wantStatus: Success,
  1418. },
  1419. {
  1420. name: "success after error status, returns error",
  1421. injects: []Code{Error, Success},
  1422. wantStatus: Error,
  1423. },
  1424. }
  1425. for _, tt := range tests {
  1426. t.Run(tt.name, func(t *testing.T) {
  1427. metrics.Register()
  1428. metrics.FrameworkExtensionPointDuration.Reset()
  1429. metrics.PluginExecutionDuration.Reset()
  1430. pluginSet := &config.PluginSet{}
  1431. r := make(Registry)
  1432. for i, inj := range tt.injects {
  1433. name := fmt.Sprintf("bind-%d", i)
  1434. plugin := &TestPlugin{name: name, inj: injectedResult{BindStatus: int(inj)}}
  1435. r.Register(name,
  1436. func(_ *runtime.Unknown, fh FrameworkHandle) (Plugin, error) {
  1437. return plugin, nil
  1438. })
  1439. pluginSet.Enabled = append(pluginSet.Enabled, config.Plugin{Name: name})
  1440. }
  1441. plugins := &config.Plugins{Bind: pluginSet}
  1442. recorder := newMetricsRecorder(100, time.Nanosecond)
  1443. fwk, err := newFrameworkWithQueueSortAndBind(r, plugins, emptyArgs, withMetricsRecorder(recorder))
  1444. if err != nil {
  1445. t.Fatal(err)
  1446. }
  1447. st := fwk.RunBindPlugins(context.Background(), state, pod, "")
  1448. if st.Code() != tt.wantStatus {
  1449. t.Errorf("got status code %s, want %s", st.Code(), tt.wantStatus)
  1450. }
  1451. // Stop the goroutine which records metrics and ensure it's stopped.
  1452. close(recorder.stopCh)
  1453. <-recorder.isStoppedCh
  1454. // Try to clean up the metrics buffer again in case it's not empty.
  1455. recorder.flushMetrics()
  1456. collectAndCompareFrameworkMetrics(t, "Bind", tt.wantStatus)
  1457. })
  1458. }
  1459. }
  1460. func TestPermitWaitDurationMetric(t *testing.T) {
  1461. tests := []struct {
  1462. name string
  1463. inject injectedResult
  1464. wantRes string
  1465. }{
  1466. {
  1467. name: "WaitOnPermit - No Wait",
  1468. },
  1469. {
  1470. name: "WaitOnPermit - Wait Timeout",
  1471. inject: injectedResult{PermitStatus: int(Wait)},
  1472. wantRes: "Unschedulable",
  1473. },
  1474. }
  1475. for _, tt := range tests {
  1476. t.Run(tt.name, func(t *testing.T) {
  1477. metrics.Register()
  1478. metrics.PermitWaitDuration.Reset()
  1479. plugin := &TestPlugin{name: testPlugin, inj: tt.inject}
  1480. r := make(Registry)
  1481. err := r.Register(testPlugin,
  1482. func(_ *runtime.Unknown, fh FrameworkHandle) (Plugin, error) {
  1483. return plugin, nil
  1484. })
  1485. if err != nil {
  1486. t.Fatal(err)
  1487. }
  1488. plugins := &config.Plugins{
  1489. Permit: &config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin, Weight: 1}}},
  1490. }
  1491. f, err := newFrameworkWithQueueSortAndBind(r, plugins, emptyArgs)
  1492. if err != nil {
  1493. t.Fatalf("Failed to create framework for testing: %v", err)
  1494. }
  1495. f.RunPermitPlugins(context.TODO(), nil, pod, "")
  1496. f.WaitOnPermit(context.TODO(), pod)
  1497. collectAndComparePermitWaitDuration(t, tt.wantRes)
  1498. })
  1499. }
  1500. }
  1501. func TestWaitOnPermit(t *testing.T) {
  1502. pod := &v1.Pod{
  1503. ObjectMeta: metav1.ObjectMeta{
  1504. Name: "pod",
  1505. UID: types.UID("pod"),
  1506. },
  1507. }
  1508. tests := []struct {
  1509. name string
  1510. action func(f Framework)
  1511. wantStatus Code
  1512. wantMessage string
  1513. }{
  1514. {
  1515. name: "Reject Waiting Pod",
  1516. action: func(f Framework) {
  1517. f.GetWaitingPod(pod.UID).Reject("reject message")
  1518. },
  1519. wantStatus: Unschedulable,
  1520. wantMessage: "pod \"pod\" rejected while waiting on permit: reject message",
  1521. },
  1522. {
  1523. name: "Allow Waiting Pod",
  1524. action: func(f Framework) {
  1525. f.GetWaitingPod(pod.UID).Allow(permitPlugin)
  1526. },
  1527. wantStatus: Success,
  1528. wantMessage: "",
  1529. },
  1530. }
  1531. for _, tt := range tests {
  1532. t.Run(tt.name, func(t *testing.T) {
  1533. testPermitPlugin := &TestPermitPlugin{}
  1534. r := make(Registry)
  1535. r.Register(permitPlugin,
  1536. func(_ *runtime.Unknown, fh FrameworkHandle) (Plugin, error) {
  1537. return testPermitPlugin, nil
  1538. })
  1539. plugins := &config.Plugins{
  1540. Permit: &config.PluginSet{Enabled: []config.Plugin{{Name: permitPlugin, Weight: 1}}},
  1541. }
  1542. f, err := newFrameworkWithQueueSortAndBind(r, plugins, emptyArgs)
  1543. if err != nil {
  1544. t.Fatalf("Failed to create framework for testing: %v", err)
  1545. }
  1546. runPermitPluginsStatus := f.RunPermitPlugins(context.Background(), nil, pod, "")
  1547. if runPermitPluginsStatus.Code() != Wait {
  1548. t.Fatalf("Expected RunPermitPlugins to return status %v, but got %v",
  1549. Wait, runPermitPluginsStatus.Code())
  1550. }
  1551. go tt.action(f)
  1552. waitOnPermitStatus := f.WaitOnPermit(context.Background(), pod)
  1553. if waitOnPermitStatus.Code() != tt.wantStatus {
  1554. t.Fatalf("Expected WaitOnPermit to return status %v, but got %v",
  1555. tt.wantStatus, waitOnPermitStatus.Code())
  1556. }
  1557. if waitOnPermitStatus.Message() != tt.wantMessage {
  1558. t.Fatalf("Expected WaitOnPermit to return status with message %q, but got %q",
  1559. tt.wantMessage, waitOnPermitStatus.Message())
  1560. }
  1561. })
  1562. }
  1563. }
  1564. func buildScoreConfigDefaultWeights(ps ...string) *config.Plugins {
  1565. return buildScoreConfigWithWeights(defaultWeights, ps...)
  1566. }
  1567. func buildScoreConfigWithWeights(weights map[string]int32, ps ...string) *config.Plugins {
  1568. var plugins []config.Plugin
  1569. for _, p := range ps {
  1570. plugins = append(plugins, config.Plugin{Name: p, Weight: weights[p]})
  1571. }
  1572. return &config.Plugins{Score: &config.PluginSet{Enabled: plugins}}
  1573. }
  1574. type injectedResult struct {
  1575. ScoreRes int64 `json:"scoreRes,omitempty"`
  1576. NormalizeRes int64 `json:"normalizeRes,omitempty"`
  1577. ScoreStatus int `json:"scoreStatus,omitempty"`
  1578. NormalizeStatus int `json:"normalizeStatus,omitempty"`
  1579. PreFilterStatus int `json:"preFilterStatus,omitempty"`
  1580. PreFilterAddPodStatus int `json:"preFilterAddPodStatus,omitempty"`
  1581. PreFilterRemovePodStatus int `json:"preFilterRemovePodStatus,omitempty"`
  1582. FilterStatus int `json:"filterStatus,omitempty"`
  1583. PreScoreStatus int `json:"preScoreStatus,omitempty"`
  1584. ReserveStatus int `json:"reserveStatus,omitempty"`
  1585. PreBindStatus int `json:"preBindStatus,omitempty"`
  1586. BindStatus int `json:"bindStatus,omitempty"`
  1587. PermitStatus int `json:"permitStatus,omitempty"`
  1588. }
  1589. func setScoreRes(inj injectedResult) (int64, *Status) {
  1590. if Code(inj.ScoreStatus) != Success {
  1591. return 0, NewStatus(Code(inj.ScoreStatus), "injecting failure.")
  1592. }
  1593. return inj.ScoreRes, nil
  1594. }
  1595. func injectNormalizeRes(inj injectedResult, scores NodeScoreList) *Status {
  1596. if Code(inj.NormalizeStatus) != Success {
  1597. return NewStatus(Code(inj.NormalizeStatus), "injecting failure.")
  1598. }
  1599. for i := range scores {
  1600. scores[i].Score = inj.NormalizeRes
  1601. }
  1602. return nil
  1603. }
  1604. func collectAndComparePluginMetrics(t *testing.T, wantExtensionPoint, wantPlugin string, wantStatus Code) {
  1605. t.Helper()
  1606. m := collectHistogramMetric(metrics.PluginExecutionDuration)
  1607. if len(m.Label) != 3 {
  1608. t.Fatalf("Unexpected number of label pairs, got: %v, want: 2", len(m.Label))
  1609. }
  1610. if *m.Label[0].Value != wantExtensionPoint {
  1611. t.Errorf("Unexpected extension point label, got: %q, want %q", *m.Label[0].Value, wantExtensionPoint)
  1612. }
  1613. if *m.Label[1].Value != wantPlugin {
  1614. t.Errorf("Unexpected plugin label, got: %q, want %q", *m.Label[1].Value, wantPlugin)
  1615. }
  1616. if *m.Label[2].Value != wantStatus.String() {
  1617. t.Errorf("Unexpected status code label, got: %q, want %q", *m.Label[2].Value, wantStatus)
  1618. }
  1619. if *m.Histogram.SampleCount == 0 {
  1620. t.Error("Expect at least 1 sample")
  1621. }
  1622. if *m.Histogram.SampleSum <= 0 {
  1623. t.Errorf("Expect latency to be greater than 0, got: %v", *m.Histogram.SampleSum)
  1624. }
  1625. }
  1626. func collectAndCompareFrameworkMetrics(t *testing.T, wantExtensionPoint string, wantStatus Code) {
  1627. t.Helper()
  1628. m := collectHistogramMetric(metrics.FrameworkExtensionPointDuration)
  1629. if len(m.Label) != 2 {
  1630. t.Fatalf("Unexpected number of label pairs, got: %v, want: 2", len(m.Label))
  1631. }
  1632. if *m.Label[0].Value != wantExtensionPoint {
  1633. t.Errorf("Unexpected extension point label, got: %q, want %q", *m.Label[0].Value, wantExtensionPoint)
  1634. }
  1635. if *m.Label[1].Value != wantStatus.String() {
  1636. t.Errorf("Unexpected status code label, got: %q, want %q", *m.Label[1].Value, wantStatus)
  1637. }
  1638. if *m.Histogram.SampleCount != 1 {
  1639. t.Errorf("Expect 1 sample, got: %v", *m.Histogram.SampleCount)
  1640. }
  1641. if *m.Histogram.SampleSum <= 0 {
  1642. t.Errorf("Expect latency to be greater than 0, got: %v", *m.Histogram.SampleSum)
  1643. }
  1644. }
  1645. func collectAndComparePermitWaitDuration(t *testing.T, wantRes string) {
  1646. m := collectHistogramMetric(metrics.PermitWaitDuration)
  1647. if wantRes == "" {
  1648. if m != nil {
  1649. t.Errorf("PermitWaitDuration shouldn't be recorded but got %+v", m)
  1650. }
  1651. return
  1652. }
  1653. if wantRes != "" {
  1654. if len(m.Label) != 1 {
  1655. t.Fatalf("Unexpected number of label pairs, got: %v, want: 1", len(m.Label))
  1656. }
  1657. if *m.Label[0].Value != wantRes {
  1658. t.Errorf("Unexpected result label, got: %q, want %q", *m.Label[0].Value, wantRes)
  1659. }
  1660. if *m.Histogram.SampleCount != 1 {
  1661. t.Errorf("Expect 1 sample, got: %v", *m.Histogram.SampleCount)
  1662. }
  1663. if *m.Histogram.SampleSum <= 0 {
  1664. t.Errorf("Expect latency to be greater than 0, got: %v", *m.Histogram.SampleSum)
  1665. }
  1666. }
  1667. }
  1668. func collectHistogramMetric(metric prometheus.Collector) *dto.Metric {
  1669. ch := make(chan prometheus.Metric, 100)
  1670. metric.Collect(ch)
  1671. select {
  1672. case got := <-ch:
  1673. m := &dto.Metric{}
  1674. got.Write(m)
  1675. return m
  1676. default:
  1677. return nil
  1678. }
  1679. }