...

Source file src/k8s.io/kubernetes/pkg/credentialprovider/plugin/config_test.go

Documentation: k8s.io/kubernetes/pkg/credentialprovider/plugin

     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  }
   529  

View as plain text