mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	add kubeconfig file
This commit is contained in:
		@@ -24,6 +24,6 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func main() {
 | 
					func main() {
 | 
				
			||||||
	clientBuilder := clientcmd.NewBuilder(clientcmd.NewPromptingAuthLoader(os.Stdin))
 | 
						clientBuilder := clientcmd.NewInteractiveClientConfig(clientcmd.Config{}, "", &clientcmd.ConfigOverrides{}, os.Stdin)
 | 
				
			||||||
	cmd.NewFactory(clientBuilder).Run(os.Stdout)
 | 
						cmd.NewFactory(clientBuilder).Run(os.Stdout)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										69
									
								
								docs/kubeconfig-file.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								docs/kubeconfig-file.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,69 @@
 | 
				
			|||||||
 | 
					# .kubeconfig files
 | 
				
			||||||
 | 
					In order to easily switch between multiple clusters, a .kubeconfig file was defined.  This file contains a series of authentication mechanisms and cluster connection information associated with nicknames.  It also introduces the concept of a tuple of authentication information (user) and cluster connection information called a context that is also associated with a nickname.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Multiple files are .kubeconfig files are allowed.  At runtime they are loaded and merged together along with override options specified from the command line (see rules below).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Related discussion
 | 
				
			||||||
 | 
					https://github.com/GoogleCloudPlatform/kubernetes/issues/1755
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Example .kubeconfig file
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					preferences: 
 | 
				
			||||||
 | 
					  colors: true
 | 
				
			||||||
 | 
					clusters:
 | 
				
			||||||
 | 
					  cow-cluster:
 | 
				
			||||||
 | 
					    server: http://cow.org:8080
 | 
				
			||||||
 | 
					    api-version: v1beta1
 | 
				
			||||||
 | 
					  horse-cluster:
 | 
				
			||||||
 | 
					    server: https://horse.org:4443
 | 
				
			||||||
 | 
					    certificate-authority: path/to/my/cafile
 | 
				
			||||||
 | 
					  pig-cluster:
 | 
				
			||||||
 | 
					    server: https://pig.org:443
 | 
				
			||||||
 | 
					    insecure-skip-tls-verify: true
 | 
				
			||||||
 | 
					users:
 | 
				
			||||||
 | 
					  black-user:
 | 
				
			||||||
 | 
					    auth-path: path/to/my/existing/.kubernetes_auth file
 | 
				
			||||||
 | 
					  blue-user:
 | 
				
			||||||
 | 
					    token: blue-token
 | 
				
			||||||
 | 
					  green-user:
 | 
				
			||||||
 | 
					    client-certificate: path/to/my/client/cert
 | 
				
			||||||
 | 
					    client-key: path/to/my/client/key
 | 
				
			||||||
 | 
					contexts:
 | 
				
			||||||
 | 
					  queen-anne-context:
 | 
				
			||||||
 | 
					    cluster: pig-cluster
 | 
				
			||||||
 | 
					    user: black-user
 | 
				
			||||||
 | 
					    namespace: saw-ns
 | 
				
			||||||
 | 
					  federal-context:
 | 
				
			||||||
 | 
					    cluster: horse-cluster
 | 
				
			||||||
 | 
					    user: green-user
 | 
				
			||||||
 | 
					    namespace: chisel-ns
 | 
				
			||||||
 | 
					current-context: federal-context
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Loading and merging rules
 | 
				
			||||||
 | 
					The rules for loading and merging the .kubeconfig files are straightforward, but there are a lot of them.  The final config is built in this order:
 | 
				
			||||||
 | 
					  1.  Merge together the kubeconfig itself.  This is done with the following hierarchy and merge rules:
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      Empty filenames are ignored.  Files with non-deserializable content produced errors.
 | 
				
			||||||
 | 
					      The first file to set a particular value or map key wins and the value or map key is never changed.
 | 
				
			||||||
 | 
					      This means that the first file to set CurrentContext will have its context preserved.  It also means that if two files specify a "red-user", only values from the first file's red-user are used.  Even non-conflicting entries from the second file's "red-user" are discarded.
 | 
				
			||||||
 | 
					      1.  CommandLineLocation - the value of the `kubeconfig` command line option
 | 
				
			||||||
 | 
					      1.  EnvVarLocation - the value of $KUBECONFIG
 | 
				
			||||||
 | 
					      1.  CurrentDirectoryLocation - ``pwd``/.kubeconfig
 | 
				
			||||||
 | 
					      1.  HomeDirectoryLocation = ~/.kube/.kubeconfig
 | 
				
			||||||
 | 
					  1.  Determine the context to use based on the first hit in this chain
 | 
				
			||||||
 | 
					      1.  command line argument - the value of the `context` command line option
 | 
				
			||||||
 | 
					      1.  current-context from the merged kubeconfig file
 | 
				
			||||||
 | 
					      1.  Empty is allowed at this stage
 | 
				
			||||||
 | 
					  1.  Determine the cluster info and user to use.  At this point, we may or may not have a context.  They are built based on the first hit in this chain.  (run it twice, once for user, once for cluster)
 | 
				
			||||||
 | 
					      1.  command line argument - `user` for user name and `cluster` for cluster name
 | 
				
			||||||
 | 
					      1.  If context is present, then use the context's value
 | 
				
			||||||
 | 
					      1.  Empty is allowed
 | 
				
			||||||
 | 
					  1.  Determine the actual cluster info to use.  At this point, we may or may not have a cluster info.  Build each piece of the cluster info based on the chain (first hit wins):
 | 
				
			||||||
 | 
					      1.  command line arguments - `server`, `api-version`, `certificate-authority`, and `insecure-skip-tls-verify`
 | 
				
			||||||
 | 
					      1.  If cluster info is present and a value for the attribute is present, use it.
 | 
				
			||||||
 | 
					      1.  If you don't have a server location, error.
 | 
				
			||||||
 | 
					  1.  User is build using the same rules as cluster info, EXCEPT that you can only have one authentication  technique per user.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      The command line flags are: `auth-path`, `client-certificate`, `client-key`, and `token`.  If there are two conflicting techniques, fail.
 | 
				
			||||||
 | 
					  1.  For any information still missing, use default values and potentially prompt for authentication information
 | 
				
			||||||
@@ -40,17 +40,16 @@ func (*defaultAuthLoader) LoadAuth(path string) (*clientauth.Info, error) {
 | 
				
			|||||||
	return clientauth.LoadFromFile(path)
 | 
						return clientauth.LoadFromFile(path)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type promptingAuthLoader struct {
 | 
					type PromptingAuthLoader struct {
 | 
				
			||||||
	reader io.Reader
 | 
						reader io.Reader
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// LoadAuth parses an AuthInfo object from a file path. It prompts user and creates file if it doesn't exist.
 | 
					// LoadAuth parses an AuthInfo object from a file path. It prompts user and creates file if it doesn't exist.
 | 
				
			||||||
func (a *promptingAuthLoader) LoadAuth(path string) (*clientauth.Info, error) {
 | 
					func (a *PromptingAuthLoader) LoadAuth(path string) (*clientauth.Info, error) {
 | 
				
			||||||
	var auth clientauth.Info
 | 
						var auth clientauth.Info
 | 
				
			||||||
	// Prompt for user/pass and write a file if none exists.
 | 
						// Prompt for user/pass and write a file if none exists.
 | 
				
			||||||
	if _, err := os.Stat(path); os.IsNotExist(err) {
 | 
						if _, err := os.Stat(path); os.IsNotExist(err) {
 | 
				
			||||||
		auth.User = promptForString("Username", a.reader)
 | 
							auth = *a.Prompt()
 | 
				
			||||||
		auth.Password = promptForString("Password", a.reader)
 | 
					 | 
				
			||||||
		data, err := json.Marshal(auth)
 | 
							data, err := json.Marshal(auth)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return &auth, err
 | 
								return &auth, err
 | 
				
			||||||
@@ -64,6 +63,16 @@ func (a *promptingAuthLoader) LoadAuth(path string) (*clientauth.Info, error) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return authPtr, nil
 | 
						return authPtr, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Prompt pulls the user and password from a reader
 | 
				
			||||||
 | 
					func (a *PromptingAuthLoader) Prompt() *clientauth.Info {
 | 
				
			||||||
 | 
						auth := &clientauth.Info{}
 | 
				
			||||||
 | 
						auth.User = promptForString("Username", a.reader)
 | 
				
			||||||
 | 
						auth.Password = promptForString("Password", a.reader)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return auth
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func promptForString(field string, r io.Reader) string {
 | 
					func promptForString(field string, r io.Reader) string {
 | 
				
			||||||
	fmt.Printf("Please enter %s: ", field)
 | 
						fmt.Printf("Please enter %s: ", field)
 | 
				
			||||||
	var result string
 | 
						var result string
 | 
				
			||||||
@@ -72,8 +81,8 @@ func promptForString(field string, r io.Reader) string {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewDefaultAuthLoader is an AuthLoader that parses an AuthInfo object from a file path. It prompts user and creates file if it doesn't exist.
 | 
					// NewDefaultAuthLoader is an AuthLoader that parses an AuthInfo object from a file path. It prompts user and creates file if it doesn't exist.
 | 
				
			||||||
func NewPromptingAuthLoader(reader io.Reader) AuthLoader {
 | 
					func NewPromptingAuthLoader(reader io.Reader) *PromptingAuthLoader {
 | 
				
			||||||
	return &promptingAuthLoader{reader}
 | 
						return &PromptingAuthLoader{reader}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewDefaultAuthLoader returns a default implementation of an AuthLoader that only reads from a config file
 | 
					// NewDefaultAuthLoader returns a default implementation of an AuthLoader that only reads from a config file
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,207 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
Copyright 2014 Google Inc. All rights reserved.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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"
 | 
					 | 
				
			||||||
	"os"
 | 
					 | 
				
			||||||
	"reflect"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/spf13/pflag"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/clientauth"
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/version"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Builder are used to bind and interpret command line flags to make it easy to get an api server client
 | 
					 | 
				
			||||||
type Builder interface {
 | 
					 | 
				
			||||||
	// BindFlags must bind and keep track of all the flags required to build a client config object
 | 
					 | 
				
			||||||
	BindFlags(flags *pflag.FlagSet)
 | 
					 | 
				
			||||||
	// Client calls BuildConfig under the covers and uses that config to return a client
 | 
					 | 
				
			||||||
	Client() (*client.Client, error)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Config uses the values of the bound flags and builds a complete client config
 | 
					 | 
				
			||||||
	Config() (*client.Config, error)
 | 
					 | 
				
			||||||
	// Override invokes Config(), then passes that to the provided function, and returns a new
 | 
					 | 
				
			||||||
	// builder that will use that config as its default. If Config() returns an error for the default
 | 
					 | 
				
			||||||
	// values the function will not be invoked, and the error will be available when Client() is called.
 | 
					 | 
				
			||||||
	Override(func(*client.Config)) Builder
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// cmdAuthInfo is used to track whether flags have been set
 | 
					 | 
				
			||||||
type cmdAuthInfo struct {
 | 
					 | 
				
			||||||
	User        StringFlag
 | 
					 | 
				
			||||||
	Password    StringFlag
 | 
					 | 
				
			||||||
	CAFile      StringFlag
 | 
					 | 
				
			||||||
	CertFile    StringFlag
 | 
					 | 
				
			||||||
	KeyFile     StringFlag
 | 
					 | 
				
			||||||
	BearerToken StringFlag
 | 
					 | 
				
			||||||
	Insecure    BoolFlag
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// builder is a default implementation of a Builder
 | 
					 | 
				
			||||||
type builder struct {
 | 
					 | 
				
			||||||
	authLoader      AuthLoader
 | 
					 | 
				
			||||||
	cmdAuthInfo     cmdAuthInfo
 | 
					 | 
				
			||||||
	authPath        string
 | 
					 | 
				
			||||||
	apiserver       string
 | 
					 | 
				
			||||||
	apiVersion      string
 | 
					 | 
				
			||||||
	matchApiVersion bool
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	config *client.Config
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// NewBuilder returns a valid Builder that uses the passed authLoader.  If authLoader is nil, the NewDefaultAuthLoader is used.
 | 
					 | 
				
			||||||
func NewBuilder(authLoader AuthLoader) Builder {
 | 
					 | 
				
			||||||
	if authLoader == nil {
 | 
					 | 
				
			||||||
		authLoader = NewDefaultAuthLoader()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return &builder{
 | 
					 | 
				
			||||||
		authLoader: authLoader,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const (
 | 
					 | 
				
			||||||
	FlagApiServer       = "server"
 | 
					 | 
				
			||||||
	FlagMatchApiVersion = "match-server-version"
 | 
					 | 
				
			||||||
	FlagApiVersion      = "api-version"
 | 
					 | 
				
			||||||
	FlagAuthPath        = "auth-path"
 | 
					 | 
				
			||||||
	FlagInsecure        = "insecure-skip-tls-verify"
 | 
					 | 
				
			||||||
	FlagCertFile        = "client-certificate"
 | 
					 | 
				
			||||||
	FlagKeyFile         = "client-key"
 | 
					 | 
				
			||||||
	FlagCAFile          = "certificate-authority"
 | 
					 | 
				
			||||||
	FlagBearerToken     = "token"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// BindFlags implements Builder
 | 
					 | 
				
			||||||
func (builder *builder) BindFlags(flags *pflag.FlagSet) {
 | 
					 | 
				
			||||||
	flags.StringVarP(&builder.apiserver, FlagApiServer, "s", builder.apiserver, "The address of the Kubernetes API server")
 | 
					 | 
				
			||||||
	flags.BoolVar(&builder.matchApiVersion, FlagMatchApiVersion, false, "Require server version to match client version")
 | 
					 | 
				
			||||||
	flags.StringVar(&builder.apiVersion, FlagApiVersion, latest.Version, "The API version to use when talking to the server")
 | 
					 | 
				
			||||||
	flags.StringVarP(&builder.authPath, FlagAuthPath, "a", os.Getenv("HOME")+"/.kubernetes_auth", "Path to the auth info file. If missing, prompt the user. Only used if using https.")
 | 
					 | 
				
			||||||
	flags.Var(&builder.cmdAuthInfo.Insecure, FlagInsecure, "If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure.")
 | 
					 | 
				
			||||||
	flags.Var(&builder.cmdAuthInfo.CertFile, FlagCertFile, "Path to a client key file for TLS.")
 | 
					 | 
				
			||||||
	flags.Var(&builder.cmdAuthInfo.KeyFile, FlagKeyFile, "Path to a client key file for TLS.")
 | 
					 | 
				
			||||||
	flags.Var(&builder.cmdAuthInfo.CAFile, FlagCAFile, "Path to a cert. file for the certificate authority.")
 | 
					 | 
				
			||||||
	flags.Var(&builder.cmdAuthInfo.BearerToken, FlagBearerToken, "Bearer token for authentication to the API server.")
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Client implements Builder
 | 
					 | 
				
			||||||
func (builder *builder) Client() (*client.Client, error) {
 | 
					 | 
				
			||||||
	clientConfig, err := builder.Config()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	c, err := client.New(clientConfig)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if builder.matchApiVersion {
 | 
					 | 
				
			||||||
		clientVersion := version.Get()
 | 
					 | 
				
			||||||
		serverVersion, err := c.ServerVersion()
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return nil, fmt.Errorf("couldn't read version from server: %v\n", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if s := *serverVersion; !reflect.DeepEqual(clientVersion, s) {
 | 
					 | 
				
			||||||
			return nil, fmt.Errorf("server version (%#v) differs from client version (%#v)!\n", s, clientVersion)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return c, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Config implements Builder
 | 
					 | 
				
			||||||
func (builder *builder) Config() (*client.Config, error) {
 | 
					 | 
				
			||||||
	if builder.config != nil {
 | 
					 | 
				
			||||||
		return builder.config, nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return builder.newConfig()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Override implements Builder
 | 
					 | 
				
			||||||
func (builder *builder) Override(fn func(*client.Config)) Builder {
 | 
					 | 
				
			||||||
	config, err := builder.newConfig()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return builder
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	fn(config)
 | 
					 | 
				
			||||||
	b := *builder
 | 
					 | 
				
			||||||
	b.config = config
 | 
					 | 
				
			||||||
	return &b
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// newConfig creates a new config object for this builder
 | 
					 | 
				
			||||||
func (builder *builder) newConfig() (*client.Config, error) {
 | 
					 | 
				
			||||||
	clientConfig := client.Config{}
 | 
					 | 
				
			||||||
	if len(builder.apiserver) > 0 {
 | 
					 | 
				
			||||||
		clientConfig.Host = builder.apiserver
 | 
					 | 
				
			||||||
	} else if len(os.Getenv("KUBERNETES_MASTER")) > 0 {
 | 
					 | 
				
			||||||
		clientConfig.Host = os.Getenv("KUBERNETES_MASTER")
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		// TODO: eventually apiserver should start on 443 and be secure by default
 | 
					 | 
				
			||||||
		clientConfig.Host = "http://localhost:8080"
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	clientConfig.Version = builder.apiVersion
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// only try to read the auth information if we are secure
 | 
					 | 
				
			||||||
	if client.IsConfigTransportTLS(&clientConfig) {
 | 
					 | 
				
			||||||
		authInfoFileFound := true
 | 
					 | 
				
			||||||
		authInfo, err := builder.authLoader.LoadAuth(builder.authPath)
 | 
					 | 
				
			||||||
		if authInfo == nil && err != nil { // only consider failing if we don't have any auth info
 | 
					 | 
				
			||||||
			if !os.IsNotExist(err) { // if it's just a case of a missing file, simply flag the auth as not found and use the command line arguments
 | 
					 | 
				
			||||||
				return nil, err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			authInfoFileFound = false
 | 
					 | 
				
			||||||
			authInfo = &clientauth.Info{}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// If provided, the command line options override options from the auth file
 | 
					 | 
				
			||||||
		if !authInfoFileFound || builder.cmdAuthInfo.User.Provided() {
 | 
					 | 
				
			||||||
			authInfo.User = builder.cmdAuthInfo.User.Value
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if !authInfoFileFound || builder.cmdAuthInfo.Password.Provided() {
 | 
					 | 
				
			||||||
			authInfo.Password = builder.cmdAuthInfo.Password.Value
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if !authInfoFileFound || builder.cmdAuthInfo.CAFile.Provided() {
 | 
					 | 
				
			||||||
			authInfo.CAFile = builder.cmdAuthInfo.CAFile.Value
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if !authInfoFileFound || builder.cmdAuthInfo.CertFile.Provided() {
 | 
					 | 
				
			||||||
			authInfo.CertFile = builder.cmdAuthInfo.CertFile.Value
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if !authInfoFileFound || builder.cmdAuthInfo.KeyFile.Provided() {
 | 
					 | 
				
			||||||
			authInfo.KeyFile = builder.cmdAuthInfo.KeyFile.Value
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if !authInfoFileFound || builder.cmdAuthInfo.BearerToken.Provided() {
 | 
					 | 
				
			||||||
			authInfo.BearerToken = builder.cmdAuthInfo.BearerToken.Value
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if !authInfoFileFound || builder.cmdAuthInfo.Insecure.Provided() {
 | 
					 | 
				
			||||||
			authInfo.Insecure = &builder.cmdAuthInfo.Insecure.Value
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		clientConfig, err = authInfo.MergeWithConfig(clientConfig)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return nil, err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return &clientConfig, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,356 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
Copyright 2014 Google Inc. All rights reserved.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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 (
 | 
					 | 
				
			||||||
	"bytes"
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"io"
 | 
					 | 
				
			||||||
	"io/ioutil"
 | 
					 | 
				
			||||||
	"os"
 | 
					 | 
				
			||||||
	"reflect"
 | 
					 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
	"testing"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/spf13/pflag"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
 | 
					 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/clientauth"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestSetAllArgumentsOnly(t *testing.T) {
 | 
					 | 
				
			||||||
	flags := pflag.NewFlagSet("test-flags", pflag.ContinueOnError)
 | 
					 | 
				
			||||||
	clientBuilder := NewBuilder(nil)
 | 
					 | 
				
			||||||
	clientBuilder.BindFlags(flags)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	args := argValues{"https://localhost:8080", "v1beta1", "/auth-path", "cert-file", "key-file", "ca-file", "bearer-token", true, true}
 | 
					 | 
				
			||||||
	flags.Parse(strings.Split(args.toArguments(), " "))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	castBuilder, ok := clientBuilder.(*builder)
 | 
					 | 
				
			||||||
	if !ok {
 | 
					 | 
				
			||||||
		t.Errorf("Got unexpected cast result: %#v", castBuilder)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	matchStringArg(args.server, castBuilder.apiserver, t)
 | 
					 | 
				
			||||||
	matchStringArg(args.apiVersion, castBuilder.apiVersion, t)
 | 
					 | 
				
			||||||
	matchStringArg(args.authPath, castBuilder.authPath, t)
 | 
					 | 
				
			||||||
	matchStringArg(args.certFile, castBuilder.cmdAuthInfo.CertFile.Value, t)
 | 
					 | 
				
			||||||
	matchStringArg(args.keyFile, castBuilder.cmdAuthInfo.KeyFile.Value, t)
 | 
					 | 
				
			||||||
	matchStringArg(args.caFile, castBuilder.cmdAuthInfo.CAFile.Value, t)
 | 
					 | 
				
			||||||
	matchStringArg(args.bearerToken, castBuilder.cmdAuthInfo.BearerToken.Value, t)
 | 
					 | 
				
			||||||
	matchBoolArg(args.insecure, castBuilder.cmdAuthInfo.Insecure.Value, t)
 | 
					 | 
				
			||||||
	matchBoolArg(args.matchApiVersion, castBuilder.matchApiVersion, t)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	clientConfig, err := clientBuilder.Config()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		t.Errorf("Unexpected error: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	matchStringArg(args.server, clientConfig.Host, t)
 | 
					 | 
				
			||||||
	matchStringArg(args.apiVersion, clientConfig.Version, t)
 | 
					 | 
				
			||||||
	matchStringArg(args.certFile, clientConfig.CertFile, t)
 | 
					 | 
				
			||||||
	matchStringArg(args.keyFile, clientConfig.KeyFile, t)
 | 
					 | 
				
			||||||
	matchStringArg(args.caFile, clientConfig.CAFile, t)
 | 
					 | 
				
			||||||
	matchStringArg(args.bearerToken, clientConfig.BearerToken, t)
 | 
					 | 
				
			||||||
	matchBoolArg(args.insecure, clientConfig.Insecure, t)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestSetInsecureArgumentsOnly(t *testing.T) {
 | 
					 | 
				
			||||||
	flags := pflag.NewFlagSet("test-flags", pflag.ContinueOnError)
 | 
					 | 
				
			||||||
	clientBuilder := NewBuilder(nil)
 | 
					 | 
				
			||||||
	clientBuilder.BindFlags(flags)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	args := argValues{"http://localhost:8080", "v1beta1", "/auth-path", "cert-file", "key-file", "ca-file", "bearer-token", true, true}
 | 
					 | 
				
			||||||
	flags.Parse(strings.Split(args.toArguments(), " "))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	clientConfig, err := clientBuilder.Config()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		t.Errorf("Unexpected error: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	matchStringArg(args.server, clientConfig.Host, t)
 | 
					 | 
				
			||||||
	matchStringArg(args.apiVersion, clientConfig.Version, t)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// all security related params should be empty in the resulting config even though we set them because we're using http transport
 | 
					 | 
				
			||||||
	matchStringArg("", clientConfig.CertFile, t)
 | 
					 | 
				
			||||||
	matchStringArg("", clientConfig.KeyFile, t)
 | 
					 | 
				
			||||||
	matchStringArg("", clientConfig.CAFile, t)
 | 
					 | 
				
			||||||
	matchStringArg("", clientConfig.BearerToken, t)
 | 
					 | 
				
			||||||
	matchBoolArg(false, clientConfig.Insecure, t)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestReadAuthFile(t *testing.T) {
 | 
					 | 
				
			||||||
	flags := pflag.NewFlagSet("test-flags", pflag.ContinueOnError)
 | 
					 | 
				
			||||||
	clientBuilder := NewBuilder(nil)
 | 
					 | 
				
			||||||
	clientBuilder.BindFlags(flags)
 | 
					 | 
				
			||||||
	authFileContents := fmt.Sprintf(`{"user": "alfa-user", "password": "bravo-password", "cAFile": "charlie", "certFile": "delta", "keyFile": "echo", "bearerToken": "foxtrot"}`)
 | 
					 | 
				
			||||||
	authFile := writeTempAuthFile(authFileContents, t)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	args := argValues{"https://localhost:8080", "v1beta1", authFile, "", "", "", "", true, true}
 | 
					 | 
				
			||||||
	flags.Parse(strings.Split(args.toArguments(), " "))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	castBuilder, ok := clientBuilder.(*builder)
 | 
					 | 
				
			||||||
	if !ok {
 | 
					 | 
				
			||||||
		t.Errorf("Got unexpected cast result: %#v", castBuilder)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	matchStringArg(args.server, castBuilder.apiserver, t)
 | 
					 | 
				
			||||||
	matchStringArg(args.apiVersion, castBuilder.apiVersion, t)
 | 
					 | 
				
			||||||
	matchStringArg(args.authPath, castBuilder.authPath, t)
 | 
					 | 
				
			||||||
	matchStringArg(args.certFile, castBuilder.cmdAuthInfo.CertFile.Value, t)
 | 
					 | 
				
			||||||
	matchStringArg(args.keyFile, castBuilder.cmdAuthInfo.KeyFile.Value, t)
 | 
					 | 
				
			||||||
	matchStringArg(args.caFile, castBuilder.cmdAuthInfo.CAFile.Value, t)
 | 
					 | 
				
			||||||
	matchStringArg(args.bearerToken, castBuilder.cmdAuthInfo.BearerToken.Value, t)
 | 
					 | 
				
			||||||
	matchBoolArg(args.insecure, castBuilder.cmdAuthInfo.Insecure.Value, t)
 | 
					 | 
				
			||||||
	matchBoolArg(args.matchApiVersion, castBuilder.matchApiVersion, t)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	clientConfig, err := clientBuilder.Config()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		t.Errorf("Unexpected error: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	matchStringArg(args.server, clientConfig.Host, t)
 | 
					 | 
				
			||||||
	matchStringArg(args.apiVersion, clientConfig.Version, t)
 | 
					 | 
				
			||||||
	matchStringArg("delta", clientConfig.CertFile, t)
 | 
					 | 
				
			||||||
	matchStringArg("echo", clientConfig.KeyFile, t)
 | 
					 | 
				
			||||||
	matchStringArg("charlie", clientConfig.CAFile, t)
 | 
					 | 
				
			||||||
	matchStringArg("foxtrot", clientConfig.BearerToken, t)
 | 
					 | 
				
			||||||
	matchStringArg("alfa-user", clientConfig.Username, t)
 | 
					 | 
				
			||||||
	matchStringArg("bravo-password", clientConfig.Password, t)
 | 
					 | 
				
			||||||
	matchBoolArg(args.insecure, clientConfig.Insecure, t)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestAuthFileOverridden(t *testing.T) {
 | 
					 | 
				
			||||||
	flags := pflag.NewFlagSet("test-flags", pflag.ContinueOnError)
 | 
					 | 
				
			||||||
	clientBuilder := NewBuilder(nil)
 | 
					 | 
				
			||||||
	clientBuilder.BindFlags(flags)
 | 
					 | 
				
			||||||
	authFileContents := fmt.Sprintf(`{"user": "alfa-user", "password": "bravo-password", "cAFile": "charlie", "certFile": "delta", "keyFile": "echo", "bearerToken": "foxtrot"}`)
 | 
					 | 
				
			||||||
	authFile := writeTempAuthFile(authFileContents, t)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	args := argValues{"https://localhost:8080", "v1beta1", authFile, "cert-file", "key-file", "ca-file", "bearer-token", true, true}
 | 
					 | 
				
			||||||
	flags.Parse(strings.Split(args.toArguments(), " "))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	castBuilder, ok := clientBuilder.(*builder)
 | 
					 | 
				
			||||||
	if !ok {
 | 
					 | 
				
			||||||
		t.Errorf("Got unexpected cast result: %#v", castBuilder)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	matchStringArg(args.server, castBuilder.apiserver, t)
 | 
					 | 
				
			||||||
	matchStringArg(args.apiVersion, castBuilder.apiVersion, t)
 | 
					 | 
				
			||||||
	matchStringArg(args.authPath, castBuilder.authPath, t)
 | 
					 | 
				
			||||||
	matchStringArg(args.certFile, castBuilder.cmdAuthInfo.CertFile.Value, t)
 | 
					 | 
				
			||||||
	matchStringArg(args.keyFile, castBuilder.cmdAuthInfo.KeyFile.Value, t)
 | 
					 | 
				
			||||||
	matchStringArg(args.caFile, castBuilder.cmdAuthInfo.CAFile.Value, t)
 | 
					 | 
				
			||||||
	matchStringArg(args.bearerToken, castBuilder.cmdAuthInfo.BearerToken.Value, t)
 | 
					 | 
				
			||||||
	matchBoolArg(args.insecure, castBuilder.cmdAuthInfo.Insecure.Value, t)
 | 
					 | 
				
			||||||
	matchBoolArg(args.matchApiVersion, castBuilder.matchApiVersion, t)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	clientConfig, err := clientBuilder.Config()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		t.Errorf("Unexpected error: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	matchStringArg(args.server, clientConfig.Host, t)
 | 
					 | 
				
			||||||
	matchStringArg(args.apiVersion, clientConfig.Version, t)
 | 
					 | 
				
			||||||
	matchStringArg(args.certFile, clientConfig.CertFile, t)
 | 
					 | 
				
			||||||
	matchStringArg(args.keyFile, clientConfig.KeyFile, t)
 | 
					 | 
				
			||||||
	matchStringArg(args.caFile, clientConfig.CAFile, t)
 | 
					 | 
				
			||||||
	matchStringArg(args.bearerToken, clientConfig.BearerToken, t)
 | 
					 | 
				
			||||||
	matchStringArg("alfa-user", clientConfig.Username, t)
 | 
					 | 
				
			||||||
	matchStringArg("bravo-password", clientConfig.Password, t)
 | 
					 | 
				
			||||||
	matchBoolArg(args.insecure, clientConfig.Insecure, t)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestUseDefaultArgumentsOnly(t *testing.T) {
 | 
					 | 
				
			||||||
	flags := pflag.NewFlagSet("test-flags", pflag.ContinueOnError)
 | 
					 | 
				
			||||||
	clientBuilder := NewBuilder(nil)
 | 
					 | 
				
			||||||
	clientBuilder.BindFlags(flags)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	flags.Parse(strings.Split("", " "))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	castBuilder, ok := clientBuilder.(*builder)
 | 
					 | 
				
			||||||
	if !ok {
 | 
					 | 
				
			||||||
		t.Errorf("Got unexpected cast result: %#v", castBuilder)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	matchStringArg("", castBuilder.apiserver, t)
 | 
					 | 
				
			||||||
	matchStringArg(latest.Version, castBuilder.apiVersion, t)
 | 
					 | 
				
			||||||
	matchStringArg(os.Getenv("HOME")+"/.kubernetes_auth", castBuilder.authPath, t)
 | 
					 | 
				
			||||||
	matchStringArg("", castBuilder.cmdAuthInfo.CertFile.Value, t)
 | 
					 | 
				
			||||||
	matchStringArg("", castBuilder.cmdAuthInfo.KeyFile.Value, t)
 | 
					 | 
				
			||||||
	matchStringArg("", castBuilder.cmdAuthInfo.CAFile.Value, t)
 | 
					 | 
				
			||||||
	matchStringArg("", castBuilder.cmdAuthInfo.BearerToken.Value, t)
 | 
					 | 
				
			||||||
	matchBoolArg(false, castBuilder.matchApiVersion, t)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestLoadClientAuthInfoOrPrompt(t *testing.T) {
 | 
					 | 
				
			||||||
	loadAuthInfoTests := []struct {
 | 
					 | 
				
			||||||
		authData string
 | 
					 | 
				
			||||||
		authInfo *clientauth.Info
 | 
					 | 
				
			||||||
		r        io.Reader
 | 
					 | 
				
			||||||
	}{
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			`{"user": "user", "password": "pass"}`,
 | 
					 | 
				
			||||||
			&clientauth.Info{User: "user", Password: "pass"},
 | 
					 | 
				
			||||||
			nil,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			"", nil, nil,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			"missing",
 | 
					 | 
				
			||||||
			&clientauth.Info{User: "user", Password: "pass"},
 | 
					 | 
				
			||||||
			bytes.NewBufferString("user\npass"),
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	for _, loadAuthInfoTest := range loadAuthInfoTests {
 | 
					 | 
				
			||||||
		tt := loadAuthInfoTest
 | 
					 | 
				
			||||||
		aifile, err := ioutil.TempFile("", "testAuthInfo")
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			t.Errorf("Unexpected error: %v", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if tt.authData != "missing" {
 | 
					 | 
				
			||||||
			defer os.Remove(aifile.Name())
 | 
					 | 
				
			||||||
			defer aifile.Close()
 | 
					 | 
				
			||||||
			_, err = aifile.WriteString(tt.authData)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				t.Errorf("Unexpected error: %v", err)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			aifile.Close()
 | 
					 | 
				
			||||||
			os.Remove(aifile.Name())
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		prompter := NewPromptingAuthLoader(tt.r)
 | 
					 | 
				
			||||||
		authInfo, err := prompter.LoadAuth(aifile.Name())
 | 
					 | 
				
			||||||
		if len(tt.authData) == 0 && tt.authData != "missing" {
 | 
					 | 
				
			||||||
			if err == nil {
 | 
					 | 
				
			||||||
				t.Error("LoadAuth didn't fail on empty file")
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			t.Errorf("Unexpected error: %v", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if !reflect.DeepEqual(authInfo, tt.authInfo) {
 | 
					 | 
				
			||||||
			t.Errorf("Expected %#v, got %#v", tt.authInfo, authInfo)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestOverride(t *testing.T) {
 | 
					 | 
				
			||||||
	b := NewBuilder(nil)
 | 
					 | 
				
			||||||
	cfg, err := b.Config()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		t.Fatalf("unexpected error: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if cfg.Version != "" {
 | 
					 | 
				
			||||||
		t.Errorf("unexpected default config version")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	newCfg, err := b.Override(func(cfg *client.Config) {
 | 
					 | 
				
			||||||
		if cfg.Version != "" {
 | 
					 | 
				
			||||||
			t.Errorf("unexpected default config version")
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		cfg.Version = "test"
 | 
					 | 
				
			||||||
	}).Config()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if newCfg.Version != "test" {
 | 
					 | 
				
			||||||
		t.Errorf("unexpected override config version")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if cfg.Version != "" {
 | 
					 | 
				
			||||||
		t.Errorf("original object should not change")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	cfg, err = b.Config()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		t.Fatalf("unexpected error: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if cfg.Version != "" {
 | 
					 | 
				
			||||||
		t.Errorf("override should not be persistent")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func matchStringArg(expected, got string, t *testing.T) {
 | 
					 | 
				
			||||||
	if expected != got {
 | 
					 | 
				
			||||||
		t.Errorf("Expected %v, got %v", expected, got)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func matchBoolArg(expected, got bool, t *testing.T) {
 | 
					 | 
				
			||||||
	if expected != got {
 | 
					 | 
				
			||||||
		t.Errorf("Expected %v, got %v", expected, got)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func writeTempAuthFile(contents string, t *testing.T) string {
 | 
					 | 
				
			||||||
	file, err := ioutil.TempFile("", "testAuthInfo")
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		t.Errorf("Failed to write config file.  Test cannot continue due to: %v", err)
 | 
					 | 
				
			||||||
		return ""
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	_, err = file.WriteString(contents)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		t.Errorf("Unexpected error: %v", err)
 | 
					 | 
				
			||||||
		return ""
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	file.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return file.Name()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type argValues struct {
 | 
					 | 
				
			||||||
	server          string
 | 
					 | 
				
			||||||
	apiVersion      string
 | 
					 | 
				
			||||||
	authPath        string
 | 
					 | 
				
			||||||
	certFile        string
 | 
					 | 
				
			||||||
	keyFile         string
 | 
					 | 
				
			||||||
	caFile          string
 | 
					 | 
				
			||||||
	bearerToken     string
 | 
					 | 
				
			||||||
	insecure        bool
 | 
					 | 
				
			||||||
	matchApiVersion bool
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (a *argValues) toArguments() string {
 | 
					 | 
				
			||||||
	args := ""
 | 
					 | 
				
			||||||
	if len(a.server) > 0 {
 | 
					 | 
				
			||||||
		args += "--" + FlagApiServer + "=" + a.server + " "
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if len(a.apiVersion) > 0 {
 | 
					 | 
				
			||||||
		args += "--" + FlagApiVersion + "=" + a.apiVersion + " "
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if len(a.authPath) > 0 {
 | 
					 | 
				
			||||||
		args += "--" + FlagAuthPath + "=" + a.authPath + " "
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if len(a.certFile) > 0 {
 | 
					 | 
				
			||||||
		args += "--" + FlagCertFile + "=" + a.certFile + " "
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if len(a.keyFile) > 0 {
 | 
					 | 
				
			||||||
		args += "--" + FlagKeyFile + "=" + a.keyFile + " "
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if len(a.caFile) > 0 {
 | 
					 | 
				
			||||||
		args += "--" + FlagCAFile + "=" + a.caFile + " "
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if len(a.bearerToken) > 0 {
 | 
					 | 
				
			||||||
		args += "--" + FlagBearerToken + "=" + a.bearerToken + " "
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	args += "--" + FlagInsecure + "=" + fmt.Sprintf("%v", a.insecure) + " "
 | 
					 | 
				
			||||||
	args += "--" + FlagMatchApiVersion + "=" + fmt.Sprintf("%v", a.matchApiVersion) + " "
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return args
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										183
									
								
								pkg/client/clientcmd/client_config.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										183
									
								
								pkg/client/clientcmd/client_config.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,183 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2014 Google Inc. All rights reserved.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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 (
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/imdario/mergo"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
 | 
				
			||||||
 | 
						"github.com/GoogleCloudPlatform/kubernetes/pkg/clientauth"
 | 
				
			||||||
 | 
						"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						// TODO: eventually apiserver should start on 443 and be secure by default
 | 
				
			||||||
 | 
						defaultCluster = Cluster{Server: "http://localhost:8080"}
 | 
				
			||||||
 | 
						envVarCluster  = Cluster{Server: os.Getenv("KUBERNETES_MASTER")}
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ClientConfig is used to make it easy to get an api server client
 | 
				
			||||||
 | 
					type ClientConfig interface {
 | 
				
			||||||
 | 
						// ClientConfig returns a complete client config
 | 
				
			||||||
 | 
						ClientConfig() (*client.Config, error)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DirectClientConfig is a ClientConfig interface that is backed by a Config, options overrides, and an optional fallbackReader for auth information
 | 
				
			||||||
 | 
					type DirectClientConfig struct {
 | 
				
			||||||
 | 
						config         Config
 | 
				
			||||||
 | 
						contextName    string
 | 
				
			||||||
 | 
						overrides      *ConfigOverrides
 | 
				
			||||||
 | 
						fallbackReader io.Reader
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewDefaultClientConfig creates a DirectClientConfig using the config.CurrentContext as the context name
 | 
				
			||||||
 | 
					func NewDefaultClientConfig(config Config, overrides *ConfigOverrides) ClientConfig {
 | 
				
			||||||
 | 
						return DirectClientConfig{config, config.CurrentContext, overrides, nil}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewNonInteractiveClientConfig creates a DirectClientConfig using the passed context name and does not have a fallback reader for auth information
 | 
				
			||||||
 | 
					func NewNonInteractiveClientConfig(config Config, contextName string, overrides *ConfigOverrides) ClientConfig {
 | 
				
			||||||
 | 
						return DirectClientConfig{config, contextName, overrides, nil}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 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 Config, contextName string, overrides *ConfigOverrides, fallbackReader io.Reader) ClientConfig {
 | 
				
			||||||
 | 
						return DirectClientConfig{config, contextName, overrides, fallbackReader}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ClientConfig implements ClientConfig
 | 
				
			||||||
 | 
					func (config DirectClientConfig) ClientConfig() (*client.Config, error) {
 | 
				
			||||||
 | 
						if err := config.ConfirmUsable(); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						configAuthInfo := config.getAuthInfo()
 | 
				
			||||||
 | 
						configClusterInfo := config.getCluster()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						clientConfig := client.Config{}
 | 
				
			||||||
 | 
						clientConfig.Host = configClusterInfo.Server
 | 
				
			||||||
 | 
						clientConfig.Version = configClusterInfo.APIVersion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// only try to read the auth information if we are secure
 | 
				
			||||||
 | 
						if client.IsConfigTransportTLS(&clientConfig) {
 | 
				
			||||||
 | 
							var authInfo *clientauth.Info
 | 
				
			||||||
 | 
							var err error
 | 
				
			||||||
 | 
							switch {
 | 
				
			||||||
 | 
							case len(configAuthInfo.AuthPath) > 0:
 | 
				
			||||||
 | 
								authInfo, err = NewDefaultAuthLoader().LoadAuth(configAuthInfo.AuthPath)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return nil, err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							case len(configAuthInfo.Token) > 0:
 | 
				
			||||||
 | 
								authInfo = &clientauth.Info{BearerToken: configAuthInfo.Token}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							case len(configAuthInfo.ClientCertificate) > 0:
 | 
				
			||||||
 | 
								authInfo = &clientauth.Info{
 | 
				
			||||||
 | 
									CertFile: configAuthInfo.ClientCertificate,
 | 
				
			||||||
 | 
									KeyFile:  configAuthInfo.ClientKey,
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								authInfo = &clientauth.Info{}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if !authInfo.Complete() && (config.fallbackReader != nil) {
 | 
				
			||||||
 | 
								prompter := NewPromptingAuthLoader(config.fallbackReader)
 | 
				
			||||||
 | 
								authInfo = prompter.Prompt()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							authInfo.Insecure = &configClusterInfo.InsecureSkipTLSVerify
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							clientConfig, err = authInfo.MergeWithConfig(clientConfig)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &clientConfig, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 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)
 | 
				
			||||||
 | 
						validationErrors = append(validationErrors, validateAuthInfo(config.getAuthInfoName(), config.getAuthInfo())...)
 | 
				
			||||||
 | 
						validationErrors = append(validationErrors, validateClusterInfo(config.getClusterName(), config.getCluster())...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return util.SliceToError(validationErrors)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (config DirectClientConfig) getContextName() string {
 | 
				
			||||||
 | 
						if len(config.overrides.CurrentContext) != 0 {
 | 
				
			||||||
 | 
							return config.overrides.CurrentContext
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(config.contextName) != 0 {
 | 
				
			||||||
 | 
							return config.contextName
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return config.config.CurrentContext
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (config DirectClientConfig) getAuthInfoName() string {
 | 
				
			||||||
 | 
						if len(config.overrides.AuthInfoName) != 0 {
 | 
				
			||||||
 | 
							return config.overrides.AuthInfoName
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return config.getContext().AuthInfo
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (config DirectClientConfig) getClusterName() string {
 | 
				
			||||||
 | 
						if len(config.overrides.ClusterName) != 0 {
 | 
				
			||||||
 | 
							return config.overrides.ClusterName
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return config.getContext().Cluster
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (config DirectClientConfig) getContext() Context {
 | 
				
			||||||
 | 
						return config.config.Contexts[config.getContextName()]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (config DirectClientConfig) getAuthInfo() AuthInfo {
 | 
				
			||||||
 | 
						authInfos := config.config.AuthInfos
 | 
				
			||||||
 | 
						authInfoName := config.getAuthInfoName()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var mergedAuthInfo AuthInfo
 | 
				
			||||||
 | 
						if configAuthInfo, exists := authInfos[authInfoName]; exists {
 | 
				
			||||||
 | 
							mergo.Merge(&mergedAuthInfo, configAuthInfo)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						mergo.Merge(&mergedAuthInfo, config.overrides.AuthInfo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return mergedAuthInfo
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (config DirectClientConfig) getCluster() Cluster {
 | 
				
			||||||
 | 
						clusterInfos := config.config.Clusters
 | 
				
			||||||
 | 
						clusterInfoName := config.getClusterName()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var mergedClusterInfo Cluster
 | 
				
			||||||
 | 
						mergo.Merge(&mergedClusterInfo, defaultCluster)
 | 
				
			||||||
 | 
						mergo.Merge(&mergedClusterInfo, envVarCluster)
 | 
				
			||||||
 | 
						if configClusterInfo, exists := clusterInfos[clusterInfoName]; exists {
 | 
				
			||||||
 | 
							mergo.Merge(&mergedClusterInfo, configClusterInfo)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						mergo.Merge(&mergedClusterInfo, config.overrides.ClusterInfo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return mergedClusterInfo
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										106
									
								
								pkg/client/clientcmd/client_config_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								pkg/client/clientcmd/client_config_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,106 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2014 Google Inc. All rights reserved.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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 (
 | 
				
			||||||
 | 
						"reflect"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
 | 
				
			||||||
 | 
						"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func createValidTestConfig() *Config {
 | 
				
			||||||
 | 
						const (
 | 
				
			||||||
 | 
							server = "https://anything.com:8080"
 | 
				
			||||||
 | 
							token  = "the-token"
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						config := NewConfig()
 | 
				
			||||||
 | 
						config.Clusters["clean"] = Cluster{
 | 
				
			||||||
 | 
							Server:     server,
 | 
				
			||||||
 | 
							APIVersion: latest.Version,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						config.AuthInfos["clean"] = AuthInfo{
 | 
				
			||||||
 | 
							Token: token,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						config.Contexts["clean"] = Context{
 | 
				
			||||||
 | 
							Cluster:  "clean",
 | 
				
			||||||
 | 
							AuthInfo: "clean",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						config.CurrentContext = "clean"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return config
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestCreateClean(t *testing.T) {
 | 
				
			||||||
 | 
						config := createValidTestConfig()
 | 
				
			||||||
 | 
						clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						clientConfig, err := clientBuilder.ClientConfig()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Errorf("Unexpected error: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						matchStringArg(config.Clusters["clean"].Server, clientConfig.Host, t)
 | 
				
			||||||
 | 
						matchStringArg(config.Clusters["clean"].APIVersion, clientConfig.Version, t)
 | 
				
			||||||
 | 
						matchBoolArg(config.Clusters["clean"].InsecureSkipTLSVerify, clientConfig.Insecure, t)
 | 
				
			||||||
 | 
						matchStringArg(config.AuthInfos["clean"].Token, clientConfig.BearerToken, t)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestCreateCleanDefault(t *testing.T) {
 | 
				
			||||||
 | 
						config := createValidTestConfig()
 | 
				
			||||||
 | 
						clientBuilder := NewDefaultClientConfig(*config, &ConfigOverrides{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						clientConfig, err := clientBuilder.ClientConfig()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Errorf("Unexpected error: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						matchStringArg(config.Clusters["clean"].Server, clientConfig.Host, t)
 | 
				
			||||||
 | 
						matchStringArg(config.Clusters["clean"].APIVersion, clientConfig.Version, t)
 | 
				
			||||||
 | 
						matchBoolArg(config.Clusters["clean"].InsecureSkipTLSVerify, clientConfig.Insecure, t)
 | 
				
			||||||
 | 
						matchStringArg(config.AuthInfos["clean"].Token, clientConfig.BearerToken, t)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestCreateMissingContext(t *testing.T) {
 | 
				
			||||||
 | 
						const expectedErrorContains = "Context was not found for specified context"
 | 
				
			||||||
 | 
						config := createValidTestConfig()
 | 
				
			||||||
 | 
						clientBuilder := NewNonInteractiveClientConfig(*config, "not-present", &ConfigOverrides{})
 | 
				
			||||||
 | 
						expectedConfig := &client.Config{Host: "http://localhost:8080"}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						clientConfig, err := clientBuilder.ClientConfig()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Errorf("Unexpected error: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if !reflect.DeepEqual(expectedConfig, clientConfig) {
 | 
				
			||||||
 | 
							t.Errorf("Expected %#v, got %#v", expectedConfig, clientConfig)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func matchBoolArg(expected, got bool, t *testing.T) {
 | 
				
			||||||
 | 
						if expected != got {
 | 
				
			||||||
 | 
							t.Errorf("Expected %v, got %v", expected, got)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func matchStringArg(expected, got string, t *testing.T) {
 | 
				
			||||||
 | 
						if expected != got {
 | 
				
			||||||
 | 
							t.Errorf("Expected %v, got %v", expected, got)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -15,11 +15,17 @@ limitations under the License.
 | 
				
			|||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
Package cmd provides one stop shopping for a command line executable to bind the correct flags,
 | 
					Package clientcmd provides one stop shopping for building a working client from a fixed config,
 | 
				
			||||||
build the client config, and create a working client. The code for usage looks like this:
 | 
					from a .kubeconfig file, from command line flags, or from any merged combination.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    clientBuilder := clientcmd.NewBuilder(clientcmd.NewDefaultAuthLoader())
 | 
					Sample usage from merged .kubeconfig files (local directory, home directory)
 | 
				
			||||||
    clientBuilder.BindFlags(cmds.PersistentFlags())
 | 
						loadingRules := clientcmd.NewKubeConfigLoadingRules()
 | 
				
			||||||
    apiClient, err := clientBuilder.Client()
 | 
						// if you want to change the loading rules (which files in which order), you can do so here
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						configOverrides := &clientcmd.ConfigOverrides{}
 | 
				
			||||||
 | 
						// if you want to change override values or bind them to flags, there are methods to help you
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingKubeConfig(loadingRules, configOverrides)
 | 
				
			||||||
 | 
						kubeConfig.Client()
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
package clientcmd
 | 
					package clientcmd
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										117
									
								
								pkg/client/clientcmd/loader.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								pkg/client/clientcmd/loader.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,117 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2014 Google Inc. All rights reserved.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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 (
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/imdario/mergo"
 | 
				
			||||||
 | 
						"gopkg.in/v2/yaml"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						RecommendedConfigPathFlag   = "kubeconfig"
 | 
				
			||||||
 | 
						RecommendedConfigPathEnvVar = "KUBECONFIG"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ClientConfigLoadingRules is a struct that calls our specific locations that are used for merging together a Config
 | 
				
			||||||
 | 
					type ClientConfigLoadingRules struct {
 | 
				
			||||||
 | 
						CommandLinePath      string
 | 
				
			||||||
 | 
						EnvVarPath           string
 | 
				
			||||||
 | 
						CurrentDirectoryPath string
 | 
				
			||||||
 | 
						HomeDirectoryPath    string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewClientConfigLoadingRules returns a ClientConfigLoadingRules object with default fields filled in.  You are not required to
 | 
				
			||||||
 | 
					// use this constructor
 | 
				
			||||||
 | 
					func NewClientConfigLoadingRules() *ClientConfigLoadingRules {
 | 
				
			||||||
 | 
						return &ClientConfigLoadingRules{
 | 
				
			||||||
 | 
							CurrentDirectoryPath: ".kubeconfig",
 | 
				
			||||||
 | 
							HomeDirectoryPath:    os.Getenv("HOME") + "/.kube/.kubeconfig",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Load takes the loading rules and merges together a Config object based on following order.
 | 
				
			||||||
 | 
					//   1.  CommandLinePath
 | 
				
			||||||
 | 
					//   2.  EnvVarPath
 | 
				
			||||||
 | 
					//   3.  CurrentDirectoryPath
 | 
				
			||||||
 | 
					//   4.  HomeDirectoryPath
 | 
				
			||||||
 | 
					// Empty filenames are ignored.  Files with non-deserializable content produced errors.
 | 
				
			||||||
 | 
					// The first file to set a particular value or map key wins and the value or map key is never changed.
 | 
				
			||||||
 | 
					// This means that the first file to set CurrentContext will have its context preserved.  It also means
 | 
				
			||||||
 | 
					// that if two files specify a "red-user", only values from the first file's red-user are used.  Even
 | 
				
			||||||
 | 
					// non-conflicting entries from the second file's "red-user" are discarded.
 | 
				
			||||||
 | 
					func (rules *ClientConfigLoadingRules) Load() (*Config, error) {
 | 
				
			||||||
 | 
						config := NewConfig()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mergeConfigWithFile(config, rules.CommandLinePath)
 | 
				
			||||||
 | 
						mergeConfigWithFile(config, rules.EnvVarPath)
 | 
				
			||||||
 | 
						mergeConfigWithFile(config, rules.CurrentDirectoryPath)
 | 
				
			||||||
 | 
						mergeConfigWithFile(config, rules.HomeDirectoryPath)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return config, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func mergeConfigWithFile(startingConfig *Config, filename string) error {
 | 
				
			||||||
 | 
						if len(filename) == 0 {
 | 
				
			||||||
 | 
							// no work to do
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						config, err := LoadFromFile(filename)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mergo.Merge(startingConfig, config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// LoadFromFile takes a filename and deserializes the contents into Config object
 | 
				
			||||||
 | 
					func LoadFromFile(filename string) (*Config, error) {
 | 
				
			||||||
 | 
						config := &Config{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						kubeconfigBytes, err := ioutil.ReadFile(filename)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = yaml.Unmarshal(kubeconfigBytes, &config)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return config, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// WriteToFile serializes the config to yaml and writes it out to a file.  If no present, it creates the file with 0644.  If it is present
 | 
				
			||||||
 | 
					// it stomps the contents
 | 
				
			||||||
 | 
					func WriteToFile(config Config, filename string) error {
 | 
				
			||||||
 | 
						content, err := yaml.Marshal(config)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = ioutil.WriteFile(filename, content, 0644)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										182
									
								
								pkg/client/clientcmd/loader_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										182
									
								
								pkg/client/clientcmd/loader_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,182 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2014 Google Inc. All rights reserved.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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/ioutil"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gopkg.in/v2/yaml"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						testConfigAlfa = Config{
 | 
				
			||||||
 | 
							AuthInfos: map[string]AuthInfo{
 | 
				
			||||||
 | 
								"red-user": {Token: "red-token"}},
 | 
				
			||||||
 | 
							Clusters: map[string]Cluster{
 | 
				
			||||||
 | 
								"cow-cluster": {Server: "http://cow.org:8080"}},
 | 
				
			||||||
 | 
							Contexts: map[string]Context{
 | 
				
			||||||
 | 
								"federal-context": {AuthInfo: "red-user", Cluster: "cow-cluster", Namespace: "hammer-ns"}},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						testConfigBravo = Config{
 | 
				
			||||||
 | 
							AuthInfos: map[string]AuthInfo{
 | 
				
			||||||
 | 
								"black-user": {Token: "black-token"}},
 | 
				
			||||||
 | 
							Clusters: map[string]Cluster{
 | 
				
			||||||
 | 
								"pig-cluster": {Server: "http://pig.org:8080"}},
 | 
				
			||||||
 | 
							Contexts: map[string]Context{
 | 
				
			||||||
 | 
								"queen-anne-context": {AuthInfo: "black-user", Cluster: "pig-cluster", Namespace: "saw-ns"}},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						testConfigCharlie = Config{
 | 
				
			||||||
 | 
							AuthInfos: map[string]AuthInfo{
 | 
				
			||||||
 | 
								"green-user": {Token: "green-token"}},
 | 
				
			||||||
 | 
							Clusters: map[string]Cluster{
 | 
				
			||||||
 | 
								"horse-cluster": {Server: "http://horse.org:8080"}},
 | 
				
			||||||
 | 
							Contexts: map[string]Context{
 | 
				
			||||||
 | 
								"shaker-context": {AuthInfo: "green-user", Cluster: "horse-cluster", Namespace: "chisel-ns"}},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						testConfigDelta = Config{
 | 
				
			||||||
 | 
							AuthInfos: map[string]AuthInfo{
 | 
				
			||||||
 | 
								"blue-user": {Token: "blue-token"}},
 | 
				
			||||||
 | 
							Clusters: map[string]Cluster{
 | 
				
			||||||
 | 
								"chicken-cluster": {Server: "http://chicken.org:8080"}},
 | 
				
			||||||
 | 
							Contexts: map[string]Context{
 | 
				
			||||||
 | 
								"gothic-context": {AuthInfo: "blue-user", Cluster: "chicken-cluster", Namespace: "plane-ns"}},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						testConfigConflictAlfa = Config{
 | 
				
			||||||
 | 
							AuthInfos: map[string]AuthInfo{
 | 
				
			||||||
 | 
								"red-user":    {Token: "a-different-red-token"},
 | 
				
			||||||
 | 
								"yellow-user": {Token: "yellow-token"}},
 | 
				
			||||||
 | 
							Clusters: map[string]Cluster{
 | 
				
			||||||
 | 
								"cow-cluster":    {Server: "http://a-different-cow.org:8080", InsecureSkipTLSVerify: true},
 | 
				
			||||||
 | 
								"donkey-cluster": {Server: "http://donkey.org:8080", InsecureSkipTLSVerify: true}},
 | 
				
			||||||
 | 
							CurrentContext: "federal-context",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ExampleMergingSomeWithConflict() {
 | 
				
			||||||
 | 
						commandLineFile, _ := ioutil.TempFile("", "")
 | 
				
			||||||
 | 
						defer os.Remove(commandLineFile.Name())
 | 
				
			||||||
 | 
						envVarFile, _ := ioutil.TempFile("", "")
 | 
				
			||||||
 | 
						defer os.Remove(envVarFile.Name())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						WriteToFile(testConfigAlfa, commandLineFile.Name())
 | 
				
			||||||
 | 
						WriteToFile(testConfigConflictAlfa, envVarFile.Name())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						loadingRules := ClientConfigLoadingRules{
 | 
				
			||||||
 | 
							CommandLinePath: commandLineFile.Name(),
 | 
				
			||||||
 | 
							EnvVarPath:      envVarFile.Name(),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mergedConfig, err := loadingRules.Load()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						output, err := yaml.Marshal(mergedConfig)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							fmt.Printf("Unexpected error: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fmt.Printf("%v", string(output))
 | 
				
			||||||
 | 
						// Output:
 | 
				
			||||||
 | 
						// preferences: {}
 | 
				
			||||||
 | 
						// clusters:
 | 
				
			||||||
 | 
						//   cow-cluster:
 | 
				
			||||||
 | 
						//     server: http://cow.org:8080
 | 
				
			||||||
 | 
						//   donkey-cluster:
 | 
				
			||||||
 | 
						//     server: http://donkey.org:8080
 | 
				
			||||||
 | 
						//     insecure-skip-tls-verify: true
 | 
				
			||||||
 | 
						// users:
 | 
				
			||||||
 | 
						//   red-user:
 | 
				
			||||||
 | 
						//     token: red-token
 | 
				
			||||||
 | 
						//   yellow-user:
 | 
				
			||||||
 | 
						//     token: yellow-token
 | 
				
			||||||
 | 
						// contexts:
 | 
				
			||||||
 | 
						//   federal-context:
 | 
				
			||||||
 | 
						//     cluster: cow-cluster
 | 
				
			||||||
 | 
						//     user: red-user
 | 
				
			||||||
 | 
						//     namespace: hammer-ns
 | 
				
			||||||
 | 
						// current-context: federal-context
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ExampleMergingEverythingNoConflicts() {
 | 
				
			||||||
 | 
						commandLineFile, _ := ioutil.TempFile("", "")
 | 
				
			||||||
 | 
						defer os.Remove(commandLineFile.Name())
 | 
				
			||||||
 | 
						envVarFile, _ := ioutil.TempFile("", "")
 | 
				
			||||||
 | 
						defer os.Remove(envVarFile.Name())
 | 
				
			||||||
 | 
						currentDirFile, _ := ioutil.TempFile("", "")
 | 
				
			||||||
 | 
						defer os.Remove(currentDirFile.Name())
 | 
				
			||||||
 | 
						homeDirFile, _ := ioutil.TempFile("", "")
 | 
				
			||||||
 | 
						defer os.Remove(homeDirFile.Name())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						WriteToFile(testConfigAlfa, commandLineFile.Name())
 | 
				
			||||||
 | 
						WriteToFile(testConfigBravo, envVarFile.Name())
 | 
				
			||||||
 | 
						WriteToFile(testConfigCharlie, currentDirFile.Name())
 | 
				
			||||||
 | 
						WriteToFile(testConfigDelta, homeDirFile.Name())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						loadingRules := ClientConfigLoadingRules{
 | 
				
			||||||
 | 
							CommandLinePath:      commandLineFile.Name(),
 | 
				
			||||||
 | 
							EnvVarPath:           envVarFile.Name(),
 | 
				
			||||||
 | 
							CurrentDirectoryPath: currentDirFile.Name(),
 | 
				
			||||||
 | 
							HomeDirectoryPath:    homeDirFile.Name(),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mergedConfig, err := loadingRules.Load()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						output, err := yaml.Marshal(mergedConfig)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							fmt.Printf("Unexpected error: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fmt.Printf("%v", string(output))
 | 
				
			||||||
 | 
						// Output:
 | 
				
			||||||
 | 
						// preferences: {}
 | 
				
			||||||
 | 
						// clusters:
 | 
				
			||||||
 | 
						//   chicken-cluster:
 | 
				
			||||||
 | 
						//     server: http://chicken.org:8080
 | 
				
			||||||
 | 
						//   cow-cluster:
 | 
				
			||||||
 | 
						//     server: http://cow.org:8080
 | 
				
			||||||
 | 
						//   horse-cluster:
 | 
				
			||||||
 | 
						//     server: http://horse.org:8080
 | 
				
			||||||
 | 
						//   pig-cluster:
 | 
				
			||||||
 | 
						//     server: http://pig.org:8080
 | 
				
			||||||
 | 
						// users:
 | 
				
			||||||
 | 
						//   black-user:
 | 
				
			||||||
 | 
						//     token: black-token
 | 
				
			||||||
 | 
						//   blue-user:
 | 
				
			||||||
 | 
						//     token: blue-token
 | 
				
			||||||
 | 
						//   green-user:
 | 
				
			||||||
 | 
						//     token: green-token
 | 
				
			||||||
 | 
						//   red-user:
 | 
				
			||||||
 | 
						//     token: red-token
 | 
				
			||||||
 | 
						// contexts:
 | 
				
			||||||
 | 
						//   federal-context:
 | 
				
			||||||
 | 
						//     cluster: cow-cluster
 | 
				
			||||||
 | 
						//     user: red-user
 | 
				
			||||||
 | 
						//     namespace: hammer-ns
 | 
				
			||||||
 | 
						//   gothic-context:
 | 
				
			||||||
 | 
						//     cluster: chicken-cluster
 | 
				
			||||||
 | 
						//     user: blue-user
 | 
				
			||||||
 | 
						//     namespace: plane-ns
 | 
				
			||||||
 | 
						//   queen-anne-context:
 | 
				
			||||||
 | 
						//     cluster: pig-cluster
 | 
				
			||||||
 | 
						//     user: black-user
 | 
				
			||||||
 | 
						//     namespace: saw-ns
 | 
				
			||||||
 | 
						//   shaker-context:
 | 
				
			||||||
 | 
						//     cluster: horse-cluster
 | 
				
			||||||
 | 
						//     user: green-user
 | 
				
			||||||
 | 
						//     namespace: chisel-ns
 | 
				
			||||||
 | 
						// current-context: ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										70
									
								
								pkg/client/clientcmd/merged_client_builder.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								pkg/client/clientcmd/merged_client_builder.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,70 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2014 Google Inc. All rights reserved.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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 (
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DeferredLoadingClientConfig is a ClientConfig interface that is backed by a set of loading rules
 | 
				
			||||||
 | 
					// It is used in cases where the loading rules may change after you've instantiated them and you want to be sure that
 | 
				
			||||||
 | 
					// the most recent rules are used.  This is useful in cases where you bind flags to loading rule parameters before
 | 
				
			||||||
 | 
					// the parse happens and you want your calling code to be ignorant of how the values are being mutated to avoid
 | 
				
			||||||
 | 
					// passing extraneous information down a call stack
 | 
				
			||||||
 | 
					type DeferredLoadingClientConfig struct {
 | 
				
			||||||
 | 
						loadingRules   *ClientConfigLoadingRules
 | 
				
			||||||
 | 
						overrides      *ConfigOverrides
 | 
				
			||||||
 | 
						fallbackReader io.Reader
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewNonInteractiveDeferredLoadingClientConfig creates a ConfigClientClientConfig using the passed context name
 | 
				
			||||||
 | 
					func NewNonInteractiveDeferredLoadingClientConfig(loadingRules *ClientConfigLoadingRules, overrides *ConfigOverrides) ClientConfig {
 | 
				
			||||||
 | 
						return DeferredLoadingClientConfig{loadingRules, overrides, nil}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewInteractiveDeferredLoadingClientConfig creates a ConfigClientClientConfig using the passed context name and the fallback auth reader
 | 
				
			||||||
 | 
					func NewInteractiveDeferredLoadingClientConfig(loadingRules *ClientConfigLoadingRules, overrides *ConfigOverrides, fallbackReader io.Reader) ClientConfig {
 | 
				
			||||||
 | 
						return DeferredLoadingClientConfig{loadingRules, overrides, fallbackReader}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (config DeferredLoadingClientConfig) createClientConfig() (ClientConfig, error) {
 | 
				
			||||||
 | 
						mergedConfig, err := config.loadingRules.Load()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var mergedClientConfig ClientConfig
 | 
				
			||||||
 | 
						if config.fallbackReader != nil {
 | 
				
			||||||
 | 
							mergedClientConfig = NewInteractiveClientConfig(*mergedConfig, config.overrides.CurrentContext, config.overrides, config.fallbackReader)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							mergedClientConfig = NewNonInteractiveClientConfig(*mergedConfig, config.overrides.CurrentContext, config.overrides)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return mergedClientConfig, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ClientConfig implements ClientConfig
 | 
				
			||||||
 | 
					func (config DeferredLoadingClientConfig) ClientConfig() (*client.Config, error) {
 | 
				
			||||||
 | 
						mergedClientConfig, err := config.createClientConfig()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return mergedClientConfig.ClientConfig()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										135
									
								
								pkg/client/clientcmd/overrides.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								pkg/client/clientcmd/overrides.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,135 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2014 Google Inc. All rights reserved.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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 (
 | 
				
			||||||
 | 
						"github.com/spf13/pflag"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ConfigOverrides holds values that should override whatever information is pulled from the actual Config object.  You can't
 | 
				
			||||||
 | 
					// simply use an actual Config object, because Configs hold maps, but overrides are restricted to "at most one"
 | 
				
			||||||
 | 
					type ConfigOverrides struct {
 | 
				
			||||||
 | 
						AuthInfo       AuthInfo
 | 
				
			||||||
 | 
						ClusterInfo    Cluster
 | 
				
			||||||
 | 
						Namespace      string
 | 
				
			||||||
 | 
						CurrentContext string
 | 
				
			||||||
 | 
						ClusterName    string
 | 
				
			||||||
 | 
						AuthInfoName   string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ConfigOverrideFlags holds the flag names to be used for binding command line flags.  Notice that this structure tightly
 | 
				
			||||||
 | 
					// corresponds to ConfigOverrides
 | 
				
			||||||
 | 
					type ConfigOverrideFlags struct {
 | 
				
			||||||
 | 
						AuthOverrideFlags    AuthOverrideFlags
 | 
				
			||||||
 | 
						ClusterOverrideFlags ClusterOverrideFlags
 | 
				
			||||||
 | 
						Namespace            string
 | 
				
			||||||
 | 
						CurrentContext       string
 | 
				
			||||||
 | 
						ClusterName          string
 | 
				
			||||||
 | 
						AuthInfoName         string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// AuthOverrideFlags holds the flag names to be used for binding command line flags for AuthInfo objects
 | 
				
			||||||
 | 
					type AuthOverrideFlags struct {
 | 
				
			||||||
 | 
						AuthPath          string
 | 
				
			||||||
 | 
						ClientCertificate string
 | 
				
			||||||
 | 
						ClientKey         string
 | 
				
			||||||
 | 
						Token             string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ClusterOverride holds the flag names to be used for binding command line flags for Cluster objects
 | 
				
			||||||
 | 
					type ClusterOverrideFlags struct {
 | 
				
			||||||
 | 
						APIServer             string
 | 
				
			||||||
 | 
						APIVersion            string
 | 
				
			||||||
 | 
						CertificateAuthority  string
 | 
				
			||||||
 | 
						InsecureSkipTLSVerify string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						FlagClusterName  = "cluster"
 | 
				
			||||||
 | 
						FlagAuthInfoName = "user"
 | 
				
			||||||
 | 
						FlagContext      = "context"
 | 
				
			||||||
 | 
						FlagNamespace    = "namespace"
 | 
				
			||||||
 | 
						FlagAPIServer    = "server"
 | 
				
			||||||
 | 
						FlagAPIVersion   = "api-version"
 | 
				
			||||||
 | 
						FlagAuthPath     = "auth-path"
 | 
				
			||||||
 | 
						FlagInsecure     = "insecure-skip-tls-verify"
 | 
				
			||||||
 | 
						FlagCertFile     = "client-certificate"
 | 
				
			||||||
 | 
						FlagKeyFile      = "client-key"
 | 
				
			||||||
 | 
						FlagCAFile       = "certificate-authority"
 | 
				
			||||||
 | 
						FlagBearerToken  = "token"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RecommendedAuthOverrideFlags is a convenience method to return recommended flag names prefixed with a string of your choosing
 | 
				
			||||||
 | 
					func RecommendedAuthOverrideFlags(prefix string) AuthOverrideFlags {
 | 
				
			||||||
 | 
						return AuthOverrideFlags{
 | 
				
			||||||
 | 
							AuthPath:          prefix + FlagAuthPath,
 | 
				
			||||||
 | 
							ClientCertificate: prefix + FlagCertFile,
 | 
				
			||||||
 | 
							ClientKey:         prefix + FlagKeyFile,
 | 
				
			||||||
 | 
							Token:             prefix + FlagBearerToken,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RecommendedClusterOverrideFlags is a convenience method to return recommended flag names prefixed with a string of your choosing
 | 
				
			||||||
 | 
					func RecommendedClusterOverrideFlags(prefix string) ClusterOverrideFlags {
 | 
				
			||||||
 | 
						return ClusterOverrideFlags{
 | 
				
			||||||
 | 
							APIServer:             prefix + FlagAPIServer,
 | 
				
			||||||
 | 
							APIVersion:            prefix + FlagAPIVersion,
 | 
				
			||||||
 | 
							CertificateAuthority:  prefix + FlagCAFile,
 | 
				
			||||||
 | 
							InsecureSkipTLSVerify: prefix + FlagInsecure,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RecommendedConfigOverrideFlags is a convenience method to return recommended flag names prefixed with a string of your choosing
 | 
				
			||||||
 | 
					func RecommendedConfigOverrideFlags(prefix string) ConfigOverrideFlags {
 | 
				
			||||||
 | 
						return ConfigOverrideFlags{
 | 
				
			||||||
 | 
							AuthOverrideFlags:    RecommendedAuthOverrideFlags(prefix),
 | 
				
			||||||
 | 
							ClusterOverrideFlags: RecommendedClusterOverrideFlags(prefix),
 | 
				
			||||||
 | 
							Namespace:            prefix + FlagNamespace,
 | 
				
			||||||
 | 
							CurrentContext:       prefix + FlagContext,
 | 
				
			||||||
 | 
							ClusterName:          prefix + FlagClusterName,
 | 
				
			||||||
 | 
							AuthInfoName:         prefix + FlagAuthInfoName,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// BindFlags is a convenience method to bind the specified flags to their associated variables
 | 
				
			||||||
 | 
					func (authInfo *AuthInfo) BindFlags(flags *pflag.FlagSet, flagNames AuthOverrideFlags) {
 | 
				
			||||||
 | 
						// TODO short flag names are impossible to prefix, decide whether to keep them or not
 | 
				
			||||||
 | 
						flags.StringVarP(&authInfo.AuthPath, flagNames.AuthPath, "a", "", "Path to the auth info file. If missing, prompt the user. Only used if using https.")
 | 
				
			||||||
 | 
						flags.StringVar(&authInfo.ClientCertificate, flagNames.ClientCertificate, "", "Path to a client key file for TLS.")
 | 
				
			||||||
 | 
						flags.StringVar(&authInfo.ClientKey, flagNames.ClientKey, "", "Path to a client key file for TLS.")
 | 
				
			||||||
 | 
						flags.StringVar(&authInfo.Token, flagNames.Token, "", "Bearer token for authentication to the API server.")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// BindFlags is a convenience method to bind the specified flags to their associated variables
 | 
				
			||||||
 | 
					func (clusterInfo *Cluster) BindFlags(flags *pflag.FlagSet, flagNames ClusterOverrideFlags) {
 | 
				
			||||||
 | 
						// TODO short flag names are impossible to prefix, decide whether to keep them or not
 | 
				
			||||||
 | 
						flags.StringVarP(&clusterInfo.Server, flagNames.APIServer, "s", "", "The address of the Kubernetes API server")
 | 
				
			||||||
 | 
						flags.StringVar(&clusterInfo.APIVersion, flagNames.APIVersion, "", "The API version to use when talking to the server")
 | 
				
			||||||
 | 
						flags.StringVar(&clusterInfo.CertificateAuthority, flagNames.CertificateAuthority, "", "Path to a cert. file for the certificate authority.")
 | 
				
			||||||
 | 
						flags.BoolVar(&clusterInfo.InsecureSkipTLSVerify, flagNames.InsecureSkipTLSVerify, false, "If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure.")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// BindFlags is a convenience method to bind the specified flags to their associated variables
 | 
				
			||||||
 | 
					func (overrides *ConfigOverrides) BindFlags(flags *pflag.FlagSet, flagNames ConfigOverrideFlags) {
 | 
				
			||||||
 | 
						(&overrides.AuthInfo).BindFlags(flags, flagNames.AuthOverrideFlags)
 | 
				
			||||||
 | 
						(&overrides.ClusterInfo).BindFlags(flags, flagNames.ClusterOverrideFlags)
 | 
				
			||||||
 | 
						// TODO not integrated yet
 | 
				
			||||||
 | 
						// flags.StringVar(&overrides.Namespace, flagNames.Namespace, "", "If present, the namespace scope for this CLI request.")
 | 
				
			||||||
 | 
						flags.StringVar(&overrides.CurrentContext, flagNames.CurrentContext, "", "The name of the kubeconfig context to use")
 | 
				
			||||||
 | 
						flags.StringVar(&overrides.ClusterName, flagNames.ClusterName, "", "The name of the kubeconfig cluster to use")
 | 
				
			||||||
 | 
						flags.StringVar(&overrides.AuthInfoName, flagNames.AuthInfoName, "", "The name of the kubeconfig user to use")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,100 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
Copyright 2014 Google Inc. All rights reserved.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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"
 | 
					 | 
				
			||||||
	"strconv"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/spf13/pflag"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// FlagProvider adds a check for whether .Set was called on this flag variable
 | 
					 | 
				
			||||||
type FlagProvider interface {
 | 
					 | 
				
			||||||
	// Provided returns true iff .Set was called on this flag
 | 
					 | 
				
			||||||
	Provided() bool
 | 
					 | 
				
			||||||
	pflag.Value
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// StringFlag implements FlagProvider
 | 
					 | 
				
			||||||
type StringFlag struct {
 | 
					 | 
				
			||||||
	Default     string
 | 
					 | 
				
			||||||
	Value       string
 | 
					 | 
				
			||||||
	WasProvided bool
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// SetDefault sets a default value for a flag while keeping Provided() false
 | 
					 | 
				
			||||||
func (flag *StringFlag) SetDefault(value string) {
 | 
					 | 
				
			||||||
	flag.Value = value
 | 
					 | 
				
			||||||
	flag.WasProvided = false
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (flag *StringFlag) Set(value string) error {
 | 
					 | 
				
			||||||
	flag.Value = value
 | 
					 | 
				
			||||||
	flag.WasProvided = true
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (flag *StringFlag) Type() string {
 | 
					 | 
				
			||||||
	return "string"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (flag *StringFlag) Provided() bool {
 | 
					 | 
				
			||||||
	return flag.WasProvided
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (flag *StringFlag) String() string {
 | 
					 | 
				
			||||||
	return flag.Value
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// BoolFlag implements FlagProvider
 | 
					 | 
				
			||||||
type BoolFlag struct {
 | 
					 | 
				
			||||||
	Default     bool
 | 
					 | 
				
			||||||
	Value       bool
 | 
					 | 
				
			||||||
	WasProvided bool
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// SetDefault sets a default value for a flag while keeping Provided() false
 | 
					 | 
				
			||||||
func (flag *BoolFlag) SetDefault(value bool) {
 | 
					 | 
				
			||||||
	flag.Value = value
 | 
					 | 
				
			||||||
	flag.WasProvided = false
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (flag *BoolFlag) Set(value string) error {
 | 
					 | 
				
			||||||
	boolValue, err := strconv.ParseBool(value)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	flag.Value = boolValue
 | 
					 | 
				
			||||||
	flag.WasProvided = true
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (flag *BoolFlag) Type() string {
 | 
					 | 
				
			||||||
	return "bool"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (flag *BoolFlag) Provided() bool {
 | 
					 | 
				
			||||||
	return flag.WasProvided
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (flag *BoolFlag) String() string {
 | 
					 | 
				
			||||||
	return fmt.Sprintf("%t", flag.Value)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										83
									
								
								pkg/client/clientcmd/types.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								pkg/client/clientcmd/types.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,83 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2014 Google Inc. All rights reserved.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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 ()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Where possible, yaml tags match the cli argument names.
 | 
				
			||||||
 | 
					// Top level config objects and all values required for proper functioning are not "omitempty".  Any truly optional piece of config is allowed to be omitted.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Config holds the information needed to build connect to remote kubernetes clusters as a given user
 | 
				
			||||||
 | 
					type Config struct {
 | 
				
			||||||
 | 
						// Preferences holds general information to be use for cli interactions
 | 
				
			||||||
 | 
						Preferences Preferences `yaml:"preferences"`
 | 
				
			||||||
 | 
						// Clusters is a map of referencable names to cluster configs
 | 
				
			||||||
 | 
						Clusters map[string]Cluster `yaml:"clusters"`
 | 
				
			||||||
 | 
						// AuthInfos is a map of referencable names to user configs
 | 
				
			||||||
 | 
						AuthInfos map[string]AuthInfo `yaml:"users"`
 | 
				
			||||||
 | 
						// Contexts is a map of referencable names to context configs
 | 
				
			||||||
 | 
						Contexts map[string]Context `yaml:"contexts"`
 | 
				
			||||||
 | 
						// CurrentContext is the name of the context that you would like to use by default
 | 
				
			||||||
 | 
						CurrentContext string `yaml:"current-context"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Preferences struct {
 | 
				
			||||||
 | 
						Colors bool `yaml:"colors,omitempty"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Cluster contains information about how to communicate with a kubernetes cluster
 | 
				
			||||||
 | 
					type Cluster struct {
 | 
				
			||||||
 | 
						// Server is the address of the kubernetes cluster (https://hostname:port).
 | 
				
			||||||
 | 
						Server string `yaml:"server"`
 | 
				
			||||||
 | 
						// APIVersion is the preferred api version for communicating with the kubernetes cluster (v1beta1, v1beta2, v1beta3, etc).
 | 
				
			||||||
 | 
						APIVersion string `yaml:"api-version,omitempty"`
 | 
				
			||||||
 | 
						// InsecureSkipTLSVerify skips the validity check for the server's certificate. This will make your HTTPS connections insecure.
 | 
				
			||||||
 | 
						InsecureSkipTLSVerify bool `yaml:"insecure-skip-tls-verify,omitempty"`
 | 
				
			||||||
 | 
						// CertificateAuthority is the path to a cert file for the certificate authority.
 | 
				
			||||||
 | 
						CertificateAuthority string `yaml:"certificate-authority,omitempty"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// AuthInfo contains information that describes identity information.  This is use to tell the kubernetes cluster who you are.
 | 
				
			||||||
 | 
					type AuthInfo struct {
 | 
				
			||||||
 | 
						// AuthPath is the path to a kubernetes auth file (~/.kubernetes_auth).  If you provide an AuthPath, the other options specified are ignored
 | 
				
			||||||
 | 
						AuthPath string `yaml:"auth-path,omitempty"`
 | 
				
			||||||
 | 
						// ClientCertificate is the path to a client cert file for TLS.
 | 
				
			||||||
 | 
						ClientCertificate string `yaml:"client-certificate,omitempty"`
 | 
				
			||||||
 | 
						// ClientKey is the path to a client key file for TLS.
 | 
				
			||||||
 | 
						ClientKey string `yaml:"client-key,omitempty"`
 | 
				
			||||||
 | 
						// Token is the bearer token for authentication to the kubernetes cluster.
 | 
				
			||||||
 | 
						Token string `yaml:"token,omitempty"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Context is a tuple of references to a cluster (how do I communicate with a kubernetes cluster), a user (how do I identify myself), and a namespace (what subset of resources do I want to work with)
 | 
				
			||||||
 | 
					type Context struct {
 | 
				
			||||||
 | 
						// Cluster is the name of the cluster for this context
 | 
				
			||||||
 | 
						Cluster string `yaml:"cluster"`
 | 
				
			||||||
 | 
						// AuthInfo is the name of the authInfo for this context
 | 
				
			||||||
 | 
						AuthInfo string `yaml:"user"`
 | 
				
			||||||
 | 
						// Namespace is the default namespace to use on unspecified requests
 | 
				
			||||||
 | 
						Namespace string `yaml:"namespace,omitempty"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewConfig is a convenience function that returns a new Config object with non-nil maps
 | 
				
			||||||
 | 
					func NewConfig() *Config {
 | 
				
			||||||
 | 
						return &Config{
 | 
				
			||||||
 | 
							Clusters:  make(map[string]Cluster),
 | 
				
			||||||
 | 
							AuthInfos: make(map[string]AuthInfo),
 | 
				
			||||||
 | 
							Contexts:  make(map[string]Context),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										121
									
								
								pkg/client/clientcmd/types_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								pkg/client/clientcmd/types_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,121 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2014 Google Inc. All rights reserved.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"gopkg.in/v2/yaml"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ExampleEmptyConfig() {
 | 
				
			||||||
 | 
						defaultConfig := NewConfig()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						output, err := yaml.Marshal(defaultConfig)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							fmt.Printf("Unexpected error: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fmt.Printf("%v", string(output))
 | 
				
			||||||
 | 
						// Output:
 | 
				
			||||||
 | 
						// preferences: {}
 | 
				
			||||||
 | 
						// clusters: {}
 | 
				
			||||||
 | 
						// users: {}
 | 
				
			||||||
 | 
						// contexts: {}
 | 
				
			||||||
 | 
						// current-context: ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ExampleOfOptionsConfig() {
 | 
				
			||||||
 | 
						defaultConfig := NewConfig()
 | 
				
			||||||
 | 
						defaultConfig.Preferences.Colors = true
 | 
				
			||||||
 | 
						defaultConfig.Clusters["alfa"] = Cluster{
 | 
				
			||||||
 | 
							Server:                "https://alfa.org:8080",
 | 
				
			||||||
 | 
							APIVersion:            "v1beta2",
 | 
				
			||||||
 | 
							InsecureSkipTLSVerify: true,
 | 
				
			||||||
 | 
							CertificateAuthority:  "path/to/my/cert-ca-filename",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defaultConfig.Clusters["bravo"] = Cluster{
 | 
				
			||||||
 | 
							Server:                "https://bravo.org:8080",
 | 
				
			||||||
 | 
							APIVersion:            "v1beta1",
 | 
				
			||||||
 | 
							InsecureSkipTLSVerify: false,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defaultConfig.AuthInfos["black-mage-via-file"] = AuthInfo{
 | 
				
			||||||
 | 
							AuthPath: "path/to/my/.kubernetes_auth",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defaultConfig.AuthInfos["white-mage-via-cert"] = AuthInfo{
 | 
				
			||||||
 | 
							ClientCertificate: "path/to/my/client-cert-filename",
 | 
				
			||||||
 | 
							ClientKey:         "path/to/my/client-key-filename",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defaultConfig.AuthInfos["red-mage-via-token"] = AuthInfo{
 | 
				
			||||||
 | 
							Token: "my-secret-token",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defaultConfig.Contexts["bravo-as-black-mage"] = Context{
 | 
				
			||||||
 | 
							Cluster:   "bravo",
 | 
				
			||||||
 | 
							AuthInfo:  "black-mage-via-file",
 | 
				
			||||||
 | 
							Namespace: "yankee",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defaultConfig.Contexts["alfa-as-black-mage"] = Context{
 | 
				
			||||||
 | 
							Cluster:   "alfa",
 | 
				
			||||||
 | 
							AuthInfo:  "black-mage-via-file",
 | 
				
			||||||
 | 
							Namespace: "zulu",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defaultConfig.Contexts["alfa-as-white-mage"] = Context{
 | 
				
			||||||
 | 
							Cluster:  "alfa",
 | 
				
			||||||
 | 
							AuthInfo: "white-mage-via-cert",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defaultConfig.CurrentContext = "alfa-as-white-mage"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						output, err := yaml.Marshal(defaultConfig)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							fmt.Printf("Unexpected error: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fmt.Printf("%v", string(output))
 | 
				
			||||||
 | 
						// Output:
 | 
				
			||||||
 | 
						// preferences:
 | 
				
			||||||
 | 
						//   colors: true
 | 
				
			||||||
 | 
						// clusters:
 | 
				
			||||||
 | 
						//   alfa:
 | 
				
			||||||
 | 
						//     server: https://alfa.org:8080
 | 
				
			||||||
 | 
						//     api-version: v1beta2
 | 
				
			||||||
 | 
						//     insecure-skip-tls-verify: true
 | 
				
			||||||
 | 
						//     certificate-authority: path/to/my/cert-ca-filename
 | 
				
			||||||
 | 
						//   bravo:
 | 
				
			||||||
 | 
						//     server: https://bravo.org:8080
 | 
				
			||||||
 | 
						//     api-version: v1beta1
 | 
				
			||||||
 | 
						// users:
 | 
				
			||||||
 | 
						//   black-mage-via-file:
 | 
				
			||||||
 | 
						//     auth-path: path/to/my/.kubernetes_auth
 | 
				
			||||||
 | 
						//   red-mage-via-token:
 | 
				
			||||||
 | 
						//     token: my-secret-token
 | 
				
			||||||
 | 
						//   white-mage-via-cert:
 | 
				
			||||||
 | 
						//     client-certificate: path/to/my/client-cert-filename
 | 
				
			||||||
 | 
						//     client-key: path/to/my/client-key-filename
 | 
				
			||||||
 | 
						// contexts:
 | 
				
			||||||
 | 
						//   alfa-as-black-mage:
 | 
				
			||||||
 | 
						//     cluster: alfa
 | 
				
			||||||
 | 
						//     user: black-mage-via-file
 | 
				
			||||||
 | 
						//     namespace: zulu
 | 
				
			||||||
 | 
						//   alfa-as-white-mage:
 | 
				
			||||||
 | 
						//     cluster: alfa
 | 
				
			||||||
 | 
						//     user: white-mage-via-cert
 | 
				
			||||||
 | 
						//   bravo-as-black-mage:
 | 
				
			||||||
 | 
						//     cluster: bravo
 | 
				
			||||||
 | 
						//     user: black-mage-via-file
 | 
				
			||||||
 | 
						//     namespace: yankee
 | 
				
			||||||
 | 
						// current-context: alfa-as-white-mage
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										182
									
								
								pkg/client/clientcmd/validation.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										182
									
								
								pkg/client/clientcmd/validation.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,182 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2014 Google Inc. All rights reserved.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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 (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var ErrNoContext = errors.New("no context chosen")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type errContextNotFound struct {
 | 
				
			||||||
 | 
						ContextName string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *errContextNotFound) Error() string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("context was not found for specified context: %v", e.ContextName)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IsContextNotFound returns a boolean indicating whether the error is known to
 | 
				
			||||||
 | 
					// report that a context was not found
 | 
				
			||||||
 | 
					func IsContextNotFound(err error) bool {
 | 
				
			||||||
 | 
						if err == nil {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return strings.Contains(err.Error(), "context was not found for specified context")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Validate checks for errors in the Config.  It does not return early so that it can find as many errors as possible.
 | 
				
			||||||
 | 
					func Validate(config Config) error {
 | 
				
			||||||
 | 
						validationErrors := make([]error, 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(config.CurrentContext) != 0 {
 | 
				
			||||||
 | 
							if _, exists := config.Contexts[config.CurrentContext]; !exists {
 | 
				
			||||||
 | 
								validationErrors = append(validationErrors, &errContextNotFound{config.CurrentContext})
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for contextName, context := range config.Contexts {
 | 
				
			||||||
 | 
							validationErrors = append(validationErrors, validateContext(contextName, context, config)...)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for authInfoName, authInfo := range config.AuthInfos {
 | 
				
			||||||
 | 
							validationErrors = append(validationErrors, validateAuthInfo(authInfoName, authInfo)...)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for clusterName, clusterInfo := range config.Clusters {
 | 
				
			||||||
 | 
							validationErrors = append(validationErrors, validateClusterInfo(clusterName, clusterInfo)...)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return util.SliceToError(validationErrors)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 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 ConfirmUsable(config Config, passedContextName string) error {
 | 
				
			||||||
 | 
						validationErrors := make([]error, 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var contextName string
 | 
				
			||||||
 | 
						if len(passedContextName) != 0 {
 | 
				
			||||||
 | 
							contextName = passedContextName
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							contextName = config.CurrentContext
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(contextName) == 0 {
 | 
				
			||||||
 | 
							return ErrNoContext
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						context, exists := config.Contexts[contextName]
 | 
				
			||||||
 | 
						if !exists {
 | 
				
			||||||
 | 
							validationErrors = append(validationErrors, &errContextNotFound{contextName})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if exists {
 | 
				
			||||||
 | 
							validationErrors = append(validationErrors, validateContext(contextName, context, config)...)
 | 
				
			||||||
 | 
							validationErrors = append(validationErrors, validateAuthInfo(context.AuthInfo, config.AuthInfos[context.AuthInfo])...)
 | 
				
			||||||
 | 
							validationErrors = append(validationErrors, validateClusterInfo(context.Cluster, config.Clusters[context.Cluster])...)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return util.SliceToError(validationErrors)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// validateClusterInfo looks for conflicts and errors in the cluster info
 | 
				
			||||||
 | 
					func validateClusterInfo(clusterName string, clusterInfo Cluster) []error {
 | 
				
			||||||
 | 
						validationErrors := make([]error, 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(clusterInfo.Server) == 0 {
 | 
				
			||||||
 | 
							validationErrors = append(validationErrors, fmt.Errorf("no server found for %v", clusterName))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(clusterInfo.CertificateAuthority) != 0 {
 | 
				
			||||||
 | 
							clientCertCA, err := os.Open(clusterInfo.CertificateAuthority)
 | 
				
			||||||
 | 
							defer clientCertCA.Close()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								validationErrors = append(validationErrors, fmt.Errorf("unable to read certificate-authority %v for %v due to %v", clusterInfo.CertificateAuthority, clusterName, err))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return validationErrors
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// validateAuthInfo looks for conflicts and errors in the auth info
 | 
				
			||||||
 | 
					func validateAuthInfo(authInfoName string, authInfo AuthInfo) []error {
 | 
				
			||||||
 | 
						validationErrors := make([]error, 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						methods := make([]string, 0, 3)
 | 
				
			||||||
 | 
						if len(authInfo.Token) != 0 {
 | 
				
			||||||
 | 
							methods = append(methods, "token")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(authInfo.AuthPath) != 0 {
 | 
				
			||||||
 | 
							methods = append(methods, "authFile")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							file, err := os.Open(authInfo.AuthPath)
 | 
				
			||||||
 | 
							os.IsNotExist(err)
 | 
				
			||||||
 | 
							defer file.Close()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								validationErrors = append(validationErrors, fmt.Errorf("unable to read auth-path %v for %v due to %v", authInfo.AuthPath, authInfoName, err))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(authInfo.ClientCertificate) != 0 {
 | 
				
			||||||
 | 
							methods = append(methods, "clientCert")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							clientCertFile, err := os.Open(authInfo.ClientCertificate)
 | 
				
			||||||
 | 
							defer clientCertFile.Close()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								validationErrors = append(validationErrors, fmt.Errorf("unable to read client-cert %v for %v due to %v", authInfo.ClientCertificate, authInfoName, err))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							clientKeyFile, err := os.Open(authInfo.ClientKey)
 | 
				
			||||||
 | 
							defer clientKeyFile.Close()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								validationErrors = append(validationErrors, fmt.Errorf("unable to read client-key %v for %v due to %v", authInfo.ClientKey, authInfoName, err))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (len(methods)) > 1 {
 | 
				
			||||||
 | 
							validationErrors = append(validationErrors, fmt.Errorf("more than one authentication method found for  %v.  Found %v, only one is allowed", authInfoName, methods))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return validationErrors
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// validateContext looks for errors in the context.  It is not transitive, so errors in the reference authInfo or cluster configs are not included in this return
 | 
				
			||||||
 | 
					func validateContext(contextName string, context Context, config Config) []error {
 | 
				
			||||||
 | 
						validationErrors := make([]error, 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(context.AuthInfo) == 0 {
 | 
				
			||||||
 | 
							validationErrors = append(validationErrors, fmt.Errorf("user was not specified for Context %v", contextName))
 | 
				
			||||||
 | 
						} else if _, exists := config.AuthInfos[context.AuthInfo]; !exists {
 | 
				
			||||||
 | 
							validationErrors = append(validationErrors, fmt.Errorf("user, %v, was not found for Context %v", context.AuthInfo, contextName))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(context.Cluster) == 0 {
 | 
				
			||||||
 | 
							validationErrors = append(validationErrors, fmt.Errorf("cluster was not specified for Context %v", contextName))
 | 
				
			||||||
 | 
						} else if _, exists := config.Clusters[context.Cluster]; !exists {
 | 
				
			||||||
 | 
							validationErrors = append(validationErrors, fmt.Errorf("cluster, %v, was not found for Context %v", context.Cluster, contextName))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (len(context.Namespace) != 0) && !util.IsDNS952Label(context.Namespace) {
 | 
				
			||||||
 | 
							validationErrors = append(validationErrors, fmt.Errorf("namespace, %v, for context %v, does not conform to the kubernetest DNS952 rules", context.Namespace, contextName))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return validationErrors
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										404
									
								
								pkg/client/clientcmd/validation_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										404
									
								
								pkg/client/clientcmd/validation_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,404 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2014 Google Inc. All rights reserved.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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 (
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestConfirmUsableBadInfoButOkConfig(t *testing.T) {
 | 
				
			||||||
 | 
						config := NewConfig()
 | 
				
			||||||
 | 
						config.Clusters["missing ca"] = Cluster{
 | 
				
			||||||
 | 
							Server:               "anything",
 | 
				
			||||||
 | 
							CertificateAuthority: "missing",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						config.AuthInfos["error"] = AuthInfo{
 | 
				
			||||||
 | 
							AuthPath: "anything",
 | 
				
			||||||
 | 
							Token:    "here",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						config.Contexts["dirty"] = Context{
 | 
				
			||||||
 | 
							Cluster:  "missing ca",
 | 
				
			||||||
 | 
							AuthInfo: "error",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						config.Clusters["clean"] = Cluster{
 | 
				
			||||||
 | 
							Server: "anything",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						config.AuthInfos["clean"] = AuthInfo{
 | 
				
			||||||
 | 
							Token: "here",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						config.Contexts["clean"] = Context{
 | 
				
			||||||
 | 
							Cluster:  "clean",
 | 
				
			||||||
 | 
							AuthInfo: "clean",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						badValidation := configValidationTest{
 | 
				
			||||||
 | 
							config:                 config,
 | 
				
			||||||
 | 
							expectedErrorSubstring: []string{"unable to read auth-path", "more than one authentication method", "unable to read certificate-authority"},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						okTest := configValidationTest{
 | 
				
			||||||
 | 
							config: config,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						okTest.testConfirmUsable("clean", t)
 | 
				
			||||||
 | 
						badValidation.testConfig(t)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					func TestConfirmUsableBadInfoConfig(t *testing.T) {
 | 
				
			||||||
 | 
						config := NewConfig()
 | 
				
			||||||
 | 
						config.Clusters["missing ca"] = Cluster{
 | 
				
			||||||
 | 
							Server:               "anything",
 | 
				
			||||||
 | 
							CertificateAuthority: "missing",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						config.AuthInfos["error"] = AuthInfo{
 | 
				
			||||||
 | 
							AuthPath: "anything",
 | 
				
			||||||
 | 
							Token:    "here",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						config.Contexts["first"] = Context{
 | 
				
			||||||
 | 
							Cluster:  "missing ca",
 | 
				
			||||||
 | 
							AuthInfo: "error",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						test := configValidationTest{
 | 
				
			||||||
 | 
							config:                 config,
 | 
				
			||||||
 | 
							expectedErrorSubstring: []string{"unable to read auth-path", "more than one authentication method", "unable to read certificate-authority"},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						test.testConfirmUsable("first", t)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					func TestConfirmUsableEmptyConfig(t *testing.T) {
 | 
				
			||||||
 | 
						config := NewConfig()
 | 
				
			||||||
 | 
						test := configValidationTest{
 | 
				
			||||||
 | 
							config:                 config,
 | 
				
			||||||
 | 
							expectedErrorSubstring: []string{"no context chosen"},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						test.testConfirmUsable("", t)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					func TestConfirmUsableMissingConfig(t *testing.T) {
 | 
				
			||||||
 | 
						config := NewConfig()
 | 
				
			||||||
 | 
						test := configValidationTest{
 | 
				
			||||||
 | 
							config:                 config,
 | 
				
			||||||
 | 
							expectedErrorSubstring: []string{"context was not found for"},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						test.testConfirmUsable("not-here", t)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					func TestValidateEmptyConfig(t *testing.T) {
 | 
				
			||||||
 | 
						config := NewConfig()
 | 
				
			||||||
 | 
						test := configValidationTest{
 | 
				
			||||||
 | 
							config: config,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						test.testConfig(t)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					func TestValidateMissingCurrentContextConfig(t *testing.T) {
 | 
				
			||||||
 | 
						config := NewConfig()
 | 
				
			||||||
 | 
						config.CurrentContext = "anything"
 | 
				
			||||||
 | 
						test := configValidationTest{
 | 
				
			||||||
 | 
							config:                 config,
 | 
				
			||||||
 | 
							expectedErrorSubstring: []string{"context was not found for specified "},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						test.testConfig(t)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					func TestIsContextNotFound(t *testing.T) {
 | 
				
			||||||
 | 
						config := NewConfig()
 | 
				
			||||||
 | 
						config.CurrentContext = "anything"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := Validate(*config)
 | 
				
			||||||
 | 
						if !IsContextNotFound(err) {
 | 
				
			||||||
 | 
							t.Errorf("Expected context not found, but got %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					func TestValidateMissingReferencesConfig(t *testing.T) {
 | 
				
			||||||
 | 
						config := NewConfig()
 | 
				
			||||||
 | 
						config.CurrentContext = "anything"
 | 
				
			||||||
 | 
						config.Contexts["anything"] = Context{Cluster: "missing", AuthInfo: "missing"}
 | 
				
			||||||
 | 
						test := configValidationTest{
 | 
				
			||||||
 | 
							config:                 config,
 | 
				
			||||||
 | 
							expectedErrorSubstring: []string{"user, missing, was not found for Context anything", "cluster, missing, was not found for Context anything"},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						test.testContext("anything", t)
 | 
				
			||||||
 | 
						test.testConfig(t)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					func TestValidateEmptyContext(t *testing.T) {
 | 
				
			||||||
 | 
						config := NewConfig()
 | 
				
			||||||
 | 
						config.CurrentContext = "anything"
 | 
				
			||||||
 | 
						config.Contexts["anything"] = Context{}
 | 
				
			||||||
 | 
						test := configValidationTest{
 | 
				
			||||||
 | 
							config:                 config,
 | 
				
			||||||
 | 
							expectedErrorSubstring: []string{"user was not specified for Context anything", "cluster was not specified for Context anything"},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						test.testContext("anything", t)
 | 
				
			||||||
 | 
						test.testConfig(t)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestValidateEmptyClusterInfo(t *testing.T) {
 | 
				
			||||||
 | 
						config := NewConfig()
 | 
				
			||||||
 | 
						config.Clusters["empty"] = Cluster{}
 | 
				
			||||||
 | 
						test := configValidationTest{
 | 
				
			||||||
 | 
							config:                 config,
 | 
				
			||||||
 | 
							expectedErrorSubstring: []string{"no server found for"},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						test.testCluster("empty", t)
 | 
				
			||||||
 | 
						test.testConfig(t)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					func TestValidateMissingCAFileClusterInfo(t *testing.T) {
 | 
				
			||||||
 | 
						config := NewConfig()
 | 
				
			||||||
 | 
						config.Clusters["missing ca"] = Cluster{
 | 
				
			||||||
 | 
							Server:               "anything",
 | 
				
			||||||
 | 
							CertificateAuthority: "missing",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						test := configValidationTest{
 | 
				
			||||||
 | 
							config:                 config,
 | 
				
			||||||
 | 
							expectedErrorSubstring: []string{"unable to read certificate-authority"},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						test.testCluster("missing ca", t)
 | 
				
			||||||
 | 
						test.testConfig(t)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					func TestValidateCleanClusterInfo(t *testing.T) {
 | 
				
			||||||
 | 
						config := NewConfig()
 | 
				
			||||||
 | 
						config.Clusters["clean"] = Cluster{
 | 
				
			||||||
 | 
							Server: "anything",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						test := configValidationTest{
 | 
				
			||||||
 | 
							config: config,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						test.testCluster("clean", t)
 | 
				
			||||||
 | 
						test.testConfig(t)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					func TestValidateCleanWithCAClusterInfo(t *testing.T) {
 | 
				
			||||||
 | 
						tempFile, _ := ioutil.TempFile("", "")
 | 
				
			||||||
 | 
						defer os.Remove(tempFile.Name())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						config := NewConfig()
 | 
				
			||||||
 | 
						config.Clusters["clean"] = Cluster{
 | 
				
			||||||
 | 
							Server:               "anything",
 | 
				
			||||||
 | 
							CertificateAuthority: tempFile.Name(),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						test := configValidationTest{
 | 
				
			||||||
 | 
							config: config,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						test.testCluster("clean", t)
 | 
				
			||||||
 | 
						test.testConfig(t)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestValidateEmptyAuthInfo(t *testing.T) {
 | 
				
			||||||
 | 
						config := NewConfig()
 | 
				
			||||||
 | 
						config.AuthInfos["error"] = AuthInfo{}
 | 
				
			||||||
 | 
						test := configValidationTest{
 | 
				
			||||||
 | 
							config: config,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						test.testAuthInfo("error", t)
 | 
				
			||||||
 | 
						test.testConfig(t)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					func TestValidateTooMayTechniquesAuthInfo(t *testing.T) {
 | 
				
			||||||
 | 
						config := NewConfig()
 | 
				
			||||||
 | 
						config.AuthInfos["error"] = AuthInfo{
 | 
				
			||||||
 | 
							AuthPath: "anything",
 | 
				
			||||||
 | 
							Token:    "here",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						test := configValidationTest{
 | 
				
			||||||
 | 
							config:                 config,
 | 
				
			||||||
 | 
							expectedErrorSubstring: []string{"more than one authentication method found"},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						test.testAuthInfo("error", t)
 | 
				
			||||||
 | 
						test.testConfig(t)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					func TestValidatePathNotFoundAuthInfo(t *testing.T) {
 | 
				
			||||||
 | 
						config := NewConfig()
 | 
				
			||||||
 | 
						config.AuthInfos["error"] = AuthInfo{
 | 
				
			||||||
 | 
							AuthPath: "missing",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						test := configValidationTest{
 | 
				
			||||||
 | 
							config:                 config,
 | 
				
			||||||
 | 
							expectedErrorSubstring: []string{"unable to read auth-path"},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						test.testAuthInfo("error", t)
 | 
				
			||||||
 | 
						test.testConfig(t)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					func TestValidateCertFilesNotFoundAuthInfo(t *testing.T) {
 | 
				
			||||||
 | 
						config := NewConfig()
 | 
				
			||||||
 | 
						config.AuthInfos["error"] = AuthInfo{
 | 
				
			||||||
 | 
							ClientCertificate: "missing",
 | 
				
			||||||
 | 
							ClientKey:         "missing",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						test := configValidationTest{
 | 
				
			||||||
 | 
							config:                 config,
 | 
				
			||||||
 | 
							expectedErrorSubstring: []string{"unable to read client-cert", "unable to read client-key"},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						test.testAuthInfo("error", t)
 | 
				
			||||||
 | 
						test.testConfig(t)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					func TestValidateCleanCertFilesAuthInfo(t *testing.T) {
 | 
				
			||||||
 | 
						tempFile, _ := ioutil.TempFile("", "")
 | 
				
			||||||
 | 
						defer os.Remove(tempFile.Name())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						config := NewConfig()
 | 
				
			||||||
 | 
						config.AuthInfos["clean"] = AuthInfo{
 | 
				
			||||||
 | 
							ClientCertificate: tempFile.Name(),
 | 
				
			||||||
 | 
							ClientKey:         tempFile.Name(),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						test := configValidationTest{
 | 
				
			||||||
 | 
							config: config,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						test.testAuthInfo("clean", t)
 | 
				
			||||||
 | 
						test.testConfig(t)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					func TestValidateCleanPathAuthInfo(t *testing.T) {
 | 
				
			||||||
 | 
						tempFile, _ := ioutil.TempFile("", "")
 | 
				
			||||||
 | 
						defer os.Remove(tempFile.Name())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						config := NewConfig()
 | 
				
			||||||
 | 
						config.AuthInfos["clean"] = AuthInfo{
 | 
				
			||||||
 | 
							AuthPath: tempFile.Name(),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						test := configValidationTest{
 | 
				
			||||||
 | 
							config: config,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						test.testAuthInfo("clean", t)
 | 
				
			||||||
 | 
						test.testConfig(t)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					func TestValidateCleanTokenAuthInfo(t *testing.T) {
 | 
				
			||||||
 | 
						config := NewConfig()
 | 
				
			||||||
 | 
						config.AuthInfos["clean"] = AuthInfo{
 | 
				
			||||||
 | 
							Token: "any-value",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						test := configValidationTest{
 | 
				
			||||||
 | 
							config: config,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						test.testAuthInfo("clean", t)
 | 
				
			||||||
 | 
						test.testConfig(t)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type configValidationTest struct {
 | 
				
			||||||
 | 
						config                 *Config
 | 
				
			||||||
 | 
						expectedErrorSubstring []string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c configValidationTest) testContext(contextName string, t *testing.T) {
 | 
				
			||||||
 | 
						errs := validateContext(contextName, c.config.Contexts[contextName], *c.config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(c.expectedErrorSubstring) != 0 {
 | 
				
			||||||
 | 
							if len(errs) == 0 {
 | 
				
			||||||
 | 
								t.Errorf("Expected error containing: %v", c.expectedErrorSubstring)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							for _, curr := range c.expectedErrorSubstring {
 | 
				
			||||||
 | 
								if len(errs) != 0 && !strings.Contains(util.SliceToError(errs).Error(), curr) {
 | 
				
			||||||
 | 
									t.Errorf("Expected error containing: %v, but got %v", c.expectedErrorSubstring, util.SliceToError(errs))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							if len(errs) != 0 {
 | 
				
			||||||
 | 
								t.Errorf("Unexpected error: %v", util.SliceToError(errs))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					func (c configValidationTest) testConfirmUsable(contextName string, t *testing.T) {
 | 
				
			||||||
 | 
						err := ConfirmUsable(*c.config, contextName)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(c.expectedErrorSubstring) != 0 {
 | 
				
			||||||
 | 
							if err == nil {
 | 
				
			||||||
 | 
								t.Errorf("Expected error containing: %v", c.expectedErrorSubstring)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								for _, curr := range c.expectedErrorSubstring {
 | 
				
			||||||
 | 
									if err != nil && !strings.Contains(err.Error(), curr) {
 | 
				
			||||||
 | 
										t.Errorf("Expected error containing: %v, but got %v", c.expectedErrorSubstring, err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								t.Errorf("Unexpected error: %v", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					func (c configValidationTest) testConfig(t *testing.T) {
 | 
				
			||||||
 | 
						err := Validate(*c.config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(c.expectedErrorSubstring) != 0 {
 | 
				
			||||||
 | 
							if err == nil {
 | 
				
			||||||
 | 
								t.Errorf("Expected error containing: %v", c.expectedErrorSubstring)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								for _, curr := range c.expectedErrorSubstring {
 | 
				
			||||||
 | 
									if err != nil && !strings.Contains(err.Error(), curr) {
 | 
				
			||||||
 | 
										t.Errorf("Expected error containing: %v, but got %v", c.expectedErrorSubstring, err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								t.Errorf("Unexpected error: %v", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					func (c configValidationTest) testCluster(clusterName string, t *testing.T) {
 | 
				
			||||||
 | 
						errs := validateClusterInfo(clusterName, c.config.Clusters[clusterName])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(c.expectedErrorSubstring) != 0 {
 | 
				
			||||||
 | 
							if len(errs) == 0 {
 | 
				
			||||||
 | 
								t.Errorf("Expected error containing: %v", c.expectedErrorSubstring)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							for _, curr := range c.expectedErrorSubstring {
 | 
				
			||||||
 | 
								if len(errs) != 0 && !strings.Contains(util.SliceToError(errs).Error(), curr) {
 | 
				
			||||||
 | 
									t.Errorf("Expected error containing: %v, but got %v", c.expectedErrorSubstring, util.SliceToError(errs))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							if len(errs) != 0 {
 | 
				
			||||||
 | 
								t.Errorf("Unexpected error: %v", util.SliceToError(errs))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c configValidationTest) testAuthInfo(authInfoName string, t *testing.T) {
 | 
				
			||||||
 | 
						errs := validateAuthInfo(authInfoName, c.config.AuthInfos[authInfoName])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(c.expectedErrorSubstring) != 0 {
 | 
				
			||||||
 | 
							if len(errs) == 0 {
 | 
				
			||||||
 | 
								t.Errorf("Expected error containing: %v", c.expectedErrorSubstring)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							for _, curr := range c.expectedErrorSubstring {
 | 
				
			||||||
 | 
								if len(errs) != 0 && !strings.Contains(util.SliceToError(errs).Error(), curr) {
 | 
				
			||||||
 | 
									t.Errorf("Expected error containing: %v, but got %v", c.expectedErrorSubstring, util.SliceToError(errs))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							if len(errs) != 0 {
 | 
				
			||||||
 | 
								t.Errorf("Unexpected error: %v", util.SliceToError(errs))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -21,9 +21,11 @@ import (
 | 
				
			|||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"net/url"
 | 
						"net/url"
 | 
				
			||||||
	"path"
 | 
						"path"
 | 
				
			||||||
 | 
						"reflect"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
 | 
						"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
 | 
				
			||||||
 | 
						"github.com/GoogleCloudPlatform/kubernetes/pkg/version"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Config holds the common attributes that can be passed to a Kubernetes client on
 | 
					// Config holds the common attributes that can be passed to a Kubernetes client on
 | 
				
			||||||
@@ -92,6 +94,24 @@ func New(c *Config) (*Client, error) {
 | 
				
			|||||||
	return &Client{client, isPreV1Beta3}, nil
 | 
						return &Client{client, isPreV1Beta3}, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func MatchesServerVersion(c *Config) error {
 | 
				
			||||||
 | 
						client, err := New(c)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						clientVersion := version.Get()
 | 
				
			||||||
 | 
						serverVersion, err := client.ServerVersion()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("couldn't read version from server: %v\n", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if s := *serverVersion; !reflect.DeepEqual(clientVersion, s) {
 | 
				
			||||||
 | 
							return fmt.Errorf("server version (%#v) differs from client version (%#v)!\n", s, clientVersion)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewOrDie creates a Kubernetes client and panics if the provided API version is not recognized.
 | 
					// NewOrDie creates a Kubernetes client and panics if the provided API version is not recognized.
 | 
				
			||||||
func NewOrDie(c *Config) *Client {
 | 
					func NewOrDie(c *Config) *Client {
 | 
				
			||||||
	client, err := New(c)
 | 
						client, err := New(c)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -117,3 +117,9 @@ func (info Info) MergeWithConfig(c client.Config) (client.Config, error) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return config, nil
 | 
						return config, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (info Info) Complete() bool {
 | 
				
			||||||
 | 
						return len(info.User) > 0 ||
 | 
				
			||||||
 | 
							len(info.CertFile) > 0 ||
 | 
				
			||||||
 | 
							len(info.BearerToken) > 0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,10 +34,14 @@ import (
 | 
				
			|||||||
	"github.com/spf13/cobra"
 | 
						"github.com/spf13/cobra"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						FlagMatchBinaryVersion = "match-server-version"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Factory provides abstractions that allow the Kubectl command to be extended across multiple types
 | 
					// Factory provides abstractions that allow the Kubectl command to be extended across multiple types
 | 
				
			||||||
// of resources and different API sets.
 | 
					// of resources and different API sets.
 | 
				
			||||||
type Factory struct {
 | 
					type Factory struct {
 | 
				
			||||||
	ClientBuilder clientcmd.Builder
 | 
						ClientConfig clientcmd.ClientConfig
 | 
				
			||||||
	Mapper       meta.RESTMapper
 | 
						Mapper       meta.RESTMapper
 | 
				
			||||||
	Typer        runtime.ObjectTyper
 | 
						Typer        runtime.ObjectTyper
 | 
				
			||||||
	Client       func(cmd *cobra.Command, mapping *meta.RESTMapping) (kubectl.RESTClient, error)
 | 
						Client       func(cmd *cobra.Command, mapping *meta.RESTMapping) (kubectl.RESTClient, error)
 | 
				
			||||||
@@ -47,14 +51,19 @@ type Factory struct {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewFactory creates a factory with the default Kubernetes resources defined
 | 
					// NewFactory creates a factory with the default Kubernetes resources defined
 | 
				
			||||||
func NewFactory(clientBuilder clientcmd.Builder) *Factory {
 | 
					func NewFactory(clientConfig clientcmd.ClientConfig) *Factory {
 | 
				
			||||||
	return &Factory{
 | 
						ret := &Factory{
 | 
				
			||||||
		ClientBuilder: clientBuilder,
 | 
							ClientConfig: clientConfig,
 | 
				
			||||||
		Mapper:       latest.RESTMapper,
 | 
							Mapper:       latest.RESTMapper,
 | 
				
			||||||
		Typer:        api.Scheme,
 | 
							Typer:        api.Scheme,
 | 
				
			||||||
		Validator: func(cmd *cobra.Command) (validation.Schema, error) {
 | 
							Printer: func(cmd *cobra.Command, mapping *meta.RESTMapping, noHeaders bool) (kubectl.ResourcePrinter, error) {
 | 
				
			||||||
 | 
								return kubectl.NewHumanReadablePrinter(noHeaders), nil
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret.Validator = func(cmd *cobra.Command) (validation.Schema, error) {
 | 
				
			||||||
		if GetFlagBool(cmd, "validate") {
 | 
							if GetFlagBool(cmd, "validate") {
 | 
				
			||||||
				client, err := clientBuilder.Client()
 | 
								client, err := getClient(ret.ClientConfig, GetFlagBool(cmd, FlagMatchBinaryVersion))
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return nil, err
 | 
									return nil, err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@@ -62,14 +71,12 @@ func NewFactory(clientBuilder clientcmd.Builder) *Factory {
 | 
				
			|||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			return validation.NullSchema{}, nil
 | 
								return validation.NullSchema{}, nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		},
 | 
						}
 | 
				
			||||||
		Client: func(cmd *cobra.Command, mapping *meta.RESTMapping) (kubectl.RESTClient, error) {
 | 
						ret.Client = func(cmd *cobra.Command, mapping *meta.RESTMapping) (kubectl.RESTClient, error) {
 | 
				
			||||||
			return clientBuilder.Override(func(c *client.Config) {
 | 
							return getClient(ret.ClientConfig, GetFlagBool(cmd, FlagMatchBinaryVersion))
 | 
				
			||||||
				c.Version = mapping.APIVersion
 | 
						}
 | 
				
			||||||
			}).Client()
 | 
						ret.Describer = func(cmd *cobra.Command, mapping *meta.RESTMapping) (kubectl.Describer, error) {
 | 
				
			||||||
		},
 | 
							client, err := getClient(ret.ClientConfig, GetFlagBool(cmd, FlagMatchBinaryVersion))
 | 
				
			||||||
		Describer: func(cmd *cobra.Command, mapping *meta.RESTMapping) (kubectl.Describer, error) {
 | 
					 | 
				
			||||||
			client, err := clientBuilder.Client()
 | 
					 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -78,11 +85,8 @@ func NewFactory(clientBuilder clientcmd.Builder) *Factory {
 | 
				
			|||||||
			return nil, fmt.Errorf("no description has been implemented for %q", mapping.Kind)
 | 
								return nil, fmt.Errorf("no description has been implemented for %q", mapping.Kind)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return describer, nil
 | 
							return describer, nil
 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		Printer: func(cmd *cobra.Command, mapping *meta.RESTMapping, noHeaders bool) (kubectl.ResourcePrinter, error) {
 | 
					 | 
				
			||||||
			return kubectl.NewHumanReadablePrinter(noHeaders), nil
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						return ret
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (f *Factory) Run(out io.Writer) {
 | 
					func (f *Factory) Run(out io.Writer) {
 | 
				
			||||||
@@ -96,12 +100,13 @@ Find more information at https://github.com/GoogleCloudPlatform/kubernetes.`,
 | 
				
			|||||||
		Run: runHelp,
 | 
							Run: runHelp,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	f.ClientBuilder.BindFlags(cmds.PersistentFlags())
 | 
						f.ClientConfig = getClientConfig(cmds)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Globally persistent flags across all subcommands.
 | 
						// Globally persistent flags across all subcommands.
 | 
				
			||||||
	// TODO Change flag names to consts to allow safer lookup from subcommands.
 | 
						// TODO Change flag names to consts to allow safer lookup from subcommands.
 | 
				
			||||||
	// TODO Add a verbose flag that turns on glog logging. Probably need a way
 | 
						// TODO Add a verbose flag that turns on glog logging. Probably need a way
 | 
				
			||||||
	// to do that automatically for every subcommand.
 | 
						// to do that automatically for every subcommand.
 | 
				
			||||||
 | 
						cmds.PersistentFlags().Bool(FlagMatchBinaryVersion, false, "Require server version to match client version")
 | 
				
			||||||
	cmds.PersistentFlags().String("ns-path", os.Getenv("HOME")+"/.kubernetes_ns", "Path to the namespace info file that holds the namespace context to use for CLI requests.")
 | 
						cmds.PersistentFlags().String("ns-path", os.Getenv("HOME")+"/.kubernetes_ns", "Path to the namespace info file that holds the namespace context to use for CLI requests.")
 | 
				
			||||||
	cmds.PersistentFlags().StringP("namespace", "n", "", "If present, the namespace scope for this CLI request.")
 | 
						cmds.PersistentFlags().StringP("namespace", "n", "", "If present, the namespace scope for this CLI request.")
 | 
				
			||||||
	cmds.PersistentFlags().Bool("validate", false, "If true, use a schema to validate the input before sending it")
 | 
						cmds.PersistentFlags().Bool("validate", false, "If true, use a schema to validate the input before sending it")
 | 
				
			||||||
@@ -124,6 +129,50 @@ Find more information at https://github.com/GoogleCloudPlatform/kubernetes.`,
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// getClientBuilder creates a clientcmd.ClientConfig that has a hierarchy like this:
 | 
				
			||||||
 | 
					//   1.  Use the kubeconfig builder.  The number of merges and overrides here gets a little crazy.  Stay with me.
 | 
				
			||||||
 | 
					//       1.  Merge together the kubeconfig itself.  This is done with the following hierarchy and merge rules:
 | 
				
			||||||
 | 
					//           1.  CommandLineLocation - this parsed from the command line, so it must be late bound
 | 
				
			||||||
 | 
					//           2.  EnvVarLocation
 | 
				
			||||||
 | 
					//           3.  CurrentDirectoryLocation
 | 
				
			||||||
 | 
					//           4.  HomeDirectoryLocation
 | 
				
			||||||
 | 
					//           Empty filenames are ignored.  Files with non-deserializable content produced errors.
 | 
				
			||||||
 | 
					//           The first file to set a particular value or map key wins and the value or map key is never changed.
 | 
				
			||||||
 | 
					//           This means that the first file to set CurrentContext will have its context preserved.  It also means
 | 
				
			||||||
 | 
					//           that if two files specify a "red-user", only values from the first file's red-user are used.  Even
 | 
				
			||||||
 | 
					//           non-conflicting entries from the second file's "red-user" are discarded.
 | 
				
			||||||
 | 
					//       2.  Determine the context to use based on the first hit in this chain
 | 
				
			||||||
 | 
					//           1.  command line argument - again, parsed from the command line, so it must be late bound
 | 
				
			||||||
 | 
					//           2.  CurrentContext from the merged kubeconfig file
 | 
				
			||||||
 | 
					//           3.  Empty is allowed at this stage
 | 
				
			||||||
 | 
					//       3.  Determine the cluster info and auth info to use.  At this point, we may or may not have a context.  They
 | 
				
			||||||
 | 
					//           are built based on the first hit in this chain.  (run it twice, once for auth, once for cluster)
 | 
				
			||||||
 | 
					//           1.  command line argument
 | 
				
			||||||
 | 
					//           2.  If context is present, then use the context value
 | 
				
			||||||
 | 
					//           3.  Empty is allowed
 | 
				
			||||||
 | 
					//       4.  Determine the actual cluster info to use.  At this point, we may or may not have a cluster info.  Build
 | 
				
			||||||
 | 
					//           each piece of the cluster info based on the chain:
 | 
				
			||||||
 | 
					//           1.  command line argument
 | 
				
			||||||
 | 
					//           2.  If cluster info is present and a value for the attribute is present, use it.
 | 
				
			||||||
 | 
					//           3.  If you don't have a server location, bail.
 | 
				
			||||||
 | 
					//       5.  Auth info is build using the same rules as cluster info, EXCEPT that you can only have one authentication
 | 
				
			||||||
 | 
					//           technique per auth info.  The following conditions result in an error:
 | 
				
			||||||
 | 
					//           1.  If there are two conflicting techniques specified from the command line, fail.
 | 
				
			||||||
 | 
					//           2.  If the command line does not specify one, and the auth info has conflicting techniques, fail.
 | 
				
			||||||
 | 
					//           3.  If the command line specifies one and the auth info specifies another, honor the command line technique.
 | 
				
			||||||
 | 
					//   2.  Use default values and potentially prompt for auth information
 | 
				
			||||||
 | 
					func getClientConfig(cmd *cobra.Command) clientcmd.ClientConfig {
 | 
				
			||||||
 | 
						loadingRules := clientcmd.NewClientConfigLoadingRules()
 | 
				
			||||||
 | 
						loadingRules.EnvVarPath = os.Getenv(clientcmd.RecommendedConfigPathEnvVar)
 | 
				
			||||||
 | 
						cmd.PersistentFlags().StringVar(&loadingRules.CommandLinePath, "kubeconfig", "", "Path to the kubeconfig file to use for CLI requests.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						overrides := &clientcmd.ConfigOverrides{}
 | 
				
			||||||
 | 
						overrides.BindFlags(cmd.PersistentFlags(), clientcmd.RecommendedConfigOverrideFlags(""))
 | 
				
			||||||
 | 
						clientConfig := clientcmd.NewInteractiveDeferredLoadingClientConfig(loadingRules, overrides, os.Stdin)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return clientConfig
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func checkErr(err error) {
 | 
					func checkErr(err error) {
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		glog.FatalDepth(1, err)
 | 
							glog.FatalDepth(1, err)
 | 
				
			||||||
@@ -195,3 +244,25 @@ func (c *clientSwaggerSchema) ValidateBytes(data []byte) error {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return schema.ValidateBytes(data)
 | 
						return schema.ValidateBytes(data)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO Need to only run server version match once per client host creation
 | 
				
			||||||
 | 
					func getClient(clientConfig clientcmd.ClientConfig, matchServerVersion bool) (*client.Client, error) {
 | 
				
			||||||
 | 
						config, err := clientConfig.ClientConfig()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if matchServerVersion {
 | 
				
			||||||
 | 
							err := client.MatchesServerVersion(config)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						client, err := client.New(config)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return client, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,6 +21,8 @@ import (
 | 
				
			|||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/spf13/cobra"
 | 
						"github.com/spf13/cobra"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (f *Factory) NewCmdLog(out io.Writer) *cobra.Command {
 | 
					func (f *Factory) NewCmdLog(out io.Writer) *cobra.Command {
 | 
				
			||||||
@@ -44,7 +46,9 @@ Examples:
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			namespace := GetKubeNamespace(cmd)
 | 
								namespace := GetKubeNamespace(cmd)
 | 
				
			||||||
			client, err := f.ClientBuilder.Client()
 | 
								config, err := f.ClientConfig.ClientConfig()
 | 
				
			||||||
 | 
								checkErr(err)
 | 
				
			||||||
 | 
								client, err := client.New(config)
 | 
				
			||||||
			checkErr(err)
 | 
								checkErr(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			podID := args[0]
 | 
								podID := args[0]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,7 +33,7 @@ func (f *Factory) NewCmdProxy(out io.Writer) *cobra.Command {
 | 
				
			|||||||
			port := GetFlagInt(cmd, "port")
 | 
								port := GetFlagInt(cmd, "port")
 | 
				
			||||||
			glog.Infof("Starting to serve on localhost:%d", port)
 | 
								glog.Infof("Starting to serve on localhost:%d", port)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			clientConfig, err := f.ClientBuilder.Config()
 | 
								clientConfig, err := f.ClientConfig.ClientConfig()
 | 
				
			||||||
			checkErr(err)
 | 
								checkErr(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			server, err := kubectl.NewProxyServer(GetFlagString(cmd, "www"), clientConfig, port)
 | 
								server, err := kubectl.NewProxyServer(GetFlagString(cmd, "www"), clientConfig, port)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,8 +19,10 @@ package cmd
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
 | 
					 | 
				
			||||||
	"github.com/spf13/cobra"
 | 
						"github.com/spf13/cobra"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
 | 
				
			||||||
 | 
						"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (f *Factory) NewCmdVersion(out io.Writer) *cobra.Command {
 | 
					func (f *Factory) NewCmdVersion(out io.Writer) *cobra.Command {
 | 
				
			||||||
@@ -31,7 +33,9 @@ func (f *Factory) NewCmdVersion(out io.Writer) *cobra.Command {
 | 
				
			|||||||
			if GetFlagBool(cmd, "client") {
 | 
								if GetFlagBool(cmd, "client") {
 | 
				
			||||||
				kubectl.GetClientVersion(out)
 | 
									kubectl.GetClientVersion(out)
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				client, err := f.ClientBuilder.Client()
 | 
									config, err := f.ClientConfig.ClientConfig()
 | 
				
			||||||
 | 
									checkErr(err)
 | 
				
			||||||
 | 
									client, err := client.New(config)
 | 
				
			||||||
				checkErr(err)
 | 
									checkErr(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				kubectl.GetVersion(out, client)
 | 
									kubectl.GetVersion(out, client)
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user