options_test.go 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709
  1. /*
  2. Copyright 2018 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 options
  14. import (
  15. "context"
  16. "fmt"
  17. "io/ioutil"
  18. "net/http"
  19. "net/http/httptest"
  20. "os"
  21. "path/filepath"
  22. "testing"
  23. "time"
  24. "github.com/google/go-cmp/cmp"
  25. "github.com/stretchr/testify/assert"
  26. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  27. "k8s.io/apimachinery/pkg/runtime"
  28. apiserveroptions "k8s.io/apiserver/pkg/server/options"
  29. componentbaseconfig "k8s.io/component-base/config"
  30. kubeschedulerconfig "k8s.io/kubernetes/pkg/scheduler/apis/config"
  31. )
  32. func TestSchedulerOptions(t *testing.T) {
  33. // temp dir
  34. tmpDir, err := ioutil.TempDir("", "scheduler-options")
  35. if err != nil {
  36. t.Fatal(err)
  37. }
  38. defer os.RemoveAll(tmpDir)
  39. // record the username requests were made with
  40. username := ""
  41. // https server
  42. server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
  43. username, _, _ = req.BasicAuth()
  44. if username == "" {
  45. username = "none, tls"
  46. }
  47. w.WriteHeader(200)
  48. w.Write([]byte(`ok`))
  49. }))
  50. defer server.Close()
  51. // http server
  52. insecureserver := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
  53. username, _, _ = req.BasicAuth()
  54. if username == "" {
  55. username = "none, http"
  56. }
  57. w.WriteHeader(200)
  58. w.Write([]byte(`ok`))
  59. }))
  60. defer insecureserver.Close()
  61. // config file and kubeconfig
  62. configFile := filepath.Join(tmpDir, "scheduler.yaml")
  63. configKubeconfig := filepath.Join(tmpDir, "config.kubeconfig")
  64. if err := ioutil.WriteFile(configFile, []byte(fmt.Sprintf(`
  65. apiVersion: kubescheduler.config.k8s.io/v1alpha2
  66. kind: KubeSchedulerConfiguration
  67. clientConnection:
  68. kubeconfig: "%s"
  69. leaderElection:
  70. leaderElect: true`, configKubeconfig)), os.FileMode(0600)); err != nil {
  71. t.Fatal(err)
  72. }
  73. if err := ioutil.WriteFile(configKubeconfig, []byte(fmt.Sprintf(`
  74. apiVersion: v1
  75. kind: Config
  76. clusters:
  77. - cluster:
  78. insecure-skip-tls-verify: true
  79. server: %s
  80. name: default
  81. contexts:
  82. - context:
  83. cluster: default
  84. user: default
  85. name: default
  86. current-context: default
  87. users:
  88. - name: default
  89. user:
  90. username: config
  91. `, server.URL)), os.FileMode(0600)); err != nil {
  92. t.Fatal(err)
  93. }
  94. oldConfigFile := filepath.Join(tmpDir, "scheduler_old.yaml")
  95. if err := ioutil.WriteFile(oldConfigFile, []byte(fmt.Sprintf(`
  96. apiVersion: componentconfig/v1alpha1
  97. kind: KubeSchedulerConfiguration
  98. clientConnection:
  99. kubeconfig: "%s"
  100. leaderElection:
  101. leaderElect: true`, configKubeconfig)), os.FileMode(0600)); err != nil {
  102. t.Fatal(err)
  103. }
  104. unknownVersionConfig := filepath.Join(tmpDir, "scheduler_invalid_wrong_api_version.yaml")
  105. if err := ioutil.WriteFile(unknownVersionConfig, []byte(fmt.Sprintf(`
  106. apiVersion: kubescheduler.config.k8s.io/unknown
  107. kind: KubeSchedulerConfiguration
  108. clientConnection:
  109. kubeconfig: "%s"
  110. leaderElection:
  111. leaderElect: true`, configKubeconfig)), os.FileMode(0600)); err != nil {
  112. t.Fatal(err)
  113. }
  114. noVersionConfig := filepath.Join(tmpDir, "scheduler_invalid_no_version.yaml")
  115. if err := ioutil.WriteFile(noVersionConfig, []byte(fmt.Sprintf(`
  116. kind: KubeSchedulerConfiguration
  117. clientConnection:
  118. kubeconfig: "%s"
  119. leaderElection:
  120. leaderElect: true`, configKubeconfig)), os.FileMode(0600)); err != nil {
  121. t.Fatal(err)
  122. }
  123. unknownFieldConfigLenient := filepath.Join(tmpDir, "scheduler_invalid_unknown_field_lenient.yaml")
  124. if err := ioutil.WriteFile(unknownFieldConfigLenient, []byte(fmt.Sprintf(`
  125. apiVersion: kubescheduler.config.k8s.io/v1alpha1
  126. kind: KubeSchedulerConfiguration
  127. clientConnection:
  128. kubeconfig: "%s"
  129. leaderElection:
  130. leaderElect: true
  131. foo: bar`, configKubeconfig)), os.FileMode(0600)); err != nil {
  132. t.Fatal(err)
  133. }
  134. unknownFieldConfig := filepath.Join(tmpDir, "scheduler_invalid_unknown_field.yaml")
  135. if err := ioutil.WriteFile(unknownFieldConfig, []byte(fmt.Sprintf(`
  136. apiVersion: kubescheduler.config.k8s.io/v1alpha2
  137. kind: KubeSchedulerConfiguration
  138. clientConnection:
  139. kubeconfig: "%s"
  140. leaderElection:
  141. leaderElect: true
  142. foo: bar`, configKubeconfig)), os.FileMode(0600)); err != nil {
  143. t.Fatal(err)
  144. }
  145. duplicateFieldConfigLenient := filepath.Join(tmpDir, "scheduler_invalid_duplicate_fields_lenient.yaml")
  146. if err := ioutil.WriteFile(duplicateFieldConfigLenient, []byte(fmt.Sprintf(`
  147. apiVersion: kubescheduler.config.k8s.io/v1alpha1
  148. kind: KubeSchedulerConfiguration
  149. clientConnection:
  150. kubeconfig: "%s"
  151. leaderElection:
  152. leaderElect: true
  153. leaderElect: false`, configKubeconfig)), os.FileMode(0600)); err != nil {
  154. t.Fatal(err)
  155. }
  156. duplicateFieldConfig := filepath.Join(tmpDir, "scheduler_invalid_duplicate_fields.yaml")
  157. if err := ioutil.WriteFile(duplicateFieldConfig, []byte(fmt.Sprintf(`
  158. apiVersion: kubescheduler.config.k8s.io/v1alpha2
  159. kind: KubeSchedulerConfiguration
  160. clientConnection:
  161. kubeconfig: "%s"
  162. leaderElection:
  163. leaderElect: true
  164. leaderElect: false`, configKubeconfig)), os.FileMode(0600)); err != nil {
  165. t.Fatal(err)
  166. }
  167. // flag-specified kubeconfig
  168. flagKubeconfig := filepath.Join(tmpDir, "flag.kubeconfig")
  169. if err := ioutil.WriteFile(flagKubeconfig, []byte(fmt.Sprintf(`
  170. apiVersion: v1
  171. kind: Config
  172. clusters:
  173. - cluster:
  174. insecure-skip-tls-verify: true
  175. server: %s
  176. name: default
  177. contexts:
  178. - context:
  179. cluster: default
  180. user: default
  181. name: default
  182. current-context: default
  183. users:
  184. - name: default
  185. user:
  186. username: flag
  187. `, server.URL)), os.FileMode(0600)); err != nil {
  188. t.Fatal(err)
  189. }
  190. // plugin config
  191. pluginconfigFile := filepath.Join(tmpDir, "plugin.yaml")
  192. if err := ioutil.WriteFile(pluginconfigFile, []byte(fmt.Sprintf(`
  193. apiVersion: kubescheduler.config.k8s.io/v1alpha2
  194. kind: KubeSchedulerConfiguration
  195. clientConnection:
  196. kubeconfig: "%s"
  197. plugins:
  198. reserve:
  199. enabled:
  200. - name: foo
  201. - name: bar
  202. disabled:
  203. - name: baz
  204. preBind:
  205. enabled:
  206. - name: foo
  207. disabled:
  208. - name: baz
  209. pluginConfig:
  210. - name: foo
  211. `, configKubeconfig)), os.FileMode(0600)); err != nil {
  212. t.Fatal(err)
  213. }
  214. // Insulate this test from picking up in-cluster config when run inside a pod
  215. // We can't assume we have permissions to write to /var/run/secrets/... from a unit test to mock in-cluster config for testing
  216. originalHost := os.Getenv("KUBERNETES_SERVICE_HOST")
  217. if len(originalHost) > 0 {
  218. os.Setenv("KUBERNETES_SERVICE_HOST", "")
  219. defer os.Setenv("KUBERNETES_SERVICE_HOST", originalHost)
  220. }
  221. defaultSource := "DefaultProvider"
  222. defaultBindTimeoutSeconds := int64(600)
  223. defaultPodInitialBackoffSeconds := int64(1)
  224. defaultPodMaxBackoffSeconds := int64(10)
  225. defaultPercentageOfNodesToScore := int32(0)
  226. testcases := []struct {
  227. name string
  228. options *Options
  229. expectedUsername string
  230. expectedError string
  231. expectedConfig kubeschedulerconfig.KubeSchedulerConfiguration
  232. checkErrFn func(err error) bool
  233. }{
  234. {
  235. name: "config file",
  236. options: &Options{
  237. ConfigFile: configFile,
  238. ComponentConfig: func() kubeschedulerconfig.KubeSchedulerConfiguration {
  239. cfg, err := newDefaultComponentConfig()
  240. if err != nil {
  241. t.Fatal(err)
  242. }
  243. return *cfg
  244. }(),
  245. SecureServing: (&apiserveroptions.SecureServingOptions{
  246. ServerCert: apiserveroptions.GeneratableKeyCert{
  247. CertDirectory: "/a/b/c",
  248. PairName: "kube-scheduler",
  249. },
  250. HTTP2MaxStreamsPerConnection: 47,
  251. }).WithLoopback(),
  252. Authentication: &apiserveroptions.DelegatingAuthenticationOptions{
  253. CacheTTL: 10 * time.Second,
  254. ClientCert: apiserveroptions.ClientCertAuthenticationOptions{},
  255. RequestHeader: apiserveroptions.RequestHeaderAuthenticationOptions{
  256. UsernameHeaders: []string{"x-remote-user"},
  257. GroupHeaders: []string{"x-remote-group"},
  258. ExtraHeaderPrefixes: []string{"x-remote-extra-"},
  259. },
  260. RemoteKubeConfigFileOptional: true,
  261. },
  262. Authorization: &apiserveroptions.DelegatingAuthorizationOptions{
  263. AllowCacheTTL: 10 * time.Second,
  264. DenyCacheTTL: 10 * time.Second,
  265. RemoteKubeConfigFileOptional: true,
  266. AlwaysAllowPaths: []string{"/healthz"}, // note: this does not match /healthz/ or /healthz/*
  267. },
  268. },
  269. expectedUsername: "config",
  270. expectedConfig: kubeschedulerconfig.KubeSchedulerConfiguration{
  271. SchedulerName: "default-scheduler",
  272. AlgorithmSource: kubeschedulerconfig.SchedulerAlgorithmSource{Provider: &defaultSource},
  273. HardPodAffinitySymmetricWeight: 1,
  274. HealthzBindAddress: "0.0.0.0:10251",
  275. MetricsBindAddress: "0.0.0.0:10251",
  276. DebuggingConfiguration: componentbaseconfig.DebuggingConfiguration{
  277. EnableProfiling: true,
  278. EnableContentionProfiling: true,
  279. },
  280. LeaderElection: kubeschedulerconfig.KubeSchedulerLeaderElectionConfiguration{
  281. LeaderElectionConfiguration: componentbaseconfig.LeaderElectionConfiguration{
  282. LeaderElect: true,
  283. LeaseDuration: metav1.Duration{Duration: 15 * time.Second},
  284. RenewDeadline: metav1.Duration{Duration: 10 * time.Second},
  285. RetryPeriod: metav1.Duration{Duration: 2 * time.Second},
  286. ResourceLock: "endpointsleases",
  287. ResourceNamespace: "kube-system",
  288. ResourceName: "kube-scheduler",
  289. },
  290. },
  291. ClientConnection: componentbaseconfig.ClientConnectionConfiguration{
  292. Kubeconfig: configKubeconfig,
  293. QPS: 50,
  294. Burst: 100,
  295. ContentType: "application/vnd.kubernetes.protobuf",
  296. },
  297. PercentageOfNodesToScore: defaultPercentageOfNodesToScore,
  298. BindTimeoutSeconds: defaultBindTimeoutSeconds,
  299. PodInitialBackoffSeconds: defaultPodInitialBackoffSeconds,
  300. PodMaxBackoffSeconds: defaultPodMaxBackoffSeconds,
  301. Plugins: nil,
  302. },
  303. },
  304. {
  305. name: "config file in componentconfig/v1alpha1",
  306. options: &Options{
  307. ConfigFile: oldConfigFile,
  308. ComponentConfig: func() kubeschedulerconfig.KubeSchedulerConfiguration {
  309. cfg, err := newDefaultComponentConfig()
  310. if err != nil {
  311. t.Fatal(err)
  312. }
  313. return *cfg
  314. }(),
  315. },
  316. expectedError: "no kind \"KubeSchedulerConfiguration\" is registered for version \"componentconfig/v1alpha1\"",
  317. },
  318. {
  319. name: "unknown version kubescheduler.config.k8s.io/unknown",
  320. options: &Options{ConfigFile: unknownVersionConfig},
  321. expectedError: "no kind \"KubeSchedulerConfiguration\" is registered for version \"kubescheduler.config.k8s.io/unknown\"",
  322. },
  323. {
  324. name: "config file with no version",
  325. options: &Options{ConfigFile: noVersionConfig},
  326. expectedError: "Object 'apiVersion' is missing",
  327. },
  328. {
  329. name: "kubeconfig flag",
  330. options: &Options{
  331. ComponentConfig: func() kubeschedulerconfig.KubeSchedulerConfiguration {
  332. cfg, _ := newDefaultComponentConfig()
  333. cfg.ClientConnection.Kubeconfig = flagKubeconfig
  334. return *cfg
  335. }(),
  336. SecureServing: (&apiserveroptions.SecureServingOptions{
  337. ServerCert: apiserveroptions.GeneratableKeyCert{
  338. CertDirectory: "/a/b/c",
  339. PairName: "kube-scheduler",
  340. },
  341. HTTP2MaxStreamsPerConnection: 47,
  342. }).WithLoopback(),
  343. Authentication: &apiserveroptions.DelegatingAuthenticationOptions{
  344. CacheTTL: 10 * time.Second,
  345. ClientCert: apiserveroptions.ClientCertAuthenticationOptions{},
  346. RequestHeader: apiserveroptions.RequestHeaderAuthenticationOptions{
  347. UsernameHeaders: []string{"x-remote-user"},
  348. GroupHeaders: []string{"x-remote-group"},
  349. ExtraHeaderPrefixes: []string{"x-remote-extra-"},
  350. },
  351. RemoteKubeConfigFileOptional: true,
  352. },
  353. Authorization: &apiserveroptions.DelegatingAuthorizationOptions{
  354. AllowCacheTTL: 10 * time.Second,
  355. DenyCacheTTL: 10 * time.Second,
  356. RemoteKubeConfigFileOptional: true,
  357. AlwaysAllowPaths: []string{"/healthz"}, // note: this does not match /healthz/ or /healthz/*
  358. },
  359. },
  360. expectedUsername: "flag",
  361. expectedConfig: kubeschedulerconfig.KubeSchedulerConfiguration{
  362. SchedulerName: "default-scheduler",
  363. AlgorithmSource: kubeschedulerconfig.SchedulerAlgorithmSource{Provider: &defaultSource},
  364. HardPodAffinitySymmetricWeight: 1,
  365. HealthzBindAddress: "", // defaults empty when not running from config file
  366. MetricsBindAddress: "", // defaults empty when not running from config file
  367. DebuggingConfiguration: componentbaseconfig.DebuggingConfiguration{
  368. EnableProfiling: true,
  369. EnableContentionProfiling: true,
  370. },
  371. LeaderElection: kubeschedulerconfig.KubeSchedulerLeaderElectionConfiguration{
  372. LeaderElectionConfiguration: componentbaseconfig.LeaderElectionConfiguration{
  373. LeaderElect: true,
  374. LeaseDuration: metav1.Duration{Duration: 15 * time.Second},
  375. RenewDeadline: metav1.Duration{Duration: 10 * time.Second},
  376. RetryPeriod: metav1.Duration{Duration: 2 * time.Second},
  377. ResourceLock: "endpointsleases",
  378. ResourceNamespace: "kube-system",
  379. ResourceName: "kube-scheduler",
  380. },
  381. },
  382. ClientConnection: componentbaseconfig.ClientConnectionConfiguration{
  383. Kubeconfig: flagKubeconfig,
  384. QPS: 50,
  385. Burst: 100,
  386. ContentType: "application/vnd.kubernetes.protobuf",
  387. },
  388. PercentageOfNodesToScore: defaultPercentageOfNodesToScore,
  389. BindTimeoutSeconds: defaultBindTimeoutSeconds,
  390. PodInitialBackoffSeconds: defaultPodInitialBackoffSeconds,
  391. PodMaxBackoffSeconds: defaultPodMaxBackoffSeconds,
  392. },
  393. },
  394. {
  395. name: "overridden master",
  396. options: &Options{
  397. ComponentConfig: func() kubeschedulerconfig.KubeSchedulerConfiguration {
  398. cfg, _ := newDefaultComponentConfig()
  399. cfg.ClientConnection.Kubeconfig = flagKubeconfig
  400. return *cfg
  401. }(),
  402. Master: insecureserver.URL,
  403. SecureServing: (&apiserveroptions.SecureServingOptions{
  404. ServerCert: apiserveroptions.GeneratableKeyCert{
  405. CertDirectory: "/a/b/c",
  406. PairName: "kube-scheduler",
  407. },
  408. HTTP2MaxStreamsPerConnection: 47,
  409. }).WithLoopback(),
  410. Authentication: &apiserveroptions.DelegatingAuthenticationOptions{
  411. CacheTTL: 10 * time.Second,
  412. RequestHeader: apiserveroptions.RequestHeaderAuthenticationOptions{
  413. UsernameHeaders: []string{"x-remote-user"},
  414. GroupHeaders: []string{"x-remote-group"},
  415. ExtraHeaderPrefixes: []string{"x-remote-extra-"},
  416. },
  417. RemoteKubeConfigFileOptional: true,
  418. },
  419. Authorization: &apiserveroptions.DelegatingAuthorizationOptions{
  420. AllowCacheTTL: 10 * time.Second,
  421. DenyCacheTTL: 10 * time.Second,
  422. RemoteKubeConfigFileOptional: true,
  423. AlwaysAllowPaths: []string{"/healthz"}, // note: this does not match /healthz/ or /healthz/*
  424. },
  425. },
  426. expectedConfig: kubeschedulerconfig.KubeSchedulerConfiguration{
  427. SchedulerName: "default-scheduler",
  428. AlgorithmSource: kubeschedulerconfig.SchedulerAlgorithmSource{Provider: &defaultSource},
  429. HardPodAffinitySymmetricWeight: 1,
  430. HealthzBindAddress: "", // defaults empty when not running from config file
  431. MetricsBindAddress: "", // defaults empty when not running from config file
  432. DebuggingConfiguration: componentbaseconfig.DebuggingConfiguration{
  433. EnableProfiling: true,
  434. EnableContentionProfiling: true,
  435. },
  436. LeaderElection: kubeschedulerconfig.KubeSchedulerLeaderElectionConfiguration{
  437. LeaderElectionConfiguration: componentbaseconfig.LeaderElectionConfiguration{
  438. LeaderElect: true,
  439. LeaseDuration: metav1.Duration{Duration: 15 * time.Second},
  440. RenewDeadline: metav1.Duration{Duration: 10 * time.Second},
  441. RetryPeriod: metav1.Duration{Duration: 2 * time.Second},
  442. ResourceLock: "endpointsleases",
  443. ResourceNamespace: "kube-system",
  444. ResourceName: "kube-scheduler",
  445. },
  446. },
  447. ClientConnection: componentbaseconfig.ClientConnectionConfiguration{
  448. Kubeconfig: flagKubeconfig,
  449. QPS: 50,
  450. Burst: 100,
  451. ContentType: "application/vnd.kubernetes.protobuf",
  452. },
  453. PercentageOfNodesToScore: defaultPercentageOfNodesToScore,
  454. BindTimeoutSeconds: defaultBindTimeoutSeconds,
  455. PodInitialBackoffSeconds: defaultPodInitialBackoffSeconds,
  456. PodMaxBackoffSeconds: defaultPodMaxBackoffSeconds,
  457. },
  458. expectedUsername: "none, http",
  459. },
  460. {
  461. name: "plugin config",
  462. options: &Options{
  463. ConfigFile: pluginconfigFile,
  464. },
  465. expectedUsername: "config",
  466. expectedConfig: kubeschedulerconfig.KubeSchedulerConfiguration{
  467. SchedulerName: "default-scheduler",
  468. AlgorithmSource: kubeschedulerconfig.SchedulerAlgorithmSource{Provider: &defaultSource},
  469. HardPodAffinitySymmetricWeight: 1,
  470. HealthzBindAddress: "0.0.0.0:10251",
  471. MetricsBindAddress: "0.0.0.0:10251",
  472. DebuggingConfiguration: componentbaseconfig.DebuggingConfiguration{
  473. EnableProfiling: true,
  474. EnableContentionProfiling: true,
  475. },
  476. LeaderElection: kubeschedulerconfig.KubeSchedulerLeaderElectionConfiguration{
  477. LeaderElectionConfiguration: componentbaseconfig.LeaderElectionConfiguration{
  478. LeaderElect: true,
  479. LeaseDuration: metav1.Duration{Duration: 15 * time.Second},
  480. RenewDeadline: metav1.Duration{Duration: 10 * time.Second},
  481. RetryPeriod: metav1.Duration{Duration: 2 * time.Second},
  482. ResourceLock: "endpointsleases",
  483. ResourceNamespace: "kube-system",
  484. ResourceName: "kube-scheduler",
  485. },
  486. },
  487. ClientConnection: componentbaseconfig.ClientConnectionConfiguration{
  488. Kubeconfig: configKubeconfig,
  489. QPS: 50,
  490. Burst: 100,
  491. ContentType: "application/vnd.kubernetes.protobuf",
  492. },
  493. PercentageOfNodesToScore: defaultPercentageOfNodesToScore,
  494. BindTimeoutSeconds: defaultBindTimeoutSeconds,
  495. PodInitialBackoffSeconds: defaultPodInitialBackoffSeconds,
  496. PodMaxBackoffSeconds: defaultPodMaxBackoffSeconds,
  497. Plugins: &kubeschedulerconfig.Plugins{
  498. Reserve: &kubeschedulerconfig.PluginSet{
  499. Enabled: []kubeschedulerconfig.Plugin{
  500. {
  501. Name: "foo",
  502. },
  503. {
  504. Name: "bar",
  505. },
  506. },
  507. Disabled: []kubeschedulerconfig.Plugin{
  508. {
  509. Name: "baz",
  510. },
  511. },
  512. },
  513. PreBind: &kubeschedulerconfig.PluginSet{
  514. Enabled: []kubeschedulerconfig.Plugin{
  515. {
  516. Name: "foo",
  517. },
  518. },
  519. Disabled: []kubeschedulerconfig.Plugin{
  520. {
  521. Name: "baz",
  522. },
  523. },
  524. },
  525. },
  526. PluginConfig: []kubeschedulerconfig.PluginConfig{
  527. {
  528. Name: "foo",
  529. Args: runtime.Unknown{},
  530. },
  531. },
  532. },
  533. },
  534. {
  535. name: "no config",
  536. options: &Options{},
  537. expectedError: "no configuration has been provided",
  538. },
  539. {
  540. name: "unknown field lenient (v1alpha1)",
  541. options: &Options{
  542. ConfigFile: unknownFieldConfigLenient,
  543. },
  544. // TODO (obitech): Remove this comment and add a new test for v1alpha2, when it's available, as this should fail then.
  545. // expectedError: "found unknown field: foo",
  546. // checkErrFn: runtime.IsStrictDecodingError,
  547. expectedUsername: "config",
  548. expectedConfig: kubeschedulerconfig.KubeSchedulerConfiguration{
  549. SchedulerName: "default-scheduler",
  550. AlgorithmSource: kubeschedulerconfig.SchedulerAlgorithmSource{Provider: &defaultSource},
  551. HardPodAffinitySymmetricWeight: 1,
  552. HealthzBindAddress: "0.0.0.0:10251",
  553. MetricsBindAddress: "0.0.0.0:10251",
  554. DebuggingConfiguration: componentbaseconfig.DebuggingConfiguration{
  555. EnableProfiling: true,
  556. EnableContentionProfiling: true,
  557. },
  558. LeaderElection: kubeschedulerconfig.KubeSchedulerLeaderElectionConfiguration{
  559. LeaderElectionConfiguration: componentbaseconfig.LeaderElectionConfiguration{
  560. LeaderElect: true,
  561. LeaseDuration: metav1.Duration{Duration: 15 * time.Second},
  562. RenewDeadline: metav1.Duration{Duration: 10 * time.Second},
  563. RetryPeriod: metav1.Duration{Duration: 2 * time.Second},
  564. ResourceLock: "endpointsleases",
  565. ResourceNamespace: "kube-system",
  566. ResourceName: "kube-scheduler",
  567. },
  568. },
  569. ClientConnection: componentbaseconfig.ClientConnectionConfiguration{
  570. Kubeconfig: configKubeconfig,
  571. QPS: 50,
  572. Burst: 100,
  573. ContentType: "application/vnd.kubernetes.protobuf",
  574. },
  575. PercentageOfNodesToScore: defaultPercentageOfNodesToScore,
  576. BindTimeoutSeconds: defaultBindTimeoutSeconds,
  577. PodInitialBackoffSeconds: defaultPodInitialBackoffSeconds,
  578. PodMaxBackoffSeconds: defaultPodMaxBackoffSeconds,
  579. Plugins: nil,
  580. },
  581. },
  582. {
  583. name: "unknown field",
  584. options: &Options{
  585. ConfigFile: unknownFieldConfig,
  586. },
  587. expectedError: "found unknown field: foo",
  588. checkErrFn: runtime.IsStrictDecodingError,
  589. },
  590. {
  591. name: "duplicate fields lenient (v1alpha1)",
  592. options: &Options{
  593. ConfigFile: duplicateFieldConfigLenient,
  594. },
  595. // TODO (obitech): Remove this comment and add a new test for v1alpha2, when it's available, as this should fail then.
  596. // expectedError: `key "leaderElect" already set`,
  597. // checkErrFn: runtime.IsStrictDecodingError,
  598. expectedUsername: "config",
  599. expectedConfig: kubeschedulerconfig.KubeSchedulerConfiguration{
  600. SchedulerName: "default-scheduler",
  601. AlgorithmSource: kubeschedulerconfig.SchedulerAlgorithmSource{Provider: &defaultSource},
  602. HardPodAffinitySymmetricWeight: 1,
  603. HealthzBindAddress: "0.0.0.0:10251",
  604. MetricsBindAddress: "0.0.0.0:10251",
  605. DebuggingConfiguration: componentbaseconfig.DebuggingConfiguration{
  606. EnableProfiling: true,
  607. EnableContentionProfiling: true,
  608. },
  609. LeaderElection: kubeschedulerconfig.KubeSchedulerLeaderElectionConfiguration{
  610. LeaderElectionConfiguration: componentbaseconfig.LeaderElectionConfiguration{
  611. LeaderElect: false,
  612. LeaseDuration: metav1.Duration{Duration: 15 * time.Second},
  613. RenewDeadline: metav1.Duration{Duration: 10 * time.Second},
  614. RetryPeriod: metav1.Duration{Duration: 2 * time.Second},
  615. ResourceLock: "endpointsleases",
  616. ResourceNamespace: "kube-system",
  617. ResourceName: "kube-scheduler",
  618. },
  619. },
  620. ClientConnection: componentbaseconfig.ClientConnectionConfiguration{
  621. Kubeconfig: configKubeconfig,
  622. QPS: 50,
  623. Burst: 100,
  624. ContentType: "application/vnd.kubernetes.protobuf",
  625. },
  626. PercentageOfNodesToScore: defaultPercentageOfNodesToScore,
  627. BindTimeoutSeconds: defaultBindTimeoutSeconds,
  628. PodInitialBackoffSeconds: defaultPodInitialBackoffSeconds,
  629. PodMaxBackoffSeconds: defaultPodMaxBackoffSeconds,
  630. Plugins: nil,
  631. },
  632. },
  633. {
  634. name: "duplicate fields",
  635. options: &Options{
  636. ConfigFile: duplicateFieldConfig,
  637. },
  638. expectedError: `key "leaderElect" already set`,
  639. checkErrFn: runtime.IsStrictDecodingError,
  640. },
  641. }
  642. for _, tc := range testcases {
  643. t.Run(tc.name, func(t *testing.T) {
  644. // create the config
  645. config, err := tc.options.Config()
  646. // handle errors
  647. if err != nil {
  648. if tc.expectedError != "" || tc.checkErrFn != nil {
  649. if tc.expectedError != "" {
  650. assert.Contains(t, err.Error(), tc.expectedError)
  651. }
  652. if tc.checkErrFn != nil {
  653. assert.True(t, tc.checkErrFn(err), "got error: %v", err)
  654. }
  655. return
  656. }
  657. assert.NoError(t, err)
  658. return
  659. }
  660. if diff := cmp.Diff(tc.expectedConfig, config.ComponentConfig); diff != "" {
  661. t.Errorf("incorrect config (-want, +got):\n%s", diff)
  662. }
  663. // ensure we have a client
  664. if config.Client == nil {
  665. t.Error("unexpected nil client")
  666. return
  667. }
  668. // test the client talks to the endpoint we expect with the credentials we expect
  669. username = ""
  670. _, err = config.Client.Discovery().RESTClient().Get().AbsPath("/").DoRaw(context.TODO())
  671. if err != nil {
  672. t.Error(err)
  673. return
  674. }
  675. if username != tc.expectedUsername {
  676. t.Errorf("expected server call with user %q, got %q", tc.expectedUsername, username)
  677. }
  678. })
  679. }
  680. }