k8s.io/kubernetes@v1.29.3/pkg/credentialprovider/plugin/config_test.go (about) 1 /* 2 Copyright 2020 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 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 }