mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-10-31 16:13:45 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			573 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			573 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| /*
 | |
| Copyright 2014 The Kubernetes Authors.
 | |
| 
 | |
| Licensed under the Apache License, Version 2.0 (the "License");
 | |
| you may not use this file except in compliance with the License.
 | |
| You may obtain a copy of the License at
 | |
| 
 | |
|     http://www.apache.org/licenses/LICENSE-2.0
 | |
| 
 | |
| Unless required by applicable law or agreed to in writing, software
 | |
| distributed under the License is distributed on an "AS IS" BASIS,
 | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| See the License for the specific language governing permissions and
 | |
| limitations under the License.
 | |
| */
 | |
| 
 | |
| package clientcmd
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"io/ioutil"
 | |
| 	"net/url"
 | |
| 	"os"
 | |
| 	"strings"
 | |
| 
 | |
| 	"github.com/golang/glog"
 | |
| 	"github.com/imdario/mergo"
 | |
| 
 | |
| 	restclient "k8s.io/client-go/rest"
 | |
| 	clientauth "k8s.io/client-go/tools/auth"
 | |
| 	clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
 | |
| )
 | |
| 
 | |
| var (
 | |
| 	// ClusterDefaults has the same behavior as the old EnvVar and DefaultCluster fields
 | |
| 	// DEPRECATED will be replaced
 | |
| 	ClusterDefaults = clientcmdapi.Cluster{Server: getDefaultServer()}
 | |
| 	// DefaultClientConfig represents the legacy behavior of this package for defaulting
 | |
| 	// DEPRECATED will be replace
 | |
| 	DefaultClientConfig = DirectClientConfig{*clientcmdapi.NewConfig(), "", &ConfigOverrides{
 | |
| 		ClusterDefaults: ClusterDefaults,
 | |
| 	}, nil, NewDefaultClientConfigLoadingRules(), promptedCredentials{}}
 | |
| )
 | |
| 
 | |
| // getDefaultServer returns a default setting for DefaultClientConfig
 | |
| // DEPRECATED
 | |
| func getDefaultServer() string {
 | |
| 	if server := os.Getenv("KUBERNETES_MASTER"); len(server) > 0 {
 | |
| 		return server
 | |
| 	}
 | |
| 	return "http://localhost:8080"
 | |
| }
 | |
| 
 | |
| // ClientConfig is used to make it easy to get an api server client
 | |
| type ClientConfig interface {
 | |
| 	// RawConfig returns the merged result of all overrides
 | |
| 	RawConfig() (clientcmdapi.Config, error)
 | |
| 	// ClientConfig returns a complete client config
 | |
| 	ClientConfig() (*restclient.Config, error)
 | |
| 	// Namespace returns the namespace resulting from the merged
 | |
| 	// result of all overrides and a boolean indicating if it was
 | |
| 	// overridden
 | |
| 	Namespace() (string, bool, error)
 | |
| 	// ConfigAccess returns the rules for loading/persisting the config.
 | |
| 	ConfigAccess() ConfigAccess
 | |
| }
 | |
| 
 | |
| type PersistAuthProviderConfigForUser func(user string) restclient.AuthProviderConfigPersister
 | |
| 
 | |
| type promptedCredentials struct {
 | |
| 	username string
 | |
| 	password string
 | |
| }
 | |
| 
 | |
| // DirectClientConfig is a ClientConfig interface that is backed by a clientcmdapi.Config, options overrides, and an optional fallbackReader for auth information
 | |
| type DirectClientConfig struct {
 | |
| 	config         clientcmdapi.Config
 | |
| 	contextName    string
 | |
| 	overrides      *ConfigOverrides
 | |
| 	fallbackReader io.Reader
 | |
| 	configAccess   ConfigAccess
 | |
| 	// promptedCredentials store the credentials input by the user
 | |
| 	promptedCredentials promptedCredentials
 | |
| }
 | |
| 
 | |
| // NewDefaultClientConfig creates a DirectClientConfig using the config.CurrentContext as the context name
 | |
| func NewDefaultClientConfig(config clientcmdapi.Config, overrides *ConfigOverrides) ClientConfig {
 | |
| 	return &DirectClientConfig{config, config.CurrentContext, overrides, nil, NewDefaultClientConfigLoadingRules(), promptedCredentials{}}
 | |
| }
 | |
| 
 | |
| // NewNonInteractiveClientConfig creates a DirectClientConfig using the passed context name and does not have a fallback reader for auth information
 | |
