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 "io" 21 "sync" 22 23 "github.com/golang/glog" 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 34type 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. 47type InClusterConfig interface { 48 ClientConfig 49 Possible() bool 50} 51 52// NewNonInteractiveDeferredLoadingClientConfig creates a ConfigClientClientConfig using the passed context name 53func NewNonInteractiveDeferredLoadingClientConfig(loader ClientConfigLoader, overrides *ConfigOverrides) ClientConfig { 54 return &DeferredLoadingClientConfig{loader: loader, overrides: overrides, icc: &inClusterClientConfig{overrides: overrides}} 55} 56 57// NewInteractiveDeferredLoadingClientConfig creates a ConfigClientClientConfig using the passed context name and the fallback auth reader 58func NewInteractiveDeferredLoadingClientConfig(loader ClientConfigLoader, overrides *ConfigOverrides, fallbackReader io.Reader) ClientConfig { 59 return &DeferredLoadingClientConfig{loader: loader, overrides: overrides, icc: &inClusterClientConfig{overrides: overrides}, fallbackReader: fallbackReader} 60} 61 62func (config *DeferredLoadingClientConfig) createClientConfig() (ClientConfig, error) { 63 if config.clientConfig == nil { 64 config.loadingLock.Lock() 65 defer config.loadingLock.Unlock() 66 67 if config.clientConfig == nil { 68 mergedConfig, err := config.loader.Load() 69 if err != nil { 70 return nil, err 71 } 72 73 var mergedClientConfig ClientConfig 74 if config.fallbackReader != nil { 75 mergedClientConfig = NewInteractiveClientConfig(*mergedConfig, config.overrides.CurrentContext, config.overrides, config.fallbackReader, config.loader) 76 } else { 77 mergedClientConfig = NewNonInteractiveClientConfig(*mergedConfig, config.overrides.CurrentContext, config.overrides, config.loader) 78 } 79 80 config.clientConfig = mergedClientConfig 81 } 82 } 83 84 return config.clientConfig, nil 85} 86 87func (config *DeferredLoadingClientConfig) RawConfig() (clientcmdapi.Config, error) { 88 mergedConfig, err := config.createClientConfig() 89 if err != nil { 90 return clientcmdapi.Config{}, err 91 } 92 93 return mergedConfig.RawConfig() 94} 95 96// ClientConfig implements ClientConfig 97func (config *DeferredLoadingClientConfig) ClientConfig() (*restclient.Config, error) { 98 mergedClientConfig, err := config.createClientConfig() 99 if err != nil { 100 return nil, err 101 } 102 103 // load the configuration and return on non-empty errors and if the 104 // content differs from the default config 105 mergedConfig, err := mergedClientConfig.ClientConfig() 106 switch { 107 case err != nil: 108 if !IsEmptyConfig(err) { 109 // return on any error except empty config 110 return nil, err 111 } 112 case mergedConfig != nil: 113 // the configuration is valid, but if this is equal to the defaults we should try 114 // in-cluster configuration 115 if !config.loader.IsDefaultConfig(mergedConfig) { 116 return mergedConfig, nil 117 } 118 } 119 120 // check for in-cluster configuration and use it 121 if config.icc.Possible() { 122 glog.V(4).Infof("Using in-cluster configuration") 123 return config.icc.ClientConfig() 124 } 125 126 // return the result of the merged client config 127 return mergedConfig, err 128} 129 130// Namespace implements KubeConfig 131func (config *DeferredLoadingClientConfig) Namespace() (string, bool, error) { 132 mergedKubeConfig, err := config.createClientConfig() 133 if err != nil { 134 return "", false, err 135 } 136 137 ns, overridden, err := mergedKubeConfig.Namespace() 138 // if we get an error and it is not empty config, or if the merged config defined an explicit namespace, or 139 // if in-cluster config is not possible, return immediately 140 if (err != nil && !IsEmptyConfig(err)) || overridden || !config.icc.Possible() { 141 // return on any error except empty config 142 return ns, overridden, err 143 } 144 145 if len(ns) > 0 { 146 // if we got a non-default namespace from the kubeconfig, use it 147 if ns != "default" { 148 return ns, false, nil 149 } 150 151 // if we got a default namespace, determine whether it was explicit or implicit 152 if raw, err := mergedKubeConfig.RawConfig(); err == nil { 153 if context := raw.Contexts[raw.CurrentContext]; context != nil && len(context.Namespace) > 0 { 154 return ns, false, nil 155 } 156 } 157 } 158 159 glog.V(4).Infof("Using in-cluster namespace") 160 161 // allow the namespace from the service account token directory to be used. 162 return config.icc.Namespace() 163} 164 165// ConfigAccess implements ClientConfig 166func (config *DeferredLoadingClientConfig) ConfigAccess() ConfigAccess { 167 return config.loader 168} 169