cluster_test.go 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804
  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 config
  14. import (
  15. "io/ioutil"
  16. "os"
  17. "path/filepath"
  18. "strings"
  19. "testing"
  20. "k8s.io/api/core/v1"
  21. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  22. "k8s.io/apimachinery/pkg/util/version"
  23. clientset "k8s.io/client-go/kubernetes"
  24. clientsetfake "k8s.io/client-go/kubernetes/fake"
  25. kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
  26. kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
  27. "k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
  28. )
  29. var k8sVersionString = kubeadmconstants.MinimumControlPlaneVersion.String()
  30. var k8sVersion = version.MustParseGeneric(k8sVersionString)
  31. var nodeName = "mynode"
  32. var cfgFiles = map[string][]byte{
  33. "InitConfiguration_v1beta1": []byte(`
  34. apiVersion: kubeadm.k8s.io/v1beta1
  35. kind: InitConfiguration
  36. `),
  37. "ClusterConfiguration_v1beta1": []byte(`
  38. apiVersion: kubeadm.k8s.io/v1beta1
  39. kind: ClusterConfiguration
  40. kubernetesVersion: ` + k8sVersionString + `
  41. `),
  42. "ClusterStatus_v1beta1": []byte(`
  43. apiVersion: kubeadm.k8s.io/v1beta1
  44. kind: ClusterStatus
  45. apiEndpoints:
  46. ` + nodeName + `:
  47. advertiseAddress: 1.2.3.4
  48. bindPort: 1234
  49. `),
  50. "ClusterStatus_v1beta1_Without_APIEndpoints": []byte(`
  51. apiVersion: kubeadm.k8s.io/v1beta1
  52. kind: ClusterStatus
  53. `),
  54. "InitConfiguration_v1beta2": []byte(`
  55. apiVersion: kubeadm.k8s.io/v1beta2
  56. kind: InitConfiguration
  57. `),
  58. "ClusterConfiguration_v1beta2": []byte(`
  59. apiVersion: kubeadm.k8s.io/v1beta2
  60. kind: ClusterConfiguration
  61. kubernetesVersion: ` + k8sVersionString + `
  62. `),
  63. "ClusterStatus_v1beta2": []byte(`
  64. apiVersion: kubeadm.k8s.io/v1beta2
  65. kind: ClusterStatus
  66. apiEndpoints:
  67. ` + nodeName + `:
  68. advertiseAddress: 1.2.3.4
  69. bindPort: 1234
  70. `),
  71. "ClusterStatus_v1beta2_Without_APIEndpoints": []byte(`
  72. apiVersion: kubeadm.k8s.io/v1beta2
  73. kind: ClusterStatus
  74. `),
  75. "Kube-proxy_componentconfig": []byte(`
  76. apiVersion: kubeproxy.config.k8s.io/v1alpha1
  77. kind: KubeProxyConfiguration
  78. `),
  79. "Kubelet_componentconfig": []byte(`
  80. apiVersion: kubelet.config.k8s.io/v1beta1
  81. kind: KubeletConfiguration
  82. `),
  83. }
  84. var kubeletConfFiles = map[string][]byte{
  85. "withoutX509Cert": []byte(`
  86. apiVersion: v1
  87. clusters:
  88. - cluster:
  89. server: https://10.0.2.15:6443
  90. name: kubernetes
  91. contexts:
  92. - context:
  93. cluster: kubernetes
  94. user: system:node:mynode
  95. name: system:node:mynode@kubernetes
  96. current-context: system:node:mynode@kubernetes
  97. kind: Config
  98. preferences: {}
  99. users:
  100. - name: system:node:mynode
  101. user:
  102. `),
  103. "configWithEmbeddedCert": []byte(`
  104. apiVersion: v1
  105. clusters:
  106. - cluster:
  107. server: https://10.0.2.15:6443
  108. name: kubernetes
  109. contexts:
  110. - context:
  111. cluster: kubernetes
  112. user: system:node:mynode
  113. name: system:node:mynode@kubernetes
  114. current-context: system:node:mynode@kubernetes
  115. kind: Config
  116. preferences: {}
  117. users:
  118. - name: system:node:mynode
  119. user:
  120. client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM4akNDQWRxZ0F3SUJBZ0lJQWl3VURhYk5vZ1F3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB4T0RBNU1ERXhOVE14TWpaYUZ3MHhPVEE1TURFeE5qQXhOVGxhTURReApGVEFUQmdOVkJBb1RESE41YzNSbGJUcHViMlJsY3pFYk1Ca0dBMVVFQXhNU2MzbHpkR1Z0T201dlpHVTZiWGx1CmIyUmxNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQWs2UXUzeStyNEZYUzZ4VkoKWU1vNE9kSkt3R1d1MHY4TEJIUnhhOUhvVHo1RXZLQnB1OVJoemt5dStUaFczb0xta2ZTRmNJcitHa0M5MW0zOApFelRmVE5JY2dsL0V5YkpmR1QvdGdUazZYd1kxY1UrUUdmSEFNNTBCVzFXTFVHc25CSllJZjA5eENnZTVoTkxLCnREeUJOWWNQZzg1bUJpOU9CNFJ2UlgyQVFRMjJwZ0xrQUpJWklOU0FEdUFrODN2b051SXM2YVY2bHBkR2Vva3YKdDlpTFdNR3p3a3ZTZUZQTlNGeWZ3Q055eENjb1FDQUNtSnJRS3NoeUE2bWNzdVhORWVXdlRQdVVFSWZPVFl4dwpxdkszRVBOK0xUYlA2anhUMWtTcFJUOSt4Z29uSlFhU0RsbUNBd20zRGJkSVppWUt3R2ppMkxKL0kvYWc0cTlzCjNLb0J2UUlEQVFBQm95Y3dKVEFPQmdOVkhROEJBZjhFQkFNQ0JhQXdFd1lEVlIwbEJBd3dDZ1lJS3dZQkJRVUgKQXdJd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFLcVVrU21jdW85OG5EK015b005VFdEV0pyTndySXpQTUNqRQpCSkdyREhVaHIwcEZlRjc0RHViODNzRXlaNjFxNUVQd2Y0enNLSzdzdDRUTzZhcE9pZWJYVmN3dnZoa09HQ2dFCmFVdGNOMjFhUGxtU0tOd0c4ai8yK3ZhbU80bGplK1NnZzRUUVB0eDZWejh5VXN2RFhxSUZycjNNd1gzSDA1RW4KWXAzN05JYkhKbGxHUW5LVHA5aTg5aXF4WXVhSERqZldiVHlEY3B5NldNVjdVaFYvY1plc3lGL0NBamNHd1V6YgowRlo5bW5tMnFONlBGWHZ4RmdMSGFWZzN2SVVCbkNmVVVyY1BDNE94VFNPK21aUmUxazh3eUFpVWovSk0rZllvCkcrMi9sbThUYVZqb1U3Rmk1S2E1RzVIWTJHTGFSN1ArSXhZY3JNSENsNjJZN1JxY3JuYz0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
  121. `),
  122. "configWithLinkedCert": []byte(`
  123. apiVersion: v1
  124. clusters:
  125. - cluster:
  126. server: https://10.0.2.15:6443
  127. name: kubernetes
  128. contexts:
  129. - context:
  130. cluster: kubernetes
  131. user: system:node:mynode
  132. name: system:node:mynode@kubernetes
  133. current-context: system:node:mynode@kubernetes
  134. kind: Config
  135. preferences: {}
  136. users:
  137. - name: system:node:mynode
  138. user:
  139. client-certificate: kubelet.pem
  140. `),
  141. }
  142. var pemFiles = map[string][]byte{
  143. "mynode.pem": []byte(`
  144. -----BEGIN CERTIFICATE-----
  145. MIIC8jCCAdqgAwIBAgIIAiwUDabNogQwDQYJKoZIhvcNAQELBQAwFTETMBEGA1UE
  146. AxMKa3ViZXJuZXRlczAeFw0xODA5MDExNTMxMjZaFw0xOTA5MDExNjAxNTlaMDQx
  147. FTATBgNVBAoTDHN5c3RlbTpub2RlczEbMBkGA1UEAxMSc3lzdGVtOm5vZGU6bXlu
  148. b2RlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk6Qu3y+r4FXS6xVJ
  149. YMo4OdJKwGWu0v8LBHRxa9HoTz5EvKBpu9Rhzkyu+ThW3oLmkfSFcIr+GkC91m38
  150. EzTfTNIcgl/EybJfGT/tgTk6XwY1cU+QGfHAM50BW1WLUGsnBJYIf09xCge5hNLK
  151. tDyBNYcPg85mBi9OB4RvRX2AQQ22pgLkAJIZINSADuAk83voNuIs6aV6lpdGeokv
  152. t9iLWMGzwkvSeFPNSFyfwCNyxCcoQCACmJrQKshyA6mcsuXNEeWvTPuUEIfOTYxw
  153. qvK3EPN+LTbP6jxT1kSpRT9+xgonJQaSDlmCAwm3DbdIZiYKwGji2LJ/I/ag4q9s
  154. 3KoBvQIDAQABoycwJTAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUH
  155. AwIwDQYJKoZIhvcNAQELBQADggEBAKqUkSmcuo98nD+MyoM9TWDWJrNwrIzPMCjE
  156. BJGrDHUhr0pFeF74Dub83sEyZ61q5EPwf4zsKK7st4TO6apOiebXVcwvvhkOGCgE
  157. aUtcN21aPlmSKNwG8j/2+vamO4lje+Sgg4TQPtx6Vz8yUsvDXqIFrr3MwX3H05En
  158. Yp37NIbHJllGQnKTp9i89iqxYuaHDjfWbTyDcpy6WMV7UhV/cZesyF/CAjcGwUzb
  159. 0FZ9mnm2qN6PFXvxFgLHaVg3vIUBnCfUUrcPC4OxTSO+mZRe1k8wyAiUj/JM+fYo
  160. G+2/lm8TaVjoU7Fi5Ka5G5HY2GLaR7P+IxYcrMHCl62Y7Rqcrnc=
  161. -----END CERTIFICATE-----
  162. `),
  163. }
  164. func TestGetNodeNameFromKubeletConfig(t *testing.T) {
  165. tmpdir, err := ioutil.TempDir("", "")
  166. if err != nil {
  167. t.Fatalf("Couldn't create tmpdir")
  168. }
  169. defer os.RemoveAll(tmpdir)
  170. var tests = []struct {
  171. name string
  172. kubeconfigContent []byte
  173. pemContent []byte
  174. expectedError bool
  175. }{
  176. {
  177. name: "valid - with embedded cert",
  178. kubeconfigContent: kubeletConfFiles["configWithEmbeddedCert"],
  179. },
  180. {
  181. name: "invalid - linked cert missing",
  182. kubeconfigContent: kubeletConfFiles["configWithLinkedCert"],
  183. expectedError: true,
  184. },
  185. {
  186. name: "valid - with linked cert",
  187. kubeconfigContent: kubeletConfFiles["configWithLinkedCert"],
  188. pemContent: pemFiles["mynode.pem"],
  189. },
  190. {
  191. name: "invalid - without embedded or linked X509Cert",
  192. kubeconfigContent: kubeletConfFiles["withoutX509Cert"],
  193. expectedError: true,
  194. },
  195. }
  196. for _, rt := range tests {
  197. t.Run(rt.name, func(t2 *testing.T) {
  198. if len(rt.pemContent) > 0 {
  199. pemPath := filepath.Join(tmpdir, "kubelet.pem")
  200. err := ioutil.WriteFile(pemPath, rt.pemContent, 0644)
  201. if err != nil {
  202. t.Errorf("Couldn't create pem file: %v", err)
  203. return
  204. }
  205. rt.kubeconfigContent = []byte(strings.Replace(string(rt.kubeconfigContent), "kubelet.pem", pemPath, -1))
  206. }
  207. kubeconfigPath := filepath.Join(tmpdir, kubeadmconstants.KubeletKubeConfigFileName)
  208. err := ioutil.WriteFile(kubeconfigPath, rt.kubeconfigContent, 0644)
  209. if err != nil {
  210. t.Errorf("Couldn't create kubeconfig: %v", err)
  211. return
  212. }
  213. name, err := getNodeNameFromKubeletConfig(tmpdir)
  214. if rt.expectedError != (err != nil) {
  215. t.Errorf("unexpected return err from getNodeRegistration: %v", err)
  216. return
  217. }
  218. if rt.expectedError {
  219. return
  220. }
  221. if name != nodeName {
  222. t.Errorf("invalid name")
  223. }
  224. })
  225. }
  226. }
  227. func TestGetNodeRegistration(t *testing.T) {
  228. tmpdir, err := ioutil.TempDir("", "")
  229. if err != nil {
  230. t.Fatalf("Couldn't create tmpdir")
  231. }
  232. defer os.RemoveAll(tmpdir)
  233. var tests = []struct {
  234. name string
  235. fileContents []byte
  236. node *v1.Node
  237. expectedError bool
  238. }{
  239. {
  240. name: "invalid - no kubelet.conf",
  241. expectedError: true,
  242. },
  243. {
  244. name: "valid",
  245. fileContents: kubeletConfFiles["configWithEmbeddedCert"],
  246. node: &v1.Node{
  247. ObjectMeta: metav1.ObjectMeta{
  248. Name: nodeName,
  249. Annotations: map[string]string{
  250. kubeadmconstants.AnnotationKubeadmCRISocket: "myCRIsocket",
  251. },
  252. },
  253. Spec: v1.NodeSpec{
  254. Taints: []v1.Taint{kubeadmconstants.ControlPlaneTaint},
  255. },
  256. },
  257. },
  258. {
  259. name: "invalid - no node",
  260. fileContents: kubeletConfFiles["configWithEmbeddedCert"],
  261. expectedError: true,
  262. },
  263. }
  264. for _, rt := range tests {
  265. t.Run(rt.name, func(t2 *testing.T) {
  266. cfgPath := filepath.Join(tmpdir, kubeadmconstants.KubeletKubeConfigFileName)
  267. if len(rt.fileContents) > 0 {
  268. err := ioutil.WriteFile(cfgPath, rt.fileContents, 0644)
  269. if err != nil {
  270. t.Errorf("Couldn't create file")
  271. return
  272. }
  273. }
  274. client := clientsetfake.NewSimpleClientset()
  275. if rt.node != nil {
  276. _, err := client.CoreV1().Nodes().Create(rt.node)
  277. if err != nil {
  278. t.Errorf("couldn't create Node")
  279. return
  280. }
  281. }
  282. cfg := &kubeadmapi.InitConfiguration{}
  283. err = getNodeRegistration(tmpdir, client, &cfg.NodeRegistration)
  284. if rt.expectedError != (err != nil) {
  285. t.Errorf("unexpected return err from getNodeRegistration: %v", err)
  286. return
  287. }
  288. if rt.expectedError {
  289. return
  290. }
  291. if cfg.NodeRegistration.Name != nodeName {
  292. t.Errorf("invalid cfg.NodeRegistration.Name")
  293. }
  294. if cfg.NodeRegistration.CRISocket != "myCRIsocket" {
  295. t.Errorf("invalid cfg.NodeRegistration.CRISocket")
  296. }
  297. if len(cfg.NodeRegistration.Taints) != 1 {
  298. t.Errorf("invalid cfg.NodeRegistration.Taints")
  299. }
  300. })
  301. }
  302. }
  303. func TestGetAPIEndpoint(t *testing.T) {
  304. var tests = []struct {
  305. name string
  306. configMap fakeConfigMap
  307. expectedError bool
  308. }{
  309. {
  310. name: "valid v1beta1",
  311. configMap: fakeConfigMap{
  312. name: kubeadmconstants.KubeadmConfigConfigMap, // ClusterConfiguration from kubeadm-config.
  313. data: map[string]string{
  314. kubeadmconstants.ClusterStatusConfigMapKey: string(cfgFiles["ClusterStatus_v1beta1"]),
  315. },
  316. },
  317. },
  318. {
  319. name: "invalid v1beta1 - No ClusterStatus in kubeadm-config ConfigMap",
  320. configMap: fakeConfigMap{
  321. name: kubeadmconstants.KubeadmConfigConfigMap, // ClusterConfiguration from kubeadm-config.
  322. data: map[string]string{},
  323. },
  324. expectedError: true,
  325. },
  326. {
  327. name: "invalid v1beta1 - ClusterStatus without APIEndopoints",
  328. configMap: fakeConfigMap{
  329. name: kubeadmconstants.KubeadmConfigConfigMap, // ClusterConfiguration from kubeadm-config.
  330. data: map[string]string{
  331. kubeadmconstants.ClusterStatusConfigMapKey: string(cfgFiles["ClusterStatus_v1beta1_Without_APIEndpoints"]),
  332. },
  333. },
  334. expectedError: true,
  335. },
  336. {
  337. name: "valid v1beta2",
  338. configMap: fakeConfigMap{
  339. name: kubeadmconstants.KubeadmConfigConfigMap, // ClusterConfiguration from kubeadm-config.
  340. data: map[string]string{
  341. kubeadmconstants.ClusterStatusConfigMapKey: string(cfgFiles["ClusterStatus_v1beta2"]),
  342. },
  343. },
  344. },
  345. {
  346. name: "invalid v1beta2 - No ClusterStatus in kubeadm-config ConfigMap",
  347. configMap: fakeConfigMap{
  348. name: kubeadmconstants.KubeadmConfigConfigMap, // ClusterConfiguration from kubeadm-config.
  349. data: map[string]string{},
  350. },
  351. expectedError: true,
  352. },
  353. {
  354. name: "invalid v1beta2 - ClusterStatus without APIEndopoints",
  355. configMap: fakeConfigMap{
  356. name: kubeadmconstants.KubeadmConfigConfigMap, // ClusterConfiguration from kubeadm-config.
  357. data: map[string]string{
  358. kubeadmconstants.ClusterStatusConfigMapKey: string(cfgFiles["ClusterStatus_v1beta2_Without_APIEndpoints"]),
  359. },
  360. },
  361. expectedError: true,
  362. },
  363. }
  364. for _, rt := range tests {
  365. t.Run(rt.name, func(t *testing.T) {
  366. cfg := &kubeadmapi.InitConfiguration{}
  367. err := getAPIEndpoint(rt.configMap.data, nodeName, &cfg.LocalAPIEndpoint)
  368. if rt.expectedError != (err != nil) {
  369. t.Errorf("unexpected return err from getInitConfigurationFromCluster: %v", err)
  370. return
  371. }
  372. if rt.expectedError {
  373. return
  374. }
  375. if cfg.LocalAPIEndpoint.AdvertiseAddress != "1.2.3.4" || cfg.LocalAPIEndpoint.BindPort != 1234 {
  376. t.Errorf("invalid cfg.APIEndpoint")
  377. }
  378. })
  379. }
  380. }
  381. func TestGetComponentConfigs(t *testing.T) {
  382. var tests = []struct {
  383. name string
  384. configMaps []fakeConfigMap
  385. expectedError bool
  386. }{
  387. {
  388. name: "valid",
  389. configMaps: []fakeConfigMap{
  390. {
  391. name: kubeadmconstants.KubeProxyConfigMap, // Kube-proxy component config from corresponding ConfigMap.
  392. data: map[string]string{
  393. kubeadmconstants.KubeProxyConfigMapKey: string(cfgFiles["Kube-proxy_componentconfig"]),
  394. },
  395. },
  396. {
  397. name: kubeadmconstants.GetKubeletConfigMapName(k8sVersion), // Kubelet component config from corresponding ConfigMap.
  398. data: map[string]string{
  399. kubeadmconstants.KubeletBaseConfigurationConfigMapKey: string(cfgFiles["Kubelet_componentconfig"]),
  400. },
  401. },
  402. },
  403. },
  404. {
  405. name: "invalid - No kubelet component config ConfigMap",
  406. configMaps: []fakeConfigMap{
  407. {
  408. name: kubeadmconstants.KubeProxyConfigMap,
  409. data: map[string]string{
  410. kubeadmconstants.KubeProxyConfigMapKey: string(cfgFiles["Kube-proxy_componentconfig"]),
  411. },
  412. },
  413. },
  414. expectedError: true,
  415. },
  416. {
  417. name: "invalid - No kube-proxy component config ConfigMap",
  418. configMaps: []fakeConfigMap{
  419. {
  420. name: kubeadmconstants.GetKubeletConfigMapName(k8sVersion),
  421. data: map[string]string{
  422. kubeadmconstants.KubeletBaseConfigurationConfigMapKey: string(cfgFiles["Kubelet_componentconfig"]),
  423. },
  424. },
  425. },
  426. expectedError: true,
  427. },
  428. }
  429. for _, rt := range tests {
  430. t.Run(rt.name, func(t *testing.T) {
  431. client := clientsetfake.NewSimpleClientset()
  432. for _, c := range rt.configMaps {
  433. err := c.create(client)
  434. if err != nil {
  435. t.Errorf("couldn't create ConfigMap %s", c.name)
  436. return
  437. }
  438. }
  439. cfg := &kubeadmapi.InitConfiguration{
  440. ClusterConfiguration: kubeadmapi.ClusterConfiguration{
  441. KubernetesVersion: k8sVersionString,
  442. },
  443. }
  444. err := getComponentConfigs(client, &cfg.ClusterConfiguration)
  445. if rt.expectedError != (err != nil) {
  446. t.Errorf("unexpected return err from getInitConfigurationFromCluster: %v", err)
  447. return
  448. }
  449. if rt.expectedError {
  450. return
  451. }
  452. // Test expected values in InitConfiguration
  453. if cfg.ComponentConfigs.Kubelet == nil {
  454. t.Errorf("invalid cfg.ComponentConfigs.Kubelet")
  455. }
  456. if cfg.ComponentConfigs.KubeProxy == nil {
  457. t.Errorf("invalid cfg.ComponentConfigs.KubeProxy")
  458. }
  459. })
  460. }
  461. }
  462. func TestGetInitConfigurationFromCluster(t *testing.T) {
  463. tmpdir, err := ioutil.TempDir("", "")
  464. if err != nil {
  465. t.Fatalf("Couldn't create tmpdir")
  466. }
  467. defer os.RemoveAll(tmpdir)
  468. var tests = []struct {
  469. name string
  470. fileContents []byte
  471. node *v1.Node
  472. configMaps []fakeConfigMap
  473. newControlPlane bool
  474. expectedError bool
  475. }{
  476. {
  477. name: "invalid - No kubeadm-config ConfigMap",
  478. expectedError: true,
  479. },
  480. {
  481. name: "invalid - No ClusterConfiguration in kubeadm-config ConfigMap",
  482. configMaps: []fakeConfigMap{
  483. {
  484. name: kubeadmconstants.KubeadmConfigConfigMap, // ClusterConfiguration from kubeadm-config.
  485. data: map[string]string{},
  486. },
  487. },
  488. expectedError: true,
  489. },
  490. {
  491. name: "valid v1beta1 - new control plane == false", // InitConfiguration composed with data from different places, with also node specific information from ClusterStatus and node
  492. configMaps: []fakeConfigMap{
  493. {
  494. name: kubeadmconstants.KubeadmConfigConfigMap, // ClusterConfiguration from kubeadm-config.
  495. data: map[string]string{
  496. kubeadmconstants.ClusterConfigurationConfigMapKey: string(cfgFiles["ClusterConfiguration_v1beta1"]),
  497. kubeadmconstants.ClusterStatusConfigMapKey: string(cfgFiles["ClusterStatus_v1beta1"]),
  498. },
  499. },
  500. {
  501. name: kubeadmconstants.KubeProxyConfigMap, // Kube-proxy component config from corresponding ConfigMap.
  502. data: map[string]string{
  503. kubeadmconstants.KubeProxyConfigMapKey: string(cfgFiles["Kube-proxy_componentconfig"]),
  504. },
  505. },
  506. {
  507. name: kubeadmconstants.GetKubeletConfigMapName(k8sVersion), // Kubelet component config from corresponding ConfigMap.
  508. data: map[string]string{
  509. kubeadmconstants.KubeletBaseConfigurationConfigMapKey: string(cfgFiles["Kubelet_componentconfig"]),
  510. },
  511. },
  512. },
  513. fileContents: kubeletConfFiles["configWithEmbeddedCert"],
  514. node: &v1.Node{
  515. ObjectMeta: metav1.ObjectMeta{
  516. Name: nodeName,
  517. Annotations: map[string]string{
  518. kubeadmconstants.AnnotationKubeadmCRISocket: "myCRIsocket",
  519. },
  520. },
  521. Spec: v1.NodeSpec{
  522. Taints: []v1.Taint{kubeadmconstants.ControlPlaneTaint},
  523. },
  524. },
  525. },
  526. {
  527. name: "valid v1beta1 - new control plane == true", // InitConfiguration composed with data from different places, without node specific information
  528. configMaps: []fakeConfigMap{
  529. {
  530. name: kubeadmconstants.KubeadmConfigConfigMap, // ClusterConfiguration from kubeadm-config.
  531. data: map[string]string{
  532. kubeadmconstants.ClusterConfigurationConfigMapKey: string(cfgFiles["ClusterConfiguration_v1beta1"]),
  533. },
  534. },
  535. {
  536. name: kubeadmconstants.KubeProxyConfigMap, // Kube-proxy component config from corresponding ConfigMap.
  537. data: map[string]string{
  538. kubeadmconstants.KubeProxyConfigMapKey: string(cfgFiles["Kube-proxy_componentconfig"]),
  539. },
  540. },
  541. {
  542. name: kubeadmconstants.GetKubeletConfigMapName(k8sVersion), // Kubelet component config from corresponding ConfigMap.
  543. data: map[string]string{
  544. kubeadmconstants.KubeletBaseConfigurationConfigMapKey: string(cfgFiles["Kubelet_componentconfig"]),
  545. },
  546. },
  547. },
  548. newControlPlane: true,
  549. },
  550. {
  551. name: "valid v1beta2 - new control plane == false", // InitConfiguration composed with data from different places, with also node specific information from ClusterStatus and node
  552. configMaps: []fakeConfigMap{
  553. {
  554. name: kubeadmconstants.KubeadmConfigConfigMap, // ClusterConfiguration from kubeadm-config.
  555. data: map[string]string{
  556. kubeadmconstants.ClusterConfigurationConfigMapKey: string(cfgFiles["ClusterConfiguration_v1beta2"]),
  557. kubeadmconstants.ClusterStatusConfigMapKey: string(cfgFiles["ClusterStatus_v1beta2"]),
  558. },
  559. },
  560. {
  561. name: kubeadmconstants.KubeProxyConfigMap, // Kube-proxy component config from corresponding ConfigMap.
  562. data: map[string]string{
  563. kubeadmconstants.KubeProxyConfigMapKey: string(cfgFiles["Kube-proxy_componentconfig"]),
  564. },
  565. },
  566. {
  567. name: kubeadmconstants.GetKubeletConfigMapName(k8sVersion), // Kubelet component config from corresponding ConfigMap.
  568. data: map[string]string{
  569. kubeadmconstants.KubeletBaseConfigurationConfigMapKey: string(cfgFiles["Kubelet_componentconfig"]),
  570. },
  571. },
  572. },
  573. fileContents: kubeletConfFiles["configWithEmbeddedCert"],
  574. node: &v1.Node{
  575. ObjectMeta: metav1.ObjectMeta{
  576. Name: nodeName,
  577. Annotations: map[string]string{
  578. kubeadmconstants.AnnotationKubeadmCRISocket: "myCRIsocket",
  579. },
  580. },
  581. Spec: v1.NodeSpec{
  582. Taints: []v1.Taint{kubeadmconstants.ControlPlaneTaint},
  583. },
  584. },
  585. },
  586. {
  587. name: "valid v1beta2 - new control plane == true", // InitConfiguration composed with data from different places, without node specific information
  588. configMaps: []fakeConfigMap{
  589. {
  590. name: kubeadmconstants.KubeadmConfigConfigMap, // ClusterConfiguration from kubeadm-config.
  591. data: map[string]string{
  592. kubeadmconstants.ClusterConfigurationConfigMapKey: string(cfgFiles["ClusterConfiguration_v1beta2"]),
  593. },
  594. },
  595. {
  596. name: kubeadmconstants.KubeProxyConfigMap, // Kube-proxy component config from corresponding ConfigMap.
  597. data: map[string]string{
  598. kubeadmconstants.KubeProxyConfigMapKey: string(cfgFiles["Kube-proxy_componentconfig"]),
  599. },
  600. },
  601. {
  602. name: kubeadmconstants.GetKubeletConfigMapName(k8sVersion), // Kubelet component config from corresponding ConfigMap.
  603. data: map[string]string{
  604. kubeadmconstants.KubeletBaseConfigurationConfigMapKey: string(cfgFiles["Kubelet_componentconfig"]),
  605. },
  606. },
  607. },
  608. newControlPlane: true,
  609. },
  610. }
  611. for _, rt := range tests {
  612. t.Run(rt.name, func(t *testing.T) {
  613. cfgPath := filepath.Join(tmpdir, kubeadmconstants.KubeletKubeConfigFileName)
  614. if len(rt.fileContents) > 0 {
  615. err := ioutil.WriteFile(cfgPath, rt.fileContents, 0644)
  616. if err != nil {
  617. t.Errorf("Couldn't create file")
  618. return
  619. }
  620. }
  621. client := clientsetfake.NewSimpleClientset()
  622. if rt.node != nil {
  623. _, err := client.CoreV1().Nodes().Create(rt.node)
  624. if err != nil {
  625. t.Errorf("couldn't create Node")
  626. return
  627. }
  628. }
  629. for _, c := range rt.configMaps {
  630. err := c.create(client)
  631. if err != nil {
  632. t.Errorf("couldn't create ConfigMap %s", c.name)
  633. return
  634. }
  635. }
  636. cfg, err := getInitConfigurationFromCluster(tmpdir, client, rt.newControlPlane)
  637. if rt.expectedError != (err != nil) {
  638. t.Errorf("unexpected return err from getInitConfigurationFromCluster: %v", err)
  639. return
  640. }
  641. if rt.expectedError {
  642. return
  643. }
  644. // Test expected values in InitConfiguration
  645. if cfg == nil {
  646. t.Errorf("unexpected nil return value")
  647. }
  648. if cfg.ClusterConfiguration.KubernetesVersion != k8sVersionString {
  649. t.Errorf("invalid ClusterConfiguration.KubernetesVersion")
  650. }
  651. if !rt.newControlPlane && (cfg.LocalAPIEndpoint.AdvertiseAddress != "1.2.3.4" || cfg.LocalAPIEndpoint.BindPort != 1234) {
  652. t.Errorf("invalid cfg.LocalAPIEndpoint")
  653. }
  654. if cfg.ComponentConfigs.Kubelet == nil {
  655. t.Errorf("invalid cfg.ComponentConfigs.Kubelet")
  656. }
  657. if cfg.ComponentConfigs.KubeProxy == nil {
  658. t.Errorf("invalid cfg.ComponentConfigs.KubeProxy")
  659. }
  660. })
  661. }
  662. }
  663. func TestGetGetClusterStatus(t *testing.T) {
  664. var tests = []struct {
  665. name string
  666. configMaps []fakeConfigMap
  667. expectedEndpoints int
  668. expectedError bool
  669. }{
  670. {
  671. name: "invalid missing config map",
  672. expectedEndpoints: 0,
  673. },
  674. {
  675. name: "valid v1beta1",
  676. configMaps: []fakeConfigMap{
  677. {
  678. name: kubeadmconstants.KubeadmConfigConfigMap,
  679. data: map[string]string{
  680. kubeadmconstants.ClusterStatusConfigMapKey: string(cfgFiles["ClusterStatus_v1beta1"]),
  681. },
  682. },
  683. },
  684. expectedEndpoints: 1,
  685. },
  686. {
  687. name: "valid v1beta2",
  688. configMaps: []fakeConfigMap{
  689. {
  690. name: kubeadmconstants.KubeadmConfigConfigMap,
  691. data: map[string]string{
  692. kubeadmconstants.ClusterStatusConfigMapKey: string(cfgFiles["ClusterStatus_v1beta2"]),
  693. },
  694. },
  695. },
  696. expectedEndpoints: 1,
  697. },
  698. {
  699. name: "invalid missing ClusterStatusConfigMapKey in the config map",
  700. configMaps: []fakeConfigMap{
  701. {
  702. name: kubeadmconstants.KubeadmConfigConfigMap,
  703. data: map[string]string{},
  704. },
  705. },
  706. expectedError: true,
  707. },
  708. {
  709. name: "invalid wrong value in the config map",
  710. configMaps: []fakeConfigMap{
  711. {
  712. name: kubeadmconstants.KubeadmConfigConfigMap,
  713. data: map[string]string{
  714. kubeadmconstants.ClusterStatusConfigMapKey: "not a kubeadm type",
  715. },
  716. },
  717. },
  718. expectedError: true,
  719. },
  720. }
  721. for _, rt := range tests {
  722. t.Run(rt.name, func(t *testing.T) {
  723. client := clientsetfake.NewSimpleClientset()
  724. for _, c := range rt.configMaps {
  725. err := c.create(client)
  726. if err != nil {
  727. t.Errorf("couldn't create ConfigMap %s", c.name)
  728. return
  729. }
  730. }
  731. clusterStatus, err := GetClusterStatus(client)
  732. if rt.expectedError != (err != nil) {
  733. t.Errorf("unexpected return err from GetClusterStatus: %v", err)
  734. return
  735. }
  736. if rt.expectedError {
  737. return
  738. }
  739. // Test expected values in clusterStatus
  740. if len(clusterStatus.APIEndpoints) != rt.expectedEndpoints {
  741. t.Errorf("unexpected ClusterStatus return value")
  742. }
  743. })
  744. }
  745. }
  746. type fakeConfigMap struct {
  747. name string
  748. data map[string]string
  749. }
  750. func (c *fakeConfigMap) create(client clientset.Interface) error {
  751. return apiclient.CreateOrUpdateConfigMap(client, &v1.ConfigMap{
  752. ObjectMeta: metav1.ObjectMeta{
  753. Name: c.name,
  754. Namespace: metav1.NamespaceSystem,
  755. },
  756. Data: c.data,
  757. })
  758. }