| func NewNonInteractiveClientConfig(config clientcmdapi.Config, contextName string, overrides *ConfigOverrides, configAccess ConfigAccess) ClientConfig {
 | |
| 	return &DirectClientConfig{config, contextName, overrides, nil, configAccess, promptedCredentials{}}
 | |
| }
 | |
| 
 | |
| // NewInteractiveClientConfig creates a DirectClientConfig using the passed context name and a reader in case auth information is not provided via files or flags
 | |
| func NewInteractiveClientConfig(config clientcmdapi.Config, contextName string, overrides *ConfigOverrides, fallbackReader io.Reader, configAccess ConfigAccess) ClientConfig {
 | |
| 	return &DirectClientConfig{config, contextName, overrides, fallbackReader, configAccess, promptedCredentials{}}
 | |
| }
 | |
| 
 | |
| // NewClientConfigFromBytes takes your kubeconfig and gives you back a ClientConfig
 | |
| func NewClientConfigFromBytes(configBytes []byte) (ClientConfig, error) {
 | |
| 	config, err := Load(configBytes)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	return &DirectClientConfig{*config, "", &ConfigOverrides{}, nil, nil, promptedCredentials{}}, nil
 | |
| }
 | |
| 
 | |
| // RESTConfigFromKubeConfig is a convenience method to give back a restconfig from your kubeconfig bytes.
 | |
| // For programmatic access, this is what you want 80% of the time
 | |
| func RESTConfigFromKubeConfig(configBytes []byte) (*restclient.Config, error) {
 | |
| 	clientConfig, err := NewClientConfigFromBytes(configBytes)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	return clientConfig.ClientConfig()
 | |
| }
 | |
| 
 | |
| func (config *DirectClientConfig) RawConfig() (clientcmdapi.Config, error) {
 | |
| 	return config.config, nil
 | |
| }
 | |
| 
 | |
| // ClientConfig implements ClientConfig
 | |
