...

Source file src/sigs.k8s.io/controller-runtime/pkg/client/config/config_test.go

Documentation: sigs.k8s.io/controller-runtime/pkg/client/config

     1  /*
     2  Copyright 2019 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  	"os"
    21  	"path/filepath"
    22  	"strings"
    23  
    24  	. "github.com/onsi/ginkgo/v2"
    25  	. "github.com/onsi/gomega"
    26  	"k8s.io/client-go/rest"
    27  	"k8s.io/client-go/tools/clientcmd"
    28  )
    29  
    30  type testCase struct {
    31  	text           string
    32  	context        string
    33  	kubeconfigFlag string
    34  	kubeconfigEnv  []string
    35  	wantHost       string
    36  }
    37  
    38  var _ = Describe("Config", func() {
    39  
    40  	var dir string
    41  
    42  	origRecommendedHomeFile := clientcmd.RecommendedHomeFile
    43  
    44  	BeforeEach(func() {
    45  		// create temporary directory for test case
    46  		var err error
    47  		dir, err = os.MkdirTemp("", "cr-test")
    48  		Expect(err).NotTo(HaveOccurred())
    49  
    50  		// override $HOME/.kube/config
    51  		clientcmd.RecommendedHomeFile = filepath.Join(dir, ".kubeconfig")
    52  	})
    53  
    54  	AfterEach(func() {
    55  		os.Unsetenv(clientcmd.RecommendedConfigPathEnvVar)
    56  		kubeconfig = ""
    57  		clientcmd.RecommendedHomeFile = origRecommendedHomeFile
    58  
    59  		err := os.RemoveAll(dir)
    60  		Expect(err).NotTo(HaveOccurred())
    61  	})
    62  
    63  	Describe("GetConfigWithContext", func() {
    64  		defineTests := func(testCases []testCase) {
    65  			for _, testCase := range testCases {
    66  				tc := testCase
    67  				It(tc.text, func() {
    68  					// set global and environment configs
    69  					setConfigs(tc, dir)
    70  
    71  					// run the test
    72  					cfg, err := GetConfigWithContext(tc.context)
    73  					Expect(err).NotTo(HaveOccurred())
    74  					Expect(cfg.Host).To(Equal(tc.wantHost))
    75  				})
    76  			}
    77  		}
    78  
    79  		Context("when kubeconfig files don't exist", func() {
    80  			It("should fail", func() {
    81  				err := os.Unsetenv(clientcmd.RecommendedConfigPathEnvVar)
    82  				Expect(err).NotTo(HaveOccurred())
    83  
    84  				cfg, err := GetConfigWithContext("")
    85  				Expect(cfg).To(BeNil())
    86  				Expect(err).To(HaveOccurred())
    87  			})
    88  		})
    89  
    90  		Context("when in-cluster", func() {
    91  			kubeconfigFiles := map[string]string{
    92  				"kubeconfig-multi-context": genKubeconfig("from-multi-env-1", "from-multi-env-2"),
    93  				".kubeconfig":              genKubeconfig("from-home"),
    94  			}
    95  			BeforeEach(func() {
    96  				err := createFiles(kubeconfigFiles, dir)
    97  				Expect(err).NotTo(HaveOccurred())
    98  
    99  				// override in-cluster config loader
   100  				loadInClusterConfig = func() (*rest.Config, error) {
   101  					return &rest.Config{Host: "from-in-cluster"}, nil
   102  				}
   103  			})
   104  			AfterEach(func() { loadInClusterConfig = rest.InClusterConfig })
   105  
   106  			testCases := []testCase{
   107  				{
   108  					text:          "should prefer the envvar over the in-cluster config",
   109  					kubeconfigEnv: []string{"kubeconfig-multi-context"},
   110  					wantHost:      "from-multi-env-1",
   111  				},
   112  				{
   113  					text:     "should prefer in-cluster over the recommended home file",
   114  					wantHost: "from-in-cluster",
   115  				},
   116  			}
   117  			defineTests(testCases)
   118  		})
   119  
   120  		Context("when outside the cluster", func() {
   121  			kubeconfigFiles := map[string]string{
   122  				"kubeconfig-flag":          genKubeconfig("from-flag"),
   123  				"kubeconfig-multi-context": genKubeconfig("from-multi-env-1", "from-multi-env-2"),
   124  				"kubeconfig-env-1":         genKubeconfig("from-env-1"),
   125  				"kubeconfig-env-2":         genKubeconfig("from-env-2"),
   126  				".kubeconfig":              genKubeconfig("from-home"),
   127  			}
   128  			BeforeEach(func() {
   129  				err := createFiles(kubeconfigFiles, dir)
   130  				Expect(err).NotTo(HaveOccurred())
   131  			})
   132  			testCases := []testCase{
   133  				{
   134  					text:           "should use the --kubeconfig flag",
   135  					kubeconfigFlag: "kubeconfig-flag",
   136  					wantHost:       "from-flag",
   137  				},
   138  				{
   139  					text:          "should use the envvar",
   140  					kubeconfigEnv: []string{"kubeconfig-multi-context"},
   141  					wantHost:      "from-multi-env-1",
   142  				},
   143  				{
   144  					text:     "should use the recommended home file",
   145  					wantHost: "from-home",
   146  				},
   147  				{
   148  					text:           "should prefer the flag over the envvar",
   149  					kubeconfigFlag: "kubeconfig-flag",
   150  					kubeconfigEnv:  []string{"kubeconfig-multi-context"},
   151  					wantHost:       "from-flag",
   152  				},
   153  				{
   154  					text:          "should prefer the envvar over the recommended home file",
   155  					kubeconfigEnv: []string{"kubeconfig-multi-context"},
   156  					wantHost:      "from-multi-env-1",
   157  				},
   158  				{
   159  					text:          "should allow overriding the context",
   160  					context:       "from-multi-env-2",
   161  					kubeconfigEnv: []string{"kubeconfig-multi-context"},
   162  					wantHost:      "from-multi-env-2",
   163  				},
   164  				{
   165  					text:          "should support a multi-value envvar",
   166  					context:       "from-env-2",
   167  					kubeconfigEnv: []string{"kubeconfig-env-1", "kubeconfig-env-2"},
   168  					wantHost:      "from-env-2",
   169  				},
   170  			}
   171  			defineTests(testCases)
   172  		})
   173  	})
   174  })
   175  
   176  func setConfigs(tc testCase, dir string) {
   177  	// Set kubeconfig flag value
   178  	if len(tc.kubeconfigFlag) > 0 {
   179  		kubeconfig = filepath.Join(dir, tc.kubeconfigFlag)
   180  	}
   181  
   182  	// Set KUBECONFIG env value
   183  	if len(tc.kubeconfigEnv) > 0 {
   184  		kubeconfigEnvPaths := []string{}
   185  		for _, k := range tc.kubeconfigEnv {
   186  			kubeconfigEnvPaths = append(kubeconfigEnvPaths, filepath.Join(dir, k))
   187  		}
   188  		os.Setenv(clientcmd.RecommendedConfigPathEnvVar, strings.Join(kubeconfigEnvPaths, ":"))
   189  	}
   190  }
   191  
   192  func createFiles(files map[string]string, dir string) error {
   193  	for path, data := range files {
   194  		if err := os.WriteFile(filepath.Join(dir, path), []byte(data), 0644); err != nil { //nolint:gosec
   195  			return err
   196  		}
   197  	}
   198  	return nil
   199  }
   200  
   201  func genKubeconfig(contexts ...string) string {
   202  	var sb strings.Builder
   203  	sb.WriteString(`---
   204  apiVersion: v1
   205  kind: Config
   206  clusters:
   207  `)
   208  	for _, ctx := range contexts {
   209  		sb.WriteString(`- cluster:
   210      server: ` + ctx + `
   211    name: ` + ctx + `
   212  `)
   213  	}
   214  	sb.WriteString("contexts:\n")
   215  	for _, ctx := range contexts {
   216  		sb.WriteString(`- context:
   217      cluster: ` + ctx + `
   218      user: ` + ctx + `
   219    name: ` + ctx + `
   220  `)
   221  	}
   222  
   223  	sb.WriteString("users:\n")
   224  	for _, ctx := range contexts {
   225  		sb.WriteString(`- name: ` + ctx + `
   226  `)
   227  	}
   228  	sb.WriteString("preferences: {}\n")
   229  	if len(contexts) > 0 {
   230  		sb.WriteString("current-context: " + contexts[0] + "\n")
   231  	}
   232  
   233  	return sb.String()
   234  }
   235  

View as plain text