1
16
17 package clientcmd
18
19 import (
20 "errors"
21 "os"
22 "path"
23 "path/filepath"
24 "reflect"
25 "sort"
26
27 "k8s.io/klog/v2"
28
29 restclient "k8s.io/client-go/rest"
30 clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
31 )
32
33
34 type ConfigAccess interface {
35
36 GetLoadingPrecedence() []string
37
38 GetStartingConfig() (*clientcmdapi.Config, error)
39
40 GetDefaultFilename() string
41
42 IsExplicitFile() bool
43
44 GetExplicitFile() string
45 }
46
47 type PathOptions struct {
48
49 GlobalFile string
50
51 EnvVar string
52
53 ExplicitFileFlag string
54
55
56 GlobalFileSubpath string
57
58 LoadingRules *ClientConfigLoadingRules
59 }
60
61 var (
62
63
64
65
66
67 UseModifyConfigLock = true
68 )
69
70 func (o *PathOptions) GetEnvVarFiles() []string {
71 if len(o.EnvVar) == 0 {
72 return []string{}
73 }
74
75 envVarValue := os.Getenv(o.EnvVar)
76 if len(envVarValue) == 0 {
77 return []string{}
78 }
79
80 fileList := filepath.SplitList(envVarValue)
81
82 return deduplicate(fileList)
83 }
84
85 func (o *PathOptions) GetLoadingPrecedence() []string {
86 if o.IsExplicitFile() {
87 return []string{o.GetExplicitFile()}
88 }
89
90 if envVarFiles := o.GetEnvVarFiles(); len(envVarFiles) > 0 {
91 return envVarFiles
92 }
93 return []string{o.GlobalFile}
94 }
95
96 func (o *PathOptions) GetStartingConfig() (*clientcmdapi.Config, error) {
97
98 loadingRules := *o.LoadingRules
99 loadingRules.Precedence = o.GetLoadingPrecedence()
100
101 clientConfig := NewNonInteractiveDeferredLoadingClientConfig(&loadingRules, &ConfigOverrides{})
102 rawConfig, err := clientConfig.RawConfig()
103 if os.IsNotExist(err) {
104 return clientcmdapi.NewConfig(), nil
105 }
106 if err != nil {
107 return nil, err
108 }
109
110 return &rawConfig, nil
111 }
112
113 func (o *PathOptions) GetDefaultFilename() string {
114 if o.IsExplicitFile() {
115 return o.GetExplicitFile()
116 }
117
118 if envVarFiles := o.GetEnvVarFiles(); len(envVarFiles) > 0 {
119 if len(envVarFiles) == 1 {
120 return envVarFiles[0]
121 }
122
123
124 for _, envVarFile := range envVarFiles {
125 if _, err := os.Stat(envVarFile); err == nil {
126 return envVarFile
127 }
128 }
129
130
131 return envVarFiles[len(envVarFiles)-1]
132 }
133
134 return o.GlobalFile
135 }
136
137 func (o *PathOptions) IsExplicitFile() bool {
138 return len(o.LoadingRules.ExplicitPath) > 0
139 }
140
141 func (o *PathOptions) GetExplicitFile() string {
142 return o.LoadingRules.ExplicitPath
143 }
144
145 func NewDefaultPathOptions() *PathOptions {
146 ret := &PathOptions{
147 GlobalFile: RecommendedHomeFile,
148 EnvVar: RecommendedConfigPathEnvVar,
149 ExplicitFileFlag: RecommendedConfigPathFlag,
150
151 GlobalFileSubpath: path.Join(RecommendedHomeDir, RecommendedFileName),
152
153 LoadingRules: NewDefaultClientConfigLoadingRules(),
154 }
155 ret.LoadingRules.DoNotResolvePaths = true
156
157 return ret
158 }
159
160
161
162
163
164
165
166 func ModifyConfig(configAccess ConfigAccess, newConfig clientcmdapi.Config, relativizePaths bool) error {
167 if UseModifyConfigLock {
168 possibleSources := configAccess.GetLoadingPrecedence()
169
170
171 sort.Strings(possibleSources)
172 for _, filename := range possibleSources {
173 if err := lockFile(filename); err != nil {
174 return err
175 }
176 defer unlockFile(filename)
177 }
178 }
179
180 startingConfig, err := configAccess.GetStartingConfig()
181 if err != nil {
182 return err
183 }
184
185
186
187 if reflect.DeepEqual(*startingConfig, newConfig) {
188
189 return nil
190 }
191
192 if startingConfig.CurrentContext != newConfig.CurrentContext {
193 if err := writeCurrentContext(configAccess, newConfig.CurrentContext); err != nil {
194 return err
195 }
196 }
197
198 if !reflect.DeepEqual(startingConfig.Preferences, newConfig.Preferences) {
199 if err := writePreferences(configAccess, newConfig.Preferences); err != nil {
200 return err
201 }
202 }
203
204
205 for key, cluster := range newConfig.Clusters {
206 startingCluster, exists := startingConfig.Clusters[key]
207 if !reflect.DeepEqual(cluster, startingCluster) || !exists {
208 destinationFile := cluster.LocationOfOrigin
209 if len(destinationFile) == 0 {
210 destinationFile = configAccess.GetDefaultFilename()
211 }
212
213 configToWrite, err := getConfigFromFile(destinationFile)
214 if err != nil {
215 return err
216 }
217 t := *cluster
218
219 configToWrite.Clusters[key] = &t
220 configToWrite.Clusters[key].LocationOfOrigin = destinationFile
221 if relativizePaths {
222 if err := RelativizeClusterLocalPaths(configToWrite.Clusters[key]); err != nil {
223 return err
224 }
225 }
226
227 if err := WriteToFile(*configToWrite, destinationFile); err != nil {
228 return err
229 }
230 }
231 }
232
233
234 seenConfigs := map[string]*clientcmdapi.Config{}
235
236 for key, context := range newConfig.Contexts {
237 startingContext, exists := startingConfig.Contexts[key]
238 if !reflect.DeepEqual(context, startingContext) || !exists {
239 destinationFile := context.LocationOfOrigin
240 if len(destinationFile) == 0 {
241 destinationFile = configAccess.GetDefaultFilename()
242 }
243
244
245
246
247
248 configToWrite, seen := seenConfigs[destinationFile]
249 if !seen {
250 var err error
251 configToWrite, err = getConfigFromFile(destinationFile)
252 if err != nil {
253 return err
254 }
255 seenConfigs[destinationFile] = configToWrite
256 }
257
258 configToWrite.Contexts[key] = context
259 }
260 }
261
262
263 for destinationFile, configToWrite := range seenConfigs {
264 if err := WriteToFile(*configToWrite, destinationFile); err != nil {
265 return err
266 }
267 }
268
269 for key, authInfo := range newConfig.AuthInfos {
270 startingAuthInfo, exists := startingConfig.AuthInfos[key]
271 if !reflect.DeepEqual(authInfo, startingAuthInfo) || !exists {
272 destinationFile := authInfo.LocationOfOrigin
273 if len(destinationFile) == 0 {
274 destinationFile = configAccess.GetDefaultFilename()
275 }
276
277 configToWrite, err := getConfigFromFile(destinationFile)
278 if err != nil {
279 return err
280 }
281 t := *authInfo
282 configToWrite.AuthInfos[key] = &t
283 configToWrite.AuthInfos[key].LocationOfOrigin = destinationFile
284 if relativizePaths {
285 if err := RelativizeAuthInfoLocalPaths(configToWrite.AuthInfos[key]); err != nil {
286 return err
287 }
288 }
289
290 if err := WriteToFile(*configToWrite, destinationFile); err != nil {
291 return err
292 }
293 }
294 }
295
296 for key, cluster := range startingConfig.Clusters {
297 if _, exists := newConfig.Clusters[key]; !exists {
298 destinationFile := cluster.LocationOfOrigin
299 if len(destinationFile) == 0 {
300 destinationFile = configAccess.GetDefaultFilename()
301 }
302
303 configToWrite, err := getConfigFromFile(destinationFile)
304 if err != nil {
305 return err
306 }
307 delete(configToWrite.Clusters, key)
308
309 if err := WriteToFile(*configToWrite, destinationFile); err != nil {
310 return err
311 }
312 }
313 }
314
315 for key, context := range startingConfig.Contexts {
316 if _, exists := newConfig.Contexts[key]; !exists {
317 destinationFile := context.LocationOfOrigin
318 if len(destinationFile) == 0 {
319 destinationFile = configAccess.GetDefaultFilename()
320 }
321
322 configToWrite, err := getConfigFromFile(destinationFile)
323 if err != nil {
324 return err
325 }
326 delete(configToWrite.Contexts, key)
327
328 if err := WriteToFile(*configToWrite, destinationFile); err != nil {
329 return err
330 }
331 }
332 }
333
334 for key, authInfo := range startingConfig.AuthInfos {
335 if _, exists := newConfig.AuthInfos[key]; !exists {
336 destinationFile := authInfo.LocationOfOrigin
337 if len(destinationFile) == 0 {
338 destinationFile = configAccess.GetDefaultFilename()
339 }
340
341 configToWrite, err := getConfigFromFile(destinationFile)
342 if err != nil {
343 return err
344 }
345 delete(configToWrite.AuthInfos, key)
346
347 if err := WriteToFile(*configToWrite, destinationFile); err != nil {
348 return err
349 }
350 }
351 }
352
353 return nil
354 }
355
356 func PersisterForUser(configAccess ConfigAccess, user string) restclient.AuthProviderConfigPersister {
357 return &persister{configAccess, user}
358 }
359
360 type persister struct {
361 configAccess ConfigAccess
362 user string
363 }
364
365 func (p *persister) Persist(config map[string]string) error {
366 newConfig, err := p.configAccess.GetStartingConfig()
367 if err != nil {
368 return err
369 }
370 authInfo, ok := newConfig.AuthInfos[p.user]
371 if ok && authInfo.AuthProvider != nil {
372 authInfo.AuthProvider.Config = config
373 return ModifyConfig(p.configAccess, *newConfig, false)
374 }
375 return nil
376 }
377
378
379
380
381
382 func writeCurrentContext(configAccess ConfigAccess, newCurrentContext string) error {
383 if startingConfig, err := configAccess.GetStartingConfig(); err != nil {
384 return err
385 } else if startingConfig.CurrentContext == newCurrentContext {
386 return nil
387 }
388
389 if configAccess.IsExplicitFile() {
390 file := configAccess.GetExplicitFile()
391 currConfig, err := getConfigFromFile(file)
392 if err != nil {
393 return err
394 }
395 currConfig.CurrentContext = newCurrentContext
396 if err := WriteToFile(*currConfig, file); err != nil {
397 return err
398 }
399
400 return nil
401 }
402
403 if len(newCurrentContext) > 0 {
404 destinationFile := configAccess.GetDefaultFilename()
405 config, err := getConfigFromFile(destinationFile)
406 if err != nil {
407 return err
408 }
409 config.CurrentContext = newCurrentContext
410
411 if err := WriteToFile(*config, destinationFile); err != nil {
412 return err
413 }
414
415 return nil
416 }
417
418
419 for _, file := range configAccess.GetLoadingPrecedence() {
420 if _, err := os.Stat(file); err == nil {
421 currConfig, err := getConfigFromFile(file)
422 if err != nil {
423 return err
424 }
425
426 if len(currConfig.CurrentContext) > 0 {
427 currConfig.CurrentContext = newCurrentContext
428 if err := WriteToFile(*currConfig, file); err != nil {
429 return err
430 }
431
432 return nil
433 }
434 }
435 }
436
437 return errors.New("no config found to write context")
438 }
439
440 func writePreferences(configAccess ConfigAccess, newPrefs clientcmdapi.Preferences) error {
441 if startingConfig, err := configAccess.GetStartingConfig(); err != nil {
442 return err
443 } else if reflect.DeepEqual(startingConfig.Preferences, newPrefs) {
444 return nil
445 }
446
447 if configAccess.IsExplicitFile() {
448 file := configAccess.GetExplicitFile()
449 currConfig, err := getConfigFromFile(file)
450 if err != nil {
451 return err
452 }
453 currConfig.Preferences = newPrefs
454 if err := WriteToFile(*currConfig, file); err != nil {
455 return err
456 }
457
458 return nil
459 }
460
461 for _, file := range configAccess.GetLoadingPrecedence() {
462 currConfig, err := getConfigFromFile(file)
463 if err != nil {
464 return err
465 }
466
467 if !reflect.DeepEqual(currConfig.Preferences, newPrefs) {
468 currConfig.Preferences = newPrefs
469 if err := WriteToFile(*currConfig, file); err != nil {
470 return err
471 }
472
473 return nil
474 }
475 }
476
477 return errors.New("no config found to write preferences")
478 }
479
480
481 func getConfigFromFile(filename string) (*clientcmdapi.Config, error) {
482 config, err := LoadFromFile(filename)
483 if err != nil && !os.IsNotExist(err) {
484 return nil, err
485 }
486 if config == nil {
487 config = clientcmdapi.NewConfig()
488 }
489 return config, nil
490 }
491
492
493 func GetConfigFromFileOrDie(filename string) *clientcmdapi.Config {
494 config, err := getConfigFromFile(filename)
495 if err != nil {
496 klog.FatalDepth(1, err)
497 }
498
499 return config
500 }
501
View as plain text