1
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
46 var err error
47 dir, err = os.MkdirTemp("", "cr-test")
48 Expect(err).NotTo(HaveOccurred())
49
50
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
69 setConfigs(tc, dir)
70
71
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
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
178 if len(tc.kubeconfigFlag) > 0 {
179 kubeconfig = filepath.Join(dir, tc.kubeconfigFlag)
180 }
181
182
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 {
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