registry_test.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  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. "reflect"
  16. "testing"
  17. "k8s.io/apimachinery/pkg/runtime"
  18. "k8s.io/kubernetes/pkg/scheduler/apis/config"
  19. "k8s.io/kubernetes/pkg/scheduler/apis/config/scheme"
  20. )
  21. func TestDecodeInto(t *testing.T) {
  22. type PluginFooConfig struct {
  23. FooTest string `json:"foo_test,omitempty"`
  24. }
  25. tests := []struct {
  26. name string
  27. schedulerConfig string
  28. expected PluginFooConfig
  29. }{
  30. {
  31. name: "test decode for JSON config",
  32. schedulerConfig: `{
  33. "kind": "KubeSchedulerConfiguration",
  34. "apiVersion": "kubescheduler.config.k8s.io/v1alpha1",
  35. "plugins": {
  36. "permit": {
  37. "enabled": [
  38. {
  39. "name": "foo"
  40. }
  41. ]
  42. }
  43. },
  44. "pluginConfig": [
  45. {
  46. "name": "foo",
  47. "args": {
  48. "foo_test": "test decode"
  49. }
  50. }
  51. ]
  52. }`,
  53. expected: PluginFooConfig{
  54. FooTest: "test decode",
  55. },
  56. },
  57. {
  58. name: "test decode for YAML config",
  59. schedulerConfig: `
  60. apiVersion: kubescheduler.config.k8s.io/v1alpha1
  61. kind: KubeSchedulerConfiguration
  62. plugins:
  63. permit:
  64. enabled:
  65. - name: foo
  66. pluginConfig:
  67. - name: foo
  68. args:
  69. foo_test: "test decode"`,
  70. expected: PluginFooConfig{
  71. FooTest: "test decode",
  72. },
  73. },
  74. }
  75. for _, test := range tests {
  76. t.Run(test.name, func(t *testing.T) {
  77. schedulerConf, err := loadConfig([]byte(test.schedulerConfig))
  78. if err != nil {
  79. t.Errorf("loadConfig(): failed to load scheduler config: %v", err)
  80. }
  81. var pluginFooConf PluginFooConfig
  82. if err := DecodeInto(&schedulerConf.PluginConfig[0].Args, &pluginFooConf); err != nil {
  83. t.Errorf("DecodeInto(): failed to decode args %+v: %v",
  84. schedulerConf.PluginConfig[0].Args, err)
  85. }
  86. if !reflect.DeepEqual(test.expected, pluginFooConf) {
  87. t.Errorf("DecodeInto(): failed to decode plugin config, expected: %+v, got: %+v",
  88. test.expected, pluginFooConf)
  89. }
  90. })
  91. }
  92. }
  93. func loadConfig(data []byte) (*config.KubeSchedulerConfiguration, error) {
  94. configObj := &config.KubeSchedulerConfiguration{}
  95. if err := runtime.DecodeInto(scheme.Codecs.UniversalDecoder(), data, configObj); err != nil {
  96. return nil, err
  97. }
  98. return configObj, nil
  99. }
  100. // isRegistryEqual compares two registries for equality. This function is used in place of
  101. // reflect.DeepEqual() and cmp() as they don't compare function values.
  102. func isRegistryEqual(registryX, registryY Registry) bool {
  103. for name, pluginFactory := range registryY {
  104. if val, ok := registryX[name]; ok {
  105. if reflect.ValueOf(pluginFactory) != reflect.ValueOf(val) {
  106. // pluginFactory functions are not the same.
  107. return false
  108. }
  109. } else {
  110. // registryY contains an entry that is not present in registryX
  111. return false
  112. }
  113. }
  114. for name := range registryX {
  115. if _, ok := registryY[name]; !ok {
  116. // registryX contains an entry that is not present in registryY
  117. return false
  118. }
  119. }
  120. return true
  121. }
  122. type mockNoopPlugin struct{}
  123. func (p *mockNoopPlugin) Name() string {
  124. return "MockNoop"
  125. }
  126. func NewMockNoopPluginFactory() PluginFactory {
  127. return func(_ *runtime.Unknown, _ FrameworkHandle) (Plugin, error) {
  128. return &mockNoopPlugin{}, nil
  129. }
  130. }
  131. func TestMerge(t *testing.T) {
  132. tests := []struct {
  133. name string
  134. primaryRegistry Registry
  135. registryToMerge Registry
  136. expected Registry
  137. shouldError bool
  138. }{
  139. {
  140. name: "valid Merge",
  141. primaryRegistry: Registry{
  142. "pluginFactory1": NewMockNoopPluginFactory(),
  143. },
  144. registryToMerge: Registry{
  145. "pluginFactory2": NewMockNoopPluginFactory(),
  146. },
  147. expected: Registry{
  148. "pluginFactory1": NewMockNoopPluginFactory(),
  149. "pluginFactory2": NewMockNoopPluginFactory(),
  150. },
  151. shouldError: false,
  152. },
  153. {
  154. name: "Merge duplicate factories",
  155. primaryRegistry: Registry{
  156. "pluginFactory1": NewMockNoopPluginFactory(),
  157. },
  158. registryToMerge: Registry{
  159. "pluginFactory1": NewMockNoopPluginFactory(),
  160. },
  161. expected: Registry{
  162. "pluginFactory1": NewMockNoopPluginFactory(),
  163. },
  164. shouldError: true,
  165. },
  166. }
  167. for _, scenario := range tests {
  168. t.Run(scenario.name, func(t *testing.T) {
  169. err := scenario.primaryRegistry.Merge(scenario.registryToMerge)
  170. if (err == nil) == scenario.shouldError {
  171. t.Errorf("Merge() shouldError is: %v, however err is: %v.", scenario.shouldError, err)
  172. return
  173. }
  174. if !isRegistryEqual(scenario.expected, scenario.primaryRegistry) {
  175. t.Errorf("Merge(). Expected %v. Got %v instead.", scenario.expected, scenario.primaryRegistry)
  176. }
  177. })
  178. }
  179. }
  180. func TestRegister(t *testing.T) {
  181. tests := []struct {
  182. name string
  183. registry Registry
  184. nameToRegister string
  185. factoryToRegister PluginFactory
  186. expected Registry
  187. shouldError bool
  188. }{
  189. {
  190. name: "valid Register",
  191. registry: Registry{},
  192. nameToRegister: "pluginFactory1",
  193. factoryToRegister: NewMockNoopPluginFactory(),
  194. expected: Registry{
  195. "pluginFactory1": NewMockNoopPluginFactory(),
  196. },
  197. shouldError: false,
  198. },
  199. {
  200. name: "Register duplicate factories",
  201. registry: Registry{
  202. "pluginFactory1": NewMockNoopPluginFactory(),
  203. },
  204. nameToRegister: "pluginFactory1",
  205. factoryToRegister: NewMockNoopPluginFactory(),
  206. expected: Registry{
  207. "pluginFactory1": NewMockNoopPluginFactory(),
  208. },
  209. shouldError: true,
  210. },
  211. }
  212. for _, scenario := range tests {
  213. t.Run(scenario.name, func(t *testing.T) {
  214. err := scenario.registry.Register(scenario.nameToRegister, scenario.factoryToRegister)
  215. if (err == nil) == scenario.shouldError {
  216. t.Errorf("Register() shouldError is: %v however err is: %v.", scenario.shouldError, err)
  217. return
  218. }
  219. if !isRegistryEqual(scenario.expected, scenario.registry) {
  220. t.Errorf("Register(). Expected %v. Got %v instead.", scenario.expected, scenario.registry)
  221. }
  222. })
  223. }
  224. }
  225. func TestUnregister(t *testing.T) {
  226. tests := []struct {
  227. name string
  228. registry Registry
  229. nameToUnregister string
  230. expected Registry
  231. shouldError bool
  232. }{
  233. {
  234. name: "valid Unregister",
  235. registry: Registry{
  236. "pluginFactory1": NewMockNoopPluginFactory(),
  237. "pluginFactory2": NewMockNoopPluginFactory(),
  238. },
  239. nameToUnregister: "pluginFactory1",
  240. expected: Registry{
  241. "pluginFactory2": NewMockNoopPluginFactory(),
  242. },
  243. shouldError: false,
  244. },
  245. {
  246. name: "Unregister non-existent plugin factory",
  247. registry: Registry{},
  248. nameToUnregister: "pluginFactory1",
  249. expected: Registry{},
  250. shouldError: true,
  251. },
  252. }
  253. for _, scenario := range tests {
  254. t.Run(scenario.name, func(t *testing.T) {
  255. err := scenario.registry.Unregister(scenario.nameToUnregister)
  256. if (err == nil) == scenario.shouldError {
  257. t.Errorf("Unregister() shouldError is: %v however err is: %v.", scenario.shouldError, err)
  258. return
  259. }
  260. if !isRegistryEqual(scenario.expected, scenario.registry) {
  261. t.Errorf("Unregister(). Expected %v. Got %v instead.", scenario.expected, scenario.registry)
  262. }
  263. })
  264. }
  265. }