blob: 690afce0cd653344edd5a5a8282a4269724f12f4 [file] [log] [blame]
Matteo Scandoloa4285862020-12-01 18:10:10 -08001/*
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 "io"
22 "io/ioutil"
23 "net/http"
24 "net/url"
25 "os"
26 "strings"
27 "unicode"
28
29 restclient "k8s.io/client-go/rest"
30 clientauth "k8s.io/client-go/tools/auth"
31 clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
32 "k8s.io/klog/v2"
33
34 "github.com/imdario/mergo"
35)
36
37var (
38 // ClusterDefaults has the same behavior as the old EnvVar and DefaultCluster fields
39 // DEPRECATED will be replaced
40 ClusterDefaults = clientcmdapi.Cluster{Server: getDefaultServer()}
41 // DefaultClientConfig represents the legacy behavior of this package for defaulting
42 // DEPRECATED will be replace
43 DefaultClientConfig = DirectClientConfig{*clientcmdapi.NewConfig(), "", &ConfigOverrides{
44 ClusterDefaults: ClusterDefaults,
45 }, nil, NewDefaultClientConfigLoadingRules(), promptedCredentials{}}
46)
47
48// getDefaultServer returns a default setting for DefaultClientConfig
49// DEPRECATED
50func getDefaultServer() string {
51 if server := os.Getenv("KUBERNETES_MASTER"); len(server) > 0 {
52 return server
53 }
54 return "http://localhost:8080"
55}
56
57// ClientConfig is used to make it easy to get an api server client
58type ClientConfig interface {
59 // RawConfig returns the merged result of all overrides
60 RawConfig() (clientcmdapi.Config, error)
61 // ClientConfig returns a complete client config
62 ClientConfig() (*restclient.Config, error)
63 // Namespace returns the namespace resulting from the merged
64 // result of all overrides and a boolean indicating if it was
65 // overridden
66 Namespace() (string, bool, error)
67 // ConfigAccess returns the rules for loading/persisting the config.
68 ConfigAccess() ConfigAccess
69}
70
71type PersistAuthProviderConfigForUser func(user string) restclient.AuthProviderConfigPersister
72
73type promptedCredentials struct {
74 username string
75 password string
76}
77
78// DirectClientConfig is a ClientConfig interface that is backed by a clientcmdapi.Config, options overrides, and an optional fallbackReader for auth information
79type DirectClientConfig struct {
80 config clientcmdapi.Config
81 contextName string
82 overrides *ConfigOverrides
83 fallbackReader io.Reader
84 configAccess ConfigAccess
85 // promptedCredentials store the credentials input by the user
86 promptedCredentials promptedCredentials
87}
88
89// NewDefaultClientConfig creates a DirectClientConfig using the config.CurrentContext as the context name
90func NewDefaultClientConfig(config clientcmdapi.Config, overrides *ConfigOverrides) ClientConfig {
91 return &DirectClientConfig{config, config.CurrentContext, overrides, nil, NewDefaultClientConfigLoadingRules(), promptedCredentials{}}
92}
93
94// NewNonInteractiveClientConfig creates a DirectClientConfig using the passed context name and does not have a fallback reader for auth information
95func NewNonInteractiveClientConfig(config clientcmdapi.Config, contextName string, overrides *ConfigOverrides, configAccess ConfigAccess) ClientConfig {
96 return &DirectClientConfig{config, contextName, overrides, nil, configAccess, promptedCredentials{}}
97}
98
99// NewInteractiveClientConfig creates a DirectClientConfig using the passed context name and a reader in case auth information is not provided via files or flags
100func NewInteractiveClientConfig(config clientcmdapi.Config, contextName string, overrides *ConfigOverrides, fallbackReader io.Reader, configAccess ConfigAccess) ClientConfig {
101 return &DirectClientConfig{config, contextName, overrides, fallbackReader, configAccess, promptedCredentials{}}
102}
103
104// NewClientConfigFromBytes takes your kubeconfig and gives you back a ClientConfig
105func NewClientConfigFromBytes(configBytes []byte) (ClientConfig, error) {
106 config, err := Load(configBytes)
107 if err != nil {
108 return nil, err
109 }
110
111 return &DirectClientConfig{*config, "", &ConfigOverrides{}, nil, nil, promptedCredentials{}}, nil
112}
113
114// RESTConfigFromKubeConfig is a convenience method to give back a restconfig from your kubeconfig bytes.
115// For programmatic access, this is what you want 80% of the time
116func RESTConfigFromKubeConfig(configBytes []byte) (*restclient.Config, error) {
117 clientConfig, err := NewClientConfigFromBytes(configBytes)
118 if err != nil {
119 return nil, err
120 }
121 return clientConfig.ClientConfig()
122}
123
124func (config *DirectClientConfig) RawConfig() (clientcmdapi.Config, error) {
125 return config.config, nil
126}
127
128// ClientConfig implements ClientConfig
129func (config *DirectClientConfig) ClientConfig() (*restclient.Config, error) {
130 // check that getAuthInfo, getContext, and getCluster do not return an error.
131 // Do this before checking if the current config is usable in the event that an
132 // AuthInfo, Context, or Cluster config with user-defined names are not found.
133 // This provides a user with the immediate cause for error if one is found
134 configAuthInfo, err := config.getAuthInfo()
135 if err != nil {
136 return nil, err
137 }
138
139 _, err = config.getContext()
140 if err != nil {
141 return nil, err
142 }
143
144 configClusterInfo, err := config.getCluster()
145 if err != nil {
146 return nil, err
147 }
148
149 if err := config.ConfirmUsable(); err != nil {
150 return nil, err
151 }
152
153 clientConfig := &restclient.Config{}
154 clientConfig.Host = configClusterInfo.Server
155 if configClusterInfo.ProxyURL != "" {
156 u, err := parseProxyURL(configClusterInfo.ProxyURL)
157 if err != nil {
158 return nil, err
159 }
160 clientConfig.Proxy = http.ProxyURL(u)
161 }
162
163 if config.overrides != nil && len(config.overrides.Timeout) > 0 {
164 timeout, err := ParseTimeout(config.overrides.Timeout)
165 if err != nil {
166 return nil, err
167 }
168 clientConfig.Timeout = timeout
169 }
170
171 if u, err := url.ParseRequestURI(clientConfig.Host); err == nil && u.Opaque == "" && len(u.Path) > 1 {
172 u.RawQuery = ""
173 u.Fragment = ""
174 clientConfig.Host = u.String()
175 }
176 if len(configAuthInfo.Impersonate) > 0 {
177 clientConfig.Impersonate = restclient.ImpersonationConfig{
178 UserName: configAuthInfo.Impersonate,
179 Groups: configAuthInfo.ImpersonateGroups,
180 Extra: configAuthInfo.ImpersonateUserExtra,
181 }
182 }
183
184 // only try to read the auth information if we are secure
185 if restclient.IsConfigTransportTLS(*clientConfig) {
186 var err error
187 var persister restclient.AuthProviderConfigPersister
188 if config.configAccess != nil {
189 authInfoName, _ := config.getAuthInfoName()
190 persister = PersisterForUser(config.configAccess, authInfoName)
191 }
192 userAuthPartialConfig, err := config.getUserIdentificationPartialConfig(configAuthInfo, config.fallbackReader, persister)
193 if err != nil {
194 return nil, err
195 }
196 mergo.MergeWithOverwrite(clientConfig, userAuthPartialConfig)
197
198 serverAuthPartialConfig, err := getServerIdentificationPartialConfig(configAuthInfo, configClusterInfo)
199 if err != nil {
200 return nil, err
201 }
202 mergo.MergeWithOverwrite(clientConfig, serverAuthPartialConfig)
203 }
204
205 return clientConfig, nil
206}
207
208// clientauth.Info object contain both user identification and server identification. We want different precedence orders for
209// both, so we have to split the objects and merge them separately
210// we want this order of precedence for the server identification
211// 1. configClusterInfo (the final result of command line flags and merged .kubeconfig files)
212// 2. configAuthInfo.auth-path (this file can contain information that conflicts with #1, and we want #1 to win the priority)
213// 3. load the ~/.kubernetes_auth file as a default
214func getServerIdentificationPartialConfig(configAuthInfo clientcmdapi.AuthInfo, configClusterInfo clientcmdapi.Cluster) (*restclient.Config, error) {
215 mergedConfig := &restclient.Config{}
216
217 // configClusterInfo holds the information identify the server provided by .kubeconfig
218 configClientConfig := &restclient.Config{}
219 configClientConfig.CAFile = configClusterInfo.CertificateAuthority
220 configClientConfig.CAData = configClusterInfo.CertificateAuthorityData
221 configClientConfig.Insecure = configClusterInfo.InsecureSkipTLSVerify
222 configClientConfig.ServerName = configClusterInfo.TLSServerName
223 mergo.MergeWithOverwrite(mergedConfig, configClientConfig)
224
225 return mergedConfig, nil
226}
227
228// clientauth.Info object contain both user identification and server identification. We want different precedence orders for
229// both, so we have to split the objects and merge them separately
230// we want this order of precedence for user identification
231// 1. configAuthInfo minus auth-path (the final result of command line flags and merged .kubeconfig files)
232// 2. configAuthInfo.auth-path (this file can contain information that conflicts with #1, and we want #1 to win the priority)
233// 3. if there is not enough information to identify the user, load try the ~/.kubernetes_auth file
234// 4. if there is not enough information to identify the user, prompt if possible
235func (config *DirectClientConfig) getUserIdentificationPartialConfig(configAuthInfo clientcmdapi.AuthInfo, fallbackReader io.Reader, persistAuthConfig restclient.AuthProviderConfigPersister) (*restclient.Config, error) {
236 mergedConfig := &restclient.Config{}
237
238 // blindly overwrite existing values based on precedence
239 if len(configAuthInfo.Token) > 0 {
240 mergedConfig.BearerToken = configAuthInfo.Token
241 mergedConfig.BearerTokenFile = configAuthInfo.TokenFile
242 } else if len(configAuthInfo.TokenFile) > 0 {
243 tokenBytes, err := ioutil.ReadFile(configAuthInfo.TokenFile)
244 if err != nil {
245 return nil, err
246 }
247 mergedConfig.BearerToken = string(tokenBytes)
248 mergedConfig.BearerTokenFile = configAuthInfo.TokenFile
249 }
250 if len(configAuthInfo.Impersonate) > 0 {
251 mergedConfig.Impersonate = restclient.ImpersonationConfig{
252 UserName: configAuthInfo.Impersonate,
253 Groups: configAuthInfo.ImpersonateGroups,
254 Extra: configAuthInfo.ImpersonateUserExtra,
255 }
256 }
257 if len(configAuthInfo.ClientCertificate) > 0 || len(configAuthInfo.ClientCertificateData) > 0 {
258 mergedConfig.CertFile = configAuthInfo.ClientCertificate
259 mergedConfig.CertData = configAuthInfo.ClientCertificateData
260 mergedConfig.KeyFile = configAuthInfo.ClientKey
261 mergedConfig.KeyData = configAuthInfo.ClientKeyData
262 }
263 if len(configAuthInfo.Username) > 0 || len(configAuthInfo.Password) > 0 {
264 mergedConfig.Username = configAuthInfo.Username
265 mergedConfig.Password = configAuthInfo.Password
266 }
267 if configAuthInfo.AuthProvider != nil {
268 mergedConfig.AuthProvider = configAuthInfo.AuthProvider
269 mergedConfig.AuthConfigPersister = persistAuthConfig
270 }
271 if configAuthInfo.Exec != nil {
272 mergedConfig.ExecProvider = configAuthInfo.Exec
273 mergedConfig.ExecProvider.InstallHint = cleanANSIEscapeCodes(mergedConfig.ExecProvider.InstallHint)
274 }
275
276 // if there still isn't enough information to authenticate the user, try prompting
277 if !canIdentifyUser(*mergedConfig) && (fallbackReader != nil) {
278 if len(config.promptedCredentials.username) > 0 && len(config.promptedCredentials.password) > 0 {
279 mergedConfig.Username = config.promptedCredentials.username
280 mergedConfig.Password = config.promptedCredentials.password
281 return mergedConfig, nil
282 }
283 prompter := NewPromptingAuthLoader(fallbackReader)
284 promptedAuthInfo, err := prompter.Prompt()
285 if err != nil {
286 return nil, err
287 }
288 promptedConfig := makeUserIdentificationConfig(*promptedAuthInfo)
289 previouslyMergedConfig := mergedConfig
290 mergedConfig = &restclient.Config{}
291 mergo.MergeWithOverwrite(mergedConfig, promptedConfig)
292 mergo.MergeWithOverwrite(mergedConfig, previouslyMergedConfig)
293 config.promptedCredentials.username = mergedConfig.Username
294 config.promptedCredentials.password = mergedConfig.Password
295 }
296
297 return mergedConfig, nil
298}
299
300// makeUserIdentificationFieldsConfig returns a client.Config capable of being merged using mergo for only user identification information
301func makeUserIdentificationConfig(info clientauth.Info) *restclient.Config {
302 config := &restclient.Config{}
303 config.Username = info.User
304 config.Password = info.Password
305 config.CertFile = info.CertFile
306 config.KeyFile = info.KeyFile
307 config.BearerToken = info.BearerToken
308 return config
309}
310
311func canIdentifyUser(config restclient.Config) bool {
312 return len(config.Username) > 0 ||
313 (len(config.CertFile) > 0 || len(config.CertData) > 0) ||
314 len(config.BearerToken) > 0 ||
315 config.AuthProvider != nil ||
316 config.ExecProvider != nil
317}
318
319// cleanANSIEscapeCodes takes an arbitrary string and ensures that there are no
320// ANSI escape sequences that could put the terminal in a weird state (e.g.,
321// "\e[1m" bolds text)
322func cleanANSIEscapeCodes(s string) string {
323 // spaceControlCharacters includes tab, new line, vertical tab, new page, and
324 // carriage return. These are in the unicode.Cc category, but that category also
325 // contains ESC (U+001B) which we don't want.
326 spaceControlCharacters := unicode.RangeTable{
327 R16: []unicode.Range16{
328 {Lo: 0x0009, Hi: 0x000D, Stride: 1},
329 },
330 }
331
332 // Why not make this deny-only (instead of allow-only)? Because unicode.C
333 // contains newline and tab characters that we want.
334 allowedRanges := []*unicode.RangeTable{
335 unicode.L,
336 unicode.M,
337 unicode.N,
338 unicode.P,
339 unicode.S,
340 unicode.Z,
341 &spaceControlCharacters,
342 }
343 builder := strings.Builder{}
344 for _, roon := range s {
345 if unicode.IsOneOf(allowedRanges, roon) {
346 builder.WriteRune(roon) // returns nil error, per go doc
347 } else {
348 fmt.Fprintf(&builder, "%U", roon)
349 }
350 }
351 return builder.String()
352}
353
354// Namespace implements ClientConfig
355func (config *DirectClientConfig) Namespace() (string, bool, error) {
356 if config.overrides != nil && config.overrides.Context.Namespace != "" {
357 // In the event we have an empty config but we do have a namespace override, we should return
358 // the namespace override instead of having config.ConfirmUsable() return an error. This allows
359 // things like in-cluster clients to execute `kubectl get pods --namespace=foo` and have the
360 // --namespace flag honored instead of being ignored.
361 return config.overrides.Context.Namespace, true, nil
362 }
363
364 if err := config.ConfirmUsable(); err != nil {
365 return "", false, err
366 }
367
368 configContext, err := config.getContext()
369 if err != nil {
370 return "", false, err
371 }
372
373 if len(configContext.Namespace) == 0 {
374 return "default", false, nil
375 }
376
377 return configContext.Namespace, false, nil
378}
379
380// ConfigAccess implements ClientConfig
381func (config *DirectClientConfig) ConfigAccess() ConfigAccess {
382 return config.configAccess
383}
384
385// ConfirmUsable looks a particular context and determines if that particular part of the config is useable. There might still be errors in the config,
386// but no errors in the sections requested or referenced. It does not return early so that it can find as many errors as possible.
387func (config *DirectClientConfig) ConfirmUsable() error {
388 validationErrors := make([]error, 0)
389
390 var contextName string
391 if len(config.contextName) != 0 {
392 contextName = config.contextName
393 } else {
394 contextName = config.config.CurrentContext
395 }
396
397 if len(contextName) > 0 {
398 _, exists := config.config.Contexts[contextName]
399 if !exists {
400 validationErrors = append(validationErrors, &errContextNotFound{contextName})
401 }
402 }
403
404 authInfoName, _ := config.getAuthInfoName()
405 authInfo, _ := config.getAuthInfo()
406 validationErrors = append(validationErrors, validateAuthInfo(authInfoName, authInfo)...)
407 clusterName, _ := config.getClusterName()
408 cluster, _ := config.getCluster()
409 validationErrors = append(validationErrors, validateClusterInfo(clusterName, cluster)...)
410 // when direct client config is specified, and our only error is that no server is defined, we should
411 // return a standard "no config" error
412 if len(validationErrors) == 1 && validationErrors[0] == ErrEmptyCluster {
413 return newErrConfigurationInvalid([]error{ErrEmptyConfig})
414 }
415 return newErrConfigurationInvalid(validationErrors)
416}
417
418// getContextName returns the default, or user-set context name, and a boolean that indicates
419// whether the default context name has been overwritten by a user-set flag, or left as its default value
420func (config *DirectClientConfig) getContextName() (string, bool) {
421 if config.overrides != nil && len(config.overrides.CurrentContext) != 0 {
422 return config.overrides.CurrentContext, true
423 }
424 if len(config.contextName) != 0 {
425 return config.contextName, false
426 }
427
428 return config.config.CurrentContext, false
429}
430
431// getAuthInfoName returns a string containing the current authinfo name for the current context,
432// and a boolean indicating whether the default authInfo name is overwritten by a user-set flag, or
433// left as its default value
434func (config *DirectClientConfig) getAuthInfoName() (string, bool) {
435 if config.overrides != nil && len(config.overrides.Context.AuthInfo) != 0 {
436 return config.overrides.Context.AuthInfo, true
437 }
438 context, _ := config.getContext()
439 return context.AuthInfo, false
440}
441
442// getClusterName returns a string containing the default, or user-set cluster name, and a boolean
443// indicating whether the default clusterName has been overwritten by a user-set flag, or left as
444// its default value
445func (config *DirectClientConfig) getClusterName() (string, bool) {
446 if config.overrides != nil && len(config.overrides.Context.Cluster) != 0 {
447 return config.overrides.Context.Cluster, true
448 }
449 context, _ := config.getContext()
450 return context.Cluster, false
451}
452
453// getContext returns the clientcmdapi.Context, or an error if a required context is not found.
454func (config *DirectClientConfig) getContext() (clientcmdapi.Context, error) {
455 contexts := config.config.Contexts
456 contextName, required := config.getContextName()
457
458 mergedContext := clientcmdapi.NewContext()
459 if configContext, exists := contexts[contextName]; exists {
460 mergo.MergeWithOverwrite(mergedContext, configContext)
461 } else if required {
462 return clientcmdapi.Context{}, fmt.Errorf("context %q does not exist", contextName)
463 }
464 if config.overrides != nil {
465 mergo.MergeWithOverwrite(mergedContext, config.overrides.Context)
466 }
467
468 return *mergedContext, nil
469}
470
471// getAuthInfo returns the clientcmdapi.AuthInfo, or an error if a required auth info is not found.
472func (config *DirectClientConfig) getAuthInfo() (clientcmdapi.AuthInfo, error) {
473 authInfos := config.config.AuthInfos
474 authInfoName, required := config.getAuthInfoName()
475
476 mergedAuthInfo := clientcmdapi.NewAuthInfo()
477 if configAuthInfo, exists := authInfos[authInfoName]; exists {
478 mergo.MergeWithOverwrite(mergedAuthInfo, configAuthInfo)
479 } else if required {
480 return clientcmdapi.AuthInfo{}, fmt.Errorf("auth info %q does not exist", authInfoName)
481 }
482 if config.overrides != nil {
483 mergo.MergeWithOverwrite(mergedAuthInfo, config.overrides.AuthInfo)
484 }
485
486 return *mergedAuthInfo, nil
487}
488
489// getCluster returns the clientcmdapi.Cluster, or an error if a required cluster is not found.
490func (config *DirectClientConfig) getCluster() (clientcmdapi.Cluster, error) {
491 clusterInfos := config.config.Clusters
492 clusterInfoName, required := config.getClusterName()
493
494 mergedClusterInfo := clientcmdapi.NewCluster()
495 if config.overrides != nil {
496 mergo.MergeWithOverwrite(mergedClusterInfo, config.overrides.ClusterDefaults)
497 }
498 if configClusterInfo, exists := clusterInfos[clusterInfoName]; exists {
499 mergo.MergeWithOverwrite(mergedClusterInfo, configClusterInfo)
500 } else if required {
501 return clientcmdapi.Cluster{}, fmt.Errorf("cluster %q does not exist", clusterInfoName)
502 }
503 if config.overrides != nil {
504 mergo.MergeWithOverwrite(mergedClusterInfo, config.overrides.ClusterInfo)
505 }
506
507 // * An override of --insecure-skip-tls-verify=true and no accompanying CA/CA data should clear already-set CA/CA data
508 // otherwise, a kubeconfig containing a CA reference would return an error that "CA and insecure-skip-tls-verify couldn't both be set".
509 // * An override of --certificate-authority should also override TLS skip settings and CA data, otherwise existing CA data will take precedence.
510 if config.overrides != nil {
511 caLen := len(config.overrides.ClusterInfo.CertificateAuthority)
512 caDataLen := len(config.overrides.ClusterInfo.CertificateAuthorityData)
513 if config.overrides.ClusterInfo.InsecureSkipTLSVerify || caLen > 0 || caDataLen > 0 {
514 mergedClusterInfo.InsecureSkipTLSVerify = config.overrides.ClusterInfo.InsecureSkipTLSVerify
515 mergedClusterInfo.CertificateAuthority = config.overrides.ClusterInfo.CertificateAuthority
516 mergedClusterInfo.CertificateAuthorityData = config.overrides.ClusterInfo.CertificateAuthorityData
517 }
518
519 // if the --tls-server-name has been set in overrides, use that value.
520 // if the --server has been set in overrides, then use the value of --tls-server-name specified on the CLI too. This gives the property
521 // that setting a --server will effectively clear the KUBECONFIG value of tls-server-name if it is specified on the command line which is
522 // usually correct.
523 if config.overrides.ClusterInfo.TLSServerName != "" || config.overrides.ClusterInfo.Server != "" {
524 mergedClusterInfo.TLSServerName = config.overrides.ClusterInfo.TLSServerName
525 }
526 }
527
528 return *mergedClusterInfo, nil
529}
530
531// inClusterClientConfig makes a config that will work from within a kubernetes cluster container environment.
532// Can take options overrides for flags explicitly provided to the command inside the cluster container.
533type inClusterClientConfig struct {
534 overrides *ConfigOverrides
535 inClusterConfigProvider func() (*restclient.Config, error)
536}
537
538var _ ClientConfig = &inClusterClientConfig{}
539
540func (config *inClusterClientConfig) RawConfig() (clientcmdapi.Config, error) {
541 return clientcmdapi.Config{}, fmt.Errorf("inCluster environment config doesn't support multiple clusters")
542}
543
544func (config *inClusterClientConfig) ClientConfig() (*restclient.Config, error) {
545 if config.inClusterConfigProvider == nil {
546 config.inClusterConfigProvider = restclient.InClusterConfig
547 }
548
549 icc, err := config.inClusterConfigProvider()
550 if err != nil {
551 return nil, err
552 }
553
554 // in-cluster configs only takes a host, token, or CA file
555 // if any of them were individually provided, overwrite anything else
556 if config.overrides != nil {
557 if server := config.overrides.ClusterInfo.Server; len(server) > 0 {
558 icc.Host = server
559 }
560 if len(config.overrides.AuthInfo.Token) > 0 || len(config.overrides.AuthInfo.TokenFile) > 0 {
561 icc.BearerToken = config.overrides.AuthInfo.Token
562 icc.BearerTokenFile = config.overrides.AuthInfo.TokenFile
563 }
564 if certificateAuthorityFile := config.overrides.ClusterInfo.CertificateAuthority; len(certificateAuthorityFile) > 0 {
565 icc.TLSClientConfig.CAFile = certificateAuthorityFile
566 }
567 }
568
569 return icc, err
570}
571
572func (config *inClusterClientConfig) Namespace() (string, bool, error) {
573 // This way assumes you've set the POD_NAMESPACE environment variable using the downward API.
574 // This check has to be done first for backwards compatibility with the way InClusterConfig was originally set up
575 if ns := os.Getenv("POD_NAMESPACE"); ns != "" {
576 return ns, false, nil
577 }
578
579 // Fall back to the namespace associated with the service account token, if available
580 if data, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace"); err == nil {
581 if ns := strings.TrimSpace(string(data)); len(ns) > 0 {
582 return ns, false, nil
583 }
584 }
585
586 return "default", false, nil
587}
588
589func (config *inClusterClientConfig) ConfigAccess() ConfigAccess {
590 return NewDefaultClientConfigLoadingRules()
591}
592
593// Possible returns true if loading an inside-kubernetes-cluster is possible.
594func (config *inClusterClientConfig) Possible() bool {
595 fi, err := os.Stat("/var/run/secrets/kubernetes.io/serviceaccount/token")
596 return os.Getenv("KUBERNETES_SERVICE_HOST") != "" &&
597 os.Getenv("KUBERNETES_SERVICE_PORT") != "" &&
598 err == nil && !fi.IsDir()
599}
600
601// BuildConfigFromFlags is a helper function that builds configs from a master
602// url or a kubeconfig filepath. These are passed in as command line flags for cluster
603// components. Warnings should reflect this usage. If neither masterUrl or kubeconfigPath
604// are passed in we fallback to inClusterConfig. If inClusterConfig fails, we fallback
605// to the default config.
606func BuildConfigFromFlags(masterUrl, kubeconfigPath string) (*restclient.Config, error) {
607 if kubeconfigPath == "" && masterUrl == "" {
608 klog.Warningf("Neither --kubeconfig nor --master was specified. Using the inClusterConfig. This might not work.")
609 kubeconfig, err := restclient.InClusterConfig()
610 if err == nil {
611 return kubeconfig, nil
612 }
613 klog.Warning("error creating inClusterConfig, falling back to default config: ", err)
614 }
615 return NewNonInteractiveDeferredLoadingClientConfig(
616 &ClientConfigLoadingRules{ExplicitPath: kubeconfigPath},
617 &ConfigOverrides{ClusterInfo: clientcmdapi.Cluster{Server: masterUrl}}).ClientConfig()
618}
619
620// BuildConfigFromKubeconfigGetter is a helper function that builds configs from a master
621// url and a kubeconfigGetter.
622func BuildConfigFromKubeconfigGetter(masterUrl string, kubeconfigGetter KubeconfigGetter) (*restclient.Config, error) {
623 // TODO: We do not need a DeferredLoader here. Refactor code and see if we can use DirectClientConfig here.
624 cc := NewNonInteractiveDeferredLoadingClientConfig(
625 &ClientConfigGetter{kubeconfigGetter: kubeconfigGetter},
626 &ConfigOverrides{ClusterInfo: clientcmdapi.Cluster{Server: masterUrl}})
627 return cc.ClientConfig()
628}