| func (config *DirectClientConfig) ClientConfig() (*restclient.Config, error) {
 | |
| 	// check that getAuthInfo, getContext, and getCluster do not return an error.
 | |
| 	// Do this before checking if the current config is usable in the event that an
 | |
| 	// AuthInfo, Context, or Cluster config with user-defined names are not found.
 | |
| 	// This provides a user with the immediate cause for error if one is found
 | |
| 	configAuthInfo, err := config.getAuthInfo()
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	_, err = config.getContext()
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	configClusterInfo, err := config.getCluster()
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	if err := config.ConfirmUsable(); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	clientConfig := &restclient.Config{}
 | |
| 	clientConfig.Host = configClusterInfo.Server
 | |
| 
 | |
| 	if len(config.overrides.Timeout) > 0 {
 | |
| 		timeout, err := ParseTimeout(config.overrides.Timeout)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		clientConfig.Timeout = timeout
 | |
| 	}
 | |
| 
 | |
| 	if u, err := url.ParseRequestURI(clientConfig.Host); err == nil && u.Opaque == "" && len(u.Path) > 1 {
 | |
| 		u.RawQuery = ""
 | |
| 		u.Fragment = ""
 | |
| 		clientConfig.Host = u.String()
 | |
| 	}
 | |
| 	if len(configAuthInfo.Impersonate) > 0 {
 | |
| 		clientConfig.Impersonate = restclient.ImpersonationConfig{
 | |
| 			UserName: configAuthInfo.Impersonate,
 | |
| 			Groups:   configAuthInfo.ImpersonateGroups,
 | |
| 			Extra:    configAuthInfo.ImpersonateUserExtra,
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// only try to read the auth information if we are secure
 | |
| 	if restclient.IsConfigTransportTLS(*clientConfig) {
 | |
| 		var err error
 | |
| 
 | |
| 		// mergo is a first write wins for map value and a last writing wins for interface values
 | |
| 		// NOTE: This behavior changed with https://github.com/imdario/mergo/commit/d304790b2ed594794496464fadd89d2bb266600a.
 | |
| 		//       Our mergo.Merge version is older than this change.
 | |
| 		var persister restclient.AuthProviderConfigPersister
 | |
| 		if config.configAccess != nil {
 | |
| 			authInfoName, _ := config.getAuthInfoName()
 | |
| 			persister = PersisterForUser(config.configAccess, authInfoName)
 | |
| 		}
 | |
| 		userAuthPartialConfig, err := config.getUserIdentificationPartialConfig(configAuthInfo, config.fallbackReader, persister)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		mergo.Merge(clientConfig, userAuthPartialConfig)
 | |
| 
 | |
| 		serverAuthPartialConfig, err := getServerIdentificationPartialConfig(configAuthInfo, configClusterInfo)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		mergo.Merge(clientConfig, serverAuthPartialConfig)
 | |
| 	}
 | |
| 
 | |
| 	return clientConfig, nil
 | |
| }
 | |
| 
 | |
| // clientauth.Info object contain both user identification and server identification.  We want different precedence orders for
 | |
| // both, so we have to split the objects and merge them separately
 | |
| // we want this order of precedence for the server identification
 | |
| // 1.  configClusterInfo (the final result of command line flags and merged .kubeconfig files)
 | |
| // 2.  configAuthInfo.auth-path (this file can contain information that conflicts with #1, and we want #1 to win the priority)
 | |
| // 3.  load the ~/.kubernetes_auth file as a default
 | |
| func getServerIdentificationPartialConfig(configAuthInfo clientcmdapi.AuthInfo, configClusterInfo clientcmdapi.Cluster) (*restclient.Config, error) {
 | |
| 	mergedConfig := &restclient.Config{}
 | |
| 
 | |
| 	// configClusterInfo holds the information identify the server provided by .kubeconfig
 | |
| 	configClientConfig := &restclient.Config{}
 | |
| 	configClientConfig.CAFile = configClusterInfo.CertificateAuthority
 | |
| 	configClientConfig.CAData = configClusterInfo.CertificateAuthorityData
 | |
| 	configClientConfig.Insecure = configClusterInfo.InsecureSkipTLSVerify
 | |
| 	mergo.Merge(mergedConfig, configClientConfig)
 | |
| 
 | |
| 	return mergedConfig, nil
 | |
| }
 | |
| 
 | |
| // clientauth.Info object contain both user identification and server identification.  We want different precedence orders for
 | |
| // both, so we have to split the objects and merge them separately
 | |
| // we want this order of precedence for user identification
 | |
| // 1.  configAuthInfo minus auth-path (the final result of command line flags and merged .kubeconfig files)
 | |
| // 2.  configAuthInfo.auth-path (this file can contain information that conflicts with #1, and we want #1 to win the priority)
 | |
| // 3.  if there is not enough information to identify the user, load try the ~/.kubernetes_auth file
 | |
| // 4.  if there is not enough information to identify the user, prompt if possible
 | |
| func (config *DirectClientConfig) getUserIdentificationPartialConfig(configAuthInfo clientcmdapi.AuthInfo, fallbackReader io.Reader, persistAuthConfig restclient.AuthProviderConfigPersister) (*restclient.Config, error) {
 | |
| 	mergedConfig := &restclient.Config{}
 | |
| 
 | |
| 	// blindly overwrite existing values based on precedence
 | |
| 	if len(configAuthInfo.Token) > 0 {
 | |
| 		mergedConfig.BearerToken = configAuthInfo.Token
 | |
| 	} else if len(configAuthInfo.TokenFile) > 0 {
 | |
| 		tokenBytes, err := ioutil.ReadFile(configAuthInfo.TokenFile)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		mergedConfig.BearerToken = string(tokenBytes)
 | |
| 	}
 | |
| 	if len(configAuthInfo.Impersonate) > 0 {
 | |
| 		mergedConfig.Impersonate = restclient.ImpersonationConfig{
 | |
| 			UserName: configAuthInfo.Impersonate,
 | |
| 			Groups:   configAuthInfo.ImpersonateGroups,
 | |
| 			Extra:    configAuthInfo.ImpersonateUserExtra,
 | |
| 		}
 | |
| 	}
 | |
| 	if len(configAuthInfo.ClientCertificate) > 0 || len(configAuthInfo.ClientCertificateData) > 0 {
 | |
| 		mergedConfig.CertFile = configAuthInfo.ClientCertificate
 | |
| 		mergedConfig.CertData = configAuthInfo.ClientCertificateData
 | |
| 		mergedConfig.KeyFile = configAuthInfo.ClientKey
 | |
| 		mergedConfig.KeyData = configAuthInfo.ClientKeyData
 | |
| 	}
 | |
| 	if len(configAuthInfo.Username) > 0 || len(configAuthInfo.Password) > 0 {
 | |
| 		mergedConfig.Username = configAuthInfo.Username
 | |
| 		mergedConfig.Password = configAuthInfo.Password
 | |
| 	}
 | |
| 	if configAuthInfo.AuthProvider != nil {
 | |
| 		mergedConfig.AuthProvider = configAuthInfo.AuthProvider
 | |
| 		mergedConfig.AuthConfigPersister = persistAuthConfig
 | |
| 	}
 | |
| 	if configAuthInfo.Exec != nil {
 | |
| 		mergedConfig.ExecProvider = configAuthInfo.Exec
 | |
| 	}
 | |
| 
 | |
| 	// if there still isn't enough information to authenticate the user, try prompting
 | |
| 	if !canIdentifyUser(*mergedConfig) && (fallbackReader != nil) {
 | |
| 		if len(config.promptedCredentials.username) > 0 && len(config.promptedCredentials.password) > 0 {
 | |
| 			mergedConfig.Username = config.promptedCredentials.username
 | |
| 			mergedConfig.Password = config.promptedCredentials.password
 | |
| 			return mergedConfig, nil
 | |
| 		}
 | |
| 		prompter := NewPromptingAuthLoader(fallbackReader)
 | |
| 		promptedAuthInfo, err := prompter.Prompt()
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		promptedConfig := makeUserIdentificationConfig(*promptedAuthInfo)
 | |
| 		previouslyMergedConfig := mergedConfig
 | |
| 		mergedConfig = &restclient.Config{}
 | |
| 		mergo.Merge(mergedConfig, promptedConfig)
 | |
| 		mergo.Merge(mergedConfig, previouslyMergedConfig)
 | |
| 		config.promptedCredentials.username = mergedConfig.Username
 | |
| 		config.promptedCredentials.password = mergedConfig.Password
 | |
| 	}
 | |
| 
 | |
| 	return mergedConfig, nil
 | |
| }
 | |
| 
 | |
| // makeUserIdentificationFieldsConfig returns a client.Config capable of being merged using mergo for only user identification information
 | |
| func makeUserIdentificationConfig(info clientauth.Info) *restclient.Config {
 | |
| 	config := &restclient.Config{}
 | |
| 	config.Username = info.User
 | |
| 	config.Password = info.Password
 | |
| 	config.CertFile = info.CertFile
 | |
| 	config.KeyFile = info.KeyFile
 | |
| 	config.BearerToken = info.BearerToken
 | |
| 	return config
 | |
| }
 | |
| 
 | |
| // makeUserIdentificationFieldsConfig returns a client.Config capable of being merged using mergo for only server identification information
 | |
| func makeServerIdentificationConfig(info clientauth.Info) restclient.Config {
 | |
| 	config := restclient.Config{}
 | |
| 	config.CAFile = info.CAFile
 | |
| 	if info.Insecure != nil {
 | |
| 		config.Insecure = *info.Insecure
 | |
| 	}
 | |
| 	return config
 | |
| }
 | |
| 
 | |
| func canIdentifyUser(config restclient.Config) bool {
 | |
| 	return len(config.Username) > 0 ||
 | |
| 		(len(config.CertFile) > 0 || len(config.CertData) > 0) ||
 | |
| 		len(config.BearerToken) > 0 ||
 | |
| 		config.AuthProvider != nil ||
 | |
| 		config.ExecProvider != nil
 | |
| }
 | |
| 
 | |
| // Namespace implements ClientConfig
 | |
| func (config *DirectClientConfig) Namespace() (string, bool, error) {
 | |
| 	if config.overrides != nil && config.overrides.Context.Namespace != "" {
 | |
| 		// In the event we have an empty config but we do have a namespace override, we should return
 | |
| 		// the namespace override instead of having config.ConfirmUsable() return an error. This allows
 | |
| 		// things like in-cluster clients to execute `kubectl get pods --namespace=foo` and have the
 | |
| 		// --namespace flag honored instead of being ignored.
 | |
| 		return config.overrides.Context.Namespace, true, nil
 | |
| 	}
 | |
| 
 | |
| 	if err := config.ConfirmUsable(); err != nil {
 | |
| 		return "", false, err
 | |
| 	}
 | |
| 
 | |
| 	configContext, err := config.getContext()
 | |
| 	if err != nil {
 | |
| 		return "", false, err
 | |
| 	}
 | |
| 
 | |
| 	if len(configContext.Namespace) == 0 {
 | |
| 		return "default", false, nil
 | |
| 	}
 | |
| 
 | |
| 	return configContext.Namespace, false, nil
 | |
| }
 | |
| 
 | |
| // ConfigAccess implements ClientConfig
 | |
| func (config *DirectClientConfig) ConfigAccess() ConfigAccess {
 | |
| 	return config.configAccess
 | |
| }
 | |
| 
 | |
| // ConfirmUsable looks a particular context and determines if that particular part of the config is useable.  There might still be errors in the config,
 | |
| // but no errors in the sections requested or referenced.  It does not return early so that it can find as many errors as possible.
 | |
| func (config *DirectClientConfig) ConfirmUsable() error {
 | |
| 	validationErrors := make([]error, 0)
 | |
| 
 | |
| 	var contextName string
 | |
| 	if len(config.contextName) != 0 {
 | |
| 		contextName = config.contextName
 | |
| 	} else {
 | |
| 		contextName = config.config.CurrentContext
 | |
| 	}
 | |
| 
 | |
| 	if len(contextName) > 0 {
 | |
| 		_, exists := config.config.Contexts[contextName]
 | |
| 		if !exists {
 | |
| 			validationErrors = append(validationErrors, &errContextNotFound{contextName})
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	authInfoName, _ := config.getAuthInfoName()
 | |
| 	authInfo, _ := config.getAuthInfo()
 | |
| 	validationErrors = append(validationErrors, validateAuthInfo(authInfoName, authInfo)...)
 | |
| 	clusterName, _ := config.getClusterName()
 | |
| 	cluster, _ := config.getCluster()
 | |
| 	validationErrors = append(validationErrors, validateClusterInfo(clusterName, cluster)...)
 | |
| 	// when direct client config is specified, and our only error is that no server is defined, we should
 | |
| 	// return a standard "no config" error
 | |
| 	if len(validationErrors) == 1 && validationErrors[0] == ErrEmptyCluster {
 | |
| 		return newErrConfigurationInvalid([]error{ErrEmptyConfig})
 | |
| 	}
 | |
| 	return newErrConfigurationInvalid(validationErrors)
 | |
| }
 | |
| 
 | |
| // getContextName returns the default, or user-set context name, and a boolean that indicates
 | |
| // whether the default context name has been overwritten by a user-set flag, or left as its default value
 | |
| func (config *DirectClientConfig) getContextName() (string, bool) {
 | |
| 	if len(config.overrides.CurrentContext) != 0 {
 | |
| 		return config.overrides.CurrentContext, true
 | |
| 	}
 | |
| 	if len(config.contextName) != 0 {
 | |
| 		return config.contextName, false
 | |
| 	}
 | |
| 
 | |
| 	return config.config.CurrentContext, false
 | |
| }
 | |
| 
 | |
| // getAuthInfoName returns a string containing the current authinfo name for the current context,
 | |
| // and a boolean indicating  whether the default authInfo name is overwritten by a user-set flag, or
 | |
| // left as its default value
 | |
| func (config *DirectClientConfig) getAuthInfoName() (string, bool) {
 | |
| 	if len(config.overrides.Context.AuthInfo) != 0 {
 | |
| 		return config.overrides.Context.AuthInfo, true
 | |
| 	}
 | |
| 	context, _ := config.getContext()
 | |
| 	return context.AuthInfo, false
 | |
| }
 | |
| 
 | |
| // getClusterName returns a string containing the default, or user-set cluster name, and a boolean
 | |
| // indicating whether the default clusterName has been overwritten by a user-set flag, or left as
 | |
| // its default value
 | |
| func (config *DirectClientConfig) getClusterName() (string, bool) {
 | |
| 	if len(config.overrides.Context.Cluster) != 0 {
 | |
| 		return config.overrides.Context.Cluster, true
 | |
| 	}
 | |
| 	context, _ := config.getContext()
 | |
| 	return context.Cluster, false
 | |
| }
 | |
| 
 | |
| // getContext returns the clientcmdapi.Context, or an error if a required context is not found.
 | |
| func (config *DirectClientConfig) getContext() (clientcmdapi.Context, error) {
 | |
| 	contexts := config.config.Contexts
 | |
| 	contextName, required := config.getContextName()
 | |
| 
 | |
| 	mergedContext := clientcmdapi.NewContext()
 | |
| 	if configContext, exists := contexts[contextName]; exists {
 | |
| 		mergo.Merge(mergedContext, configContext)
 | |
| 	} else if required {
 | |
| 		return clientcmdapi.Context{}, fmt.Errorf("context %q does not exist", contextName)
 | |
| 	}
 | |
| 	mergo.Merge(mergedContext, config.overrides.Context)
 | |
| 
 | |
| 	return *mergedContext, nil
 | |
| }
 | |
| 
 | |
| // getAuthInfo returns the clientcmdapi.AuthInfo, or an error if a required auth info is not found.
 | |
| func (config *DirectClientConfig) getAuthInfo() (clientcmdapi.AuthInfo, error) {
 | |
| 	authInfos := config.config.AuthInfos
 | |
| 	authInfoName, required := config.getAuthInfoName()
 | |
| 
 | |
| 	mergedAuthInfo := clientcmdapi.NewAuthInfo()
 | |
| 	if configAuthInfo, exists := authInfos[authInfoName]; exists {
 | |
| 		mergo.Merge(mergedAuthInfo, configAuthInfo)
 | |
| 	} else if required {
 | |
| 		return clientcmdapi.AuthInfo{}, fmt.Errorf("auth info %q does not exist", authInfoName)
 | |
| 	}
 | |
| 	mergo.Merge(mergedAuthInfo, config.overrides.AuthInfo)
 | |
| 
 | |
| 	return *mergedAuthInfo, nil
 | |
| }
 | |
| 
 | |
| // getCluster returns the clientcmdapi.Cluster, or an error if a required cluster is not found.
 | |
| func (config *DirectClientConfig) getCluster() (clientcmdapi.Cluster, error) {
 | |
| 	clusterInfos := config.config.Clusters
 | |
| 	clusterInfoName, required := config.getClusterName()
 | |
| 
 | |
| 	mergedClusterInfo := clientcmdapi.NewCluster()
 | |
| 	mergo.Merge(mergedClusterInfo, config.overrides.ClusterDefaults)
 | |
| 	if configClusterInfo, exists := clusterInfos[clusterInfoName]; exists {
 | |
| 		mergo.Merge(mergedClusterInfo, configClusterInfo)
 | |
| 	} else if required {
 | |
| 		return clientcmdapi.Cluster{}, fmt.Errorf("cluster %q does not exist", clusterInfoName)
 | |
| 	}
 | |
| 	mergo.Merge(mergedClusterInfo, config.overrides.ClusterInfo)
 | |
| 	// An override of --insecure-skip-tls-verify=true and no accompanying CA/CA data should clear already-set CA/CA data
 | |
| 	// otherwise, a kubeconfig containing a CA reference would return an error that "CA and insecure-skip-tls-verify couldn't both be set"
 | |
| 	caLen := len(config.overrides.ClusterInfo.CertificateAuthority)
 | |
| 	caDataLen := len(config.overrides.ClusterInfo.CertificateAuthorityData)
 | |
| 	if config.overrides.ClusterInfo.InsecureSkipTLSVerify && caLen == 0 && caDataLen == 0 {
 | |
| 		mergedClusterInfo.CertificateAuthority = ""
 | |
| 		mergedClusterInfo.CertificateAuthorityData = nil
 | |
| 	}
 | |
| 
 | |
| 	return *mergedClusterInfo, nil
 | |
| }
 | |
| 
 | |
| // inClusterClientConfig makes a config that will work from within a kubernetes cluster container environment.
 | |
| // Can take options overrides for flags explicitly provided to the command inside the cluster container.
 | |
| type inClusterClientConfig struct {
 | |
| 	overrides               *ConfigOverrides
 | |
| 	inClusterConfigProvider func() (*restclient.Config, error)
 | |
| }
 | |
| 
 | |
| var _ ClientConfig = &inClusterClientConfig{}
 | |
| 
 | |
| func (config *inClusterClientConfig) RawConfig() (clientcmdapi.Config, error) {
 | |
| 	return clientcmdapi.Config{}, fmt.Errorf("inCluster environment config doesn't support multiple clusters")
 | |
| }
 | |
| 
 | |
| func (config *inClusterClientConfig) ClientConfig() (*restclient.Config, error) {
 | |
| 	if config.inClusterConfigProvider == nil {
 | |
| 		config.inClusterConfigProvider = restclient.InClusterConfig
 | |
| 	}
 | |
| 
 | |
| 	icc, err := config.inClusterConfigProvider()
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	// in-cluster configs only takes a host, token, or CA file
 | |
| 	// if any of them were individually provided, overwrite anything else
 | |
| 	if config.overrides != nil {
 | |
| 		if server := config.overrides.ClusterInfo.Server; len(server) > 0 {
 | |
| 			icc.Host = server
 | |
| 		}
 | |
| 		if token := config.overrides.AuthInfo.Token; len(token) > 0 {
 | |
| 			icc.BearerToken = token
 | |
| 		}
 | |
| 		if certificateAuthorityFile := config.overrides.ClusterInfo.CertificateAuthority; len(certificateAuthorityFile) > 0 {
 | |
| 			icc.TLSClientConfig.CAFile = certificateAuthorityFile
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return icc, err
 | |
| }
 | |
| 
 | |
| func (config *inClusterClientConfig) Namespace() (string, bool, error) {
 | |
| 	// This way assumes you've set the POD_NAMESPACE environment variable using the downward API.
 | |
| 	// This check has to be done first for backwards compatibility with the way InClusterConfig was originally set up
 | |
| 	if ns := os.Getenv("POD_NAMESPACE"); ns != "" {
 | |
| 		return ns, false, nil
 | |
| 	}
 | |
| 
 | |
| 	// Fall back to the namespace associated with the service account token, if available
 | |
| 	if data, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace"); err == nil {
 | |
| 		if ns := strings.TrimSpace(string(data)); len(ns) > 0 {
 | |
| 			return ns, false, nil
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return "default", false, nil
 | |
| }
 | |
| 
 | |
| func (config *inClusterClientConfig) ConfigAccess() ConfigAccess {
 | |
| 	return NewDefaultClientConfigLoadingRules()
 | |
| }
 | |
| 
 | |
| // Possible returns true if loading an inside-kubernetes-cluster is possible.
 | |
| func (config *inClusterClientConfig) Possible() bool {
 | |
| 	fi, err := os.Stat("/var/run/secrets/kubernetes.io/serviceaccount/token")
 | |
| 	return os.Getenv("KUBERNETES_SERVICE_HOST") != "" &&
 | |
| 		os.Getenv("KUBERNETES_SERVICE_PORT") != "" &&
 | |
| 		err == nil && !fi.IsDir()
 | |
| }
 | |
| 
 | |
| // BuildConfigFromFlags is a helper function that builds configs from a master
 | |
| // url or a kubeconfig filepath. These are passed in as command line flags for cluster
 | |
| // components. Warnings should reflect this usage. If neither masterUrl or kubeconfigPath
 | |
| // are passed in we fallback to inClusterConfig. If inClusterConfig fails, we fallback
 | |
| // to the default config.
 | |
| func BuildConfigFromFlags(masterUrl, kubeconfigPath string) (*restclient.Config, error) {
 | |
| 	if kubeconfigPath == "" && masterUrl == "" {
 | |
| 		glog.Warningf("Neither --kubeconfig nor --master was specified.  Using the inClusterConfig.  This might not work.")
 | |
| 		kubeconfig, err := restclient.InClusterConfig()
 | |
| 		if err == nil {
 | |
| 			return kubeconfig, nil
 | |
| 		}
 | |
| 		glog.Warning("error creating inClusterConfig, falling back to default config: ", err)
 | |
| 	}
 | |
| 	return NewNonInteractiveDeferredLoadingClientConfig(
 | |
| 		&ClientConfigLoadingRules{ExplicitPath: kubeconfigPath},
 | |
| 		&ConfigOverrides{ClusterInfo: clientcmdapi.Cluster{Server: masterUrl}}).ClientConfig()
 | |
| }
 | |
| 
 | |
| // BuildConfigFromKubeconfigGetter is a helper function that builds configs from a master
 | |
| // url and a kubeconfigGetter.
 | |
| func BuildConfigFromKubeconfigGetter(masterUrl string, kubeconfigGetter KubeconfigGetter) (*restclient.Config, error) {
 | |
| 	// TODO: We do not need a DeferredLoader here. Refactor code and see if we can use DirectClientConfig here.
 | |
| 	cc := NewNonInteractiveDeferredLoadingClientConfig(
 | |
| 		&ClientConfigGetter{kubeconfigGetter: kubeconfigGetter},
 | |
| 		&ConfigOverrides{ClusterInfo: clientcmdapi.Cluster{Server: masterUrl}})
 | |
| 	return cc.ClientConfig()
 | |
| }
 | 
