1
16
17 package plugin
18
19 import (
20 "os"
21 "reflect"
22 "testing"
23 "time"
24
25 utiltesting "k8s.io/client-go/util/testing"
26
27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28 kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config"
29 )
30
31 func Test_readCredentialProviderConfigFile(t *testing.T) {
32 testcases := []struct {
33 name string
34 configData string
35 config *kubeletconfig.CredentialProviderConfig
36 expectErr bool
37 }{
38 {
39 name: "config with 1 plugin and 1 image matcher",
40 configData: `---
41 kind: CredentialProviderConfig
42 apiVersion: kubelet.config.k8s.io/v1alpha1
43 providers:
44 - name: test
45 matchImages:
46 - "registry.io/foobar"
47 defaultCacheDuration: 10m
48 apiVersion: credentialprovider.kubelet.k8s.io/v1alpha1
49 args:
50 - --v=5
51 env:
52 - name: FOO
53 value: BAR`,
54 config: &kubeletconfig.CredentialProviderConfig{
55 Providers: []kubeletconfig.CredentialProvider{
56 {
57 Name: "test",
58 MatchImages: []string{"registry.io/foobar"},
59 DefaultCacheDuration: &metav1.Duration{Duration: 10 * time.Minute},
60 APIVersion: "credentialprovider.kubelet.k8s.io/v1alpha1",
61 Args: []string{"--v=5"},
62 Env: []kubeletconfig.ExecEnvVar{
63 {
64 Name: "FOO",
65 Value: "BAR",
66 },
67 },
68 },
69 },
70 },
71 },
72 {
73 name: "config with 1 plugin and a wildcard image match",
74 configData: `---
75 kind: CredentialProviderConfig
76 apiVersion: kubelet.config.k8s.io/v1alpha1
77 providers:
78 - name: test
79 matchImages:
80 - "registry.io/*"
81 defaultCacheDuration: 10m
82 apiVersion: credentialprovider.kubelet.k8s.io/v1alpha1
83 args:
84 - --v=5
85 env:
86 - name: FOO
87 value: BAR`,
88 config: &kubeletconfig.CredentialProviderConfig{
89 Providers: []kubeletconfig.CredentialProvider{
90 {
91 Name: "test",
92 MatchImages: []string{"registry.io/*"},
93 DefaultCacheDuration: &metav1.Duration{Duration: 10 * time.Minute},
94 APIVersion: "credentialprovider.kubelet.k8s.io/v1alpha1",
95 Args: []string{"--v=5"},
96 Env: []kubeletconfig.ExecEnvVar{
97 {
98 Name: "FOO",
99 Value: "BAR",
100 },
101 },
102 },
103 },
104 },
105 },
106 {
107 name: "config with 1 plugin and multiple image matchers",
108 configData: `---
109 kind: CredentialProviderConfig
110 apiVersion: kubelet.config.k8s.io/v1alpha1
111 providers:
112 - name: test
113 matchImages:
114 - "registry.io/*"
115 - "foobar.registry.io/*"
116 defaultCacheDuration: 10m
117 apiVersion: credentialprovider.kubelet.k8s.io/v1alpha1
118 args:
119 - --v=5
120 env:
121 - name: FOO
122 value: BAR`,
123 config: &kubeletconfig.CredentialProviderConfig{
124 Providers: []kubeletconfig.CredentialProvider{
125 {
126 Name: "test",
127 MatchImages: []string{"registry.io/*", "foobar.registry.io/*"},
128 DefaultCacheDuration: &metav1.Duration{Duration: 10 * time.Minute},
129 APIVersion: "credentialprovider.kubelet.k8s.io/v1alpha1",
130 Args: []string{"--v=5"},
131 Env: []kubeletconfig.ExecEnvVar{
132 {
133 Name: "FOO",
134 Value: "BAR",
135 },
136 },
137 },
138 },
139 },
140 },
141 {
142 name: "config with multiple providers",
143 configData: `---
144 kind: CredentialProviderConfig
145 apiVersion: kubelet.config.k8s.io/v1alpha1
146 providers:
147 - name: test1
148 matchImages:
149 - "registry.io/one"
150 defaultCacheDuration: 10m
151 apiVersion: credentialprovider.kubelet.k8s.io/v1alpha1
152 - name: test2
153 matchImages:
154 - "registry.io/two"
155 defaultCacheDuration: 10m
156 apiVersion: credentialprovider.kubelet.k8s.io/v1alpha1
157 args:
158 - --v=5
159 env:
160 - name: FOO
161 value: BAR`,
162
163 config: &kubeletconfig.CredentialProviderConfig{
164 Providers: []kubeletconfig.CredentialProvider{
165 {
166 Name: "test1",
167 MatchImages: []string{"registry.io/one"},
168 DefaultCacheDuration: &metav1.Duration{Duration: 10 * time.Minute},
169 APIVersion: "credentialprovider.kubelet.k8s.io/v1alpha1",
170 },
171 {
172 Name: "test2",
173 MatchImages: []string{"registry.io/two"},
174 DefaultCacheDuration: &metav1.Duration{Duration: 10 * time.Minute},
175 APIVersion: "credentialprovider.kubelet.k8s.io/v1alpha1",
176 Args: []string{"--v=5"},
177 Env: []kubeletconfig.ExecEnvVar{
178 {
179 Name: "FOO",
180 Value: "BAR",
181 },
182 },
183 },
184 },
185 },
186 },
187 {
188 name: "v1beta1 config with multiple providers",
189 configData: `---
190 kind: CredentialProviderConfig
191 apiVersion: kubelet.config.k8s.io/v1beta1
192 providers:
193 - name: test1
194 matchImages:
195 - "registry.io/one"
196 defaultCacheDuration: 10m
197 apiVersion: credentialprovider.kubelet.k8s.io/v1beta1
198 - name: test2
199 matchImages:
200 - "registry.io/two"
201 defaultCacheDuration: 10m
202 apiVersion: credentialprovider.kubelet.k8s.io/v1beta1
203 args:
204 - --v=5
205 env:
206 - name: FOO
207 value: BAR`,
208
209 config: &kubeletconfig.CredentialProviderConfig{
210 Providers: []kubeletconfig.CredentialProvider{
211 {
212 Name: "test1",
213 MatchImages: []string{"registry.io/one"},
214 DefaultCacheDuration: &metav1.Duration{Duration: 10 * time.Minute},
215 APIVersion: "credentialprovider.kubelet.k8s.io/v1beta1",
216 },
217 {
218 Name: "test2",
219 MatchImages: []string{"registry.io/two"},
220 DefaultCacheDuration: &metav1.Duration{Duration: 10 * time.Minute},
221 APIVersion: "credentialprovider.kubelet.k8s.io/v1beta1",
222 Args: []string{"--v=5"},
223 Env: []kubeletconfig.ExecEnvVar{
224 {
225 Name: "FOO",
226 Value: "BAR",
227 },
228 },
229 },
230 },
231 },
232 },
233 {
234 name: "v1 config with multiple providers",
235 configData: `---
236 kind: CredentialProviderConfig
237 apiVersion: kubelet.config.k8s.io/v1
238 providers:
239 - name: test1
240 matchImages:
241 - "registry.io/one"
242 defaultCacheDuration: 10m
243 apiVersion: credentialprovider.kubelet.k8s.io/v1
244 - name: test2
245 matchImages:
246 - "registry.io/two"
247 defaultCacheDuration: 10m
248 apiVersion: credentialprovider.kubelet.k8s.io/v1
249 args:
250 - --v=5
251 env:
252 - name: FOO
253 value: BAR`,
254
255 config: &kubeletconfig.CredentialProviderConfig{
256 Providers: []kubeletconfig.CredentialProvider{
257 {
258 Name: "test1",
259 MatchImages: []string{"registry.io/one"},
260 DefaultCacheDuration: &metav1.Duration{Duration: 10 * time.Minute},
261 APIVersion: "credentialprovider.kubelet.k8s.io/v1",
262 },
263 {
264 Name: "test2",
265 MatchImages: []string{"registry.io/two"},
266 DefaultCacheDuration: &metav1.Duration{Duration: 10 * time.Minute},
267 APIVersion: "credentialprovider.kubelet.k8s.io/v1",
268 Args: []string{"--v=5"},
269 Env: []kubeletconfig.ExecEnvVar{
270 {
271 Name: "FOO",
272 Value: "BAR",
273 },
274 },
275 },
276 },
277 },
278 },
279 {
280 name: "config with wrong Kind",
281 configData: `---
282 kind: WrongKind
283 apiVersion: kubelet.config.k8s.io/v1alpha1
284 providers:
285 - name: test
286 matchImages:
287 - "registry.io/foobar"
288 defaultCacheDuration: 10m
289 apiVersion: credentialprovider.kubelet.k8s.io/v1alpha1
290 args:
291 - --v=5
292 env:
293 - name: FOO
294 value: BAR`,
295 config: nil,
296 expectErr: true,
297 },
298 {
299 name: "config with wrong apiversion",
300 configData: `---
301 kind: CredentialProviderConfig
302 apiVersion: foobar/v1alpha1
303 providers:
304 - name: test
305 matchImages:
306 - "registry.io/foobar"
307 defaultCacheDuration: 10m
308 apiVersion: credentialprovider.kubelet.k8s.io/v1alpha1
309 args:
310 - --v=5
311 env:
312 - name: FOO
313 value: BAR`,
314 config: nil,
315 expectErr: true,
316 },
317 }
318
319 for _, testcase := range testcases {
320 t.Run(testcase.name, func(t *testing.T) {
321 file, err := os.CreateTemp("", "config.yaml")
322 if err != nil {
323 t.Fatal(err)
324 }
325 defer utiltesting.CloseAndRemove(t, file)
326
327 _, err = file.WriteString(testcase.configData)
328 if err != nil {
329 t.Fatal(err)
330 }
331
332 authConfig, err := readCredentialProviderConfigFile(file.Name())
333 if err != nil && !testcase.expectErr {
334 t.Fatal(err)
335 }
336
337 if err == nil && testcase.expectErr {
338 t.Error("expected error but got none")
339 }
340
341 if !reflect.DeepEqual(authConfig, testcase.config) {
342 t.Logf("actual auth config: %#v", authConfig)
343 t.Logf("expected auth config: %#v", testcase.config)
344 t.Error("credential provider config did not match")
345 }
346 })
347 }
348 }
349
350 func Test_validateCredentialProviderConfig(t *testing.T) {
351 testcases := []struct {
352 name string
353 config *kubeletconfig.CredentialProviderConfig
354 shouldErr bool
355 }{
356 {
357 name: "no providers provided",
358 config: &kubeletconfig.CredentialProviderConfig{},
359 shouldErr: true,
360 },
361 {
362 name: "no matchImages provided",
363 config: &kubeletconfig.CredentialProviderConfig{
364 Providers: []kubeletconfig.CredentialProvider{
365 {
366 Name: "foobar",
367 MatchImages: []string{},
368 DefaultCacheDuration: &metav1.Duration{Duration: time.Minute},
369 APIVersion: "credentialprovider.kubelet.k8s.io/v1alpha1",
370 },
371 },
372 },
373 shouldErr: true,
374 },
375 {
376 name: "no default cache duration provided",
377 config: &kubeletconfig.CredentialProviderConfig{
378 Providers: []kubeletconfig.CredentialProvider{
379 {
380 Name: "foobar",
381 MatchImages: []string{"foobar.registry.io"},
382 APIVersion: "credentialprovider.kubelet.k8s.io/v1alpha1",
383 },
384 },
385 },
386 shouldErr: true,
387 },
388 {
389 name: "name contains '/'",
390 config: &kubeletconfig.CredentialProviderConfig{
391 Providers: []kubeletconfig.CredentialProvider{
392 {
393 Name: "foo/../bar",
394 MatchImages: []string{"foobar.registry.io"},
395 DefaultCacheDuration: &metav1.Duration{Duration: time.Minute},
396 APIVersion: "credentialprovider.kubelet.k8s.io/v1alpha1",
397 },
398 },
399 },
400 shouldErr: true,
401 },
402 {
403 name: "name is '.'",
404 config: &kubeletconfig.CredentialProviderConfig{
405 Providers: []kubeletconfig.CredentialProvider{
406 {
407 Name: ".",
408 MatchImages: []string{"foobar.registry.io"},
409 DefaultCacheDuration: &metav1.Duration{Duration: time.Minute},
410 APIVersion: "credentialprovider.kubelet.k8s.io/v1alpha1",
411 },
412 },
413 },
414 shouldErr: true,
415 },
416 {
417 name: "name is '..'",
418 config: &kubeletconfig.CredentialProviderConfig{
419 Providers: []kubeletconfig.CredentialProvider{
420 {
421 Name: "..",
422 MatchImages: []string{"foobar.registry.io"},
423 DefaultCacheDuration: &metav1.Duration{Duration: time.Minute},
424 APIVersion: "credentialprovider.kubelet.k8s.io/v1alpha1",
425 },
426 },
427 },
428 shouldErr: true,
429 },
430 {
431 name: "name contains spaces",
432 config: &kubeletconfig.CredentialProviderConfig{
433 Providers: []kubeletconfig.CredentialProvider{
434 {
435 Name: "foo bar",
436 MatchImages: []string{"foobar.registry.io"},
437 DefaultCacheDuration: &metav1.Duration{Duration: time.Minute},
438 APIVersion: "credentialprovider.kubelet.k8s.io/v1alpha1",
439 },
440 },
441 },
442 shouldErr: true,
443 },
444 {
445 name: "no apiVersion",
446 config: &kubeletconfig.CredentialProviderConfig{
447 Providers: []kubeletconfig.CredentialProvider{
448 {
449 Name: "foobar",
450 MatchImages: []string{"foobar.registry.io"},
451 DefaultCacheDuration: &metav1.Duration{Duration: time.Minute},
452 APIVersion: "",
453 },
454 },
455 },
456 shouldErr: true,
457 },
458 {
459 name: "invalid apiVersion",
460 config: &kubeletconfig.CredentialProviderConfig{
461 Providers: []kubeletconfig.CredentialProvider{
462 {
463 Name: "foobar",
464 MatchImages: []string{"foobar.registry.io"},
465 DefaultCacheDuration: &metav1.Duration{Duration: time.Minute},
466 APIVersion: "credentialprovider.kubelet.k8s.io/v1alpha0",
467 },
468 },
469 },
470 shouldErr: true,
471 },
472 {
473 name: "negative default cache duration",
474 config: &kubeletconfig.CredentialProviderConfig{
475 Providers: []kubeletconfig.CredentialProvider{
476 {
477 Name: "foobar",
478 MatchImages: []string{"foobar.registry.io"},
479 DefaultCacheDuration: &metav1.Duration{Duration: -1 * time.Minute},
480 APIVersion: "credentialprovider.kubelet.k8s.io/v1alpha1",
481 },
482 },
483 },
484 shouldErr: true,
485 },
486 {
487 name: "invalid match image",
488 config: &kubeletconfig.CredentialProviderConfig{
489 Providers: []kubeletconfig.CredentialProvider{
490 {
491 Name: "foobar",
492 MatchImages: []string{"%invalid%"},
493 DefaultCacheDuration: &metav1.Duration{Duration: time.Minute},
494 APIVersion: "credentialprovider.kubelet.k8s.io/v1alpha1",
495 },
496 },
497 },
498 shouldErr: true,
499 },
500 {
501 name: "valid config",
502 config: &kubeletconfig.CredentialProviderConfig{
503 Providers: []kubeletconfig.CredentialProvider{
504 {
505 Name: "foobar",
506 MatchImages: []string{"foobar.registry.io"},
507 DefaultCacheDuration: &metav1.Duration{Duration: time.Minute},
508 APIVersion: "credentialprovider.kubelet.k8s.io/v1alpha1",
509 },
510 },
511 },
512 shouldErr: false,
513 },
514 }
515
516 for _, testcase := range testcases {
517 t.Run(testcase.name, func(t *testing.T) {
518 errs := validateCredentialProviderConfig(testcase.config)
519
520 if testcase.shouldErr && len(errs) == 0 {
521 t.Errorf("expected error but got none")
522 } else if !testcase.shouldErr && len(errs) > 0 {
523 t.Errorf("expected no error but received errors: %v", errs.ToAggregate())
524
525 }
526 })
527 }
528 }
529
View as plain text