kubeconfig_test.go 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682
  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 kubeconfig
  14. import (
  15. "bytes"
  16. "crypto"
  17. "crypto/x509"
  18. "fmt"
  19. "io"
  20. "os"
  21. "path/filepath"
  22. "reflect"
  23. "testing"
  24. "github.com/lithammer/dedent"
  25. "k8s.io/client-go/tools/clientcmd"
  26. clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
  27. kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
  28. kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
  29. kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
  30. certstestutil "k8s.io/kubernetes/cmd/kubeadm/app/util/certs"
  31. pkiutil "k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil"
  32. testutil "k8s.io/kubernetes/cmd/kubeadm/test"
  33. kubeconfigtestutil "k8s.io/kubernetes/cmd/kubeadm/test/kubeconfig"
  34. )
  35. func TestGetKubeConfigSpecsFailsIfCADoesntExists(t *testing.T) {
  36. // Create temp folder for the test case (without a CA)
  37. tmpdir := testutil.SetupTempDir(t)
  38. defer os.RemoveAll(tmpdir)
  39. // Creates an InitConfiguration pointing to the pkidir folder
  40. cfg := &kubeadmapi.InitConfiguration{
  41. ClusterConfiguration: kubeadmapi.ClusterConfiguration{
  42. CertificatesDir: tmpdir,
  43. },
  44. }
  45. // Executes getKubeConfigSpecs
  46. if _, err := getKubeConfigSpecs(cfg); err == nil {
  47. t.Error("getKubeConfigSpecs didnt failed when expected")
  48. }
  49. }
  50. func TestGetKubeConfigSpecs(t *testing.T) {
  51. // Create temp folder for the test case
  52. tmpdir := testutil.SetupTempDir(t)
  53. defer os.RemoveAll(tmpdir)
  54. // Adds a pki folder with a ca certs to the temp folder
  55. pkidir := testutil.SetupPkiDirWithCertificateAuthority(t, tmpdir)
  56. // Creates InitConfigurations pointing to the pkidir folder
  57. cfgs := []*kubeadmapi.InitConfiguration{
  58. {
  59. LocalAPIEndpoint: kubeadmapi.APIEndpoint{AdvertiseAddress: "1.2.3.4", BindPort: 1234},
  60. ClusterConfiguration: kubeadmapi.ClusterConfiguration{
  61. CertificatesDir: pkidir,
  62. },
  63. NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: "valid-node-name"},
  64. },
  65. {
  66. LocalAPIEndpoint: kubeadmapi.APIEndpoint{AdvertiseAddress: "1.2.3.4", BindPort: 1234},
  67. ClusterConfiguration: kubeadmapi.ClusterConfiguration{
  68. ControlPlaneEndpoint: "api.k8s.io",
  69. CertificatesDir: pkidir,
  70. },
  71. NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: "valid-node-name"},
  72. },
  73. {
  74. LocalAPIEndpoint: kubeadmapi.APIEndpoint{AdvertiseAddress: "1.2.3.4", BindPort: 1234},
  75. ClusterConfiguration: kubeadmapi.ClusterConfiguration{
  76. ControlPlaneEndpoint: "api.k8s.io:4321",
  77. CertificatesDir: pkidir,
  78. },
  79. NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: "valid-node-name"},
  80. },
  81. {
  82. LocalAPIEndpoint: kubeadmapi.APIEndpoint{AdvertiseAddress: "1.2.3.4", BindPort: 1234},
  83. ClusterConfiguration: kubeadmapi.ClusterConfiguration{
  84. ControlPlaneEndpoint: "api.k8s.io",
  85. CertificatesDir: pkidir,
  86. },
  87. NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: "valid-node-name"},
  88. },
  89. {
  90. LocalAPIEndpoint: kubeadmapi.APIEndpoint{AdvertiseAddress: "1.2.3.4", BindPort: 1234},
  91. ClusterConfiguration: kubeadmapi.ClusterConfiguration{
  92. ControlPlaneEndpoint: "api.k8s.io:4321",
  93. CertificatesDir: pkidir,
  94. },
  95. NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: "valid-node-name"},
  96. },
  97. }
  98. for i, cfg := range cfgs {
  99. var assertions = []struct {
  100. kubeConfigFile string
  101. clientName string
  102. organizations []string
  103. }{
  104. {
  105. kubeConfigFile: kubeadmconstants.AdminKubeConfigFileName,
  106. clientName: "kubernetes-admin",
  107. organizations: []string{kubeadmconstants.SystemPrivilegedGroup},
  108. },
  109. {
  110. kubeConfigFile: kubeadmconstants.KubeletKubeConfigFileName,
  111. clientName: fmt.Sprintf("%s%s", kubeadmconstants.NodesUserPrefix, cfg.NodeRegistration.Name),
  112. organizations: []string{kubeadmconstants.NodesGroup},
  113. },
  114. {
  115. kubeConfigFile: kubeadmconstants.ControllerManagerKubeConfigFileName,
  116. clientName: kubeadmconstants.ControllerManagerUser,
  117. },
  118. {
  119. kubeConfigFile: kubeadmconstants.SchedulerKubeConfigFileName,
  120. clientName: kubeadmconstants.SchedulerUser,
  121. },
  122. }
  123. for _, assertion := range assertions {
  124. t.Run(fmt.Sprintf("%d-%s", i, assertion.clientName), func(t *testing.T) {
  125. // Executes getKubeConfigSpecs
  126. specs, err := getKubeConfigSpecs(cfg)
  127. if err != nil {
  128. t.Fatal("getKubeConfigSpecs failed!")
  129. }
  130. var spec *kubeConfigSpec
  131. var ok bool
  132. // assert the spec for the kubeConfigFile exists
  133. if spec, ok = specs[assertion.kubeConfigFile]; !ok {
  134. t.Errorf("getKubeConfigSpecs didn't create spec for %s ", assertion.kubeConfigFile)
  135. return
  136. }
  137. // Assert clientName
  138. if spec.ClientName != assertion.clientName {
  139. t.Errorf("getKubeConfigSpecs for %s clientName is %s, expected %s", assertion.kubeConfigFile, spec.ClientName, assertion.clientName)
  140. }
  141. // Assert Organizations
  142. if spec.ClientCertAuth == nil || !reflect.DeepEqual(spec.ClientCertAuth.Organizations, assertion.organizations) {
  143. t.Errorf("getKubeConfigSpecs for %s Organizations is %v, expected %v", assertion.kubeConfigFile, spec.ClientCertAuth.Organizations, assertion.organizations)
  144. }
  145. // Asserts InitConfiguration values injected into spec
  146. controlPlaneEndpoint, err := kubeadmutil.GetControlPlaneEndpoint(cfg.ControlPlaneEndpoint, &cfg.LocalAPIEndpoint)
  147. if err != nil {
  148. t.Error(err)
  149. }
  150. if spec.APIServer != controlPlaneEndpoint {
  151. t.Errorf("getKubeConfigSpecs didn't injected cfg.APIServer endpoint into spec for %s", assertion.kubeConfigFile)
  152. }
  153. // Asserts CA certs and CA keys loaded into specs
  154. if spec.CACert == nil {
  155. t.Errorf("getKubeConfigSpecs didn't loaded CACert into spec for %s!", assertion.kubeConfigFile)
  156. }
  157. if spec.ClientCertAuth == nil || spec.ClientCertAuth.CAKey == nil {
  158. t.Errorf("getKubeConfigSpecs didn't loaded CAKey into spec for %s!", assertion.kubeConfigFile)
  159. }
  160. })
  161. }
  162. }
  163. }
  164. func TestBuildKubeConfigFromSpecWithClientAuth(t *testing.T) {
  165. // Creates a CA
  166. caCert, caKey := certstestutil.SetupCertificateAuthority(t)
  167. // Executes buildKubeConfigFromSpec passing a KubeConfigSpec with a ClientAuth
  168. config := setupdKubeConfigWithClientAuth(t, caCert, caKey, "https://1.2.3.4:1234", "myClientName", "test-cluster", "myOrg1", "myOrg2")
  169. // Asserts spec data are propagated to the kubeconfig
  170. kubeconfigtestutil.AssertKubeConfigCurrentCluster(t, config, "https://1.2.3.4:1234", caCert)
  171. kubeconfigtestutil.AssertKubeConfigCurrentAuthInfoWithClientCert(t, config, caCert, "myClientName", "myOrg1", "myOrg2")
  172. }
  173. func TestBuildKubeConfigFromSpecWithTokenAuth(t *testing.T) {
  174. // Creates a CA
  175. caCert, _ := certstestutil.SetupCertificateAuthority(t)
  176. // Executes buildKubeConfigFromSpec passing a KubeConfigSpec with a Token
  177. config := setupdKubeConfigWithTokenAuth(t, caCert, "https://1.2.3.4:1234", "myClientName", "123456", "test-cluster")
  178. // Asserts spec data are propagated to the kubeconfig
  179. kubeconfigtestutil.AssertKubeConfigCurrentCluster(t, config, "https://1.2.3.4:1234", caCert)
  180. kubeconfigtestutil.AssertKubeConfigCurrentAuthInfoWithToken(t, config, "myClientName", "123456")
  181. }
  182. func TestCreateKubeConfigFileIfNotExists(t *testing.T) {
  183. // Creates a CAs
  184. caCert, caKey := certstestutil.SetupCertificateAuthority(t)
  185. anotherCaCert, anotherCaKey := certstestutil.SetupCertificateAuthority(t)
  186. // build kubeconfigs (to be used to test kubeconfigs equality/not equality)
  187. config := setupdKubeConfigWithClientAuth(t, caCert, caKey, "https://1.2.3.4:1234", "test-cluster", "myOrg1", "myOrg2")
  188. configWithAnotherClusterCa := setupdKubeConfigWithClientAuth(t, anotherCaCert, anotherCaKey, "https://1.2.3.4:1234", "test-cluster", "myOrg1", "myOrg2")
  189. configWithAnotherClusterAddress := setupdKubeConfigWithClientAuth(t, caCert, caKey, "https://3.4.5.6:3456", "myOrg1", "test-cluster", "myOrg2")
  190. invalidConfig := setupdKubeConfigWithClientAuth(t, caCert, caKey, "https://1.2.3.4:1234", "test-cluster", "myOrg1", "myOrg2")
  191. invalidConfig.CurrentContext = "invalid context"
  192. var tests = []struct {
  193. name string
  194. existingKubeConfig *clientcmdapi.Config
  195. kubeConfig *clientcmdapi.Config
  196. expectedError bool
  197. }{
  198. { // if there is no existing KubeConfig, creates the kubeconfig
  199. name: "KubeConfig doesn't exist",
  200. kubeConfig: config,
  201. },
  202. { // if KubeConfig is invalid raise error
  203. name: "KubeConfig is invalid",
  204. existingKubeConfig: invalidConfig,
  205. kubeConfig: invalidConfig,
  206. expectedError: true,
  207. },
  208. { // if KubeConfig is equal to the existingKubeConfig - refers to the same cluster -, use the existing (Test idempotency)
  209. name: "KubeConfig refers to the same cluster",
  210. existingKubeConfig: config,
  211. kubeConfig: config,
  212. },
  213. { // if KubeConfig is not equal to the existingKubeConfig - refers to the another cluster (a cluster with another Ca) -, raise error
  214. name: "KubeConfig refers to the cluster with another CA",
  215. existingKubeConfig: config,
  216. kubeConfig: configWithAnotherClusterCa,
  217. expectedError: true,
  218. },
  219. { // if KubeConfig is not equal to the existingKubeConfig - refers to the another cluster (a cluster with another address) -, raise error
  220. name: "KubeConfig referst to the cluster with another address",
  221. existingKubeConfig: config,
  222. kubeConfig: configWithAnotherClusterAddress,
  223. expectedError: true,
  224. },
  225. }
  226. for _, test := range tests {
  227. t.Run(test.name, func(t *testing.T) {
  228. // Create temp folder for the test case
  229. tmpdir := testutil.SetupTempDir(t)
  230. defer os.RemoveAll(tmpdir)
  231. // Writes the existing kubeconfig file to disk
  232. if test.existingKubeConfig != nil {
  233. if err := createKubeConfigFileIfNotExists(tmpdir, "test.conf", test.existingKubeConfig); err != nil {
  234. t.Errorf("createKubeConfigFileIfNotExists failed")
  235. }
  236. }
  237. // Writes the kubeconfig file to disk
  238. err := createKubeConfigFileIfNotExists(tmpdir, "test.conf", test.kubeConfig)
  239. if test.expectedError && err == nil {
  240. t.Errorf("createKubeConfigFileIfNotExists didn't failed when expected to fail")
  241. }
  242. if !test.expectedError && err != nil {
  243. t.Errorf("createKubeConfigFileIfNotExists failed")
  244. }
  245. // Assert that the created file is there
  246. testutil.AssertFileExists(t, tmpdir, "test.conf")
  247. })
  248. }
  249. }
  250. func TestCreateKubeconfigFilesAndWrappers(t *testing.T) {
  251. var tests = []struct {
  252. name string
  253. createKubeConfigFunction func(outDir string, cfg *kubeadmapi.InitConfiguration) error
  254. expectedFiles []string
  255. expectedError bool
  256. }{
  257. { // Test createKubeConfigFiles fails for unknown kubeconfig is requested
  258. name: "createKubeConfigFiles",
  259. createKubeConfigFunction: func(outDir string, cfg *kubeadmapi.InitConfiguration) error {
  260. return createKubeConfigFiles(outDir, cfg, "unknown.conf")
  261. },
  262. expectedError: true,
  263. },
  264. { // Test CreateJoinControlPlaneKubeConfigFiles (wrapper to createKubeConfigFile)
  265. name: "CreateJoinControlPlaneKubeConfigFiles",
  266. createKubeConfigFunction: CreateJoinControlPlaneKubeConfigFiles,
  267. expectedFiles: []string{
  268. kubeadmconstants.AdminKubeConfigFileName,
  269. kubeadmconstants.ControllerManagerKubeConfigFileName,
  270. kubeadmconstants.SchedulerKubeConfigFileName,
  271. },
  272. },
  273. }
  274. for _, test := range tests {
  275. t.Run(test.name, func(t *testing.T) {
  276. // Create temp folder for the test case
  277. tmpdir := testutil.SetupTempDir(t)
  278. defer os.RemoveAll(tmpdir)
  279. // Adds a pki folder with a ca certs to the temp folder
  280. pkidir := testutil.SetupPkiDirWithCertificateAuthority(t, tmpdir)
  281. // Creates an InitConfiguration pointing to the pkidir folder
  282. cfg := &kubeadmapi.InitConfiguration{
  283. LocalAPIEndpoint: kubeadmapi.APIEndpoint{AdvertiseAddress: "1.2.3.4", BindPort: 1234},
  284. ClusterConfiguration: kubeadmapi.ClusterConfiguration{
  285. CertificatesDir: pkidir,
  286. },
  287. }
  288. // Execs the createKubeConfigFunction
  289. err := test.createKubeConfigFunction(tmpdir, cfg)
  290. if test.expectedError && err == nil {
  291. t.Errorf("createKubeConfigFunction didn't failed when expected to fail")
  292. return
  293. }
  294. if !test.expectedError && err != nil {
  295. t.Errorf("createKubeConfigFunction failed")
  296. return
  297. }
  298. // Assert expected files are there
  299. testutil.AssertFileExists(t, tmpdir, test.expectedFiles...)
  300. })
  301. }
  302. }
  303. func TestWriteKubeConfigFailsIfCADoesntExists(t *testing.T) {
  304. // Temporary folders for the test case (without a CA)
  305. tmpdir := testutil.SetupTempDir(t)
  306. defer os.RemoveAll(tmpdir)
  307. // Creates an InitConfiguration pointing to the tmpdir folder
  308. cfg := &kubeadmapi.InitConfiguration{
  309. ClusterConfiguration: kubeadmapi.ClusterConfiguration{
  310. CertificatesDir: tmpdir,
  311. },
  312. }
  313. var tests = []struct {
  314. name string
  315. writeKubeConfigFunction func(out io.Writer) error
  316. }{
  317. {
  318. name: "WriteKubeConfigWithClientCert",
  319. writeKubeConfigFunction: func(out io.Writer) error {
  320. return WriteKubeConfigWithClientCert(out, cfg, "myUser", []string{"myOrg"})
  321. },
  322. },
  323. {
  324. name: "WriteKubeConfigWithToken",
  325. writeKubeConfigFunction: func(out io.Writer) error {
  326. return WriteKubeConfigWithToken(out, cfg, "myUser", "12345")
  327. },
  328. },
  329. }
  330. for _, test := range tests {
  331. t.Run(test.name, func(t *testing.T) {
  332. buf := new(bytes.Buffer)
  333. // executes writeKubeConfigFunction
  334. if err := test.writeKubeConfigFunction(buf); err == nil {
  335. t.Error("writeKubeConfigFunction didnt failed when expected")
  336. }
  337. })
  338. }
  339. }
  340. func TestWriteKubeConfig(t *testing.T) {
  341. // Temporary folders for the test case
  342. tmpdir := testutil.SetupTempDir(t)
  343. defer os.RemoveAll(tmpdir)
  344. // Adds a pki folder with a ca cert to the temp folder
  345. pkidir := testutil.SetupPkiDirWithCertificateAuthority(t, tmpdir)
  346. // Retrieves ca cert for assertions
  347. caCert, _, err := pkiutil.TryLoadCertAndKeyFromDisk(pkidir, kubeadmconstants.CACertAndKeyBaseName)
  348. if err != nil {
  349. t.Fatalf("couldn't retrieve ca cert: %v", err)
  350. }
  351. // Creates an InitConfiguration pointing to the pkidir folder
  352. cfg := &kubeadmapi.InitConfiguration{
  353. LocalAPIEndpoint: kubeadmapi.APIEndpoint{AdvertiseAddress: "1.2.3.4", BindPort: 1234},
  354. ClusterConfiguration: kubeadmapi.ClusterConfiguration{
  355. CertificatesDir: pkidir,
  356. },
  357. }
  358. var tests = []struct {
  359. name string
  360. writeKubeConfigFunction func(out io.Writer) error
  361. withClientCert bool
  362. withToken bool
  363. }{
  364. {
  365. name: "WriteKubeConfigWithClientCert",
  366. writeKubeConfigFunction: func(out io.Writer) error {
  367. return WriteKubeConfigWithClientCert(out, cfg, "myUser", []string{"myOrg"})
  368. },
  369. withClientCert: true,
  370. },
  371. {
  372. name: "WriteKubeConfigWithToken",
  373. writeKubeConfigFunction: func(out io.Writer) error {
  374. return WriteKubeConfigWithToken(out, cfg, "myUser", "12345")
  375. },
  376. withToken: true,
  377. },
  378. }
  379. for _, test := range tests {
  380. t.Run(test.name, func(t *testing.T) {
  381. buf := new(bytes.Buffer)
  382. // executes writeKubeConfigFunction
  383. if err := test.writeKubeConfigFunction(buf); err != nil {
  384. t.Error("writeKubeConfigFunction failed")
  385. return
  386. }
  387. // reads kubeconfig written to stdout
  388. config, err := clientcmd.Load(buf.Bytes())
  389. if err != nil {
  390. t.Errorf("Couldn't read kubeconfig file from buffer: %v", err)
  391. return
  392. }
  393. // checks that CLI flags are properly propagated
  394. kubeconfigtestutil.AssertKubeConfigCurrentCluster(t, config, "https://1.2.3.4:1234", caCert)
  395. if test.withClientCert {
  396. // checks that kubeconfig files have expected client cert
  397. kubeconfigtestutil.AssertKubeConfigCurrentAuthInfoWithClientCert(t, config, caCert, "myUser")
  398. }
  399. if test.withToken {
  400. // checks that kubeconfig files have expected token
  401. kubeconfigtestutil.AssertKubeConfigCurrentAuthInfoWithToken(t, config, "myUser", "12345")
  402. }
  403. })
  404. }
  405. }
  406. func TestValidateKubeConfig(t *testing.T) {
  407. caCert, caKey := certstestutil.SetupCertificateAuthority(t)
  408. anotherCaCert, anotherCaKey := certstestutil.SetupCertificateAuthority(t)
  409. config := setupdKubeConfigWithClientAuth(t, caCert, caKey, "https://1.2.3.4:1234", "test-cluster", "myOrg1")
  410. configWithAnotherClusterCa := setupdKubeConfigWithClientAuth(t, anotherCaCert, anotherCaKey, "https://1.2.3.4:1234", "test-cluster", "myOrg1")
  411. configWithAnotherServerURL := setupdKubeConfigWithClientAuth(t, caCert, caKey, "https://4.3.2.1:4321", "test-cluster", "myOrg1")
  412. // create a valid config but with whitespace around the CA PEM.
  413. // validateKubeConfig() should tollerate that.
  414. configWhitespace := config.DeepCopy()
  415. configWhitespaceCtx := configWhitespace.Contexts[configWhitespace.CurrentContext]
  416. configWhitespaceCA := string(configWhitespace.Clusters[configWhitespaceCtx.Cluster].CertificateAuthorityData)
  417. configWhitespaceCA = "\n" + configWhitespaceCA + "\n"
  418. configWhitespace.Clusters[configWhitespaceCtx.Cluster].CertificateAuthorityData = []byte(configWhitespaceCA)
  419. tests := map[string]struct {
  420. existingKubeConfig *clientcmdapi.Config
  421. kubeConfig *clientcmdapi.Config
  422. expectedError bool
  423. }{
  424. "kubeconfig don't exist": {
  425. kubeConfig: config,
  426. expectedError: true,
  427. },
  428. "kubeconfig exist and has invalid ca": {
  429. existingKubeConfig: configWithAnotherClusterCa,
  430. kubeConfig: config,
  431. expectedError: true,
  432. },
  433. "kubeconfig exist and has invalid server url": {
  434. existingKubeConfig: configWithAnotherServerURL,
  435. kubeConfig: config,
  436. expectedError: true,
  437. },
  438. "kubeconfig exist and is valid": {
  439. existingKubeConfig: config,
  440. kubeConfig: config,
  441. expectedError: false,
  442. },
  443. "kubeconfig exist and is valid even if its CA contains whitespace": {
  444. existingKubeConfig: configWhitespace,
  445. kubeConfig: config,
  446. expectedError: false,
  447. },
  448. }
  449. for name, test := range tests {
  450. t.Run(name, func(t *testing.T) {
  451. tmpdir := testutil.SetupTempDir(t)
  452. defer os.RemoveAll(tmpdir)
  453. if test.existingKubeConfig != nil {
  454. if err := createKubeConfigFileIfNotExists(tmpdir, "test.conf", test.existingKubeConfig); err != nil {
  455. t.Errorf("createKubeConfigFileIfNotExists failed")
  456. }
  457. }
  458. err := validateKubeConfig(tmpdir, "test.conf", test.kubeConfig)
  459. if (err != nil) != test.expectedError {
  460. t.Fatalf(dedent.Dedent(
  461. "validateKubeConfig failed\n%s\nexpected error: %t\n\tgot: %t\nerror: %v"),
  462. name,
  463. test.expectedError,
  464. (err != nil),
  465. err,
  466. )
  467. }
  468. })
  469. }
  470. }
  471. func TestValidateKubeconfigsForExternalCA(t *testing.T) {
  472. tmpDir := testutil.SetupTempDir(t)
  473. defer os.RemoveAll(tmpDir)
  474. pkiDir := filepath.Join(tmpDir, "pki")
  475. initConfig := &kubeadmapi.InitConfiguration{
  476. ClusterConfiguration: kubeadmapi.ClusterConfiguration{
  477. CertificatesDir: pkiDir,
  478. },
  479. LocalAPIEndpoint: kubeadmapi.APIEndpoint{
  480. BindPort: 1234,
  481. AdvertiseAddress: "1.2.3.4",
  482. },
  483. }
  484. // creates CA, write to pkiDir and remove ca.key to get into external CA condition
  485. caCert, caKey := certstestutil.SetupCertificateAuthority(t)
  486. if err := pkiutil.WriteCertAndKey(pkiDir, kubeadmconstants.CACertAndKeyBaseName, caCert, caKey); err != nil {
  487. t.Fatalf("failure while saving CA certificate and key: %v", err)
  488. }
  489. if err := os.Remove(filepath.Join(pkiDir, kubeadmconstants.CAKeyName)); err != nil {
  490. t.Fatalf("failure while deleting ca.key: %v", err)
  491. }
  492. // create a valid config
  493. config := setupdKubeConfigWithClientAuth(t, caCert, caKey, "https://1.2.3.4:1234", "test-cluster", "myOrg1")
  494. // create a config with another CA
  495. anotherCaCert, anotherCaKey := certstestutil.SetupCertificateAuthority(t)
  496. configWithAnotherClusterCa := setupdKubeConfigWithClientAuth(t, anotherCaCert, anotherCaKey, "https://1.2.3.4:1234", "test-cluster", "myOrg1")
  497. // create a config with another server URL
  498. configWithAnotherServerURL := setupdKubeConfigWithClientAuth(t, caCert, caKey, "https://4.3.2.1:4321", "test-cluster", "myOrg1")
  499. tests := map[string]struct {
  500. filesToWrite map[string]*clientcmdapi.Config
  501. initConfig *kubeadmapi.InitConfiguration
  502. expectedError bool
  503. }{
  504. "files don't exist": {
  505. initConfig: initConfig,
  506. expectedError: true,
  507. },
  508. "some files don't exist": {
  509. filesToWrite: map[string]*clientcmdapi.Config{
  510. kubeadmconstants.AdminKubeConfigFileName: config,
  511. kubeadmconstants.KubeletKubeConfigFileName: config,
  512. },
  513. initConfig: initConfig,
  514. expectedError: true,
  515. },
  516. "some files have invalid CA": {
  517. filesToWrite: map[string]*clientcmdapi.Config{
  518. kubeadmconstants.AdminKubeConfigFileName: config,
  519. kubeadmconstants.KubeletKubeConfigFileName: config,
  520. kubeadmconstants.ControllerManagerKubeConfigFileName: configWithAnotherClusterCa,
  521. kubeadmconstants.SchedulerKubeConfigFileName: config,
  522. },
  523. initConfig: initConfig,
  524. expectedError: true,
  525. },
  526. "some files have invalid Server Url": {
  527. filesToWrite: map[string]*clientcmdapi.Config{
  528. kubeadmconstants.AdminKubeConfigFileName: config,
  529. kubeadmconstants.KubeletKubeConfigFileName: config,
  530. kubeadmconstants.ControllerManagerKubeConfigFileName: config,
  531. kubeadmconstants.SchedulerKubeConfigFileName: configWithAnotherServerURL,
  532. },
  533. initConfig: initConfig,
  534. expectedError: true,
  535. },
  536. "all files are valid": {
  537. filesToWrite: map[string]*clientcmdapi.Config{
  538. kubeadmconstants.AdminKubeConfigFileName: config,
  539. kubeadmconstants.KubeletKubeConfigFileName: config,
  540. kubeadmconstants.ControllerManagerKubeConfigFileName: config,
  541. kubeadmconstants.SchedulerKubeConfigFileName: config,
  542. },
  543. initConfig: initConfig,
  544. expectedError: false,
  545. },
  546. }
  547. for name, test := range tests {
  548. t.Run(name, func(t *testing.T) {
  549. tmpdir := testutil.SetupTempDir(t)
  550. defer os.RemoveAll(tmpdir)
  551. for name, config := range test.filesToWrite {
  552. if err := createKubeConfigFileIfNotExists(tmpdir, name, config); err != nil {
  553. t.Errorf("createKubeConfigFileIfNotExists failed: %v", err)
  554. }
  555. }
  556. err := ValidateKubeconfigsForExternalCA(tmpdir, test.initConfig)
  557. if (err != nil) != test.expectedError {
  558. t.Fatalf(dedent.Dedent(
  559. "ValidateKubeconfigsForExternalCA failed\n%s\nexpected error: %t\n\tgot: %t\nerror: %v"),
  560. name,
  561. test.expectedError,
  562. (err != nil),
  563. err,
  564. )
  565. }
  566. })
  567. }
  568. }
  569. // setupdKubeConfigWithClientAuth is a test utility function that wraps buildKubeConfigFromSpec for building a KubeConfig object With ClientAuth
  570. func setupdKubeConfigWithClientAuth(t *testing.T, caCert *x509.Certificate, caKey crypto.Signer, APIServer, clientName, clustername string, organizations ...string) *clientcmdapi.Config {
  571. spec := &kubeConfigSpec{
  572. CACert: caCert,
  573. APIServer: APIServer,
  574. ClientName: clientName,
  575. ClientCertAuth: &clientCertAuth{
  576. CAKey: caKey,
  577. Organizations: organizations,
  578. },
  579. }
  580. config, err := buildKubeConfigFromSpec(spec, clustername)
  581. if err != nil {
  582. t.Fatal("buildKubeConfigFromSpec failed!")
  583. }
  584. return config
  585. }
  586. // setupdKubeConfigWithClientAuth is a test utility function that wraps buildKubeConfigFromSpec for building a KubeConfig object With Token
  587. func setupdKubeConfigWithTokenAuth(t *testing.T, caCert *x509.Certificate, APIServer, clientName, token, clustername string) *clientcmdapi.Config {
  588. spec := &kubeConfigSpec{
  589. CACert: caCert,
  590. APIServer: APIServer,
  591. ClientName: clientName,
  592. TokenAuth: &tokenAuth{
  593. Token: token,
  594. },
  595. }
  596. config, err := buildKubeConfigFromSpec(spec, clustername)
  597. if err != nil {
  598. t.Fatal("buildKubeConfigFromSpec failed!")
  599. }
  600. return config
  601. }