k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/cmd/kubeadm/app/util/kubeconfig/kubeconfig_test.go (about) 1 /* 2 Copyright 2017 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package kubeconfig 18 19 import ( 20 "bytes" 21 "fmt" 22 "os" 23 "reflect" 24 "testing" 25 26 clientcmdapi "k8s.io/client-go/tools/clientcmd/api" 27 ) 28 29 const ( 30 configOut1 = `apiVersion: v1 31 clusters: 32 - cluster: 33 server: "" 34 name: k8s 35 contexts: 36 - context: 37 cluster: k8s 38 user: user1 39 name: user1@k8s 40 current-context: user1@k8s 41 kind: Config 42 preferences: {} 43 users: 44 - name: user1 45 user: 46 token: abc 47 ` 48 configOut2 = `apiVersion: v1 49 clusters: 50 - cluster: 51 server: localhost:8080 52 name: kubernetes 53 contexts: 54 - context: 55 cluster: kubernetes 56 user: user2 57 name: user2@kubernetes 58 current-context: user2@kubernetes 59 kind: Config 60 preferences: {} 61 users: 62 - name: user2 63 user: 64 token: cba 65 ` 66 ) 67 68 type configClient struct { 69 clusterName string 70 userName string 71 serverURL string 72 caCert []byte 73 } 74 75 type configClientWithCerts struct { 76 clientKey []byte 77 clientCert []byte 78 } 79 80 type configClientWithToken struct { 81 token string 82 } 83 84 func TestCreateWithCerts(t *testing.T) { 85 var createBasicTest = []struct { 86 name string 87 cc configClient 88 ccWithCerts configClientWithCerts 89 expected string 90 }{ 91 {"empty config", configClient{}, configClientWithCerts{}, ""}, 92 {"clusterName kubernetes", configClient{clusterName: "kubernetes"}, configClientWithCerts{}, ""}, 93 } 94 for _, rt := range createBasicTest { 95 t.Run(rt.name, func(t *testing.T) { 96 cwc := CreateWithCerts( 97 rt.cc.serverURL, 98 rt.cc.clusterName, 99 rt.cc.userName, 100 rt.cc.caCert, 101 rt.ccWithCerts.clientKey, 102 rt.ccWithCerts.clientCert, 103 ) 104 if cwc.Kind != rt.expected { 105 t.Errorf( 106 "failed CreateWithCerts:\n\texpected: %s\n\t actual: %s", 107 rt.expected, 108 cwc.Kind, 109 ) 110 } 111 }) 112 } 113 } 114 115 func TestCreateWithToken(t *testing.T) { 116 var createBasicTest = []struct { 117 name string 118 cc configClient 119 ccWithToken configClientWithToken 120 expected string 121 }{ 122 {"empty config", configClient{}, configClientWithToken{}, ""}, 123 {"clusterName kubernetes", configClient{clusterName: "kubernetes"}, configClientWithToken{}, ""}, 124 } 125 for _, rt := range createBasicTest { 126 t.Run(rt.name, func(t *testing.T) { 127 cwc := CreateWithToken( 128 rt.cc.serverURL, 129 rt.cc.clusterName, 130 rt.cc.userName, 131 rt.cc.caCert, 132 rt.ccWithToken.token, 133 ) 134 if cwc.Kind != rt.expected { 135 t.Errorf( 136 "failed CreateWithToken:\n\texpected: %s\n\t actual: %s", 137 rt.expected, 138 cwc.Kind, 139 ) 140 } 141 }) 142 } 143 } 144 145 func TestWriteKubeconfigToDisk(t *testing.T) { 146 tmpdir, err := os.MkdirTemp("", "") 147 if err != nil { 148 t.Fatalf("Couldn't create tmpdir") 149 } 150 defer os.RemoveAll(tmpdir) 151 152 var writeConfig = []struct { 153 name string 154 cc configClient 155 ccWithToken configClientWithToken 156 expected error 157 file []byte 158 }{ 159 {"test1", configClient{clusterName: "k8s", userName: "user1"}, configClientWithToken{token: "abc"}, nil, []byte(configOut1)}, 160 {"test2", configClient{clusterName: "kubernetes", userName: "user2", serverURL: "localhost:8080"}, configClientWithToken{token: "cba"}, nil, []byte(configOut2)}, 161 } 162 for _, rt := range writeConfig { 163 t.Run(rt.name, func(t *testing.T) { 164 c := CreateWithToken( 165 rt.cc.serverURL, 166 rt.cc.clusterName, 167 rt.cc.userName, 168 rt.cc.caCert, 169 rt.ccWithToken.token, 170 ) 171 configPath := fmt.Sprintf("%s/etc/kubernetes/%s.conf", tmpdir, rt.name) 172 err := WriteToDisk(configPath, c) 173 if err != rt.expected { 174 t.Errorf( 175 "failed WriteToDisk with an error:\n\texpected: %s\n\t actual: %s", 176 rt.expected, 177 err, 178 ) 179 } 180 newFile, _ := os.ReadFile(configPath) 181 if !bytes.Equal(newFile, rt.file) { 182 t.Errorf( 183 "failed WriteToDisk config write:\n\texpected: %s\n\t actual: %s", 184 rt.file, 185 newFile, 186 ) 187 } 188 }) 189 } 190 } 191 192 func TestGetCurrentAuthInfo(t *testing.T) { 193 var testCases = []struct { 194 name string 195 config *clientcmdapi.Config 196 expected bool 197 }{ 198 { 199 name: "nil context", 200 config: nil, 201 expected: false, 202 }, 203 { 204 name: "no CurrentContext value", 205 config: &clientcmdapi.Config{}, 206 expected: false, 207 }, 208 { 209 name: "no CurrentContext object", 210 config: &clientcmdapi.Config{CurrentContext: "kubernetes"}, 211 expected: false, 212 }, 213 { 214 name: "CurrentContext object with bad contents", 215 config: &clientcmdapi.Config{ 216 CurrentContext: "kubernetes", 217 Contexts: map[string]*clientcmdapi.Context{"NOTkubernetes": {}}, 218 }, 219 expected: false, 220 }, 221 { 222 name: "no AuthInfo value", 223 config: &clientcmdapi.Config{ 224 CurrentContext: "kubernetes", 225 Contexts: map[string]*clientcmdapi.Context{"kubernetes": {}}, 226 }, 227 expected: false, 228 }, 229 { 230 name: "no AuthInfo object", 231 config: &clientcmdapi.Config{ 232 CurrentContext: "kubernetes", 233 Contexts: map[string]*clientcmdapi.Context{"kubernetes": {AuthInfo: "kubernetes"}}, 234 }, 235 expected: false, 236 }, 237 { 238 name: "AuthInfo object with bad contents", 239 config: &clientcmdapi.Config{ 240 CurrentContext: "kubernetes", 241 Contexts: map[string]*clientcmdapi.Context{"kubernetes": {AuthInfo: "kubernetes"}}, 242 AuthInfos: map[string]*clientcmdapi.AuthInfo{"NOTkubernetes": {}}, 243 }, 244 expected: false, 245 }, 246 { 247 name: "valid AuthInfo", 248 config: &clientcmdapi.Config{ 249 CurrentContext: "kubernetes", 250 Contexts: map[string]*clientcmdapi.Context{"kubernetes": {AuthInfo: "kubernetes"}}, 251 AuthInfos: map[string]*clientcmdapi.AuthInfo{"kubernetes": {}}, 252 }, 253 expected: true, 254 }, 255 } 256 for _, rt := range testCases { 257 t.Run(rt.name, func(t *testing.T) { 258 r := getCurrentAuthInfo(rt.config) 259 if rt.expected != (r != nil) { 260 t.Errorf( 261 "failed TestHasCredentials:\n\texpected: %v\n\t actual: %v", 262 rt.expected, 263 r, 264 ) 265 } 266 }) 267 } 268 } 269 270 func TestHasCredentials(t *testing.T) { 271 var testCases = []struct { 272 name string 273 config *clientcmdapi.Config 274 expected bool 275 }{ 276 { 277 name: "no authInfo", 278 config: nil, 279 expected: false, 280 }, 281 { 282 name: "no credentials", 283 config: &clientcmdapi.Config{ 284 CurrentContext: "kubernetes", 285 Contexts: map[string]*clientcmdapi.Context{"kubernetes": {AuthInfo: "kubernetes"}}, 286 AuthInfos: map[string]*clientcmdapi.AuthInfo{"kubernetes": {}}, 287 }, 288 expected: false, 289 }, 290 { 291 name: "token authentication credentials", 292 config: &clientcmdapi.Config{ 293 CurrentContext: "kubernetes", 294 Contexts: map[string]*clientcmdapi.Context{"kubernetes": {AuthInfo: "kubernetes"}}, 295 AuthInfos: map[string]*clientcmdapi.AuthInfo{"kubernetes": {Token: "123"}}, 296 }, 297 expected: true, 298 }, 299 { 300 name: "basic authentication credentials", 301 config: &clientcmdapi.Config{ 302 CurrentContext: "kubernetes", 303 Contexts: map[string]*clientcmdapi.Context{"kubernetes": {AuthInfo: "kubernetes"}}, 304 AuthInfos: map[string]*clientcmdapi.AuthInfo{"kubernetes": {Username: "A", Password: "B"}}, 305 }, 306 expected: true, 307 }, 308 { 309 name: "X509 authentication credentials", 310 config: &clientcmdapi.Config{ 311 CurrentContext: "kubernetes", 312 Contexts: map[string]*clientcmdapi.Context{"kubernetes": {AuthInfo: "kubernetes"}}, 313 AuthInfos: map[string]*clientcmdapi.AuthInfo{"kubernetes": {ClientKey: "A", ClientCertificate: "B"}}, 314 }, 315 expected: true, 316 }, 317 { 318 name: "exec authentication credentials", 319 config: &clientcmdapi.Config{ 320 CurrentContext: "kubernetes", 321 Contexts: map[string]*clientcmdapi.Context{"kubernetes": {AuthInfo: "kubernetes"}}, 322 AuthInfos: map[string]*clientcmdapi.AuthInfo{"kubernetes": {Exec: &clientcmdapi.ExecConfig{Command: "command"}}}, 323 }, 324 expected: true, 325 }, 326 { 327 name: "authprovider authentication credentials", 328 config: &clientcmdapi.Config{ 329 CurrentContext: "kubernetes", 330 Contexts: map[string]*clientcmdapi.Context{"kubernetes": {AuthInfo: "kubernetes"}}, 331 AuthInfos: map[string]*clientcmdapi.AuthInfo{"kubernetes": {AuthProvider: &clientcmdapi.AuthProviderConfig{Name: "A"}}}, 332 }, 333 expected: true, 334 }, 335 } 336 for _, rt := range testCases { 337 t.Run(rt.name, func(t *testing.T) { 338 r := HasAuthenticationCredentials(rt.config) 339 if rt.expected != r { 340 t.Errorf( 341 "failed TestHasCredentials:\n\texpected: %v\n\t actual: %v", 342 rt.expected, 343 r, 344 ) 345 } 346 }) 347 } 348 } 349 350 func TestGetClusterFromKubeConfig(t *testing.T) { 351 tests := []struct { 352 name string 353 config *clientcmdapi.Config 354 expectedClusterName string 355 expectedCluster *clientcmdapi.Cluster 356 }{ 357 { 358 name: "cluster is empty", 359 config: &clientcmdapi.Config{ 360 CurrentContext: "kubernetes", 361 }, 362 expectedClusterName: "", 363 expectedCluster: nil, 364 }, 365 { 366 name: "cluster and currentContext are not empty", 367 config: &clientcmdapi.Config{ 368 CurrentContext: "foo", 369 Contexts: map[string]*clientcmdapi.Context{ 370 "foo": {AuthInfo: "foo", Cluster: "foo"}, 371 "bar": {AuthInfo: "bar", Cluster: "bar"}, 372 }, 373 Clusters: map[string]*clientcmdapi.Cluster{ 374 "foo": {Server: "http://foo:8080"}, 375 "bar": {Server: "https://bar:16443"}, 376 }, 377 }, 378 expectedClusterName: "foo", 379 expectedCluster: &clientcmdapi.Cluster{ 380 Server: "http://foo:8080", 381 }, 382 }, 383 { 384 name: "cluster is not empty and currentContext is not in contexts", 385 config: &clientcmdapi.Config{ 386 CurrentContext: "foo", 387 Contexts: map[string]*clientcmdapi.Context{ 388 "bar": {AuthInfo: "bar", Cluster: "bar"}, 389 }, 390 Clusters: map[string]*clientcmdapi.Cluster{ 391 "foo": {Server: "http://foo:8080"}, 392 "bar": {Server: "https://bar:16443"}, 393 }, 394 }, 395 expectedClusterName: "", 396 expectedCluster: nil, 397 }, 398 } 399 for _, rt := range tests { 400 t.Run(rt.name, func(t *testing.T) { 401 clusterName, cluster := GetClusterFromKubeConfig(rt.config) 402 if clusterName != rt.expectedClusterName { 403 t.Errorf("got cluster name = %s, expected %s", clusterName, rt.expectedClusterName) 404 } 405 if !reflect.DeepEqual(cluster, rt.expectedCluster) { 406 t.Errorf("got cluster = %+v, expected %+v", cluster, rt.expectedCluster) 407 } 408 }) 409 } 410 } 411 412 func TestEnsureAuthenticationInfoAreEmbedded(t *testing.T) { 413 file, err := os.CreateTemp("", t.Name()) 414 if err != nil { 415 t.Fatal(err) 416 } 417 defer os.Remove(file.Name()) 418 defer file.Close() 419 420 tests := []struct { 421 name string 422 config *clientcmdapi.Config 423 wantErr bool 424 }{ 425 { 426 name: "get data from file", 427 config: &clientcmdapi.Config{ 428 CurrentContext: "kubernetes", 429 Contexts: map[string]*clientcmdapi.Context{"kubernetes": {AuthInfo: "kubernetes"}}, 430 AuthInfos: map[string]*clientcmdapi.AuthInfo{"kubernetes": { 431 ClientCertificate: file.Name(), 432 ClientKey: file.Name(), 433 TokenFile: file.Name(), 434 }, 435 }, 436 }, 437 wantErr: false, 438 }, 439 { 440 name: "get data from config", 441 config: &clientcmdapi.Config{ 442 CurrentContext: "kubernetes", 443 Contexts: map[string]*clientcmdapi.Context{"kubernetes": {AuthInfo: "kubernetes"}}, 444 AuthInfos: map[string]*clientcmdapi.AuthInfo{"kubernetes": { 445 ClientCertificateData: []byte{'f', 'o', 'o'}, 446 ClientKeyData: []byte{'b', 'a', 'r'}, 447 Token: "k8s", 448 }, 449 }, 450 }, 451 wantErr: false, 452 }, 453 { 454 name: "invalid authInfo: no authInfo", 455 config: nil, 456 wantErr: true, 457 }, 458 { 459 name: "get data from file but the file doesn't exist", 460 config: &clientcmdapi.Config{ 461 CurrentContext: "kubernetes", 462 Contexts: map[string]*clientcmdapi.Context{"kubernetes": {AuthInfo: "kubernetes"}}, 463 AuthInfos: map[string]*clientcmdapi.AuthInfo{"kubernetes": { 464 ClientCertificate: "unknownfile", 465 ClientKey: "unknownfile", 466 TokenFile: "unknownfile", 467 }, 468 }, 469 }, 470 wantErr: true, 471 }, 472 } 473 for _, rt := range tests { 474 t.Run(rt.name, func(t *testing.T) { 475 if err := EnsureAuthenticationInfoAreEmbedded(rt.config); (err != nil) != rt.wantErr { 476 t.Errorf("error = %v, wantErr %v", err, rt.wantErr) 477 } 478 }) 479 } 480 } 481 482 func TestEnsureCertificateAuthorityIsEmbedded(t *testing.T) { 483 file, err := os.CreateTemp("", t.Name()) 484 if err != nil { 485 t.Fatal(err) 486 } 487 defer os.Remove(file.Name()) 488 defer file.Close() 489 490 tests := []struct { 491 name string 492 cluster *clientcmdapi.Cluster 493 wantErr bool 494 }{ 495 { 496 name: "get data from file", 497 cluster: &clientcmdapi.Cluster{ 498 CertificateAuthority: file.Name(), 499 }, 500 wantErr: false, 501 }, 502 { 503 name: "get data from config", 504 cluster: &clientcmdapi.Cluster{ 505 CertificateAuthorityData: []byte{'f', 'o', 'o'}, 506 }, 507 wantErr: false, 508 }, 509 { 510 name: "cluster is nil", 511 cluster: nil, 512 wantErr: true, 513 }, 514 { 515 name: "get data from file but the file doesn't exist", 516 cluster: &clientcmdapi.Cluster{ 517 CertificateAuthority: "unknownfile", 518 }, 519 wantErr: true, 520 }, 521 } 522 for _, rt := range tests { 523 t.Run(rt.name, func(t *testing.T) { 524 if err := EnsureCertificateAuthorityIsEmbedded(rt.cluster); (err != nil) != rt.wantErr { 525 t.Errorf("error = %v, wantErr %v", err, rt.wantErr) 526 } 527 }) 528 } 529 }