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 testLoader struct {
28	ClientConfigLoader
29
30	called bool
31	config *clientcmdapi.Config
32	err    error
33}
34
35func (l *testLoader) Load() (*clientcmdapi.Config, error) {
36	l.called = true
37	return l.config, l.err
38}
39
40type testClientConfig struct {
41	rawconfig          *clientcmdapi.Config
42	config             *restclient.Config
43	namespace          string
44	namespaceSpecified bool
45	err                error
46}
47
48func (c *testClientConfig) RawConfig() (clientcmdapi.Config, error) {
49	if c.rawconfig == nil {
50		return clientcmdapi.Config{}, fmt.Errorf("unexpected call")
51	}
52	return *c.rawconfig, nil
53}
54func (c *testClientConfig) ClientConfig() (*restclient.Config, error) {
55	return c.config, c.err
56}
57func (c *testClientConfig) Namespace() (string, bool, error) {
58	return c.namespace, c.namespaceSpecified, c.err
59}
60func (c *testClientConfig) ConfigAccess() ConfigAccess {
61	return nil
62}
63
64type testICC struct {
65	testClientConfig
66
67	possible bool
68	called   bool
69}
70
71func (icc *testICC) Possible() bool {
72	icc.called = true
73	return icc.possible
74}
75
76func TestInClusterConfig(t *testing.T) {
77	default1 := &DirectClientConfig{
78		config:      *createValidTestConfig(),
79		contextName: "clean",
80		overrides:   &ConfigOverrides{},
81	}
82	invalidDefaultConfig := clientcmdapi.NewConfig()
83	invalidDefaultConfig.Clusters["clean"] = &clientcmdapi.Cluster{
84		Server: "http://localhost:8080",
85	}
86	invalidDefaultConfig.Contexts["other"] = &clientcmdapi.Context{
87		Cluster: "clean",
88	}
89	invalidDefaultConfig.CurrentContext = "clean"
90
91	defaultInvalid := &DirectClientConfig{
92		config:    *invalidDefaultConfig,
93		overrides: &ConfigOverrides{},
94	}
95	if _, err := defaultInvalid.ClientConfig(); err == nil || !IsConfigurationInvalid(err) {
96		t.Fatal(err)
97	}
98	config1, err := default1.ClientConfig()
99	if err != nil {
100		t.Fatal(err)
101	}
102	config2 := &restclient.Config{Host: "config2"}
103	err1 := fmt.Errorf("unique error")
104
105	testCases := map[string]struct {
106		clientConfig  *testClientConfig
107		icc           *testICC
108		defaultConfig *DirectClientConfig
109
110		checkedICC bool
111		result     *restclient.Config
112		err        error
113	}{
114		"in-cluster checked on other error": {
115			clientConfig: &testClientConfig{err: ErrEmptyConfig},
116			icc:          &testICC{},
117
118			checkedICC: true,
119			result:     nil,
120			err:        ErrEmptyConfig,
121		},
122
123		"in-cluster not checked on non-empty error": {
124			clientConfig: &testClientConfig{err: ErrEmptyCluster},
125			icc:          &testICC{},
126
127			checkedICC: false,
128			result:     nil,
129			err:        ErrEmptyCluster,
130		},
131
132		"in-cluster checked when config is default": {
133			defaultConfig: default1,
134			clientConfig:  &testClientConfig{config: config1},
135			icc:           &testICC{},
136
137			checkedICC: true,
138			result:     config1,
139			err:        nil,
140		},
141
142		"in-cluster not checked when default config is invalid": {
143			defaultConfig: defaultInvalid,
144			clientConfig:  &testClientConfig{config: config1},
145			icc:           &testICC{},
146
147			checkedICC: false,
148			result:     config1,
149			err:        nil,
150		},
151
152		"in-cluster not checked when config is not equal to default": {
153			defaultConfig: default1,
154			clientConfig:  &testClientConfig{config: config2},
155			icc:           &testICC{},
156
157			checkedICC: false,
158			result:     config2,
159			err:        nil,
160		},
161
162		"in-cluster checked when config is not equal to default and error is empty": {
163			clientConfig: &testClientConfig{config: config2, err: ErrEmptyConfig},
164			icc:          &testICC{},
165
166			checkedICC: true,
167			result:     config2,
168			err:        ErrEmptyConfig,
169		},
170
171		"in-cluster error returned when config is empty": {
172			clientConfig: &testClientConfig{err: ErrEmptyConfig},
173			icc: &testICC{
174				possible: true,
175				testClientConfig: testClientConfig{
176					err: err1,
177				},
178			},
179
180			checkedICC: true,
181			result:     nil,
182			err:        err1,
183		},
184
185		"in-cluster config returned when config is empty": {
186			clientConfig: &testClientConfig{err: ErrEmptyConfig},
187			icc: &testICC{
188				possible: true,
189				testClientConfig: testClientConfig{
190					config: config2,
191				},
192			},
193
194			checkedICC: true,
195			result:     config2,
196			err:        nil,
197		},
198
199		"in-cluster not checked when standard default is invalid": {
200			defaultConfig: &DefaultClientConfig,
201			clientConfig:  &testClientConfig{config: config2},
202			icc:           &testICC{},
203
204			checkedICC: false,
205			result:     config2,
206			err:        nil,
207		},
208	}
209
210	for name, test := range testCases {
211		c := &DeferredLoadingClientConfig{icc: test.icc}
212		c.loader = &ClientConfigLoadingRules{DefaultClientConfig: test.defaultConfig}
213		c.clientConfig = test.clientConfig
214
215		cfg, err := c.ClientConfig()
216		if test.icc.called != test.checkedICC {
217			t.Errorf("%s: unexpected in-cluster-config call %t", name, test.icc.called)
218		}
219		if err != test.err || cfg != test.result {
220			t.Errorf("%s: unexpected result: %v %#v", name, err, cfg)
221		}
222	}
223}
224
225func TestInClusterConfigNamespace(t *testing.T) {
226	err1 := fmt.Errorf("unique error")
227
228	testCases := map[string]struct {
229		clientConfig *testClientConfig
230		icc          *testICC
231		overrides    *ConfigOverrides
232
233		checkedICC bool
234		result     string
235		overridden bool
236		err        error
237	}{
238		"in-cluster checked on empty error": {
239			clientConfig: &testClientConfig{err: ErrEmptyConfig},
240			icc:          &testICC{},
241
242			checkedICC: true,
243			err:        ErrEmptyConfig,
244		},
245
246		"in-cluster not checked on non-empty error": {
247			clientConfig: &testClientConfig{err: ErrEmptyCluster},
248			icc:          &testICC{},
249
250			err: ErrEmptyCluster,
251		},
252
253		"in-cluster checked when config is default": {
254			clientConfig: &testClientConfig{},
255			icc:          &testICC{},
256
257			checkedICC: true,
258		},
259
260		"in-cluster not checked when config is not equal to default": {
261			clientConfig: &testClientConfig{namespace: "test", namespaceSpecified: true},
262			icc:          &testICC{},
263
264			result:     "test",
265			overridden: true,
266		},
267
268		"in-cluster checked when namespace is not specified, but is defaulted": {
269			clientConfig: &testClientConfig{namespace: "test", namespaceSpecified: false},
270			icc:          &testICC{},
271
272			checkedICC: true,
273			result:     "test",
274			overridden: false,
275		},
276
277		"in-cluster error returned when config is empty": {
278			clientConfig: &testClientConfig{err: ErrEmptyConfig},
279			icc: &testICC{
280				possible: true,
281				testClientConfig: testClientConfig{
282					err: err1,
283				},
284			},
285
286			checkedICC: true,
287			err:        err1,
288		},
289
290		"in-cluster config returned when config is empty": {
291			clientConfig: &testClientConfig{err: ErrEmptyConfig},
292			icc: &testICC{
293				possible: true,
294				testClientConfig: testClientConfig{
295					namespace:          "test",
296					namespaceSpecified: true,
297				},
298			},
299
300			checkedICC: true,
301			result:     "test",
302			overridden: true,
303		},
304
305		"in-cluster config returned when config is empty and namespace is defaulted but not explicitly set": {
306			clientConfig: &testClientConfig{err: ErrEmptyConfig},
307			icc: &testICC{
308				possible: true,
309				testClientConfig: testClientConfig{
310					namespace:          "test",
311					namespaceSpecified: false,
312				},
313			},
314
315			checkedICC: true,
316			result:     "test",
317			overridden: false,
318		},
319
320		"overridden context used to verify explicit namespace in config": {
321			clientConfig: &testClientConfig{
322				namespace:          "default",
323				namespaceSpecified: false, // a namespace that comes from a context is not considered overridden
324				rawconfig:          &clientcmdapi.Config{Contexts: map[string]*clientcmdapi.Context{"overridden-context": {Namespace: "default"}}},
325			},
326			overrides: &ConfigOverrides{CurrentContext: "overridden-context"},
327			icc: &testICC{
328				possible: true,
329				testClientConfig: testClientConfig{
330					namespace:          "icc",
331					namespaceSpecified: false, // a namespace that comes from icc is not considered overridden
332				},
333			},
334			checkedICC: true,
335			result:     "default",
336			overridden: false, // a namespace that comes from a context is not considered overridden
337		},
338	}
339
340	for name, test := range testCases {
341		t.Run(name, func(t *testing.T) {
342			c := &DeferredLoadingClientConfig{icc: test.icc, overrides: test.overrides}
343			c.clientConfig = test.clientConfig
344
345			ns, overridden, err := c.Namespace()
346			if test.icc.called != test.checkedICC {
347				t.Errorf("%s: unexpected in-cluster-config call %t", name, test.icc.called)
348			}
349			if err != test.err || ns != test.result || overridden != test.overridden {
350				t.Errorf("%s: unexpected result: %v %s %t", name, err, ns, overridden)
351			}
352		})
353	}
354}
355