1/*
2Copyright 2014 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package clientcmd
18
19import (
20	"fmt"
21	"testing"
22
23	restclient "k8s.io/client-go/rest"
24	clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
25)
26
27type testClientConfig struct {
28	rawconfig          *clientcmdapi.Config
29	config             *restclient.Config
30	namespace          string
31	namespaceSpecified bool
32	err                error
33}
34
35func (c *testClientConfig) RawConfig() (clientcmdapi.Config, error) {
36	if c.rawconfig == nil {
37		return clientcmdapi.Config{}, fmt.Errorf("unexpected call")
38	}
39	return *c.rawconfig, nil
40}
41func (c *testClientConfig) ClientConfig() (*restclient.Config, error) {
42	return c.config, c.err
43}
44func (c *testClientConfig) Namespace() (string, bool, error) {
45	return c.namespace, c.namespaceSpecified, c.err
46}
47func (c *testClientConfig) ConfigAccess() ConfigAccess {
48	return nil
49}
50
51type testICC struct {
52	testClientConfig
53
54	possible bool
55	called   bool
56}
57
58func (icc *testICC) Possible() bool {
59	icc.called = true
60	return icc.possible
61}
62
63func TestInClusterConfig(t *testing.T) {
64	default1 := &DirectClientConfig{
65		config:      *createValidTestConfig(),
66		contextName: "clean",
67		overrides:   &ConfigOverrides{},
68	}
69	invalidDefaultConfig := clientcmdapi.NewConfig()
70	invalidDefaultConfig.Clusters["clean"] = &clientcmdapi.Cluster{
71		Server: "http://localhost:8080",
72	}
73	invalidDefaultConfig.Contexts["other"] = &clientcmdapi.Context{
74		Cluster: "clean",
75	}
76	invalidDefaultConfig.CurrentContext = "clean"
77
78	defaultInvalid := &DirectClientConfig{
79		config:    *invalidDefaultConfig,
80		overrides: &ConfigOverrides{},
81	}
82	if _, err := defaultInvalid.ClientConfig(); err == nil || !IsConfigurationInvalid(err) {
83		t.Fatal(err)
84	}
85	config1, err := default1.ClientConfig()
86	if err != nil {
87		t.Fatal(err)
88	}
89	config2 := &restclient.Config{Host: "config2"}
90	err1 := fmt.Errorf("unique error")
91
92	testCases := map[string]struct {
93		clientConfig  *testClientConfig
94		icc           *testICC
95		defaultConfig *DirectClientConfig
96
97		checkedICC bool
98		result     *restclient.Config
99		err        error
100	}{
101		"in-cluster checked on other error": {
102			clientConfig: &testClientConfig{err: ErrEmptyConfig},
103			icc:          &testICC{},
104
105			checkedICC: true,
106			result:     nil,
107			err:        ErrEmptyConfig,
108		},
109
110		"in-cluster not checked on non-empty error": {
111			clientConfig: &testClientConfig{err: ErrEmptyCluster},
112			icc:          &testICC{},
113
114			checkedICC: false,
115			result:     nil,
116			err:        ErrEmptyCluster,
117		},
118
119		"in-cluster checked when config is default": {
120			defaultConfig: default1,
121			clientConfig:  &testClientConfig{config: config1},
122			icc:           &testICC{},
123
124			checkedICC: true,
125			result:     config1,
126			err:        nil,
127		},
128
129		"in-cluster not checked when default config is invalid": {
130			defaultConfig: defaultInvalid,
131			clientConfig:  &testClientConfig{config: config1},
132			icc:           &testICC{},
133
134			checkedICC: false,
135			result:     config1,
136			err:        nil,
137		},
138
139		"in-cluster not checked when config is not equal to default": {
140			defaultConfig: default1,
141			clientConfig:  &testClientConfig{config: config2},
142			icc:           &testICC{},
143
144			checkedICC: false,
145			result:     config2,
146			err:        nil,
147		},
148
149		"in-cluster checked when config is not equal to default and error is empty": {
150			clientConfig: &testClientConfig{config: config2, err: ErrEmptyConfig},
151			icc:          &testICC{},
152
153			checkedICC: true,
154			result:     config2,
155			err:        ErrEmptyConfig,
156		},
157
158		"in-cluster error returned when config is empty": {
159			clientConfig: &testClientConfig{err: ErrEmptyConfig},
160			icc: &testICC{
161				possible: true,
162				testClientConfig: testClientConfig{
163					err: err1,
164				},
165			},
166
167			checkedICC: true,
168			result:     nil,
169			err:        err1,
170		},
171
172		"in-cluster config returned when config is empty": {
173			clientConfig: &testClientConfig{err: ErrEmptyConfig},
174			icc: &testICC{
175				possible: true,
176				testClientConfig: testClientConfig{
177					config: config2,
178				},
179			},
180
181			checkedICC: true,
182			result:     config2,
183			err:        nil,
184		},
185
186		"in-cluster not checked when standard default is invalid": {
187			defaultConfig: &DefaultClientConfig,
188			clientConfig:  &testClientConfig{config: config2},
189			icc:           &testICC{},
190
191			checkedICC: false,
192			result:     config2,
193			err:        nil,
194		},
195	}
196
197	for name, test := range testCases {
198		c := &DeferredLoadingClientConfig{icc: test.icc}
199		c.loader = &ClientConfigLoadingRules{DefaultClientConfig: test.defaultConfig}
200		c.clientConfig = test.clientConfig
201
202		cfg, err := c.ClientConfig()
203		if test.icc.called != test.checkedICC {
204			t.Errorf("%s: unexpected in-cluster-config call %t", name, test.icc.called)
205		}
206		if err != test.err || cfg != test.result {
207			t.Errorf("%s: unexpected result: %v %#v", name, err, cfg)
208		}
209	}
210}
211
212func TestInClusterConfigNamespace(t *testing.T) {
213	err1 := fmt.Errorf("unique error")
214
215	testCases := map[string]struct {
216		clientConfig *testClientConfig
217		icc          *testICC
218		overrides    *ConfigOverrides
219
220		checkedICC bool
221		result     string
222		overridden bool
223		err        error
224	}{
225		"in-cluster checked on empty error": {
226			clientConfig: &testClientConfig{err: ErrEmptyConfig},
227			icc:          &testICC{},
228
229			checkedICC: true,
230			err:        ErrEmptyConfig,
231		},
232
233		"in-cluster not checked on non-empty error": {
234			clientConfig: &testClientConfig{err: ErrEmptyCluster},
235			icc:          &testICC{},
236
237			err: ErrEmptyCluster,
238		},
239
240		"in-cluster checked when config is default": {
241			clientConfig: &testClientConfig{},
242			icc:          &testICC{},
243
244			checkedICC: true,
245		},
246
247		"in-cluster not checked when config is not equal to default": {
248			clientConfig: &testClientConfig{namespace: "test", namespaceSpecified: true},
249			icc:          &testICC{},
250
251			result:     "test",
252			overridden: true,
253		},
254
255		"in-cluster checked when namespace is not specified, but is defaulted": {
256			clientConfig: &testClientConfig{namespace: "test", namespaceSpecified: false},
257			icc:          &testICC{},
258
259			checkedICC: true,
260			result:     "test",
261			overridden: false,
262		},
263
264		"in-cluster error returned when config is empty": {
265			clientConfig: &testClientConfig{err: ErrEmptyConfig},
266			icc: &testICC{
267				possible: true,
268				testClientConfig: testClientConfig{
269					err: err1,
270				},
271			},
272
273			checkedICC: true,
274			err:        err1,
275		},
276
277		"in-cluster config returned when config is empty": {
278			clientConfig: &testClientConfig{err: ErrEmptyConfig},
279			icc: &testICC{
280				possible: true,
281				testClientConfig: testClientConfig{
282					namespace:          "test",
283					namespaceSpecified: true,
284				},
285			},
286
287			checkedICC: true,
288			result:     "test",
289			overridden: true,
290		},
291
292		"in-cluster config returned when config is empty and namespace is defaulted but not explicitly set": {
293			clientConfig: &testClientConfig{err: ErrEmptyConfig},
294			icc: &testICC{
295				possible: true,
296				testClientConfig: testClientConfig{
297					namespace:          "test",
298					namespaceSpecified: false,
299				},
300			},
301
302			checkedICC: true,
303			result:     "test",
304			overridden: false,
305		},
306
307		"overridden context used to verify explicit namespace in config": {
308			clientConfig: &testClientConfig{
309				namespace:          "default",
310				namespaceSpecified: false, // a namespace that comes from a context is not considered overridden
311				rawconfig:          &clientcmdapi.Config{Contexts: map[string]*clientcmdapi.Context{"overridden-context": {Namespace: "default"}}},
312			},
313			overrides: &ConfigOverrides{CurrentContext: "overridden-context"},
314			icc: &testICC{
315				possible: true,
316				testClientConfig: testClientConfig{
317					namespace:          "icc",
318					namespaceSpecified: false, // a namespace that comes from icc is not considered overridden
319				},
320			},
321			checkedICC: true,
322			result:     "default",
323			overridden: false, // a namespace that comes from a context is not considered overridden
324		},
325	}
326
327	for name, test := range testCases {
328		t.Run(name, func(t *testing.T) {
329			c := &DeferredLoadingClientConfig{icc: test.icc, overrides: test.overrides}
330			c.clientConfig = test.clientConfig
331
332			ns, overridden, err := c.Namespace()
333			if test.icc.called != test.checkedICC {
334				t.Errorf("%s: unexpected in-cluster-config call %t", name, test.icc.called)
335			}
336			if err != test.err || ns != test.result || overridden != test.overridden {
337				t.Errorf("%s: unexpected result: %v %s %t", name, err, ns, overridden)
338			}
339		})
340	}
341}
342