...

Source file src/k8s.io/kubectl/pkg/cmd/config/set_credentials_test.go

Documentation: k8s.io/kubectl/pkg/cmd/config

     1  /*
     2  Copyright 2016 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 config
    18  
    19  import (
    20  	"bytes"
    21  	utiltesting "k8s.io/client-go/util/testing"
    22  	"os"
    23  	"reflect"
    24  	"testing"
    25  
    26  	"k8s.io/client-go/tools/clientcmd"
    27  	clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
    28  	cliflag "k8s.io/component-base/cli/flag"
    29  )
    30  
    31  func stringFlagFor(s string) cliflag.StringFlag {
    32  	var f cliflag.StringFlag
    33  	f.Set(s)
    34  	return f
    35  }
    36  
    37  func TestSetCredentialsOptions(t *testing.T) {
    38  	tests := []struct {
    39  		name            string
    40  		flags           []string
    41  		wantParseErr    bool
    42  		wantCompleteErr bool
    43  		wantValidateErr bool
    44  
    45  		wantOptions *setCredentialsOptions
    46  	}{
    47  		{
    48  			name: "test1",
    49  			flags: []string{
    50  				"me",
    51  			},
    52  			wantOptions: &setCredentialsOptions{
    53  				name: "me",
    54  			},
    55  		},
    56  		{
    57  			name: "test2",
    58  			flags: []string{
    59  				"me",
    60  				"--token=foo",
    61  			},
    62  			wantOptions: &setCredentialsOptions{
    63  				name:  "me",
    64  				token: stringFlagFor("foo"),
    65  			},
    66  		},
    67  		{
    68  			name: "test3",
    69  			flags: []string{
    70  				"me",
    71  				"--username=jane",
    72  				"--password=bar",
    73  			},
    74  			wantOptions: &setCredentialsOptions{
    75  				name:     "me",
    76  				username: stringFlagFor("jane"),
    77  				password: stringFlagFor("bar"),
    78  			},
    79  		},
    80  		{
    81  			name: "test4",
    82  			// Cannot provide both token and basic auth.
    83  			flags: []string{
    84  				"me",
    85  				"--token=foo",
    86  				"--username=jane",
    87  				"--password=bar",
    88  			},
    89  			wantValidateErr: true,
    90  		},
    91  		{
    92  			name: "test5",
    93  			flags: []string{
    94  				"--auth-provider=oidc",
    95  				"--auth-provider-arg=client-id=foo",
    96  				"--auth-provider-arg=client-secret=bar",
    97  				"me",
    98  			},
    99  			wantOptions: &setCredentialsOptions{
   100  				name:         "me",
   101  				authProvider: stringFlagFor("oidc"),
   102  				authProviderArgs: map[string]string{
   103  					"client-id":     "foo",
   104  					"client-secret": "bar",
   105  				},
   106  				authProviderArgsToRemove: []string{},
   107  			},
   108  		},
   109  		{
   110  			name: "test6",
   111  			flags: []string{
   112  				"--auth-provider=oidc",
   113  				"--auth-provider-arg=client-id-",
   114  				"--auth-provider-arg=client-secret-",
   115  				"me",
   116  			},
   117  			wantOptions: &setCredentialsOptions{
   118  				name:             "me",
   119  				authProvider:     stringFlagFor("oidc"),
   120  				authProviderArgs: map[string]string{},
   121  				authProviderArgsToRemove: []string{
   122  					"client-id",
   123  					"client-secret",
   124  				},
   125  			},
   126  		},
   127  		{
   128  			name: "test7",
   129  			flags: []string{
   130  				"--auth-provider-arg=client-id-", // auth provider name not required
   131  				"--auth-provider-arg=client-secret-",
   132  				"me",
   133  			},
   134  			wantOptions: &setCredentialsOptions{
   135  				name:             "me",
   136  				authProviderArgs: map[string]string{},
   137  				authProviderArgsToRemove: []string{
   138  					"client-id",
   139  					"client-secret",
   140  				},
   141  			},
   142  		},
   143  		{
   144  			name: "test8",
   145  			flags: []string{
   146  				"--auth-provider=oidc",
   147  				"--auth-provider-arg=client-id", // values must be of form 'key=value' or 'key-'
   148  				"me",
   149  			},
   150  			wantCompleteErr: true,
   151  		},
   152  		{
   153  			name:  "test9",
   154  			flags: []string{
   155  				// No name for authinfo provided.
   156  			},
   157  			wantCompleteErr: true,
   158  		},
   159  		{
   160  			name: "test10",
   161  			flags: []string{
   162  				"--exec-command=example-client-go-exec-plugin",
   163  				"me",
   164  			},
   165  			wantOptions: &setCredentialsOptions{
   166  				name:        "me",
   167  				execCommand: stringFlagFor("example-client-go-exec-plugin"),
   168  			},
   169  		},
   170  		{
   171  			name: "test11",
   172  			flags: []string{
   173  				"--exec-command=example-client-go-exec-plugin",
   174  				"--exec-arg=arg1",
   175  				"--exec-arg=arg2",
   176  				"me",
   177  			},
   178  			wantOptions: &setCredentialsOptions{
   179  				name:        "me",
   180  				execCommand: stringFlagFor("example-client-go-exec-plugin"),
   181  				execArgs:    []string{"arg1", "arg2"},
   182  			},
   183  		},
   184  		{
   185  			name: "test12",
   186  			flags: []string{
   187  				"--exec-command=example-client-go-exec-plugin",
   188  				"--exec-env=key1=val1",
   189  				"--exec-env=key2=val2",
   190  				"--exec-env=env-remove1-",
   191  				"--exec-env=env-remove2-",
   192  				"me",
   193  			},
   194  			wantOptions: &setCredentialsOptions{
   195  				name:            "me",
   196  				execCommand:     stringFlagFor("example-client-go-exec-plugin"),
   197  				execEnv:         map[string]string{"key1": "val1", "key2": "val2"},
   198  				execEnvToRemove: []string{"env-remove1", "env-remove2"},
   199  			},
   200  		},
   201  	}
   202  
   203  	for _, tt := range tests {
   204  		t.Run(tt.name, func(t *testing.T) {
   205  			buff := new(bytes.Buffer)
   206  
   207  			opts := new(setCredentialsOptions)
   208  			cmd := newCmdConfigSetCredentials(buff, opts)
   209  			if err := cmd.ParseFlags(tt.flags); err != nil {
   210  				if !tt.wantParseErr {
   211  					t.Errorf("case %s: parsing error for flags %q: %v: %s", tt.name, tt.flags, err, buff)
   212  				}
   213  				return
   214  			}
   215  			if tt.wantParseErr {
   216  				t.Errorf("case %s: expected parsing error for flags %q: %s", tt.name, tt.flags, buff)
   217  				return
   218  			}
   219  
   220  			if err := opts.complete(cmd); err != nil {
   221  				if !tt.wantCompleteErr {
   222  					t.Errorf("case %s: complete() error for flags %q: %s", tt.name, tt.flags, buff)
   223  				}
   224  				return
   225  			}
   226  			if tt.wantCompleteErr {
   227  				t.Errorf("case %s: complete() expected errors for flags %q: %s", tt.name, tt.flags, buff)
   228  				return
   229  			}
   230  
   231  			if err := opts.validate(); err != nil {
   232  				if !tt.wantValidateErr {
   233  					t.Errorf("case %s: flags %q: validate failed: %v", tt.name, tt.flags, err)
   234  				}
   235  				return
   236  			}
   237  
   238  			if tt.wantValidateErr {
   239  				t.Errorf("case %s: flags %q: expected validate to fail", tt.name, tt.flags)
   240  				return
   241  			}
   242  
   243  			if !reflect.DeepEqual(opts, tt.wantOptions) {
   244  				t.Errorf("case %s: flags %q: mis-matched options,\nwanted=%#v\ngot=   %#v", tt.name, tt.flags, tt.wantOptions, opts)
   245  			}
   246  		})
   247  	}
   248  }
   249  
   250  func TestModifyExistingAuthInfo(t *testing.T) {
   251  	tests := []struct {
   252  		name            string
   253  		flags           []string
   254  		wantParseErr    bool
   255  		wantCompleteErr bool
   256  		wantValidateErr bool
   257  
   258  		existingAuthInfo clientcmdapi.AuthInfo
   259  		wantAuthInfo     clientcmdapi.AuthInfo
   260  	}{
   261  		{
   262  			name: "1. create new exec config",
   263  			flags: []string{
   264  				"--exec-command=example-client-go-exec-plugin",
   265  				"--exec-api-version=client.authentication.k8s.io/v1",
   266  				"me",
   267  			},
   268  			existingAuthInfo: clientcmdapi.AuthInfo{},
   269  			wantAuthInfo: clientcmdapi.AuthInfo{
   270  				Exec: &clientcmdapi.ExecConfig{
   271  					Command:    "example-client-go-exec-plugin",
   272  					APIVersion: "client.authentication.k8s.io/v1",
   273  				},
   274  			},
   275  		},
   276  		{
   277  			name: "2. redefine exec args",
   278  			flags: []string{
   279  				"--exec-arg=new-arg1",
   280  				"--exec-arg=new-arg2",
   281  				"me",
   282  			},
   283  			existingAuthInfo: clientcmdapi.AuthInfo{
   284  				Exec: &clientcmdapi.ExecConfig{
   285  					Command:    "example-client-go-exec-plugin",
   286  					APIVersion: "client.authentication.k8s.io/v1beta1",
   287  					Args:       []string{"existing-arg1", "existing-arg2"},
   288  				},
   289  			},
   290  			wantAuthInfo: clientcmdapi.AuthInfo{
   291  				Exec: &clientcmdapi.ExecConfig{
   292  					Command:    "example-client-go-exec-plugin",
   293  					APIVersion: "client.authentication.k8s.io/v1beta1",
   294  					Args:       []string{"new-arg1", "new-arg2"},
   295  				},
   296  			},
   297  		},
   298  		{
   299  			name: "3. reset exec args",
   300  			flags: []string{
   301  				"--exec-command=example-client-go-exec-plugin",
   302  				"me",
   303  			},
   304  			existingAuthInfo: clientcmdapi.AuthInfo{
   305  				Exec: &clientcmdapi.ExecConfig{
   306  					Command:    "example-client-go-exec-plugin",
   307  					APIVersion: "client.authentication.k8s.io/v1beta1",
   308  					Args:       []string{"existing-arg1", "existing-arg2"},
   309  				},
   310  			},
   311  			wantAuthInfo: clientcmdapi.AuthInfo{
   312  				Exec: &clientcmdapi.ExecConfig{
   313  					Command:    "example-client-go-exec-plugin",
   314  					APIVersion: "client.authentication.k8s.io/v1beta1",
   315  				},
   316  			},
   317  		},
   318  		{
   319  			name: "4. modify exec env variables",
   320  			flags: []string{
   321  				"--exec-command=example-client-go-exec-plugin",
   322  				"--exec-env=name1=value1000",
   323  				"--exec-env=name3=value3",
   324  				"--exec-env=name2-",
   325  				"--exec-env=non-existing-",
   326  				"me",
   327  			},
   328  			existingAuthInfo: clientcmdapi.AuthInfo{
   329  				Exec: &clientcmdapi.ExecConfig{
   330  					Command:    "existing-command",
   331  					APIVersion: "client.authentication.k8s.io/v1beta1",
   332  					Env: []clientcmdapi.ExecEnvVar{
   333  						{Name: "name1", Value: "value1"},
   334  						{Name: "name2", Value: "value2"},
   335  					},
   336  				},
   337  			},
   338  			wantAuthInfo: clientcmdapi.AuthInfo{
   339  				Exec: &clientcmdapi.ExecConfig{
   340  					Command:    "example-client-go-exec-plugin",
   341  					APIVersion: "client.authentication.k8s.io/v1beta1",
   342  					Env: []clientcmdapi.ExecEnvVar{
   343  						{Name: "name1", Value: "value1000"},
   344  						{Name: "name3", Value: "value3"},
   345  					},
   346  				},
   347  			},
   348  		},
   349  		{
   350  			name: "5. modify auth provider arguments",
   351  			flags: []string{
   352  				"--auth-provider=new-auth-provider",
   353  				"--auth-provider-arg=key1=val1000",
   354  				"--auth-provider-arg=key3=val3",
   355  				"--auth-provider-arg=key2-",
   356  				"--auth-provider-arg=non-existing-",
   357  				"me",
   358  			},
   359  			existingAuthInfo: clientcmdapi.AuthInfo{
   360  				AuthProvider: &clientcmdapi.AuthProviderConfig{
   361  					Name: "auth-provider",
   362  					Config: map[string]string{
   363  						"key1": "val1",
   364  						"key2": "val2",
   365  					},
   366  				},
   367  			},
   368  			wantAuthInfo: clientcmdapi.AuthInfo{
   369  				AuthProvider: &clientcmdapi.AuthProviderConfig{
   370  					Name: "new-auth-provider",
   371  					Config: map[string]string{
   372  						"key1": "val1000",
   373  						"key3": "val3",
   374  					},
   375  				},
   376  			},
   377  		},
   378  	}
   379  
   380  	for _, tt := range tests {
   381  		t.Run(tt.name, func(t *testing.T) {
   382  			buff := new(bytes.Buffer)
   383  
   384  			opts := new(setCredentialsOptions)
   385  			cmd := newCmdConfigSetCredentials(buff, opts)
   386  			if err := cmd.ParseFlags(tt.flags); err != nil {
   387  				if !tt.wantParseErr {
   388  					t.Errorf("case %s: parsing error for flags %q: %v: %s", tt.name, tt.flags, err, buff)
   389  				}
   390  				return
   391  			}
   392  			if tt.wantParseErr {
   393  				t.Errorf("case %s: expected parsing error for flags %q: %s", tt.name, tt.flags, buff)
   394  				return
   395  			}
   396  
   397  			if err := opts.complete(cmd); err != nil {
   398  				if !tt.wantCompleteErr {
   399  					t.Errorf("case %s: complete() error for flags %q: %s", tt.name, tt.flags, buff)
   400  				}
   401  				return
   402  			}
   403  			if tt.wantCompleteErr {
   404  				t.Errorf("case %s: complete() expected errors for flags %q: %s", tt.name, tt.flags, buff)
   405  				return
   406  			}
   407  
   408  			if err := opts.validate(); err != nil {
   409  				if !tt.wantValidateErr {
   410  					t.Errorf("case %s: flags %q: validate failed: %v", tt.name, tt.flags, err)
   411  				}
   412  				return
   413  			}
   414  
   415  			if tt.wantValidateErr {
   416  				t.Errorf("case %s: flags %q: expected validate to fail", tt.name, tt.flags)
   417  				return
   418  			}
   419  
   420  			modifiedAuthInfo := opts.modifyAuthInfo(tt.existingAuthInfo)
   421  
   422  			if !reflect.DeepEqual(modifiedAuthInfo, tt.wantAuthInfo) {
   423  				t.Errorf("case %s: flags %q: mis-matched auth info,\nwanted=%#v\ngot=   %#v", tt.name, tt.flags, tt.wantAuthInfo, modifiedAuthInfo)
   424  			}
   425  		})
   426  	}
   427  }
   428  
   429  type setCredentialsTest struct {
   430  	description    string
   431  	config         clientcmdapi.Config
   432  	args           []string
   433  	flags          []string
   434  	expected       string
   435  	expectedConfig clientcmdapi.Config
   436  }
   437  
   438  func TestSetCredentials(t *testing.T) {
   439  	conf := clientcmdapi.Config{}
   440  	test := setCredentialsTest{
   441  		description: "Testing set credentials",
   442  		config:      conf,
   443  		args:        []string{"cluster-admin"},
   444  		flags: []string{
   445  			"--username=admin",
   446  			"--password=uXFGweU9l35qcif",
   447  		},
   448  		expected: `User "cluster-admin" set.` + "\n",
   449  		expectedConfig: clientcmdapi.Config{
   450  			AuthInfos: map[string]*clientcmdapi.AuthInfo{
   451  				"cluster-admin": {Username: "admin", Password: "uXFGweU9l35qcif"}},
   452  		},
   453  	}
   454  	test.run(t)
   455  }
   456  func (test setCredentialsTest) run(t *testing.T) {
   457  	fakeKubeFile, err := os.CreateTemp(os.TempDir(), "")
   458  	if err != nil {
   459  		t.Fatalf("unexpected error: %v", err)
   460  	}
   461  	defer utiltesting.CloseAndRemove(t, fakeKubeFile)
   462  	err = clientcmd.WriteToFile(test.config, fakeKubeFile.Name())
   463  	if err != nil {
   464  		t.Fatalf("unexpected error: %v", err)
   465  	}
   466  
   467  	pathOptions := clientcmd.NewDefaultPathOptions()
   468  	pathOptions.GlobalFile = fakeKubeFile.Name()
   469  	pathOptions.EnvVar = ""
   470  	buf := bytes.NewBuffer([]byte{})
   471  	cmd := NewCmdConfigSetCredentials(buf, pathOptions)
   472  	cmd.SetArgs(test.args)
   473  	cmd.Flags().Parse(test.flags)
   474  	if err := cmd.Execute(); err != nil {
   475  		t.Fatalf("unexpected error executing command: %v,kubectl config set-credentials  args: %v,flags: %v", err, test.args, test.flags)
   476  	}
   477  	config, err := clientcmd.LoadFromFile(fakeKubeFile.Name())
   478  	if err != nil {
   479  		t.Fatalf("unexpected error loading kubeconfig file: %v", err)
   480  	}
   481  	if len(test.expected) != 0 {
   482  		if buf.String() != test.expected {
   483  			t.Errorf("Fail in %q:\n expected %v\n but got %v\n", test.description, test.expected, buf.String())
   484  		}
   485  	}
   486  	if test.expectedConfig.AuthInfos != nil {
   487  		expectAuthInfo := test.expectedConfig.AuthInfos[test.args[0]]
   488  		actualAuthInfo := config.AuthInfos[test.args[0]]
   489  		if expectAuthInfo.Username != actualAuthInfo.Username || expectAuthInfo.Password != actualAuthInfo.Password {
   490  			t.Errorf("Fail in %q:\n expected AuthInfo%v\n but found %v in kubeconfig\n", test.description, expectAuthInfo, actualAuthInfo)
   491  		}
   492  	}
   493  }
   494  

View as plain text