...

Source file src/k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig/kubeconfig_test.go

Documentation: k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig

     1  /*
     2  Copyright 2017 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 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