1
16
17 package kubeconfig
18
19 import (
20 "bytes"
21 "context"
22 "crypto"
23 "crypto/x509"
24 "fmt"
25 "io"
26 "os"
27 "path/filepath"
28 "reflect"
29 "testing"
30 "time"
31
32 "github.com/lithammer/dedent"
33 "github.com/pkg/errors"
34
35 rbac "k8s.io/api/rbac/v1"
36 apierrors "k8s.io/apimachinery/pkg/api/errors"
37 "k8s.io/apimachinery/pkg/runtime"
38 "k8s.io/apimachinery/pkg/runtime/schema"
39 clientset "k8s.io/client-go/kubernetes"
40 clientsetfake "k8s.io/client-go/kubernetes/fake"
41 clientgotesting "k8s.io/client-go/testing"
42 "k8s.io/client-go/tools/clientcmd"
43 clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
44
45 kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
46 kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
47 certsphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs"
48 kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
49 certstestutil "k8s.io/kubernetes/cmd/kubeadm/app/util/certs"
50 "k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil"
51 testutil "k8s.io/kubernetes/cmd/kubeadm/test"
52 kubeconfigtestutil "k8s.io/kubernetes/cmd/kubeadm/test/kubeconfig"
53 )
54
55 func TestGetKubeConfigSpecsFailsIfCADoesntExists(t *testing.T) {
56
57 tmpdir := testutil.SetupTempDir(t)
58 defer os.RemoveAll(tmpdir)
59
60
61 cfg := &kubeadmapi.InitConfiguration{
62 ClusterConfiguration: kubeadmapi.ClusterConfiguration{
63 CertificatesDir: tmpdir,
64 },
65 }
66
67
68 if _, err := getKubeConfigSpecs(cfg); err == nil {
69 t.Error("getKubeConfigSpecs didnt failed when expected")
70 }
71 }
72
73 func TestGetKubeConfigSpecs(t *testing.T) {
74
75 tmpdir := testutil.SetupTempDir(t)
76 defer os.RemoveAll(tmpdir)
77
78
79 pkidir := testutil.SetupPkiDirWithCertificateAuthority(t, tmpdir)
80
81
82 cfgs := []*kubeadmapi.InitConfiguration{
83 {
84 LocalAPIEndpoint: kubeadmapi.APIEndpoint{AdvertiseAddress: "1.2.3.4", BindPort: 1234},
85 ClusterConfiguration: kubeadmapi.ClusterConfiguration{
86 CertificatesDir: pkidir,
87 },
88 NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: "valid-node-name"},
89 },
90 {
91 LocalAPIEndpoint: kubeadmapi.APIEndpoint{AdvertiseAddress: "1.2.3.4", BindPort: 1234},
92 ClusterConfiguration: kubeadmapi.ClusterConfiguration{
93 ControlPlaneEndpoint: "api.k8s.io",
94 CertificatesDir: pkidir,
95 },
96 NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: "valid-node-name"},
97 },
98 {
99 LocalAPIEndpoint: kubeadmapi.APIEndpoint{AdvertiseAddress: "1.2.3.4", BindPort: 1234},
100 ClusterConfiguration: kubeadmapi.ClusterConfiguration{
101 ControlPlaneEndpoint: "api.k8s.io:4321",
102 CertificatesDir: pkidir,
103 },
104 NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: "valid-node-name"},
105 },
106 {
107 LocalAPIEndpoint: kubeadmapi.APIEndpoint{AdvertiseAddress: "1.2.3.4", BindPort: 1234},
108 ClusterConfiguration: kubeadmapi.ClusterConfiguration{
109 ControlPlaneEndpoint: "api.k8s.io",
110 CertificatesDir: pkidir,
111 },
112 NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: "valid-node-name"},
113 },
114 {
115 LocalAPIEndpoint: kubeadmapi.APIEndpoint{AdvertiseAddress: "1.2.3.4", BindPort: 1234},
116 ClusterConfiguration: kubeadmapi.ClusterConfiguration{
117 ControlPlaneEndpoint: "api.k8s.io:4321",
118 CertificatesDir: pkidir,
119 },
120 NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: "valid-node-name"},
121 },
122 }
123
124 for i, cfg := range cfgs {
125 var assertions = []struct {
126 kubeConfigFile string
127 clientName string
128 organizations []string
129 }{
130 {
131 kubeConfigFile: kubeadmconstants.AdminKubeConfigFileName,
132 clientName: "kubernetes-admin",
133 organizations: []string{kubeadmconstants.ClusterAdminsGroupAndClusterRoleBinding},
134 },
135 {
136 kubeConfigFile: kubeadmconstants.SuperAdminKubeConfigFileName,
137 clientName: "kubernetes-super-admin",
138 organizations: []string{kubeadmconstants.SystemPrivilegedGroup},
139 },
140 {
141 kubeConfigFile: kubeadmconstants.KubeletKubeConfigFileName,
142 clientName: fmt.Sprintf("%s%s", kubeadmconstants.NodesUserPrefix, cfg.NodeRegistration.Name),
143 organizations: []string{kubeadmconstants.NodesGroup},
144 },
145 {
146 kubeConfigFile: kubeadmconstants.ControllerManagerKubeConfigFileName,
147 clientName: kubeadmconstants.ControllerManagerUser,
148 },
149 {
150 kubeConfigFile: kubeadmconstants.SchedulerKubeConfigFileName,
151 clientName: kubeadmconstants.SchedulerUser,
152 },
153 }
154
155 for _, assertion := range assertions {
156 t.Run(fmt.Sprintf("%d-%s", i, assertion.clientName), func(t *testing.T) {
157
158 specs, err := getKubeConfigSpecs(cfg)
159 if err != nil {
160 t.Fatal("getKubeConfigSpecs failed!")
161 }
162
163 var spec *kubeConfigSpec
164 var ok bool
165
166
167 if spec, ok = specs[assertion.kubeConfigFile]; !ok {
168 t.Errorf("getKubeConfigSpecs didn't create spec for %s ", assertion.kubeConfigFile)
169 return
170 }
171
172
173 if spec.ClientName != assertion.clientName {
174 t.Errorf("getKubeConfigSpecs for %s clientName is %s, expected %s", assertion.kubeConfigFile, spec.ClientName, assertion.clientName)
175 }
176
177
178 if spec.ClientCertAuth == nil || !reflect.DeepEqual(spec.ClientCertAuth.Organizations, assertion.organizations) {
179 t.Errorf("getKubeConfigSpecs for %s Organizations is %v, expected %v", assertion.kubeConfigFile, spec.ClientCertAuth.Organizations, assertion.organizations)
180 }
181
182
183 controlPlaneEndpoint, err := kubeadmutil.GetControlPlaneEndpoint(cfg.ControlPlaneEndpoint, &cfg.LocalAPIEndpoint)
184 if err != nil {
185 t.Error(err)
186 }
187 localAPIEndpoint, err := kubeadmutil.GetLocalAPIEndpoint(&cfg.LocalAPIEndpoint)
188 if err != nil {
189 t.Error(err)
190 }
191
192 switch assertion.kubeConfigFile {
193 case kubeadmconstants.AdminKubeConfigFileName, kubeadmconstants.SuperAdminKubeConfigFileName, kubeadmconstants.KubeletKubeConfigFileName:
194 if spec.APIServer != controlPlaneEndpoint {
195 t.Errorf("expected getKubeConfigSpecs for %s to set cfg.APIServer to %s, got %s",
196 assertion.kubeConfigFile, controlPlaneEndpoint, spec.APIServer)
197 }
198 case kubeadmconstants.ControllerManagerKubeConfigFileName, kubeadmconstants.SchedulerKubeConfigFileName:
199 if spec.APIServer != localAPIEndpoint {
200 t.Errorf("expected getKubeConfigSpecs for %s to set cfg.APIServer to %s, got %s",
201 assertion.kubeConfigFile, localAPIEndpoint, spec.APIServer)
202 }
203 }
204
205
206 if spec.CACert == nil {
207 t.Errorf("getKubeConfigSpecs didn't loaded CACert into spec for %s!", assertion.kubeConfigFile)
208 }
209 if spec.ClientCertAuth == nil || spec.ClientCertAuth.CAKey == nil {
210 t.Errorf("getKubeConfigSpecs didn't loaded CAKey into spec for %s!", assertion.kubeConfigFile)
211 }
212 })
213 }
214 }
215 }
216
217 func TestBuildKubeConfigFromSpecWithClientAuth(t *testing.T) {
218
219 caCert, caKey := certstestutil.SetupCertificateAuthority(t)
220
221
222 config := setupdKubeConfigWithClientAuth(t, caCert, caKey, "https://1.2.3.4:1234", "myClientName", "test-cluster", "myOrg1", "myOrg2")
223
224
225 kubeconfigtestutil.AssertKubeConfigCurrentCluster(t, config, "https://1.2.3.4:1234", caCert)
226 kubeconfigtestutil.AssertKubeConfigCurrentAuthInfoWithClientCert(t, config, caCert, "myClientName", "myOrg1", "myOrg2")
227 }
228
229 func TestBuildKubeConfigFromSpecWithTokenAuth(t *testing.T) {
230
231 caCert, _ := certstestutil.SetupCertificateAuthority(t)
232
233
234 config := setupdKubeConfigWithTokenAuth(t, caCert, "https://1.2.3.4:1234", "myClientName", "123456", "test-cluster")
235
236
237 kubeconfigtestutil.AssertKubeConfigCurrentCluster(t, config, "https://1.2.3.4:1234", caCert)
238 kubeconfigtestutil.AssertKubeConfigCurrentAuthInfoWithToken(t, config, "myClientName", "123456")
239 }
240
241 func TestCreateKubeConfigFileIfNotExists(t *testing.T) {
242
243
244 caCert, caKey := certstestutil.SetupCertificateAuthority(t)
245 anotherCaCert, anotherCaKey := certstestutil.SetupCertificateAuthority(t)
246
247
248 config := setupdKubeConfigWithClientAuth(t, caCert, caKey, "https://1.2.3.4:1234", "test-cluster", "myOrg1", "myOrg2")
249 configWithAnotherClusterCa := setupdKubeConfigWithClientAuth(t, anotherCaCert, anotherCaKey, "https://1.2.3.4:1234", "test-cluster", "myOrg1", "myOrg2")
250 configWithAnotherClusterAddress := setupdKubeConfigWithClientAuth(t, caCert, caKey, "https://3.4.5.6:3456", "myOrg1", "test-cluster", "myOrg2")
251 invalidConfig := setupdKubeConfigWithClientAuth(t, caCert, caKey, "https://1.2.3.4:1234", "test-cluster", "myOrg1", "myOrg2")
252 invalidConfig.CurrentContext = "invalid context"
253
254 var tests = []struct {
255 name string
256 existingKubeConfig *clientcmdapi.Config
257 kubeConfig *clientcmdapi.Config
258 expectedError bool
259 }{
260 {
261 name: "KubeConfig doesn't exist",
262 kubeConfig: config,
263 },
264 {
265 name: "KubeConfig is invalid",
266 existingKubeConfig: invalidConfig,
267 kubeConfig: invalidConfig,
268 expectedError: true,
269 },
270 {
271 name: "KubeConfig refers to the same cluster",
272 existingKubeConfig: config,
273 kubeConfig: config,
274 },
275 {
276 name: "KubeConfig refers to the cluster with another CA",
277 existingKubeConfig: config,
278 kubeConfig: configWithAnotherClusterCa,
279 expectedError: true,
280 },
281 {
282 name: "KubeConfig referst to the cluster with another address",
283 existingKubeConfig: config,
284 kubeConfig: configWithAnotherClusterAddress,
285 },
286 }
287
288 for _, test := range tests {
289 t.Run(test.name, func(t *testing.T) {
290
291 tmpdir := testutil.SetupTempDir(t)
292 defer os.RemoveAll(tmpdir)
293
294
295 if test.existingKubeConfig != nil {
296 if err := createKubeConfigFileIfNotExists(tmpdir, "test.conf", test.existingKubeConfig); err != nil {
297 t.Errorf("createKubeConfigFileIfNotExists failed")
298 }
299 }
300
301
302 err := createKubeConfigFileIfNotExists(tmpdir, "test.conf", test.kubeConfig)
303 if test.expectedError && err == nil {
304 t.Errorf("createKubeConfigFileIfNotExists didn't failed when expected to fail")
305 }
306 if !test.expectedError && err != nil {
307 t.Errorf("createKubeConfigFileIfNotExists failed")
308 }
309
310
311 testutil.AssertFileExists(t, tmpdir, "test.conf")
312 })
313 }
314 }
315
316 func TestCreateKubeconfigFilesAndWrappers(t *testing.T) {
317 var tests = []struct {
318 name string
319 createKubeConfigFunction func(outDir string, cfg *kubeadmapi.InitConfiguration) error
320 expectedFiles []string
321 expectedError bool
322 }{
323 {
324 name: "createKubeConfigFiles",
325 createKubeConfigFunction: func(outDir string, cfg *kubeadmapi.InitConfiguration) error {
326 return createKubeConfigFiles(outDir, cfg, "unknown.conf")
327 },
328 expectedError: true,
329 },
330 {
331 name: "CreateJoinControlPlaneKubeConfigFiles",
332 createKubeConfigFunction: CreateJoinControlPlaneKubeConfigFiles,
333 expectedFiles: []string{
334 kubeadmconstants.AdminKubeConfigFileName,
335 kubeadmconstants.ControllerManagerKubeConfigFileName,
336 kubeadmconstants.SchedulerKubeConfigFileName,
337 },
338 },
339 }
340
341 for _, test := range tests {
342 t.Run(test.name, func(t *testing.T) {
343
344 tmpdir := testutil.SetupTempDir(t)
345 defer os.RemoveAll(tmpdir)
346
347
348 pkidir := testutil.SetupPkiDirWithCertificateAuthority(t, tmpdir)
349
350
351 cfg := &kubeadmapi.InitConfiguration{
352 LocalAPIEndpoint: kubeadmapi.APIEndpoint{AdvertiseAddress: "1.2.3.4", BindPort: 1234},
353 ClusterConfiguration: kubeadmapi.ClusterConfiguration{
354 CertificatesDir: pkidir,
355 },
356 }
357
358
359 err := test.createKubeConfigFunction(tmpdir, cfg)
360 if test.expectedError && err == nil {
361 t.Errorf("createKubeConfigFunction didn't failed when expected to fail")
362 return
363 }
364 if !test.expectedError && err != nil {
365 t.Errorf("createKubeConfigFunction failed")
366 return
367 }
368
369
370 testutil.AssertFileExists(t, tmpdir, test.expectedFiles...)
371 })
372 }
373 }
374
375 func TestWriteKubeConfigFailsIfCADoesntExists(t *testing.T) {
376
377 tmpdir := testutil.SetupTempDir(t)
378 defer os.RemoveAll(tmpdir)
379
380
381 cfg := &kubeadmapi.InitConfiguration{
382 ClusterConfiguration: kubeadmapi.ClusterConfiguration{
383 CertificatesDir: tmpdir,
384 },
385 }
386
387 var tests = []struct {
388 name string
389 writeKubeConfigFunction func(out io.Writer) error
390 }{
391 {
392 name: "WriteKubeConfigWithClientCert",
393 writeKubeConfigFunction: func(out io.Writer) error {
394 return WriteKubeConfigWithClientCert(out, cfg, "myUser", []string{"myOrg"}, nil)
395 },
396 },
397 {
398 name: "WriteKubeConfigWithToken",
399 writeKubeConfigFunction: func(out io.Writer) error {
400 return WriteKubeConfigWithToken(out, cfg, "myUser", "12345", nil)
401 },
402 },
403 }
404
405 for _, test := range tests {
406 t.Run(test.name, func(t *testing.T) {
407 buf := new(bytes.Buffer)
408
409
410 if err := test.writeKubeConfigFunction(buf); err == nil {
411 t.Error("writeKubeConfigFunction didnt failed when expected")
412 }
413 })
414 }
415 }
416
417 func TestWriteKubeConfig(t *testing.T) {
418
419 tmpdir := testutil.SetupTempDir(t)
420 defer os.RemoveAll(tmpdir)
421
422
423 pkidir := testutil.SetupPkiDirWithCertificateAuthority(t, tmpdir)
424
425
426 caCert, _, err := pkiutil.TryLoadCertAndKeyFromDisk(pkidir, kubeadmconstants.CACertAndKeyBaseName)
427 if err != nil {
428 t.Fatalf("couldn't retrieve ca cert: %v", err)
429 }
430
431
432 cfg := &kubeadmapi.InitConfiguration{
433 LocalAPIEndpoint: kubeadmapi.APIEndpoint{AdvertiseAddress: "1.2.3.4", BindPort: 1234},
434 ClusterConfiguration: kubeadmapi.ClusterConfiguration{
435 CertificatesDir: pkidir,
436 },
437 }
438
439 var tests = []struct {
440 name string
441 writeKubeConfigFunction func(out io.Writer) error
442 withClientCert bool
443 withToken bool
444 }{
445 {
446 name: "WriteKubeConfigWithClientCert",
447 writeKubeConfigFunction: func(out io.Writer) error {
448 return WriteKubeConfigWithClientCert(out, cfg, "myUser", []string{"myOrg"}, nil)
449 },
450 withClientCert: true,
451 },
452 {
453 name: "WriteKubeConfigWithToken",
454 writeKubeConfigFunction: func(out io.Writer) error {
455 return WriteKubeConfigWithToken(out, cfg, "myUser", "12345", nil)
456 },
457 withToken: true,
458 },
459 }
460
461 for _, test := range tests {
462 t.Run(test.name, func(t *testing.T) {
463 buf := new(bytes.Buffer)
464
465
466 if err := test.writeKubeConfigFunction(buf); err != nil {
467 t.Error("writeKubeConfigFunction failed")
468 return
469 }
470
471
472 config, err := clientcmd.Load(buf.Bytes())
473 if err != nil {
474 t.Errorf("Couldn't read kubeconfig file from buffer: %v", err)
475 return
476 }
477
478
479 kubeconfigtestutil.AssertKubeConfigCurrentCluster(t, config, "https://1.2.3.4:1234", caCert)
480
481 if test.withClientCert {
482
483 kubeconfigtestutil.AssertKubeConfigCurrentAuthInfoWithClientCert(t, config, caCert, "myUser", "myOrg")
484 }
485
486 if test.withToken {
487
488 kubeconfigtestutil.AssertKubeConfigCurrentAuthInfoWithToken(t, config, "myUser", "12345")
489 }
490 })
491 }
492 }
493
494 func TestValidateKubeConfig(t *testing.T) {
495 caCert, caKey := certstestutil.SetupCertificateAuthority(t)
496 anotherCaCert, anotherCaKey := certstestutil.SetupCertificateAuthority(t)
497
498 config := setupdKubeConfigWithClientAuth(t, caCert, caKey, "https://1.2.3.4:1234", "test-cluster", "myOrg1")
499 configWithAnotherClusterCa := setupdKubeConfigWithClientAuth(t, anotherCaCert, anotherCaKey, "https://1.2.3.4:1234", "test-cluster", "myOrg1")
500 configWithAnotherServerURL := setupdKubeConfigWithClientAuth(t, caCert, caKey, "https://4.3.2.1:4321", "test-cluster", "myOrg1")
501
502 configWithSameClusterCaByExternalFile := config.DeepCopy()
503 currentCtx, exists := configWithSameClusterCaByExternalFile.Contexts[configWithSameClusterCaByExternalFile.CurrentContext]
504 if !exists {
505 t.Fatal("failed to find CurrentContext in Contexts of the kubeconfig")
506 }
507 if configWithSameClusterCaByExternalFile.Clusters[currentCtx.Cluster] == nil {
508 t.Fatal("failed to find the given CurrentContext Cluster in Clusters of the kubeconfig")
509 }
510 tmpfile, err := os.CreateTemp("", "external-ca.crt")
511 if err != nil {
512 t.Fatal(err)
513 }
514 defer os.Remove(tmpfile.Name())
515 if _, err := tmpfile.Write(pkiutil.EncodeCertPEM(caCert)); err != nil {
516 t.Fatal(err)
517 }
518 configWithSameClusterCaByExternalFile.Clusters[currentCtx.Cluster].CertificateAuthorityData = nil
519 configWithSameClusterCaByExternalFile.Clusters[currentCtx.Cluster].CertificateAuthority = tmpfile.Name()
520
521
522
523 configWhitespace := config.DeepCopy()
524 configWhitespaceCtx := configWhitespace.Contexts[configWhitespace.CurrentContext]
525 configWhitespaceCA := string(configWhitespace.Clusters[configWhitespaceCtx.Cluster].CertificateAuthorityData)
526 configWhitespaceCA = "\n" + configWhitespaceCA + "\n"
527 configWhitespace.Clusters[configWhitespaceCtx.Cluster].CertificateAuthorityData = []byte(configWhitespaceCA)
528
529 tests := map[string]struct {
530 existingKubeConfig *clientcmdapi.Config
531 kubeConfig *clientcmdapi.Config
532 expectedError bool
533 }{
534 "kubeconfig don't exist": {
535 kubeConfig: config,
536 expectedError: true,
537 },
538 "kubeconfig exist and has invalid ca": {
539 existingKubeConfig: configWithAnotherClusterCa,
540 kubeConfig: config,
541 expectedError: true,
542 },
543 "kubeconfig exist and has a different server url": {
544 existingKubeConfig: configWithAnotherServerURL,
545 kubeConfig: config,
546 },
547 "kubeconfig exist and is valid": {
548 existingKubeConfig: config,
549 kubeConfig: config,
550 expectedError: false,
551 },
552 "kubeconfig exist and is valid even if its CA contains whitespace": {
553 existingKubeConfig: configWhitespace,
554 kubeConfig: config,
555 expectedError: false,
556 },
557 "kubeconfig exist and is valid even if its CA is provided as an external file": {
558 existingKubeConfig: configWithSameClusterCaByExternalFile,
559 kubeConfig: config,
560 expectedError: false,
561 },
562 }
563
564 for name, test := range tests {
565 t.Run(name, func(t *testing.T) {
566 tmpdir := testutil.SetupTempDir(t)
567 defer os.RemoveAll(tmpdir)
568
569 if test.existingKubeConfig != nil {
570 if err := createKubeConfigFileIfNotExists(tmpdir, "test.conf", test.existingKubeConfig); err != nil {
571 t.Errorf("createKubeConfigFileIfNotExists failed")
572 }
573 }
574
575 err := validateKubeConfig(tmpdir, "test.conf", test.kubeConfig)
576 if (err != nil) != test.expectedError {
577 t.Fatalf(dedent.Dedent(
578 "validateKubeConfig failed\n%s\nexpected error: %t\n\tgot: %t\nerror: %v"),
579 name,
580 test.expectedError,
581 (err != nil),
582 err,
583 )
584 }
585 })
586 }
587 }
588
589 func TestValidateKubeconfigsForExternalCA(t *testing.T) {
590 tmpDir := testutil.SetupTempDir(t)
591 defer os.RemoveAll(tmpDir)
592 pkiDir := filepath.Join(tmpDir, "pki")
593
594 initConfig := &kubeadmapi.InitConfiguration{
595 ClusterConfiguration: kubeadmapi.ClusterConfiguration{
596 CertificatesDir: pkiDir,
597 },
598 LocalAPIEndpoint: kubeadmapi.APIEndpoint{
599 BindPort: 1234,
600 AdvertiseAddress: "1.2.3.4",
601 },
602 }
603
604
605 caCert, caKey := certstestutil.SetupCertificateAuthority(t)
606 if err := pkiutil.WriteCertAndKey(pkiDir, kubeadmconstants.CACertAndKeyBaseName, caCert, caKey); err != nil {
607 t.Fatalf("failure while saving CA certificate and key: %v", err)
608 }
609 if err := os.Remove(filepath.Join(pkiDir, kubeadmconstants.CAKeyName)); err != nil {
610 t.Fatalf("failure while deleting ca.key: %v", err)
611 }
612
613
614 config := setupdKubeConfigWithClientAuth(t, caCert, caKey, "https://1.2.3.4:1234", "test-cluster", "myOrg1")
615
616
617 anotherCaCert, anotherCaKey := certstestutil.SetupCertificateAuthority(t)
618 configWithAnotherClusterCa := setupdKubeConfigWithClientAuth(t, anotherCaCert, anotherCaKey, "https://1.2.3.4:1234", "test-cluster", "myOrg1")
619
620
621 configWithAnotherServerURL := setupdKubeConfigWithClientAuth(t, caCert, caKey, "https://4.3.2.1:4321", "test-cluster", "myOrg1")
622
623 tests := map[string]struct {
624 filesToWrite map[string]*clientcmdapi.Config
625 initConfig *kubeadmapi.InitConfiguration
626 expectedError bool
627 }{
628 "files don't exist": {
629 initConfig: initConfig,
630 expectedError: true,
631 },
632 "some files don't exist": {
633 filesToWrite: map[string]*clientcmdapi.Config{
634 kubeadmconstants.AdminKubeConfigFileName: config,
635 kubeadmconstants.SuperAdminKubeConfigFileName: config,
636 kubeadmconstants.KubeletKubeConfigFileName: config,
637 },
638 initConfig: initConfig,
639 expectedError: true,
640 },
641 "some files have invalid CA": {
642 filesToWrite: map[string]*clientcmdapi.Config{
643 kubeadmconstants.AdminKubeConfigFileName: config,
644 kubeadmconstants.SuperAdminKubeConfigFileName: config,
645 kubeadmconstants.KubeletKubeConfigFileName: config,
646 kubeadmconstants.ControllerManagerKubeConfigFileName: configWithAnotherClusterCa,
647 kubeadmconstants.SchedulerKubeConfigFileName: config,
648 },
649 initConfig: initConfig,
650 expectedError: true,
651 },
652 "some files have a different Server URL": {
653 filesToWrite: map[string]*clientcmdapi.Config{
654 kubeadmconstants.AdminKubeConfigFileName: config,
655 kubeadmconstants.SuperAdminKubeConfigFileName: config,
656 kubeadmconstants.KubeletKubeConfigFileName: config,
657 kubeadmconstants.ControllerManagerKubeConfigFileName: config,
658 kubeadmconstants.SchedulerKubeConfigFileName: configWithAnotherServerURL,
659 },
660 initConfig: initConfig,
661 },
662 "all files are valid": {
663 filesToWrite: map[string]*clientcmdapi.Config{
664 kubeadmconstants.AdminKubeConfigFileName: config,
665 kubeadmconstants.SuperAdminKubeConfigFileName: config,
666 kubeadmconstants.KubeletKubeConfigFileName: config,
667 kubeadmconstants.ControllerManagerKubeConfigFileName: config,
668 kubeadmconstants.SchedulerKubeConfigFileName: config,
669 },
670 initConfig: initConfig,
671 expectedError: false,
672 },
673 }
674
675 for name, test := range tests {
676 t.Run(name, func(t *testing.T) {
677 tmpdir := testutil.SetupTempDir(t)
678 defer os.RemoveAll(tmpdir)
679
680 for name, config := range test.filesToWrite {
681 if err := createKubeConfigFileIfNotExists(tmpdir, name, config); err != nil {
682 t.Errorf("createKubeConfigFileIfNotExists failed: %v", err)
683 }
684 }
685
686 err := ValidateKubeconfigsForExternalCA(tmpdir, test.initConfig)
687 if (err != nil) != test.expectedError {
688 t.Fatalf(dedent.Dedent(
689 "ValidateKubeconfigsForExternalCA failed\n%s\nexpected error: %t\n\tgot: %t\nerror: %v"),
690 name,
691 test.expectedError,
692 (err != nil),
693 err,
694 )
695 }
696 })
697 }
698 }
699
700
701 func setupdKubeConfigWithClientAuth(t *testing.T, caCert *x509.Certificate, caKey crypto.Signer, APIServer, clientName, clustername string, organizations ...string) *clientcmdapi.Config {
702 spec := &kubeConfigSpec{
703 CACert: caCert,
704 APIServer: APIServer,
705 ClientName: clientName,
706 ClientCertAuth: &clientCertAuth{
707 CAKey: caKey,
708 Organizations: organizations,
709 },
710 }
711
712 config, err := buildKubeConfigFromSpec(spec, clustername, nil)
713 if err != nil {
714 t.Fatal("buildKubeConfigFromSpec failed!")
715 }
716
717 return config
718 }
719
720
721 func setupdKubeConfigWithTokenAuth(t *testing.T, caCert *x509.Certificate, APIServer, clientName, token, clustername string) *clientcmdapi.Config {
722 spec := &kubeConfigSpec{
723 CACert: caCert,
724 APIServer: APIServer,
725 ClientName: clientName,
726 TokenAuth: &tokenAuth{
727 Token: token,
728 },
729 }
730
731 config, err := buildKubeConfigFromSpec(spec, clustername, nil)
732 if err != nil {
733 t.Fatal("buildKubeConfigFromSpec failed!")
734 }
735
736 return config
737 }
738
739 func TestEnsureAdminClusterRoleBinding(t *testing.T) {
740 dir := testutil.SetupTempDir(t)
741 defer os.RemoveAll(dir)
742
743 cfg := testutil.GetDefaultInternalConfig(t)
744 cfg.CertificatesDir = dir
745
746 ca := certsphase.KubeadmCertRootCA()
747 _, _, err := ca.CreateAsCA(cfg)
748 if err != nil {
749 t.Fatal(err)
750 }
751
752 tests := []struct {
753 name string
754 expectedRBACError bool
755 expectedError bool
756 missingAdminConf bool
757 missingSuperAdminConf bool
758 }{
759 {
760 name: "no errors",
761 },
762 {
763 name: "expect RBAC error",
764 expectedRBACError: true,
765 expectedError: true,
766 },
767 {
768 name: "admin.conf is missing",
769 missingAdminConf: true,
770 expectedError: true,
771 },
772 {
773 name: "super-admin.conf is missing",
774 missingSuperAdminConf: true,
775 expectedError: false,
776 },
777 }
778
779 for _, tc := range tests {
780 t.Run(tc.name, func(t *testing.T) {
781 ensureRBACFunc := func(_ context.Context, adminClient clientset.Interface, superAdminClient clientset.Interface,
782 _ time.Duration, _ time.Duration) (clientset.Interface, error) {
783
784 if tc.expectedRBACError {
785 return nil, errors.New("ensureRBACFunc error")
786 }
787 return adminClient, nil
788 }
789
790
791
792 os.Remove(filepath.Join(dir, kubeadmconstants.AdminKubeConfigFileName))
793 if !tc.missingAdminConf {
794 if err := CreateKubeConfigFile(kubeadmconstants.AdminKubeConfigFileName, dir, cfg); err != nil {
795 t.Fatal(err)
796 }
797 }
798 os.Remove(filepath.Join(dir, kubeadmconstants.SuperAdminKubeConfigFileName))
799 if !tc.missingSuperAdminConf {
800 if err := CreateKubeConfigFile(kubeadmconstants.SuperAdminKubeConfigFileName, dir, cfg); err != nil {
801 t.Fatal(err)
802 }
803 }
804
805 client, err := EnsureAdminClusterRoleBinding(dir, ensureRBACFunc)
806 if (err != nil) != tc.expectedError {
807 t.Fatalf("expected error: %v, got: %v, error: %v", err != nil, tc.expectedError, err)
808 }
809
810 if err == nil && client == nil {
811 t.Fatal("got nil client")
812 }
813 })
814 }
815 }
816
817 func TestEnsureAdminClusterRoleBindingImpl(t *testing.T) {
818 tests := []struct {
819 name string
820 setupAdminClient func(*clientsetfake.Clientset)
821 setupSuperAdminClient func(*clientsetfake.Clientset)
822 expectedError bool
823 }{
824 {
825 name: "admin.conf: handle forbidden errors when the super-admin.conf client is nil",
826 setupAdminClient: func(client *clientsetfake.Clientset) {
827 client.PrependReactor("create", "clusterrolebindings", func(action clientgotesting.Action) (bool, runtime.Object, error) {
828 return true, nil, apierrors.NewForbidden(
829 schema.GroupResource{}, "name", errors.New(""))
830 })
831 },
832 expectedError: true,
833 },
834 {
835
836 name: "admin.conf: handle forbidden error and returned CRBs, when the super-admin.conf client is nil",
837 setupAdminClient: func(client *clientsetfake.Clientset) {
838 client.PrependReactor("create", "clusterrolebindings", func(action clientgotesting.Action) (bool, runtime.Object, error) {
839 return true, &rbac.ClusterRoleBinding{}, apierrors.NewForbidden(
840 schema.GroupResource{}, "name", errors.New(""))
841 })
842 },
843 expectedError: true,
844 },
845 {
846 name: "admin.conf: CRB already exists, use the admin.conf client",
847 setupAdminClient: func(client *clientsetfake.Clientset) {
848 client.PrependReactor("create", "clusterrolebindings", func(action clientgotesting.Action) (bool, runtime.Object, error) {
849 return true, nil, apierrors.NewAlreadyExists(
850 schema.GroupResource{}, "name")
851 })
852 },
853 setupSuperAdminClient: func(client *clientsetfake.Clientset) {
854 client.PrependReactor("create", "clusterrolebindings", func(action clientgotesting.Action) (bool, runtime.Object, error) {
855 return true, nil, apierrors.NewAlreadyExists(
856 schema.GroupResource{}, "name")
857 })
858 },
859 expectedError: false,
860 },
861 {
862 name: "admin.conf: handle other errors, such as a server timeout",
863 setupAdminClient: func(client *clientsetfake.Clientset) {
864 client.PrependReactor("create", "clusterrolebindings", func(action clientgotesting.Action) (bool, runtime.Object, error) {
865 return true, nil, apierrors.NewServerTimeout(
866 schema.GroupResource{}, "create", 0)
867 })
868 },
869 expectedError: true,
870 },
871 {
872 name: "admin.conf: CRB exists, return a client from admin.conf",
873 setupAdminClient: func(client *clientsetfake.Clientset) {
874 client.PrependReactor("create", "clusterrolebindings", func(action clientgotesting.Action) (bool, runtime.Object, error) {
875 return true, &rbac.ClusterRoleBinding{}, nil
876 })
877 },
878 expectedError: false,
879 },
880 {
881 name: "super-admin.conf: error while creating CRB",
882 setupAdminClient: func(client *clientsetfake.Clientset) {
883 client.PrependReactor("create", "clusterrolebindings", func(action clientgotesting.Action) (bool, runtime.Object, error) {
884 return true, nil, apierrors.NewForbidden(
885 schema.GroupResource{}, "name", errors.New(""))
886 })
887 },
888 setupSuperAdminClient: func(client *clientsetfake.Clientset) {
889 client.PrependReactor("create", "clusterrolebindings", func(action clientgotesting.Action) (bool, runtime.Object, error) {
890 return true, nil, apierrors.NewServerTimeout(
891 schema.GroupResource{}, "create", 0)
892 })
893 },
894 expectedError: true,
895 },
896 {
897 name: "super-admin.conf: admin.conf cannot create CRB, create CRB with super-admin.conf, return client from admin.conf",
898 setupAdminClient: func(client *clientsetfake.Clientset) {
899 client.PrependReactor("create", "clusterrolebindings", func(action clientgotesting.Action) (bool, runtime.Object, error) {
900 return true, nil, apierrors.NewForbidden(
901 schema.GroupResource{}, "name", errors.New(""))
902 })
903 },
904 setupSuperAdminClient: func(client *clientsetfake.Clientset) {
905 client.PrependReactor("create", "clusterrolebindings", func(action clientgotesting.Action) (bool, runtime.Object, error) {
906 return true, &rbac.ClusterRoleBinding{}, nil
907 })
908 },
909 expectedError: false,
910 },
911 {
912 name: "super-admin.conf: admin.conf cannot create CRB, try to create CRB with super-admin.conf, encounter 'already exists' error",
913 setupAdminClient: func(client *clientsetfake.Clientset) {
914 client.PrependReactor("create", "clusterrolebindings", func(action clientgotesting.Action) (bool, runtime.Object, error) {
915 return true, nil, apierrors.NewForbidden(
916 schema.GroupResource{}, "name", errors.New(""))
917 })
918 },
919 setupSuperAdminClient: func(client *clientsetfake.Clientset) {
920 client.PrependReactor("create", "clusterrolebindings", func(action clientgotesting.Action) (bool, runtime.Object, error) {
921 return true, nil, apierrors.NewAlreadyExists(
922 schema.GroupResource{}, "name")
923 })
924 },
925 expectedError: false,
926 },
927 }
928
929 for _, tc := range tests {
930 t.Run(tc.name, func(t *testing.T) {
931 adminClient := clientsetfake.NewSimpleClientset()
932 tc.setupAdminClient(adminClient)
933
934 var superAdminClient clientset.Interface
935 if tc.setupSuperAdminClient != nil {
936 fakeSuperAdminClient := clientsetfake.NewSimpleClientset()
937 tc.setupSuperAdminClient(fakeSuperAdminClient)
938 superAdminClient = fakeSuperAdminClient
939 }
940
941 client, err := EnsureAdminClusterRoleBindingImpl(
942 context.Background(), adminClient, superAdminClient, 0, 0)
943 if (err != nil) != tc.expectedError {
944 t.Fatalf("expected error: %v, got %v, error: %v", tc.expectedError, err != nil, err)
945 }
946
947 if err == nil && client == nil {
948 t.Fatal("got nil client")
949 }
950 })
951 }
952 }
953
954 func TestCreateKubeConfigAndCSR(t *testing.T) {
955 tmpDir := testutil.SetupTempDir(t)
956 testutil.SetupEmptyFiles(t, tmpDir, "testfile", "bar.csr", "bar.key")
957 defer func() {
958 if err := os.RemoveAll(tmpDir); err != nil {
959 t.Error(err)
960 }
961 }()
962 caCert, caKey := certstestutil.SetupCertificateAuthority(t)
963
964 type args struct {
965 kubeConfigDir string
966 kubeadmConfig *kubeadmapi.InitConfiguration
967 name string
968 spec *kubeConfigSpec
969 }
970 tests := []struct {
971 name string
972 args args
973 expectedError bool
974 }{
975 {
976 name: "kubeadmConfig is nil",
977 args: args{
978 kubeConfigDir: tmpDir,
979 kubeadmConfig: nil,
980 name: "foo",
981 spec: &kubeConfigSpec{
982 CACert: caCert,
983 APIServer: "10.0.0.1",
984 ClientName: "foo",
985 TokenAuth: &tokenAuth{Token: "test"},
986 ClientCertAuth: &clientCertAuth{CAKey: caKey},
987 },
988 },
989 expectedError: true,
990 },
991 {
992 name: "The kubeConfigDir is empty",
993 args: args{
994 kubeConfigDir: "",
995 kubeadmConfig: &kubeadmapi.InitConfiguration{},
996 name: "foo",
997 spec: &kubeConfigSpec{
998 CACert: caCert,
999 APIServer: "10.0.0.1",
1000 ClientName: "foo",
1001 TokenAuth: &tokenAuth{Token: "test"},
1002 ClientCertAuth: &clientCertAuth{CAKey: caKey},
1003 },
1004 },
1005 expectedError: true,
1006 },
1007 {
1008 name: "The name is empty",
1009 args: args{
1010 kubeConfigDir: tmpDir,
1011 kubeadmConfig: &kubeadmapi.InitConfiguration{},
1012 name: "",
1013 spec: &kubeConfigSpec{
1014 CACert: caCert,
1015 APIServer: "10.0.0.1",
1016 ClientName: "foo",
1017 TokenAuth: &tokenAuth{Token: "test"},
1018 ClientCertAuth: &clientCertAuth{CAKey: caKey},
1019 },
1020 },
1021 expectedError: true,
1022 },
1023 {
1024 name: "The spec is empty",
1025 args: args{
1026 kubeConfigDir: tmpDir,
1027 kubeadmConfig: &kubeadmapi.InitConfiguration{},
1028 name: "foo",
1029 spec: nil,
1030 },
1031 expectedError: true,
1032 },
1033 {
1034 name: "The kubeconfig file already exists",
1035 args: args{
1036 kubeConfigDir: tmpDir,
1037 kubeadmConfig: &kubeadmapi.InitConfiguration{},
1038 name: "testfile",
1039 spec: &kubeConfigSpec{
1040 CACert: caCert,
1041 APIServer: "10.0.0.1",
1042 ClientName: "foo",
1043 TokenAuth: &tokenAuth{Token: "test"},
1044 ClientCertAuth: &clientCertAuth{CAKey: caKey},
1045 },
1046 },
1047 expectedError: true,
1048 },
1049 {
1050 name: "The CSR or key files already exists",
1051 args: args{
1052 kubeConfigDir: tmpDir,
1053 kubeadmConfig: &kubeadmapi.InitConfiguration{},
1054 name: "bar",
1055 spec: &kubeConfigSpec{
1056 CACert: caCert,
1057 APIServer: "10.0.0.1",
1058 ClientName: "foo",
1059 TokenAuth: &tokenAuth{Token: "test"},
1060 ClientCertAuth: &clientCertAuth{CAKey: caKey},
1061 },
1062 },
1063 expectedError: true,
1064 },
1065 {
1066 name: "configuration is valid, expect no errors",
1067 args: args{
1068 kubeConfigDir: tmpDir,
1069 kubeadmConfig: &kubeadmapi.InitConfiguration{},
1070 name: "test",
1071 spec: &kubeConfigSpec{
1072 CACert: caCert,
1073 APIServer: "10.0.0.1",
1074 ClientName: "foo",
1075 TokenAuth: &tokenAuth{Token: "test"},
1076 ClientCertAuth: &clientCertAuth{CAKey: caKey},
1077 },
1078 },
1079 expectedError: false,
1080 },
1081 }
1082 for _, tc := range tests {
1083 t.Run(tc.name, func(t *testing.T) {
1084 if err := createKubeConfigAndCSR(tc.args.kubeConfigDir, tc.args.kubeadmConfig, tc.args.name, tc.args.spec); (err != nil) != tc.expectedError {
1085 t.Errorf("createKubeConfigAndCSR() error = %v, wantErr %v", err, tc.expectedError)
1086 }
1087 })
1088 }
1089 }
1090
1091 func TestCreateDefaultKubeConfigsAndCSRFiles(t *testing.T) {
1092 tmpDir := testutil.SetupTempDir(t)
1093 defer func() {
1094 if err := os.RemoveAll(tmpDir); err != nil {
1095 t.Error(err)
1096 }
1097 }()
1098 type args struct {
1099 kubeConfigDir string
1100 kubeadmConfig *kubeadmapi.InitConfiguration
1101 }
1102 tests := []struct {
1103 name string
1104 args args
1105 wantErr bool
1106 }{
1107 {
1108 name: "kubeadmConfig is empty",
1109 args: args{
1110 kubeConfigDir: tmpDir,
1111 kubeadmConfig: &kubeadmapi.InitConfiguration{},
1112 },
1113 wantErr: true,
1114 },
1115 {
1116 name: "The APIEndpoint is invalid",
1117 args: args{
1118 kubeConfigDir: tmpDir,
1119 kubeadmConfig: &kubeadmapi.InitConfiguration{
1120 LocalAPIEndpoint: kubeadmapi.APIEndpoint{
1121 AdvertiseAddress: "x.12.FOo.1",
1122 BindPort: 6443,
1123 },
1124 },
1125 },
1126 wantErr: true,
1127 },
1128 {
1129 name: "The APIEndpoint is valid",
1130 args: args{
1131 kubeConfigDir: tmpDir,
1132 kubeadmConfig: &kubeadmapi.InitConfiguration{
1133 LocalAPIEndpoint: kubeadmapi.APIEndpoint{
1134 AdvertiseAddress: "127.0.0.1",
1135 BindPort: 6443,
1136 },
1137 },
1138 },
1139 wantErr: false,
1140 },
1141 }
1142 for _, tc := range tests {
1143 t.Run(tc.name, func(t *testing.T) {
1144 out := &bytes.Buffer{}
1145 if err := CreateDefaultKubeConfigsAndCSRFiles(out, tc.args.kubeConfigDir, tc.args.kubeadmConfig); (err != nil) != tc.wantErr {
1146 t.Errorf("CreateDefaultKubeConfigsAndCSRFiles() error = %v, wantErr %v", err, tc.wantErr)
1147 return
1148 }
1149 })
1150 }
1151 }
1152
View as plain text