1
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 }
530
View as plain text