k8s.io/client-go@v0.31.1/tools/clientcmd/merged_client_builder.go (about) 1 /* 2 Copyright 2014 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 clientcmd 18 19 import ( 20 "io" 21 "sync" 22 23 "k8s.io/klog/v2" 24 25 restclient "k8s.io/client-go/rest" 26 clientcmdapi "k8s.io/client-go/tools/clientcmd/api" 27 ) 28 29 // DeferredLoadingClientConfig is a ClientConfig interface that is backed by a client config loader. 30 // It is used in cases where the loading rules may change after you've instantiated them and you want to be sure that 31 // the most recent rules are used. This is useful in cases where you bind flags to loading rule parameters before 32 // the parse happens and you want your calling code to be ignorant of how the values are being mutated to avoid 33 // passing extraneous information down a call stack 34 type DeferredLoadingClientConfig struct { 35 loader ClientConfigLoader 36 overrides *ConfigOverrides 37 fallbackReader io.Reader 38 39 clientConfig ClientConfig 40 loadingLock sync.Mutex 41 42 // provided for testing 43 icc InClusterConfig 44 } 45 46 // InClusterConfig abstracts details of whether the client is running in a cluster for testing. 47 type InClusterConfig interface { 48 ClientConfig 49 Possible() bool 50 } 51 52 // NewNonInteractiveDeferredLoadingClientConfig creates a ClientConfig using the passed context name 53 func NewNonInteractiveDeferredLoadingClientConfig(loader ClientConfigLoader, overrides *ConfigOverrides) ClientConfig { 54 return &DeferredLoadingClientConfig{loader: loader, overrides: overrides, icc: &inClusterClientConfig{overrides: overrides}} 55 } 56 57 // NewInteractiveDeferredLoadingClientConfig creates a ClientConfig using the passed context name and the fallback auth reader 58 func NewInteractiveDeferredLoadingClientConfig(loader ClientConfigLoader, overrides *ConfigOverrides, fallbackReader io.Reader) ClientConfig { 59 return &DeferredLoadingClientConfig{loader: loader, overrides: overrides, icc: &inClusterClientConfig{overrides: overrides}, fallbackReader: fallbackReader} 60 } 61 62 func (config *DeferredLoadingClientConfig) createClientConfig() (ClientConfig, error) { 63 config.loadingLock.Lock() 64 defer config.loadingLock.Unlock() 65 66 if config.clientConfig != nil { 67 return config.clientConfig, nil 68 } 69 mergedConfig, err := config.loader.Load() 70 if err != nil { 71 return nil, err 72 } 73 74 var currentContext string 75 if config.overrides != nil { 76 currentContext = config.overrides.CurrentContext 77 } 78 if config.fallbackReader != nil { 79 config.clientConfig = NewInteractiveClientConfig(*mergedConfig, currentContext, config.overrides, config.fallbackReader, config.loader) 80 } else { 81 config.clientConfig = NewNonInteractiveClientConfig(*mergedConfig, currentContext, config.overrides, config.loader) 82 } 83 return config.clientConfig, nil 84 } 85 86 func (config *DeferredLoadingClientConfig) RawConfig() (clientcmdapi.Config, error) { 87 mergedConfig, err := config.createClientConfig() 88 if err != nil { 89 return clientcmdapi.Config{}, err 90 } 91 92 return mergedConfig.RawConfig() 93 } 94 95 // ClientConfig implements ClientConfig 96 func (config *DeferredLoadingClientConfig) ClientConfig() (*restclient.Config, error) { 97 mergedClientConfig, err := config.createClientConfig() 98 if err != nil { 99 return nil, err 100 } 101 102 // load the configuration and return on non-empty errors and if the 103 // content differs from the default config 104 mergedConfig, err := mergedClientConfig.ClientConfig() 105 switch { 106 case err != nil: 107 if !IsEmptyConfig(err) { 108 // return on any error except empty config 109 return nil, err 110 } 111 case mergedConfig != nil: 112 // the configuration is valid, but if this is equal to the defaults we should try 113 // in-cluster configuration 114 if !config.loader.IsDefaultConfig(mergedConfig) { 115 return mergedConfig, nil 116 } 117 } 118 119 // check for in-cluster configuration and use it 120 if config.icc.Possible() { 121 klog.V(4).Infof("Using in-cluster configuration") 122 return config.icc.ClientConfig() 123 } 124 125 // return the result of the merged client config 126 return mergedConfig, err 127 } 128 129 // Namespace implements KubeConfig 130 func (config *DeferredLoadingClientConfig) Namespace() (string, bool, error) { 131 mergedKubeConfig, err := config.createClientConfig() 132 if err != nil { 133 return "", false, err 134 } 135 136 ns, overridden, err := mergedKubeConfig.Namespace() 137 // if we get an error and it is not empty config, or if the merged config defined an explicit namespace, or 138 // if in-cluster config is not possible, return immediately 139 if (err != nil && !IsEmptyConfig(err)) || overridden || !config.icc.Possible() { 140 // return on any error except empty config 141 return ns, overridden, err 142 } 143 144 if len(ns) > 0 { 145 // if we got a non-default namespace from the kubeconfig, use it 146 if ns != "default" { 147 return ns, false, nil 148 } 149 150 // if we got a default namespace, determine whether it was explicit or implicit 151 if raw, err := mergedKubeConfig.RawConfig(); err == nil { 152 // determine the current context 153 currentContext := raw.CurrentContext 154 if config.overrides != nil && len(config.overrides.CurrentContext) > 0 { 155 currentContext = config.overrides.CurrentContext 156 } 157 if context := raw.Contexts[currentContext]; context != nil && len(context.Namespace) > 0 { 158 return ns, false, nil 159 } 160 } 161 } 162 163 klog.V(4).Infof("Using in-cluster namespace") 164 165 // allow the namespace from the service account token directory to be used. 166 return config.icc.Namespace() 167 } 168 169 // ConfigAccess implements ClientConfig 170 func (config *DeferredLoadingClientConfig) ConfigAccess() ConfigAccess { 171 return config.loader 172 }