2015-01-07 20:59:22 +00:00
/ *
2015-05-01 16:19:44 +00:00
Copyright 2014 The Kubernetes Authors All rights reserved .
2015-01-07 20:59:22 +00:00
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 (
2015-06-01 23:58:19 +00:00
"fmt"
2015-01-07 20:59:22 +00:00
"io"
2016-02-11 19:46:56 +00:00
"io/ioutil"
2015-06-03 04:04:07 +00:00
"net/url"
2015-01-07 20:59:22 +00:00
"os"
2016-02-11 19:46:56 +00:00
"strings"
2015-01-07 20:59:22 +00:00
2015-11-06 18:34:49 +00:00
"github.com/golang/glog"
2015-01-07 20:59:22 +00:00
"github.com/imdario/mergo"
2015-08-05 22:03:47 +00:00
"k8s.io/kubernetes/pkg/api"
2015-11-13 21:20:54 +00:00
"k8s.io/kubernetes/pkg/api/unversioned"
2015-08-12 17:35:07 +00:00
client "k8s.io/kubernetes/pkg/client/unversioned"
clientauth "k8s.io/kubernetes/pkg/client/unversioned/auth"
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
2015-01-07 20:59:22 +00:00
)
var (
2015-01-21 15:38:58 +00:00
// DefaultCluster is the cluster config used when no other config is specified
2015-01-07 20:59:22 +00:00
// TODO: eventually apiserver should start on 443 and be secure by default
2015-01-21 15:38:58 +00:00
DefaultCluster = clientcmdapi . Cluster { Server : "http://localhost:8080" }
// EnvVarCluster allows overriding the DefaultCluster using an envvar for the server name
EnvVarCluster = clientcmdapi . Cluster { Server : os . Getenv ( "KUBERNETES_MASTER" ) }
2015-06-19 17:32:14 +00:00
DefaultClientConfig = DirectClientConfig { * clientcmdapi . NewConfig ( ) , "" , & ConfigOverrides { } , nil }
2015-01-07 20:59:22 +00:00
)
// ClientConfig is used to make it easy to get an api server client
type ClientConfig interface {
2015-01-02 18:08:37 +00:00
// RawConfig returns the merged result of all overrides
2014-12-17 13:03:03 +00:00
RawConfig ( ) ( clientcmdapi . Config , error )
2015-01-07 20:59:22 +00:00
// ClientConfig returns a complete client config
ClientConfig ( ) ( * client . Config , error )
2015-06-26 20:49:34 +00:00
// Namespace returns the namespace resulting from the merged
// result of all overrides and a boolean indicating if it was
// overridden
Namespace ( ) ( string , bool , error )
2015-01-07 20:59:22 +00:00
}
2014-12-17 13:03:03 +00:00
// DirectClientConfig is a ClientConfig interface that is backed by a clientcmdapi.Config, options overrides, and an optional fallbackReader for auth information
2015-01-07 20:59:22 +00:00
type DirectClientConfig struct {
2014-12-17 13:03:03 +00:00
config clientcmdapi . Config
2015-01-07 20:59:22 +00:00
contextName string
overrides * ConfigOverrides
fallbackReader io . Reader
}
// NewDefaultClientConfig creates a DirectClientConfig using the config.CurrentContext as the context name
2014-12-17 13:03:03 +00:00
func NewDefaultClientConfig ( config clientcmdapi . Config , overrides * ConfigOverrides ) ClientConfig {
2016-02-15 15:42:38 +00:00
return & DirectClientConfig { config , config . CurrentContext , overrides , nil }
2015-01-07 20:59:22 +00:00
}
// NewNonInteractiveClientConfig creates a DirectClientConfig using the passed context name and does not have a fallback reader for auth information
2014-12-17 13:03:03 +00:00
func NewNonInteractiveClientConfig ( config clientcmdapi . Config , contextName string , overrides * ConfigOverrides ) ClientConfig {
2016-02-15 15:42:38 +00:00
return & DirectClientConfig { config , contextName , overrides , nil }
2015-01-07 20:59:22 +00:00
}
// NewInteractiveClientConfig creates a DirectClientConfig using the passed context name and a reader in case auth information is not provided via files or flags
2014-12-17 13:03:03 +00:00
func NewInteractiveClientConfig ( config clientcmdapi . Config , contextName string , overrides * ConfigOverrides , fallbackReader io . Reader ) ClientConfig {
2016-02-15 15:42:38 +00:00
return & DirectClientConfig { config , contextName , overrides , fallbackReader }
2015-01-07 20:59:22 +00:00
}
2016-02-15 15:42:38 +00:00
func ( config * DirectClientConfig ) RawConfig ( ) ( clientcmdapi . Config , error ) {
2014-12-17 13:03:03 +00:00
return config . config , nil
}
2015-01-07 20:59:22 +00:00
// ClientConfig implements ClientConfig
2016-02-15 15:42:38 +00:00
func ( config * DirectClientConfig ) ClientConfig ( ) ( * client . Config , error ) {
2015-01-07 20:59:22 +00:00
if err := config . ConfirmUsable ( ) ; err != nil {
return nil , err
}
configAuthInfo := config . getAuthInfo ( )
configClusterInfo := config . getCluster ( )
2015-01-08 13:40:02 +00:00
clientConfig := & client . Config { }
2015-01-07 20:59:22 +00:00
clientConfig . Host = configClusterInfo . Server
2015-06-03 04:04:07 +00:00
if u , err := url . ParseRequestURI ( clientConfig . Host ) ; err == nil && u . Opaque == "" && len ( u . Path ) > 1 {
u . RawQuery = ""
u . Fragment = ""
clientConfig . Host = u . String ( )
}
2015-11-13 21:20:54 +00:00
if len ( configClusterInfo . APIVersion ) != 0 {
gv , err := unversioned . ParseGroupVersion ( configClusterInfo . APIVersion )
if err != nil {
return nil , err
}
clientConfig . GroupVersion = & gv
}
2015-01-07 20:59:22 +00:00
// only try to read the auth information if we are secure
2015-01-08 13:40:02 +00:00
if client . IsConfigTransportTLS ( * clientConfig ) {
2015-01-07 20:59:22 +00:00
var err error
2015-01-08 13:40:02 +00:00
// mergo is a first write wins for map value and a last writing wins for interface values
2016-02-04 07:02:05 +00:00
// NOTE: This behavior changed with https://github.com/imdario/mergo/commit/d304790b2ed594794496464fadd89d2bb266600a.
// Our mergo.Merge version is older than this change.
2015-01-08 13:40:02 +00:00
userAuthPartialConfig , err := getUserIdentificationPartialConfig ( configAuthInfo , config . fallbackReader )
if err != nil {
return nil , err
2015-01-07 20:59:22 +00:00
}
2015-01-08 13:40:02 +00:00
mergo . Merge ( clientConfig , userAuthPartialConfig )
2015-01-07 20:59:22 +00:00
2015-01-08 13:40:02 +00:00
serverAuthPartialConfig , err := getServerIdentificationPartialConfig ( configAuthInfo , configClusterInfo )
if err != nil {
return nil , err
2015-01-07 20:59:22 +00:00
}
2015-01-08 13:40:02 +00:00
mergo . Merge ( clientConfig , serverAuthPartialConfig )
}
return clientConfig , nil
}
2015-01-07 20:59:22 +00:00
2015-01-08 13:40:02 +00:00
// 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
2014-12-17 13:03:03 +00:00
func getServerIdentificationPartialConfig ( configAuthInfo clientcmdapi . AuthInfo , configClusterInfo clientcmdapi . Cluster ) ( * client . Config , error ) {
2015-01-08 13:40:02 +00:00
mergedConfig := & client . Config { }
// configClusterInfo holds the information identify the server provided by .kubeconfig
configClientConfig := & client . Config { }
configClientConfig . CAFile = configClusterInfo . CertificateAuthority
2015-02-18 02:37:43 +00:00
configClientConfig . CAData = configClusterInfo . CertificateAuthorityData
2015-01-08 13:40:02 +00:00
configClientConfig . Insecure = configClusterInfo . InsecureSkipTLSVerify
mergo . Merge ( mergedConfig , configClientConfig )
return mergedConfig , nil
}
2015-01-07 20:59:22 +00:00
2015-01-08 13:40:02 +00:00
// 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 identifcation
// 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 idenfity the user, load try the ~/.kubernetes_auth file
// 4. if there is not enough information to identify the user, prompt if possible
2014-12-17 13:03:03 +00:00
func getUserIdentificationPartialConfig ( configAuthInfo clientcmdapi . AuthInfo , fallbackReader io . Reader ) ( * client . Config , error ) {
2015-01-08 13:40:02 +00:00
mergedConfig := & client . Config { }
// blindly overwrite existing values based on precedence
if len ( configAuthInfo . Token ) > 0 {
mergedConfig . BearerToken = configAuthInfo . Token
}
2015-02-18 02:37:43 +00:00
if len ( configAuthInfo . ClientCertificate ) > 0 || len ( configAuthInfo . ClientCertificateData ) > 0 {
2015-01-08 13:40:02 +00:00
mergedConfig . CertFile = configAuthInfo . ClientCertificate
2015-02-18 02:37:43 +00:00
mergedConfig . CertData = configAuthInfo . ClientCertificateData
2015-01-08 13:40:02 +00:00
mergedConfig . KeyFile = configAuthInfo . ClientKey
2015-02-18 02:37:43 +00:00
mergedConfig . KeyData = configAuthInfo . ClientKeyData
}
if len ( configAuthInfo . Username ) > 0 || len ( configAuthInfo . Password ) > 0 {
mergedConfig . Username = configAuthInfo . Username
mergedConfig . Password = configAuthInfo . Password
2015-01-08 13:40:02 +00:00
}
// if there still isn't enough information to authenticate the user, try prompting
if ! canIdentifyUser ( * mergedConfig ) && ( fallbackReader != nil ) {
prompter := NewPromptingAuthLoader ( fallbackReader )
promptedAuthInfo := prompter . Prompt ( )
promptedConfig := makeUserIdentificationConfig ( * promptedAuthInfo )
previouslyMergedConfig := mergedConfig
mergedConfig = & client . Config { }
mergo . Merge ( mergedConfig , promptedConfig )
mergo . Merge ( mergedConfig , previouslyMergedConfig )
}
return mergedConfig , nil
}
// makeUserIdentificationFieldsConfig returns a client.Config capable of being merged using mergo for only user identification information
func makeUserIdentificationConfig ( info clientauth . Info ) * client . Config {
config := & client . 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 ) client . Config {
config := client . Config { }
config . CAFile = info . CAFile
if info . Insecure != nil {
config . Insecure = * info . Insecure
}
return config
}
func canIdentifyUser ( config client . Config ) bool {
return len ( config . Username ) > 0 ||
2015-02-18 02:37:43 +00:00
( len ( config . CertFile ) > 0 || len ( config . CertData ) > 0 ) ||
2015-01-08 13:40:02 +00:00
len ( config . BearerToken ) > 0
2015-01-07 20:59:22 +00:00
}
2015-01-02 18:08:37 +00:00
// Namespace implements KubeConfig
2016-02-15 15:42:38 +00:00
func ( config * DirectClientConfig ) Namespace ( ) ( string , bool , error ) {
2015-01-02 18:08:37 +00:00
if err := config . ConfirmUsable ( ) ; err != nil {
2015-06-26 20:49:34 +00:00
return "" , false , err
2015-01-02 18:08:37 +00:00
}
configContext := config . getContext ( )
2015-01-23 13:13:32 +00:00
if len ( configContext . Namespace ) == 0 {
2015-06-26 20:49:34 +00:00
return api . NamespaceDefault , false , nil
2015-01-02 18:08:37 +00:00
}
2015-06-26 20:49:34 +00:00
overridden := false
if config . overrides != nil && config . overrides . Context . Namespace != "" {
overridden = true
}
return configContext . Namespace , overridden , nil
2015-01-02 18:08:37 +00:00
}
2015-01-07 20:59:22 +00:00
// 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.
2016-02-15 15:42:38 +00:00
func ( config * DirectClientConfig ) ConfirmUsable ( ) error {
2015-01-07 20:59:22 +00:00
validationErrors := make ( [ ] error , 0 )
validationErrors = append ( validationErrors , validateAuthInfo ( config . getAuthInfoName ( ) , config . getAuthInfo ( ) ) ... )
validationErrors = append ( validationErrors , validateClusterInfo ( config . getClusterName ( ) , config . getCluster ( ) ) ... )
2015-11-05 19:59:22 +00:00
// 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 } )
}
2015-04-02 05:38:25 +00:00
return newErrConfigurationInvalid ( validationErrors )
2015-01-07 20:59:22 +00:00
}
2016-02-15 15:42:38 +00:00
func ( config * DirectClientConfig ) getContextName ( ) string {
2015-01-07 20:59:22 +00:00
if len ( config . overrides . CurrentContext ) != 0 {
return config . overrides . CurrentContext
}
if len ( config . contextName ) != 0 {
return config . contextName
}
return config . config . CurrentContext
}
2016-02-15 15:42:38 +00:00
func ( config * DirectClientConfig ) getAuthInfoName ( ) string {
2015-01-02 18:08:37 +00:00
if len ( config . overrides . Context . AuthInfo ) != 0 {
return config . overrides . Context . AuthInfo
2015-01-07 20:59:22 +00:00
}
return config . getContext ( ) . AuthInfo
}
2016-02-15 15:42:38 +00:00
func ( config * DirectClientConfig ) getClusterName ( ) string {
2015-01-02 18:08:37 +00:00
if len ( config . overrides . Context . Cluster ) != 0 {
return config . overrides . Context . Cluster
2015-01-07 20:59:22 +00:00
}
return config . getContext ( ) . Cluster
}
2016-02-15 15:42:38 +00:00
func ( config * DirectClientConfig ) getContext ( ) clientcmdapi . Context {
2015-01-02 18:08:37 +00:00
contexts := config . config . Contexts
contextName := config . getContextName ( )
var mergedContext clientcmdapi . Context
if configContext , exists := contexts [ contextName ] ; exists {
mergo . Merge ( & mergedContext , configContext )
}
mergo . Merge ( & mergedContext , config . overrides . Context )
return mergedContext
2015-01-07 20:59:22 +00:00
}
2016-02-15 15:42:38 +00:00
func ( config * DirectClientConfig ) getAuthInfo ( ) clientcmdapi . AuthInfo {
2015-01-07 20:59:22 +00:00
authInfos := config . config . AuthInfos
authInfoName := config . getAuthInfoName ( )
2014-12-17 13:03:03 +00:00
var mergedAuthInfo clientcmdapi . AuthInfo
2015-01-07 20:59:22 +00:00
if configAuthInfo , exists := authInfos [ authInfoName ] ; exists {
mergo . Merge ( & mergedAuthInfo , configAuthInfo )
}
mergo . Merge ( & mergedAuthInfo , config . overrides . AuthInfo )
return mergedAuthInfo
}
2016-02-15 15:42:38 +00:00
func ( config * DirectClientConfig ) getCluster ( ) clientcmdapi . Cluster {
2015-01-07 20:59:22 +00:00
clusterInfos := config . config . Clusters
clusterInfoName := config . getClusterName ( )
2014-12-17 13:03:03 +00:00
var mergedClusterInfo clientcmdapi . Cluster
2015-01-21 15:38:58 +00:00
mergo . Merge ( & mergedClusterInfo , DefaultCluster )
mergo . Merge ( & mergedClusterInfo , EnvVarCluster )
2015-01-07 20:59:22 +00:00
if configClusterInfo , exists := clusterInfos [ clusterInfoName ] ; exists {
mergo . Merge ( & mergedClusterInfo , configClusterInfo )
}
mergo . Merge ( & mergedClusterInfo , config . overrides . ClusterInfo )
return mergedClusterInfo
}
2015-06-01 23:58:19 +00:00
// inClusterClientConfig makes a config that will work from within a kubernetes cluster container environment.
type inClusterClientConfig struct { }
func ( inClusterClientConfig ) RawConfig ( ) ( clientcmdapi . Config , error ) {
return clientcmdapi . Config { } , fmt . Errorf ( "inCluster environment config doesn't support multiple clusters" )
}
func ( inClusterClientConfig ) ClientConfig ( ) ( * client . Config , error ) {
return client . InClusterConfig ( )
}
func ( inClusterClientConfig ) Namespace ( ) ( string , error ) {
2016-02-11 19:46:56 +00:00
// 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
2015-06-01 23:58:19 +00:00
if ns := os . Getenv ( "POD_NAMESPACE" ) ; ns != "" {
return ns , nil
}
2016-02-11 19:46:56 +00:00
// 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 , nil
}
}
2015-06-01 23:58:19 +00:00
return "default" , nil
}
// Possible returns true if loading an inside-kubernetes-cluster is possible.
func ( 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 ( )
}
2015-11-06 18:34:49 +00:00
// 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 ) ( * client . Config , error ) {
if kubeconfigPath == "" && masterUrl == "" {
glog . Warningf ( "Neither --kubeconfig nor --master was specified. Using the inClusterConfig. This might not work." )
kubeconfig , err := client . InClusterConfig ( )
if err == nil {
return kubeconfig , nil
}
2016-01-20 06:32:09 +00:00
glog . Warning ( "error creating inClusterConfig, falling back to default config: " , err )
2015-11-06 18:34:49 +00:00
}
return NewNonInteractiveDeferredLoadingClientConfig (
& ClientConfigLoadingRules { ExplicitPath : kubeconfigPath } ,
& ConfigOverrides { ClusterInfo : clientcmdapi . Cluster { Server : masterUrl } } ) . ClientConfig ( )
}