mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-10-31 10:18:13 +00:00 
			
		
		
		
	add kubectl config
This commit is contained in:
		| @@ -8,36 +8,46 @@ https://github.com/GoogleCloudPlatform/kubernetes/issues/1755 | ||||
|  | ||||
| ## Example .kubeconfig file | ||||
| ``` | ||||
| preferences:  | ||||
|   colors: true | ||||
| apiVersion: v1 | ||||
| clusters: | ||||
|   cow-cluster: | ||||
|     server: http://cow.org:8080 | ||||
| - cluster: | ||||
|     api-version: v1beta1 | ||||
|   horse-cluster: | ||||
|     server: https://horse.org:4443 | ||||
|     server: http://cow.org:8080 | ||||
|   name: cow-cluster | ||||
| - cluster: | ||||
|     certificate-authority: path/to/my/cafile | ||||
|   pig-cluster: | ||||
|     server: https://pig.org:443 | ||||
|     server: https://horse.org:4443 | ||||
|   name: horse-cluster | ||||
| - cluster: | ||||
|     insecure-skip-tls-verify: true | ||||
|     server: https://pig.org:443 | ||||
|   name: pig-cluster | ||||
| contexts: | ||||
| - context: | ||||
|     cluster: horse-cluster | ||||
|     namespace: chisel-ns | ||||
|     user: green-user | ||||
|   name: federal-context | ||||
| - context: | ||||
|     cluster: pig-cluster | ||||
|     namespace: saw-ns | ||||
|     user: black-user | ||||
|   name: queen-anne-context | ||||
| current-context: federal-context | ||||
| kind: Config | ||||
| preferences: | ||||
|   colors: true | ||||
| users: | ||||
|   black-user: | ||||
|     auth-path: path/to/my/existing/.kubernetes_auth file | ||||
|   blue-user: | ||||
| - name: black-user | ||||
|   user: | ||||
|     auth-path: path/to/my/existing/.kubernetes_auth_file | ||||
| - name: blue-user | ||||
|   user: | ||||
|     token: blue-token | ||||
|   green-user: | ||||
| - name: green-user | ||||
|   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 | ||||
| @@ -67,3 +77,86 @@ The rules for loading and merging the .kubeconfig files are straightforward, but | ||||
|  | ||||
|       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 | ||||
|  | ||||
| ## Manipulation of .kubeconfig via `kubectl config <subcommand>` | ||||
| In order to more easily manipulate .kubeconfig files, there are a series of subcommands to `kubectl config` to help. | ||||
| ``` | ||||
| kubectl config set-credentials name --auth-path=path/to/authfile --client-certificate=path/to/cert --client-key=path/to/key --token=string | ||||
|   Sets a user entry in .kubeconfig.  If the referenced name already exists, it will be overwritten. | ||||
| kubectl config set-cluster name --server=server --skip-tls=bool --certificate-authority=path/to/ca --api-version=string | ||||
|   Sets a cluster entry in .kubeconfig.  If the referenced name already exists, it will be overwritten. | ||||
| kubectl config set-context name --user=string --cluster=string --namespace=string | ||||
|   Sets a config entry in .kubeconfig.  If the referenced name already exists, it will be overwritten. | ||||
| kubectl config use-context name | ||||
|   Sets current-context to name | ||||
| kubectl config set property-name property-value | ||||
|   Sets arbitrary value in .kubeconfig | ||||
| kubectl config unset property-name | ||||
|   Unsets arbitrary value in .kubeconfig | ||||
| kubectl config view --local=true --global=false --kubeconfig=specific/filename --merged | ||||
|   Displays the merged (or not) result of the specified .kubeconfig file | ||||
|  | ||||
| --local, --global, and --kubeconfig are valid flags for all of these operations. | ||||
| ``` | ||||
|  | ||||
| ### Example | ||||
| ``` | ||||
| $kubectl config set-credentials myself --auth-path=path/to/my/existing/auth-file | ||||
| $kubectl config set-cluster local-server --server=http://localhost:8080 | ||||
| $kubectl config set-context default-context --cluster=local-server --user=myself | ||||
| $kubectl config use-context default-context | ||||
| $kubectl config set contexts.default-context.namespace the-right-prefix | ||||
| $kubectl config view | ||||
| ``` | ||||
| produces this output | ||||
| ``` | ||||
| clusters: | ||||
|   local-server: | ||||
|     server: http://localhost:8080 | ||||
| contexts: | ||||
|   default-context: | ||||
|     cluster: local-server | ||||
|     namespace: the-right-prefix | ||||
|     user: myself | ||||
| current-context: default-context | ||||
| preferences: {} | ||||
| users: | ||||
|   myself: | ||||
|     auth-path: path/to/my/existing/auth-file | ||||
|  | ||||
| ``` | ||||
| and a .kubeconfig file that looks like this | ||||
| ``` | ||||
| apiVersion: v1 | ||||
| clusters: | ||||
| - cluster: | ||||
|     server: http://localhost:8080 | ||||
|   name: local-server | ||||
| contexts: | ||||
| - context: | ||||
|     cluster: local-server | ||||
|     namespace: the-right-prefix | ||||
|     user: myself | ||||
|   name: default-context | ||||
| current-context: default-context | ||||
| kind: Config | ||||
| preferences: {} | ||||
| users: | ||||
| - name: myself | ||||
|   user: | ||||
|     auth-path: path/to/my/existing/auth-file | ||||
| ``` | ||||
|  | ||||
| #### Commands for the example file | ||||
| ``` | ||||
| $kubectl config set preferences.colors true | ||||
| $kubectl config set-cluster cow-cluster --server=http://cow.org:8080 --api-version=v1beta1 | ||||
| $kubectl config set-cluster horse-cluster --server=https://horse.org:4443 --certificate-authority=path/to/my/cafile | ||||
| $kubectl config set-cluster pig-cluster --server=https://pig.org:443 --insecure-skip-tls-verify=true | ||||
| $kubectl config set-credentials black-user --auth-path=path/to/my/existing/.kubernetes_auth_file | ||||
| $kubectl config set-credentials blue-user --token=blue-token | ||||
| $kubectl config set-credentials green-user --client-certificate=path/to/my/client/cert --client-key=path/to/my/client/key | ||||
| $kubectl config set-context queen-anne-context --cluster=pig-cluster --user=black-user --namespace=saw-ns | ||||
| $kubectl config set-context federal-context --cluster=horse-cluster --user=green-user --namespace=chisel-ns | ||||
| $kubectl config use-context federal-context | ||||
| ``` | ||||
							
								
								
									
										40
									
								
								pkg/client/clientcmd/api/latest/latest.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								pkg/client/clientcmd/api/latest/latest.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | ||||
| /* | ||||
| 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 latest | ||||
|  | ||||
| import ( | ||||
| 	"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api/v1" | ||||
| ) | ||||
|  | ||||
| // Version is the string that represents the current external default version. | ||||
| const Version = "v1" | ||||
|  | ||||
| // OldestVersion is the string that represents the oldest server version supported, | ||||
| // for client code that wants to hardcode the lowest common denominator. | ||||
| const OldestVersion = "v1" | ||||
|  | ||||
| // Versions is the list of versions that are recognized in code. The order provided | ||||
| // may be assumed to be least feature rich to most feature rich, and clients may | ||||
| // choose to prefer the latter items in the list over the former items when presented | ||||
| // with a set of versions to choose. | ||||
| var Versions = []string{"v1"} | ||||
|  | ||||
| // Codec is the default codec for serializing output that should use | ||||
| // the latest supported version.  Use this Codec when writing to | ||||
| // disk, a data store that is not dynamically versioned, or in tests. | ||||
| // This codec can decode any object that Kubernetes is aware of. | ||||
| var Codec = v1.Codec | ||||
							
								
								
									
										32
									
								
								pkg/client/clientcmd/api/register.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								pkg/client/clientcmd/api/register.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| /* | ||||
| 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 api | ||||
|  | ||||
| import ( | ||||
| 	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" | ||||
| ) | ||||
|  | ||||
| // Scheme is the default instance of runtime.Scheme to which types in the Kubernetes API are already registered. | ||||
| var Scheme = runtime.NewScheme() | ||||
|  | ||||
| func init() { | ||||
| 	Scheme.AddKnownTypes("", | ||||
| 		&Config{}, | ||||
| 	) | ||||
| } | ||||
|  | ||||
| func (*Config) IsAnAPIObject() {} | ||||
							
								
								
									
										119
									
								
								pkg/client/clientcmd/api/types.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								pkg/client/clientcmd/api/types.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,119 @@ | ||||
| /* | ||||
| 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 api | ||||
|  | ||||
| import ( | ||||
| 	"github.com/GoogleCloudPlatform/kubernetes/pkg/api" | ||||
| 	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" | ||||
| ) | ||||
|  | ||||
| // Where possible, json 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 { | ||||
| 	api.TypeMeta `json:",inline"` | ||||
| 	// Preferences holds general information to be use for cli interactions | ||||
| 	Preferences Preferences `json:"preferences"` | ||||
| 	// Clusters is a map of referencable names to cluster configs | ||||
| 	Clusters map[string]Cluster `json:"clusters"` | ||||
| 	// AuthInfos is a map of referencable names to user configs | ||||
| 	AuthInfos map[string]AuthInfo `json:"users"` | ||||
| 	// Contexts is a map of referencable names to context configs | ||||
| 	Contexts map[string]Context `json:"contexts"` | ||||
| 	// CurrentContext is the name of the context that you would like to use by default | ||||
| 	CurrentContext string `json:"current-context"` | ||||
| 	// Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields | ||||
| 	Extensions map[string]runtime.EmbeddedObject `json:"extensions,omitempty"` | ||||
| } | ||||
|  | ||||
| type Preferences struct { | ||||
| 	Colors bool `json:"colors,omitempty"` | ||||
| 	// Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields | ||||
| 	Extensions map[string]runtime.EmbeddedObject `json:"extensions,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 `json:"server"` | ||||
| 	// APIVersion is the preferred api version for communicating with the kubernetes cluster (v1beta1, v1beta2, v1beta3, etc). | ||||
| 	APIVersion string `json:"api-version,omitempty"` | ||||
| 	// InsecureSkipTLSVerify skips the validity check for the server's certificate. This will make your HTTPS connections insecure. | ||||
| 	InsecureSkipTLSVerify bool `json:"insecure-skip-tls-verify,omitempty"` | ||||
| 	// CertificateAuthority is the path to a cert file for the certificate authority. | ||||
| 	CertificateAuthority string `json:"certificate-authority,omitempty"` | ||||
| 	// Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields | ||||
| 	Extensions map[string]runtime.EmbeddedObject `json:"extensions,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 `json:"auth-path,omitempty"` | ||||
| 	// ClientCertificate is the path to a client cert file for TLS. | ||||
| 	ClientCertificate string `json:"client-certificate,omitempty"` | ||||
| 	// ClientKey is the path to a client key file for TLS. | ||||
| 	ClientKey string `json:"client-key,omitempty"` | ||||
| 	// Token is the bearer token for authentication to the kubernetes cluster. | ||||
| 	Token string `json:"token,omitempty"` | ||||
| 	// Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields | ||||
| 	Extensions map[string]runtime.EmbeddedObject `json:"extensions,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 `json:"cluster"` | ||||
| 	// AuthInfo is the name of the authInfo for this context | ||||
| 	AuthInfo string `json:"user"` | ||||
| 	// Namespace is the default namespace to use on unspecified requests | ||||
| 	Namespace string `json:"namespace,omitempty"` | ||||
| 	// Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields | ||||
| 	Extensions map[string]runtime.EmbeddedObject `json:"extensions,omitempty"` | ||||
| } | ||||
|  | ||||
| // NewConfig is a convenience function that returns a new Config object with non-nil maps | ||||
| func NewConfig() *Config { | ||||
| 	return &Config{ | ||||
| 		Preferences: *NewPreferences(), | ||||
| 		Clusters:    make(map[string]Cluster), | ||||
| 		AuthInfos:   make(map[string]AuthInfo), | ||||
| 		Contexts:    make(map[string]Context), | ||||
| 		Extensions:  make(map[string]runtime.EmbeddedObject), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // NewConfig is a convenience function that returns a new Config object with non-nil maps | ||||
| func NewContext() *Context { | ||||
| 	return &Context{Extensions: make(map[string]runtime.EmbeddedObject)} | ||||
| } | ||||
|  | ||||
| // NewConfig is a convenience function that returns a new Config object with non-nil maps | ||||
| func NewCluster() *Cluster { | ||||
| 	return &Cluster{Extensions: make(map[string]runtime.EmbeddedObject)} | ||||
| } | ||||
|  | ||||
| // NewConfig is a convenience function that returns a new Config object with non-nil maps | ||||
| func NewAuthInfo() *AuthInfo { | ||||
| 	return &AuthInfo{Extensions: make(map[string]runtime.EmbeddedObject)} | ||||
| } | ||||
|  | ||||
| // NewConfig is a convenience function that returns a new Config object with non-nil maps | ||||
| func NewPreferences() *Preferences { | ||||
| 	return &Preferences{Extensions: make(map[string]runtime.EmbeddedObject)} | ||||
| } | ||||
| @@ -14,12 +14,12 @@ See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| package clientcmd | ||||
| package api | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 
 | ||||
| 	"gopkg.in/v2/yaml" | ||||
| 	"github.com/ghodss/yaml" | ||||
| ) | ||||
| 
 | ||||
| func ExampleEmptyConfig() { | ||||
| @@ -32,11 +32,11 @@ func ExampleEmptyConfig() { | ||||
| 
 | ||||
| 	fmt.Printf("%v", string(output)) | ||||
| 	// Output: | ||||
| 	// preferences: {} | ||||
| 	// clusters: {} | ||||
| 	// users: {} | ||||
| 	// contexts: {} | ||||
| 	// current-context: "" | ||||
| 	// preferences: {} | ||||
| 	// users: {} | ||||
| } | ||||
| 
 | ||||
| func ExampleOfOptionsConfig() { | ||||
| @@ -86,17 +86,30 @@ func ExampleOfOptionsConfig() { | ||||
| 
 | ||||
| 	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 | ||||
| 	//     insecure-skip-tls-verify: true | ||||
| 	//     server: https://alfa.org:8080 | ||||
| 	//   bravo: | ||||
| 	//     server: https://bravo.org:8080 | ||||
| 	//     api-version: v1beta1 | ||||
| 	//     server: https://bravo.org:8080 | ||||
| 	// contexts: | ||||
| 	//   alfa-as-black-mage: | ||||
| 	//     cluster: alfa | ||||
| 	//     namespace: zulu | ||||
| 	//     user: black-mage-via-file | ||||
| 	//   alfa-as-white-mage: | ||||
| 	//     cluster: alfa | ||||
| 	//     user: white-mage-via-cert | ||||
| 	//   bravo-as-black-mage: | ||||
| 	//     cluster: bravo | ||||
| 	//     namespace: yankee | ||||
| 	//     user: black-mage-via-file | ||||
| 	// current-context: alfa-as-white-mage | ||||
| 	// preferences: | ||||
| 	//   colors: true | ||||
| 	// users: | ||||
| 	//   black-mage-via-file: | ||||
| 	//     auth-path: path/to/my/.kubernetes_auth | ||||
| @@ -105,17 +118,4 @@ func ExampleOfOptionsConfig() { | ||||
| 	//   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 | ||||
| } | ||||
							
								
								
									
										206
									
								
								pkg/client/clientcmd/api/v1/conversion.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										206
									
								
								pkg/client/clientcmd/api/v1/conversion.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,206 @@ | ||||
| /* | ||||
| 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 v1 | ||||
|  | ||||
| import ( | ||||
| 	"sort" | ||||
|  | ||||
| 	newer "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api" | ||||
| 	"github.com/GoogleCloudPlatform/kubernetes/pkg/conversion" | ||||
| 	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" | ||||
| ) | ||||
|  | ||||
| func init() { | ||||
| 	err := newer.Scheme.AddConversionFuncs( | ||||
| 		func(in *Config, out *newer.Config, s conversion.Scope) error { | ||||
| 			out.CurrentContext = in.CurrentContext | ||||
| 			if err := s.Convert(&in.Preferences, &out.Preferences, 0); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
|  | ||||
| 			out.Clusters = make(map[string]newer.Cluster) | ||||
| 			if err := s.Convert(&in.Clusters, &out.Clusters, 0); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			out.AuthInfos = make(map[string]newer.AuthInfo) | ||||
| 			if err := s.Convert(&in.AuthInfos, &out.AuthInfos, 0); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			out.Contexts = make(map[string]newer.Context) | ||||
| 			if err := s.Convert(&in.Contexts, &out.Contexts, 0); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			out.Extensions = make(map[string]runtime.EmbeddedObject) | ||||
| 			if err := s.Convert(&in.Extensions, &out.Extensions, 0); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			return nil | ||||
| 		}, | ||||
| 		func(in *newer.Config, out *Config, s conversion.Scope) error { | ||||
| 			out.CurrentContext = in.CurrentContext | ||||
| 			if err := s.Convert(&in.Preferences, &out.Preferences, 0); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
|  | ||||
| 			out.Clusters = make([]NamedCluster, 0, 0) | ||||
| 			if err := s.Convert(&in.Clusters, &out.Clusters, 0); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			out.AuthInfos = make([]NamedAuthInfo, 0, 0) | ||||
| 			if err := s.Convert(&in.AuthInfos, &out.AuthInfos, 0); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			out.Contexts = make([]NamedContext, 0, 0) | ||||
| 			if err := s.Convert(&in.Contexts, &out.Contexts, 0); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			out.Extensions = make([]NamedExtension, 0, 0) | ||||
| 			if err := s.Convert(&in.Extensions, &out.Extensions, 0); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			return nil | ||||
| 		}, | ||||
| 		func(in *[]NamedCluster, out *map[string]newer.Cluster, s conversion.Scope) error { | ||||
| 			for _, curr := range *in { | ||||
| 				newCluster := newer.NewCluster() | ||||
| 				if err := s.Convert(&curr.Cluster, newCluster, 0); err != nil { | ||||
| 					return err | ||||
| 				} | ||||
| 				(*out)[curr.Name] = *newCluster | ||||
| 			} | ||||
|  | ||||
| 			return nil | ||||
| 		}, | ||||
| 		func(in *map[string]newer.Cluster, out *[]NamedCluster, s conversion.Scope) error { | ||||
| 			allKeys := make([]string, 0, len(*in)) | ||||
| 			for key := range *in { | ||||
| 				allKeys = append(allKeys, key) | ||||
| 			} | ||||
| 			sort.Strings(allKeys) | ||||
|  | ||||
| 			for _, key := range allKeys { | ||||
| 				newCluster := (*in)[key] | ||||
| 				oldCluster := &Cluster{} | ||||
| 				if err := s.Convert(&newCluster, oldCluster, 0); err != nil { | ||||
| 					return err | ||||
| 				} | ||||
|  | ||||
| 				namedCluster := NamedCluster{key, *oldCluster} | ||||
| 				*out = append(*out, namedCluster) | ||||
| 			} | ||||
|  | ||||
| 			return nil | ||||
| 		}, | ||||
| 		func(in *[]NamedAuthInfo, out *map[string]newer.AuthInfo, s conversion.Scope) error { | ||||
| 			for _, curr := range *in { | ||||
| 				newAuthInfo := newer.NewAuthInfo() | ||||
| 				if err := s.Convert(&curr.AuthInfo, newAuthInfo, 0); err != nil { | ||||
| 					return err | ||||
| 				} | ||||
| 				(*out)[curr.Name] = *newAuthInfo | ||||
| 			} | ||||
|  | ||||
| 			return nil | ||||
| 		}, | ||||
| 		func(in *map[string]newer.AuthInfo, out *[]NamedAuthInfo, s conversion.Scope) error { | ||||
| 			allKeys := make([]string, 0, len(*in)) | ||||
| 			for key := range *in { | ||||
| 				allKeys = append(allKeys, key) | ||||
| 			} | ||||
| 			sort.Strings(allKeys) | ||||
|  | ||||
| 			for _, key := range allKeys { | ||||
| 				newAuthInfo := (*in)[key] | ||||
| 				oldAuthInfo := &AuthInfo{} | ||||
| 				if err := s.Convert(&newAuthInfo, oldAuthInfo, 0); err != nil { | ||||
| 					return err | ||||
| 				} | ||||
|  | ||||
| 				namedAuthInfo := NamedAuthInfo{key, *oldAuthInfo} | ||||
| 				*out = append(*out, namedAuthInfo) | ||||
| 			} | ||||
|  | ||||
| 			return nil | ||||
| 		}, | ||||
| 		func(in *[]NamedContext, out *map[string]newer.Context, s conversion.Scope) error { | ||||
| 			for _, curr := range *in { | ||||
| 				newContext := newer.NewContext() | ||||
| 				if err := s.Convert(&curr.Context, newContext, 0); err != nil { | ||||
| 					return err | ||||
| 				} | ||||
| 				(*out)[curr.Name] = *newContext | ||||
| 			} | ||||
|  | ||||
| 			return nil | ||||
| 		}, | ||||
| 		func(in *map[string]newer.Context, out *[]NamedContext, s conversion.Scope) error { | ||||
| 			allKeys := make([]string, 0, len(*in)) | ||||
| 			for key := range *in { | ||||
| 				allKeys = append(allKeys, key) | ||||
| 			} | ||||
| 			sort.Strings(allKeys) | ||||
|  | ||||
| 			for _, key := range allKeys { | ||||
| 				newContext := (*in)[key] | ||||
| 				oldContext := &Context{} | ||||
| 				if err := s.Convert(&newContext, oldContext, 0); err != nil { | ||||
| 					return err | ||||
| 				} | ||||
|  | ||||
| 				namedContext := NamedContext{key, *oldContext} | ||||
| 				*out = append(*out, namedContext) | ||||
| 			} | ||||
|  | ||||
| 			return nil | ||||
| 		}, | ||||
| 		func(in *[]NamedExtension, out *map[string]runtime.EmbeddedObject, s conversion.Scope) error { | ||||
| 			for _, curr := range *in { | ||||
| 				newExtension := &runtime.EmbeddedObject{} | ||||
| 				if err := s.Convert(&curr.Extension, newExtension, 0); err != nil { | ||||
| 					return err | ||||
| 				} | ||||
| 				(*out)[curr.Name] = *newExtension | ||||
| 			} | ||||
|  | ||||
| 			return nil | ||||
| 		}, | ||||
| 		func(in *map[string]runtime.EmbeddedObject, out *[]NamedExtension, s conversion.Scope) error { | ||||
| 			allKeys := make([]string, 0, len(*in)) | ||||
| 			for key := range *in { | ||||
| 				allKeys = append(allKeys, key) | ||||
| 			} | ||||
| 			sort.Strings(allKeys) | ||||
|  | ||||
| 			for _, key := range allKeys { | ||||
| 				newExtension := (*in)[key] | ||||
| 				oldExtension := &runtime.RawExtension{} | ||||
| 				if err := s.Convert(&newExtension, oldExtension, 0); err != nil { | ||||
| 					return err | ||||
| 				} | ||||
|  | ||||
| 				namedExtension := NamedExtension{key, *oldExtension} | ||||
| 				*out = append(*out, namedExtension) | ||||
| 			} | ||||
|  | ||||
| 			return nil | ||||
| 		}, | ||||
| 	) | ||||
| 	if err != nil { | ||||
| 		// If one of the conversion functions is malformed, detect it immediately. | ||||
| 		panic(err) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										33
									
								
								pkg/client/clientcmd/api/v1/register.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								pkg/client/clientcmd/api/v1/register.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| /* | ||||
| 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 v1 | ||||
|  | ||||
| import ( | ||||
| 	"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api" | ||||
| 	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" | ||||
| ) | ||||
|  | ||||
| // Codec encodes internal objects to the v1 scheme | ||||
| var Codec = runtime.CodecFor(api.Scheme, "v1") | ||||
|  | ||||
| func init() { | ||||
| 	api.Scheme.AddKnownTypes("v1", | ||||
| 		&Config{}, | ||||
| 	) | ||||
| } | ||||
|  | ||||
| func (*Config) IsAnAPIObject() {} | ||||
							
								
								
									
										120
									
								
								pkg/client/clientcmd/api/v1/types.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								pkg/client/clientcmd/api/v1/types.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,120 @@ | ||||
| /* | ||||
| 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 v1 | ||||
|  | ||||
| import ( | ||||
| 	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta3" | ||||
| 	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" | ||||
| ) | ||||
|  | ||||
| // Where possible, json 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 { | ||||
| 	v1beta3.TypeMeta `json:",inline"` | ||||
| 	// Preferences holds general information to be use for cli interactions | ||||
| 	Preferences Preferences `json:"preferences"` | ||||
| 	// Clusters is a map of referencable names to cluster configs | ||||
| 	Clusters []NamedCluster `json:"clusters"` | ||||
| 	// AuthInfos is a map of referencable names to user configs | ||||
| 	AuthInfos []NamedAuthInfo `json:"users"` | ||||
| 	// Contexts is a map of referencable names to context configs | ||||
| 	Contexts []NamedContext `json:"contexts"` | ||||
| 	// CurrentContext is the name of the context that you would like to use by default | ||||
| 	CurrentContext string `json:"current-context"` | ||||
| 	// Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields | ||||
| 	Extensions []NamedExtension `json:"extensions,omitempty"` | ||||
| } | ||||
|  | ||||
| type Preferences struct { | ||||
| 	Colors bool `json:"colors,omitempty"` | ||||
| 	// Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields | ||||
| 	Extensions []NamedExtension `json:"extensions,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 `json:"server"` | ||||
| 	// APIVersion is the preferred api version for communicating with the kubernetes cluster (v1beta1, v1beta2, v1beta3, etc). | ||||
| 	APIVersion string `json:"api-version,omitempty"` | ||||
| 	// InsecureSkipTLSVerify skips the validity check for the server's certificate. This will make your HTTPS connections insecure. | ||||
| 	InsecureSkipTLSVerify bool `json:"insecure-skip-tls-verify,omitempty"` | ||||
| 	// CertificateAuthority is the path to a cert file for the certificate authority. | ||||
| 	CertificateAuthority string `json:"certificate-authority,omitempty"` | ||||
| 	// Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields | ||||
| 	Extensions []NamedExtension `json:"extensions,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 `json:"auth-path,omitempty"` | ||||
| 	// ClientCertificate is the path to a client cert file for TLS. | ||||
| 	ClientCertificate string `json:"client-certificate,omitempty"` | ||||
| 	// ClientKey is the path to a client key file for TLS. | ||||
| 	ClientKey string `json:"client-key,omitempty"` | ||||
| 	// Token is the bearer token for authentication to the kubernetes cluster. | ||||
| 	Token string `json:"token,omitempty"` | ||||
| 	// Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields | ||||
| 	Extensions []NamedExtension `json:"extensions,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 `json:"cluster"` | ||||
| 	// AuthInfo is the name of the authInfo for this context | ||||
| 	AuthInfo string `json:"user"` | ||||
| 	// Namespace is the default namespace to use on unspecified requests | ||||
| 	Namespace string `json:"namespace,omitempty"` | ||||
| 	// Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields | ||||
| 	Extensions []NamedExtension `json:"extensions,omitempty"` | ||||
| } | ||||
|  | ||||
| // NamedCluster relates nicknames to cluster information | ||||
| type NamedCluster struct { | ||||
| 	// Name is the nickname for this Cluster | ||||
| 	Name string `json:"name"` | ||||
| 	// Cluster holds the cluster information | ||||
| 	Cluster Cluster `json:"cluster"` | ||||
| } | ||||
|  | ||||
| // NamedContext relates nicknames to context information | ||||
| type NamedContext struct { | ||||
| 	// Name is the nickname for this Context | ||||
| 	Name string `json:"name"` | ||||
| 	// Context holds the context information | ||||
| 	Context Context `json:"context"` | ||||
| } | ||||
|  | ||||
| // NamedAuthInfo relates nicknames to auth information | ||||
| type NamedAuthInfo struct { | ||||
| 	// Name is the nickname for this AuthInfo | ||||
| 	Name string `json:"name"` | ||||
| 	// AuthInfo holds the auth information | ||||
| 	AuthInfo AuthInfo `json:"user"` | ||||
| } | ||||
|  | ||||
| // NamedExtension relates nicknames to extension information | ||||
| type NamedExtension struct { | ||||
| 	// Name is the nickname for this Extension | ||||
| 	Name string `json:"name"` | ||||
| 	// Extension holds the extension information | ||||
| 	Extension runtime.RawExtension `json:"extension"` | ||||
| } | ||||
| @@ -23,45 +23,51 @@ import ( | ||||
| 	"github.com/imdario/mergo" | ||||
|  | ||||
| 	"github.com/GoogleCloudPlatform/kubernetes/pkg/client" | ||||
| 	clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api" | ||||
| 	"github.com/GoogleCloudPlatform/kubernetes/pkg/clientauth" | ||||
| 	"github.com/GoogleCloudPlatform/kubernetes/pkg/util/errors" | ||||
| ) | ||||
|  | ||||
| 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")} | ||||
| 	defaultCluster = clientcmdapi.Cluster{Server: "http://localhost:8080"} | ||||
| 	envVarCluster  = clientcmdapi.Cluster{Server: os.Getenv("KUBERNETES_MASTER")} | ||||
| ) | ||||
|  | ||||
| // ClientConfig is used to make it easy to get an api server client | ||||
| type ClientConfig interface { | ||||
| 	RawConfig() (clientcmdapi.Config, error) | ||||
| 	// 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 | ||||
| // DirectClientConfig is a ClientConfig interface that is backed by a clientcmdapi.Config, options overrides, and an optional fallbackReader for auth information | ||||
| type DirectClientConfig struct { | ||||
| 	config         Config | ||||
| 	config         clientcmdapi.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 { | ||||
| func NewDefaultClientConfig(config clientcmdapi.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 { | ||||
| func NewNonInteractiveClientConfig(config clientcmdapi.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 { | ||||
| func NewInteractiveClientConfig(config clientcmdapi.Config, contextName string, overrides *ConfigOverrides, fallbackReader io.Reader) ClientConfig { | ||||
| 	return DirectClientConfig{config, contextName, overrides, fallbackReader} | ||||
| } | ||||
|  | ||||
| func (config DirectClientConfig) RawConfig() (clientcmdapi.Config, error) { | ||||
| 	return config.config, nil | ||||
| } | ||||
|  | ||||
| // ClientConfig implements ClientConfig | ||||
| func (config DirectClientConfig) ClientConfig() (*client.Config, error) { | ||||
| 	if err := config.ConfirmUsable(); err != nil { | ||||
| @@ -102,7 +108,7 @@ func (config DirectClientConfig) ClientConfig() (*client.Config, error) { | ||||
| // 1.  configClusterInfo (the final result of command line flags and merged .kubeconfig files) | ||||
| // 2.  configAuthInfo.auth-path (this file can contain information that conflicts with #1, and we want #1 to win the priority) | ||||
| // 3.  load the ~/.kubernetes_auth file as a default | ||||
| func getServerIdentificationPartialConfig(configAuthInfo AuthInfo, configClusterInfo Cluster) (*client.Config, error) { | ||||
| func getServerIdentificationPartialConfig(configAuthInfo clientcmdapi.AuthInfo, configClusterInfo clientcmdapi.Cluster) (*client.Config, error) { | ||||
| 	mergedConfig := &client.Config{} | ||||
|  | ||||
| 	defaultAuthPathInfo, err := NewDefaultAuthLoader().LoadAuth(os.Getenv("HOME") + "/.kubernetes_auth") | ||||
| @@ -140,7 +146,7 @@ func getServerIdentificationPartialConfig(configAuthInfo AuthInfo, configCluster | ||||
| // 2.  configAuthInfo.auth-path (this file can contain information that conflicts with #1, and we want #1 to win the priority) | ||||
| // 3.  if there is not enough information to idenfity the user, load try the ~/.kubernetes_auth file | ||||
| // 4.  if there is not enough information to identify the user, prompt if possible | ||||
| func getUserIdentificationPartialConfig(configAuthInfo AuthInfo, fallbackReader io.Reader) (*client.Config, error) { | ||||
| func getUserIdentificationPartialConfig(configAuthInfo clientcmdapi.AuthInfo, fallbackReader io.Reader) (*client.Config, error) { | ||||
| 	mergedConfig := &client.Config{} | ||||
|  | ||||
| 	if len(configAuthInfo.AuthPath) > 0 { | ||||
| @@ -255,15 +261,15 @@ func (config DirectClientConfig) getClusterName() string { | ||||
| 	return config.getContext().Cluster | ||||
| } | ||||
|  | ||||
| func (config DirectClientConfig) getContext() Context { | ||||
| func (config DirectClientConfig) getContext() clientcmdapi.Context { | ||||
| 	return config.config.Contexts[config.getContextName()] | ||||
| } | ||||
|  | ||||
| func (config DirectClientConfig) getAuthInfo() AuthInfo { | ||||
| func (config DirectClientConfig) getAuthInfo() clientcmdapi.AuthInfo { | ||||
| 	authInfos := config.config.AuthInfos | ||||
| 	authInfoName := config.getAuthInfoName() | ||||
|  | ||||
| 	var mergedAuthInfo AuthInfo | ||||
| 	var mergedAuthInfo clientcmdapi.AuthInfo | ||||
| 	if configAuthInfo, exists := authInfos[authInfoName]; exists { | ||||
| 		mergo.Merge(&mergedAuthInfo, configAuthInfo) | ||||
| 	} | ||||
| @@ -272,11 +278,11 @@ func (config DirectClientConfig) getAuthInfo() AuthInfo { | ||||
| 	return mergedAuthInfo | ||||
| } | ||||
|  | ||||
| func (config DirectClientConfig) getCluster() Cluster { | ||||
| func (config DirectClientConfig) getCluster() clientcmdapi.Cluster { | ||||
| 	clusterInfos := config.config.Clusters | ||||
| 	clusterInfoName := config.getClusterName() | ||||
|  | ||||
| 	var mergedClusterInfo Cluster | ||||
| 	var mergedClusterInfo clientcmdapi.Cluster | ||||
| 	mergo.Merge(&mergedClusterInfo, defaultCluster) | ||||
| 	mergo.Merge(&mergedClusterInfo, envVarCluster) | ||||
| 	if configClusterInfo, exists := clusterInfos[clusterInfoName]; exists { | ||||
|   | ||||
| @@ -22,23 +22,24 @@ import ( | ||||
|  | ||||
| 	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest" | ||||
| 	"github.com/GoogleCloudPlatform/kubernetes/pkg/client" | ||||
| 	clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api" | ||||
| ) | ||||
|  | ||||
| func createValidTestConfig() *Config { | ||||
| func createValidTestConfig() *clientcmdapi.Config { | ||||
| 	const ( | ||||
| 		server = "https://anything.com:8080" | ||||
| 		token  = "the-token" | ||||
| 	) | ||||
|  | ||||
| 	config := NewConfig() | ||||
| 	config.Clusters["clean"] = Cluster{ | ||||
| 	config := clientcmdapi.NewConfig() | ||||
| 	config.Clusters["clean"] = clientcmdapi.Cluster{ | ||||
| 		Server:     server, | ||||
| 		APIVersion: latest.Version, | ||||
| 	} | ||||
| 	config.AuthInfos["clean"] = AuthInfo{ | ||||
| 	config.AuthInfos["clean"] = clientcmdapi.AuthInfo{ | ||||
| 		Token: token, | ||||
| 	} | ||||
| 	config.Contexts["clean"] = Context{ | ||||
| 	config.Contexts["clean"] = clientcmdapi.Context{ | ||||
| 		Cluster:  "clean", | ||||
| 		AuthInfo: "clean", | ||||
| 	} | ||||
|   | ||||
| @@ -20,8 +20,11 @@ import ( | ||||
| 	"io/ioutil" | ||||
| 	"os" | ||||
|  | ||||
| 	"github.com/ghodss/yaml" | ||||
| 	"github.com/imdario/mergo" | ||||
| 	"gopkg.in/v2/yaml" | ||||
|  | ||||
| 	clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api" | ||||
| 	clientcmdlatest "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api/latest" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| @@ -56,8 +59,8 @@ func NewClientConfigLoadingRules() *ClientConfigLoadingRules { | ||||
| // 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() | ||||
| func (rules *ClientConfigLoadingRules) Load() (*clientcmdapi.Config, error) { | ||||
| 	config := clientcmdapi.NewConfig() | ||||
|  | ||||
| 	mergeConfigWithFile(config, rules.CommandLinePath) | ||||
| 	mergeConfigWithFile(config, rules.EnvVarPath) | ||||
| @@ -67,7 +70,7 @@ func (rules *ClientConfigLoadingRules) Load() (*Config, error) { | ||||
| 	return config, nil | ||||
| } | ||||
|  | ||||
| func mergeConfigWithFile(startingConfig *Config, filename string) error { | ||||
| func mergeConfigWithFile(startingConfig *clientcmdapi.Config, filename string) error { | ||||
| 	if len(filename) == 0 { | ||||
| 		// no work to do | ||||
| 		return nil | ||||
| @@ -84,16 +87,15 @@ func mergeConfigWithFile(startingConfig *Config, filename string) error { | ||||
| } | ||||
|  | ||||
| // LoadFromFile takes a filename and deserializes the contents into Config object | ||||
| func LoadFromFile(filename string) (*Config, error) { | ||||
| 	config := &Config{} | ||||
| func LoadFromFile(filename string) (*clientcmdapi.Config, error) { | ||||
| 	config := &clientcmdapi.Config{} | ||||
|  | ||||
| 	kubeconfigBytes, err := ioutil.ReadFile(filename) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	err = yaml.Unmarshal(kubeconfigBytes, &config) | ||||
| 	if err != nil { | ||||
| 	if err := clientcmdlatest.Codec.DecodeInto(kubeconfigBytes, config); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| @@ -102,16 +104,20 @@ func LoadFromFile(filename string) (*Config, error) { | ||||
|  | ||||
| // 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) | ||||
| func WriteToFile(config clientcmdapi.Config, filename string) error { | ||||
| 	json, err := clientcmdlatest.Codec.Encode(&config) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	err = ioutil.WriteFile(filename, content, 0644) | ||||
| 	content, err := yaml.JSONToYAML(json) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if err := ioutil.WriteFile(filename, content, 0644); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|   | ||||
| @@ -21,47 +21,50 @@ import ( | ||||
| 	"io/ioutil" | ||||
| 	"os" | ||||
|  | ||||
| 	"gopkg.in/v2/yaml" | ||||
| 	"github.com/ghodss/yaml" | ||||
|  | ||||
| 	clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api" | ||||
| 	clientcmdlatest "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api/latest" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	testConfigAlfa = Config{ | ||||
| 		AuthInfos: map[string]AuthInfo{ | ||||
| 	testConfigAlfa = clientcmdapi.Config{ | ||||
| 		AuthInfos: map[string]clientcmdapi.AuthInfo{ | ||||
| 			"red-user": {Token: "red-token"}}, | ||||
| 		Clusters: map[string]Cluster{ | ||||
| 		Clusters: map[string]clientcmdapi.Cluster{ | ||||
| 			"cow-cluster": {Server: "http://cow.org:8080"}}, | ||||
| 		Contexts: map[string]Context{ | ||||
| 		Contexts: map[string]clientcmdapi.Context{ | ||||
| 			"federal-context": {AuthInfo: "red-user", Cluster: "cow-cluster", Namespace: "hammer-ns"}}, | ||||
| 	} | ||||
| 	testConfigBravo = Config{ | ||||
| 		AuthInfos: map[string]AuthInfo{ | ||||
| 	testConfigBravo = clientcmdapi.Config{ | ||||
| 		AuthInfos: map[string]clientcmdapi.AuthInfo{ | ||||
| 			"black-user": {Token: "black-token"}}, | ||||
| 		Clusters: map[string]Cluster{ | ||||
| 		Clusters: map[string]clientcmdapi.Cluster{ | ||||
| 			"pig-cluster": {Server: "http://pig.org:8080"}}, | ||||
| 		Contexts: map[string]Context{ | ||||
| 		Contexts: map[string]clientcmdapi.Context{ | ||||
| 			"queen-anne-context": {AuthInfo: "black-user", Cluster: "pig-cluster", Namespace: "saw-ns"}}, | ||||
| 	} | ||||
| 	testConfigCharlie = Config{ | ||||
| 		AuthInfos: map[string]AuthInfo{ | ||||
| 	testConfigCharlie = clientcmdapi.Config{ | ||||
| 		AuthInfos: map[string]clientcmdapi.AuthInfo{ | ||||
| 			"green-user": {Token: "green-token"}}, | ||||
| 		Clusters: map[string]Cluster{ | ||||
| 		Clusters: map[string]clientcmdapi.Cluster{ | ||||
| 			"horse-cluster": {Server: "http://horse.org:8080"}}, | ||||
| 		Contexts: map[string]Context{ | ||||
| 		Contexts: map[string]clientcmdapi.Context{ | ||||
| 			"shaker-context": {AuthInfo: "green-user", Cluster: "horse-cluster", Namespace: "chisel-ns"}}, | ||||
| 	} | ||||
| 	testConfigDelta = Config{ | ||||
| 		AuthInfos: map[string]AuthInfo{ | ||||
| 	testConfigDelta = clientcmdapi.Config{ | ||||
| 		AuthInfos: map[string]clientcmdapi.AuthInfo{ | ||||
| 			"blue-user": {Token: "blue-token"}}, | ||||
| 		Clusters: map[string]Cluster{ | ||||
| 		Clusters: map[string]clientcmdapi.Cluster{ | ||||
| 			"chicken-cluster": {Server: "http://chicken.org:8080"}}, | ||||
| 		Contexts: map[string]Context{ | ||||
| 		Contexts: map[string]clientcmdapi.Context{ | ||||
| 			"gothic-context": {AuthInfo: "blue-user", Cluster: "chicken-cluster", Namespace: "plane-ns"}}, | ||||
| 	} | ||||
| 	testConfigConflictAlfa = Config{ | ||||
| 		AuthInfos: map[string]AuthInfo{ | ||||
| 	testConfigConflictAlfa = clientcmdapi.Config{ | ||||
| 		AuthInfos: map[string]clientcmdapi.AuthInfo{ | ||||
| 			"red-user":    {Token: "a-different-red-token"}, | ||||
| 			"yellow-user": {Token: "yellow-token"}}, | ||||
| 		Clusters: map[string]Cluster{ | ||||
| 		Clusters: map[string]clientcmdapi.Cluster{ | ||||
| 			"cow-cluster":    {Server: "http://a-different-cow.org:8080", InsecureSkipTLSVerify: true}, | ||||
| 			"donkey-cluster": {Server: "http://donkey.org:8080", InsecureSkipTLSVerify: true}}, | ||||
| 		CurrentContext: "federal-context", | ||||
| @@ -84,31 +87,42 @@ func ExampleMergingSomeWithConflict() { | ||||
|  | ||||
| 	mergedConfig, err := loadingRules.Load() | ||||
|  | ||||
| 	output, err := yaml.Marshal(mergedConfig) | ||||
| 	json, err := clientcmdlatest.Codec.Encode(mergedConfig) | ||||
| 	if err != nil { | ||||
| 		fmt.Printf("Unexpected error: %v", err) | ||||
| 	} | ||||
| 	output, err := yaml.JSONToYAML(json) | ||||
| 	if err != nil { | ||||
| 		fmt.Printf("Unexpected error: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	fmt.Printf("%v", string(output)) | ||||
| 	// Output: | ||||
| 	// preferences: {} | ||||
| 	// apiVersion: v1 | ||||
| 	// clusters: | ||||
| 	//   cow-cluster: | ||||
| 	// - cluster: | ||||
| 	//     server: http://cow.org:8080 | ||||
| 	//   donkey-cluster: | ||||
| 	//     server: http://donkey.org:8080 | ||||
| 	//   name: cow-cluster | ||||
| 	// - cluster: | ||||
| 	//     insecure-skip-tls-verify: true | ||||
| 	// users: | ||||
| 	//   red-user: | ||||
| 	//     token: red-token | ||||
| 	//   yellow-user: | ||||
| 	//     token: yellow-token | ||||
| 	//     server: http://donkey.org:8080 | ||||
| 	//   name: donkey-cluster | ||||
| 	// contexts: | ||||
| 	//   federal-context: | ||||
| 	// - context: | ||||
| 	//     cluster: cow-cluster | ||||
| 	//     user: red-user | ||||
| 	//     namespace: hammer-ns | ||||
| 	//     user: red-user | ||||
| 	//   name: federal-context | ||||
| 	// current-context: federal-context | ||||
| 	// kind: Config | ||||
| 	// preferences: {} | ||||
| 	// users: | ||||
| 	// - name: red-user | ||||
| 	//   user: | ||||
| 	//     token: red-token | ||||
| 	// - name: yellow-user | ||||
| 	//   user: | ||||
| 	//     token: yellow-token | ||||
| } | ||||
|  | ||||
| func ExampleMergingEverythingNoConflicts() { | ||||
| @@ -135,48 +149,66 @@ func ExampleMergingEverythingNoConflicts() { | ||||
|  | ||||
| 	mergedConfig, err := loadingRules.Load() | ||||
|  | ||||
| 	output, err := yaml.Marshal(mergedConfig) | ||||
| 	json, err := clientcmdlatest.Codec.Encode(mergedConfig) | ||||
| 	if err != nil { | ||||
| 		fmt.Printf("Unexpected error: %v", err) | ||||
| 	} | ||||
| 	output, err := yaml.JSONToYAML(json) | ||||
| 	if err != nil { | ||||
| 		fmt.Printf("Unexpected error: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	fmt.Printf("%v", string(output)) | ||||
| 	// Output: | ||||
| 	// preferences: {} | ||||
| 	// 	apiVersion: v1 | ||||
| 	// clusters: | ||||
| 	//   chicken-cluster: | ||||
| 	// - cluster: | ||||
| 	//     server: http://chicken.org:8080 | ||||
| 	//   cow-cluster: | ||||
| 	//   name: chicken-cluster | ||||
| 	// - cluster: | ||||
| 	//     server: http://cow.org:8080 | ||||
| 	//   horse-cluster: | ||||
| 	//   name: cow-cluster | ||||
| 	// - cluster: | ||||
| 	//     server: http://horse.org:8080 | ||||
| 	//   pig-cluster: | ||||
| 	//   name: horse-cluster | ||||
| 	// - 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 | ||||
| 	//   name: pig-cluster | ||||
| 	// contexts: | ||||
| 	//   federal-context: | ||||
| 	// - context: | ||||
| 	//     cluster: cow-cluster | ||||
| 	//     user: red-user | ||||
| 	//     namespace: hammer-ns | ||||
| 	//   gothic-context: | ||||
| 	//     user: red-user | ||||
| 	//   name: federal-context | ||||
| 	// - context: | ||||
| 	//     cluster: chicken-cluster | ||||
| 	//     user: blue-user | ||||
| 	//     namespace: plane-ns | ||||
| 	//   queen-anne-context: | ||||
| 	//     user: blue-user | ||||
| 	//   name: gothic-context | ||||
| 	// - context: | ||||
| 	//     cluster: pig-cluster | ||||
| 	//     user: black-user | ||||
| 	//     namespace: saw-ns | ||||
| 	//   shaker-context: | ||||
| 	//     user: black-user | ||||
| 	//   name: queen-anne-context | ||||
| 	// - context: | ||||
| 	//     cluster: horse-cluster | ||||
| 	//     user: green-user | ||||
| 	//     namespace: chisel-ns | ||||
| 	//     user: green-user | ||||
| 	//   name: shaker-context | ||||
| 	// current-context: "" | ||||
| 	// kind: Config | ||||
| 	// preferences: {} | ||||
| 	// users: | ||||
| 	// - name: black-user | ||||
| 	//   user: | ||||
| 	//     token: black-token | ||||
| 	// - name: blue-user | ||||
| 	//   user: | ||||
| 	//     token: blue-token | ||||
| 	// - name: green-user | ||||
| 	//   user: | ||||
| 	//     token: green-token | ||||
| 	// - name: red-user | ||||
| 	//   user: | ||||
| 	//     token: red-token | ||||
| } | ||||
|   | ||||
| @@ -20,6 +20,7 @@ import ( | ||||
| 	"io" | ||||
|  | ||||
| 	"github.com/GoogleCloudPlatform/kubernetes/pkg/client" | ||||
| 	clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api" | ||||
| ) | ||||
|  | ||||
| // DeferredLoadingClientConfig is a ClientConfig interface that is backed by a set of loading rules | ||||
| @@ -59,6 +60,15 @@ func (config DeferredLoadingClientConfig) createClientConfig() (ClientConfig, er | ||||
| 	return mergedClientConfig, nil | ||||
| } | ||||
|  | ||||
| func (config DeferredLoadingClientConfig) RawConfig() (clientcmdapi.Config, error) { | ||||
| 	mergedConfig, err := config.createClientConfig() | ||||
| 	if err != nil { | ||||
| 		return clientcmdapi.Config{}, err | ||||
| 	} | ||||
|  | ||||
| 	return mergedConfig.RawConfig() | ||||
| } | ||||
|  | ||||
| // ClientConfig implements ClientConfig | ||||
| func (config DeferredLoadingClientConfig) ClientConfig() (*client.Config, error) { | ||||
| 	mergedClientConfig, err := config.createClientConfig() | ||||
|   | ||||
| @@ -85,7 +85,7 @@ func testBindClientConfig(cmd *cobra.Command) ClientConfig { | ||||
| 	cmd.PersistentFlags().StringVar(&loadingRules.CommandLinePath, "kubeconfig", "", "Path to the kubeconfig file to use for CLI requests.") | ||||
|  | ||||
| 	overrides := &ConfigOverrides{} | ||||
| 	overrides.BindFlags(cmd.PersistentFlags(), RecommendedConfigOverrideFlags("")) | ||||
| 	BindOverrideFlags(overrides, cmd.PersistentFlags(), RecommendedConfigOverrideFlags("")) | ||||
| 	clientConfig := NewInteractiveDeferredLoadingClientConfig(loadingRules, overrides, os.Stdin) | ||||
|  | ||||
| 	return clientConfig | ||||
|   | ||||
| @@ -18,13 +18,15 @@ package clientcmd | ||||
|  | ||||
| import ( | ||||
| 	"github.com/spf13/pflag" | ||||
|  | ||||
| 	clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api" | ||||
| ) | ||||
|  | ||||
| // 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 | ||||
| 	AuthInfo       clientcmdapi.AuthInfo | ||||
| 	ClusterInfo    clientcmdapi.Cluster | ||||
| 	Namespace      string | ||||
| 	CurrentContext string | ||||
| 	ClusterName    string | ||||
| @@ -105,8 +107,8 @@ func RecommendedConfigOverrideFlags(prefix string) ConfigOverrideFlags { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // BindFlags is a convenience method to bind the specified flags to their associated variables | ||||
| func (authInfo *AuthInfo) BindFlags(flags *pflag.FlagSet, flagNames AuthOverrideFlags) { | ||||
| // BindAuthInfoFlags is a convenience method to bind the specified flags to their associated variables | ||||
| func BindAuthInfoFlags(authInfo *clientcmdapi.AuthInfo, 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.") | ||||
| @@ -114,8 +116,8 @@ func (authInfo *AuthInfo) BindFlags(flags *pflag.FlagSet, flagNames AuthOverride | ||||
| 	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) { | ||||
| // BindClusterFlags is a convenience method to bind the specified flags to their associated variables | ||||
| func BindClusterFlags(clusterInfo *clientcmdapi.Cluster, 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") | ||||
| @@ -123,10 +125,10 @@ func (clusterInfo *Cluster) BindFlags(flags *pflag.FlagSet, flagNames ClusterOve | ||||
| 	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) | ||||
| // BindOverrideFlags is a convenience method to bind the specified flags to their associated variables | ||||
| func BindOverrideFlags(overrides *ConfigOverrides, flags *pflag.FlagSet, flagNames ConfigOverrideFlags) { | ||||
| 	BindAuthInfoFlags(&overrides.AuthInfo, flags, flagNames.AuthOverrideFlags) | ||||
| 	BindClusterFlags(&overrides.ClusterInfo, 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") | ||||
|   | ||||
| @@ -1,83 +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 () | ||||
|  | ||||
| // 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), | ||||
| 	} | ||||
| } | ||||
| @@ -22,6 +22,7 @@ import ( | ||||
| 	"os" | ||||
| 	"strings" | ||||
|  | ||||
| 	clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api" | ||||
| 	"github.com/GoogleCloudPlatform/kubernetes/pkg/util" | ||||
| 	utilerrors "github.com/GoogleCloudPlatform/kubernetes/pkg/util/errors" | ||||
| ) | ||||
| @@ -47,7 +48,7 @@ func IsContextNotFound(err error) bool { | ||||
| } | ||||
|  | ||||
| // 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 { | ||||
| func Validate(config clientcmdapi.Config) error { | ||||
| 	validationErrors := make([]error, 0) | ||||
|  | ||||
| 	if len(config.CurrentContext) != 0 { | ||||
| @@ -73,7 +74,7 @@ func Validate(config Config) error { | ||||
|  | ||||
| // 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 { | ||||
| func ConfirmUsable(config clientcmdapi.Config, passedContextName string) error { | ||||
| 	validationErrors := make([]error, 0) | ||||
|  | ||||
| 	var contextName string | ||||
| @@ -102,7 +103,7 @@ func ConfirmUsable(config Config, passedContextName string) error { | ||||
| } | ||||
|  | ||||
| // validateClusterInfo looks for conflicts and errors in the cluster info | ||||
| func validateClusterInfo(clusterName string, clusterInfo Cluster) []error { | ||||
| func validateClusterInfo(clusterName string, clusterInfo clientcmdapi.Cluster) []error { | ||||
| 	validationErrors := make([]error, 0) | ||||
|  | ||||
| 	if len(clusterInfo.Server) == 0 { | ||||
| @@ -120,7 +121,7 @@ func validateClusterInfo(clusterName string, clusterInfo Cluster) []error { | ||||
| } | ||||
|  | ||||
| // validateAuthInfo looks for conflicts and errors in the auth info | ||||
| func validateAuthInfo(authInfoName string, authInfo AuthInfo) []error { | ||||
| func validateAuthInfo(authInfoName string, authInfo clientcmdapi.AuthInfo) []error { | ||||
| 	validationErrors := make([]error, 0) | ||||
|  | ||||
| 	usingAuthPath := false | ||||
| @@ -163,7 +164,7 @@ func validateAuthInfo(authInfoName string, authInfo AuthInfo) []error { | ||||
| } | ||||
|  | ||||
| // 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 { | ||||
| func validateContext(contextName string, context clientcmdapi.Context, config clientcmdapi.Config) []error { | ||||
| 	validationErrors := make([]error, 0) | ||||
|  | ||||
| 	if len(context.AuthInfo) == 0 { | ||||
|   | ||||
| @@ -22,30 +22,31 @@ import ( | ||||
| 	"strings" | ||||
| 	"testing" | ||||
|  | ||||
| 	clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api" | ||||
| 	"github.com/GoogleCloudPlatform/kubernetes/pkg/util/errors" | ||||
| ) | ||||
|  | ||||
| func TestConfirmUsableBadInfoButOkConfig(t *testing.T) { | ||||
| 	config := NewConfig() | ||||
| 	config.Clusters["missing ca"] = Cluster{ | ||||
| 	config := clientcmdapi.NewConfig() | ||||
| 	config.Clusters["missing ca"] = clientcmdapi.Cluster{ | ||||
| 		Server:               "anything", | ||||
| 		CertificateAuthority: "missing", | ||||
| 	} | ||||
| 	config.AuthInfos["error"] = AuthInfo{ | ||||
| 	config.AuthInfos["error"] = clientcmdapi.AuthInfo{ | ||||
| 		AuthPath: "anything", | ||||
| 		Token:    "here", | ||||
| 	} | ||||
| 	config.Contexts["dirty"] = Context{ | ||||
| 	config.Contexts["dirty"] = clientcmdapi.Context{ | ||||
| 		Cluster:  "missing ca", | ||||
| 		AuthInfo: "error", | ||||
| 	} | ||||
| 	config.Clusters["clean"] = Cluster{ | ||||
| 	config.Clusters["clean"] = clientcmdapi.Cluster{ | ||||
| 		Server: "anything", | ||||
| 	} | ||||
| 	config.AuthInfos["clean"] = AuthInfo{ | ||||
| 	config.AuthInfos["clean"] = clientcmdapi.AuthInfo{ | ||||
| 		Token: "here", | ||||
| 	} | ||||
| 	config.Contexts["clean"] = Context{ | ||||
| 	config.Contexts["clean"] = clientcmdapi.Context{ | ||||
| 		Cluster:  "clean", | ||||
| 		AuthInfo: "clean", | ||||
| 	} | ||||
| @@ -62,16 +63,16 @@ func TestConfirmUsableBadInfoButOkConfig(t *testing.T) { | ||||
| 	badValidation.testConfig(t) | ||||
| } | ||||
| func TestConfirmUsableBadInfoConfig(t *testing.T) { | ||||
| 	config := NewConfig() | ||||
| 	config.Clusters["missing ca"] = Cluster{ | ||||
| 	config := clientcmdapi.NewConfig() | ||||
| 	config.Clusters["missing ca"] = clientcmdapi.Cluster{ | ||||
| 		Server:               "anything", | ||||
| 		CertificateAuthority: "missing", | ||||
| 	} | ||||
| 	config.AuthInfos["error"] = AuthInfo{ | ||||
| 	config.AuthInfos["error"] = clientcmdapi.AuthInfo{ | ||||
| 		AuthPath: "anything", | ||||
| 		Token:    "here", | ||||
| 	} | ||||
| 	config.Contexts["first"] = Context{ | ||||
| 	config.Contexts["first"] = clientcmdapi.Context{ | ||||
| 		Cluster:  "missing ca", | ||||
| 		AuthInfo: "error", | ||||
| 	} | ||||
| @@ -83,7 +84,7 @@ func TestConfirmUsableBadInfoConfig(t *testing.T) { | ||||
| 	test.testConfirmUsable("first", t) | ||||
| } | ||||
| func TestConfirmUsableEmptyConfig(t *testing.T) { | ||||
| 	config := NewConfig() | ||||
| 	config := clientcmdapi.NewConfig() | ||||
| 	test := configValidationTest{ | ||||
| 		config:                 config, | ||||
| 		expectedErrorSubstring: []string{"no context chosen"}, | ||||
| @@ -92,7 +93,7 @@ func TestConfirmUsableEmptyConfig(t *testing.T) { | ||||
| 	test.testConfirmUsable("", t) | ||||
| } | ||||
| func TestConfirmUsableMissingConfig(t *testing.T) { | ||||
| 	config := NewConfig() | ||||
| 	config := clientcmdapi.NewConfig() | ||||
| 	test := configValidationTest{ | ||||
| 		config:                 config, | ||||
| 		expectedErrorSubstring: []string{"context was not found for"}, | ||||
| @@ -101,7 +102,7 @@ func TestConfirmUsableMissingConfig(t *testing.T) { | ||||
| 	test.testConfirmUsable("not-here", t) | ||||
| } | ||||
| func TestValidateEmptyConfig(t *testing.T) { | ||||
| 	config := NewConfig() | ||||
| 	config := clientcmdapi.NewConfig() | ||||
| 	test := configValidationTest{ | ||||
| 		config: config, | ||||
| 	} | ||||
| @@ -109,7 +110,7 @@ func TestValidateEmptyConfig(t *testing.T) { | ||||
| 	test.testConfig(t) | ||||
| } | ||||
| func TestValidateMissingCurrentContextConfig(t *testing.T) { | ||||
| 	config := NewConfig() | ||||
| 	config := clientcmdapi.NewConfig() | ||||
| 	config.CurrentContext = "anything" | ||||
| 	test := configValidationTest{ | ||||
| 		config:                 config, | ||||
| @@ -119,7 +120,7 @@ func TestValidateMissingCurrentContextConfig(t *testing.T) { | ||||
| 	test.testConfig(t) | ||||
| } | ||||
| func TestIsContextNotFound(t *testing.T) { | ||||
| 	config := NewConfig() | ||||
| 	config := clientcmdapi.NewConfig() | ||||
| 	config.CurrentContext = "anything" | ||||
|  | ||||
| 	err := Validate(*config) | ||||
| @@ -128,9 +129,9 @@ func TestIsContextNotFound(t *testing.T) { | ||||
| 	} | ||||
| } | ||||
| func TestValidateMissingReferencesConfig(t *testing.T) { | ||||
| 	config := NewConfig() | ||||
| 	config := clientcmdapi.NewConfig() | ||||
| 	config.CurrentContext = "anything" | ||||
| 	config.Contexts["anything"] = Context{Cluster: "missing", AuthInfo: "missing"} | ||||
| 	config.Contexts["anything"] = clientcmdapi.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"}, | ||||
| @@ -140,9 +141,9 @@ func TestValidateMissingReferencesConfig(t *testing.T) { | ||||
| 	test.testConfig(t) | ||||
| } | ||||
| func TestValidateEmptyContext(t *testing.T) { | ||||
| 	config := NewConfig() | ||||
| 	config := clientcmdapi.NewConfig() | ||||
| 	config.CurrentContext = "anything" | ||||
| 	config.Contexts["anything"] = Context{} | ||||
| 	config.Contexts["anything"] = clientcmdapi.Context{} | ||||
| 	test := configValidationTest{ | ||||
| 		config:                 config, | ||||
| 		expectedErrorSubstring: []string{"user was not specified for Context anything", "cluster was not specified for Context anything"}, | ||||
| @@ -153,8 +154,8 @@ func TestValidateEmptyContext(t *testing.T) { | ||||
| } | ||||
|  | ||||
| func TestValidateEmptyClusterInfo(t *testing.T) { | ||||
| 	config := NewConfig() | ||||
| 	config.Clusters["empty"] = Cluster{} | ||||
| 	config := clientcmdapi.NewConfig() | ||||
| 	config.Clusters["empty"] = clientcmdapi.Cluster{} | ||||
| 	test := configValidationTest{ | ||||
| 		config:                 config, | ||||
| 		expectedErrorSubstring: []string{"no server found for"}, | ||||
| @@ -164,8 +165,8 @@ func TestValidateEmptyClusterInfo(t *testing.T) { | ||||
| 	test.testConfig(t) | ||||
| } | ||||
| func TestValidateMissingCAFileClusterInfo(t *testing.T) { | ||||
| 	config := NewConfig() | ||||
| 	config.Clusters["missing ca"] = Cluster{ | ||||
| 	config := clientcmdapi.NewConfig() | ||||
| 	config.Clusters["missing ca"] = clientcmdapi.Cluster{ | ||||
| 		Server:               "anything", | ||||
| 		CertificateAuthority: "missing", | ||||
| 	} | ||||
| @@ -178,8 +179,8 @@ func TestValidateMissingCAFileClusterInfo(t *testing.T) { | ||||
| 	test.testConfig(t) | ||||
| } | ||||
| func TestValidateCleanClusterInfo(t *testing.T) { | ||||
| 	config := NewConfig() | ||||
| 	config.Clusters["clean"] = Cluster{ | ||||
| 	config := clientcmdapi.NewConfig() | ||||
| 	config.Clusters["clean"] = clientcmdapi.Cluster{ | ||||
| 		Server: "anything", | ||||
| 	} | ||||
| 	test := configValidationTest{ | ||||
| @@ -193,8 +194,8 @@ func TestValidateCleanWithCAClusterInfo(t *testing.T) { | ||||
| 	tempFile, _ := ioutil.TempFile("", "") | ||||
| 	defer os.Remove(tempFile.Name()) | ||||
|  | ||||
| 	config := NewConfig() | ||||
| 	config.Clusters["clean"] = Cluster{ | ||||
| 	config := clientcmdapi.NewConfig() | ||||
| 	config.Clusters["clean"] = clientcmdapi.Cluster{ | ||||
| 		Server:               "anything", | ||||
| 		CertificateAuthority: tempFile.Name(), | ||||
| 	} | ||||
| @@ -207,8 +208,8 @@ func TestValidateCleanWithCAClusterInfo(t *testing.T) { | ||||
| } | ||||
|  | ||||
| func TestValidateEmptyAuthInfo(t *testing.T) { | ||||
| 	config := NewConfig() | ||||
| 	config.AuthInfos["error"] = AuthInfo{} | ||||
| 	config := clientcmdapi.NewConfig() | ||||
| 	config.AuthInfos["error"] = clientcmdapi.AuthInfo{} | ||||
| 	test := configValidationTest{ | ||||
| 		config: config, | ||||
| 	} | ||||
| @@ -217,8 +218,8 @@ func TestValidateEmptyAuthInfo(t *testing.T) { | ||||
| 	test.testConfig(t) | ||||
| } | ||||
| func TestValidatePathNotFoundAuthInfo(t *testing.T) { | ||||
| 	config := NewConfig() | ||||
| 	config.AuthInfos["error"] = AuthInfo{ | ||||
| 	config := clientcmdapi.NewConfig() | ||||
| 	config.AuthInfos["error"] = clientcmdapi.AuthInfo{ | ||||
| 		AuthPath: "missing", | ||||
| 	} | ||||
| 	test := configValidationTest{ | ||||
| @@ -230,8 +231,8 @@ func TestValidatePathNotFoundAuthInfo(t *testing.T) { | ||||
| 	test.testConfig(t) | ||||
| } | ||||
| func TestValidateCertFilesNotFoundAuthInfo(t *testing.T) { | ||||
| 	config := NewConfig() | ||||
| 	config.AuthInfos["error"] = AuthInfo{ | ||||
| 	config := clientcmdapi.NewConfig() | ||||
| 	config.AuthInfos["error"] = clientcmdapi.AuthInfo{ | ||||
| 		ClientCertificate: "missing", | ||||
| 		ClientKey:         "missing", | ||||
| 	} | ||||
| @@ -247,8 +248,8 @@ func TestValidateCleanCertFilesAuthInfo(t *testing.T) { | ||||
| 	tempFile, _ := ioutil.TempFile("", "") | ||||
| 	defer os.Remove(tempFile.Name()) | ||||
|  | ||||
| 	config := NewConfig() | ||||
| 	config.AuthInfos["clean"] = AuthInfo{ | ||||
| 	config := clientcmdapi.NewConfig() | ||||
| 	config.AuthInfos["clean"] = clientcmdapi.AuthInfo{ | ||||
| 		ClientCertificate: tempFile.Name(), | ||||
| 		ClientKey:         tempFile.Name(), | ||||
| 	} | ||||
| @@ -263,8 +264,8 @@ func TestValidateCleanPathAuthInfo(t *testing.T) { | ||||
| 	tempFile, _ := ioutil.TempFile("", "") | ||||
| 	defer os.Remove(tempFile.Name()) | ||||
|  | ||||
| 	config := NewConfig() | ||||
| 	config.AuthInfos["clean"] = AuthInfo{ | ||||
| 	config := clientcmdapi.NewConfig() | ||||
| 	config.AuthInfos["clean"] = clientcmdapi.AuthInfo{ | ||||
| 		AuthPath: tempFile.Name(), | ||||
| 	} | ||||
| 	test := configValidationTest{ | ||||
| @@ -275,8 +276,8 @@ func TestValidateCleanPathAuthInfo(t *testing.T) { | ||||
| 	test.testConfig(t) | ||||
| } | ||||
| func TestValidateCleanTokenAuthInfo(t *testing.T) { | ||||
| 	config := NewConfig() | ||||
| 	config.AuthInfos["clean"] = AuthInfo{ | ||||
| 	config := clientcmdapi.NewConfig() | ||||
| 	config.AuthInfos["clean"] = clientcmdapi.AuthInfo{ | ||||
| 		Token: "any-value", | ||||
| 	} | ||||
| 	test := configValidationTest{ | ||||
| @@ -288,7 +289,7 @@ func TestValidateCleanTokenAuthInfo(t *testing.T) { | ||||
| } | ||||
|  | ||||
| type configValidationTest struct { | ||||
| 	config                 *Config | ||||
| 	config                 *clientcmdapi.Config | ||||
| 	expectedErrorSubstring []string | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -28,6 +28,7 @@ import ( | ||||
| 	"github.com/GoogleCloudPlatform/kubernetes/pkg/client" | ||||
| 	"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd" | ||||
| 	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl" | ||||
| 	cmdconfig "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/config" | ||||
| 	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource" | ||||
| 	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" | ||||
| 	"github.com/GoogleCloudPlatform/kubernetes/pkg/util" | ||||
| @@ -168,6 +169,7 @@ Find more information at https://github.com/GoogleCloudPlatform/kubernetes.`, | ||||
| 	cmds.AddCommand(f.NewCmdUpdate(out)) | ||||
| 	cmds.AddCommand(f.NewCmdDelete(out)) | ||||
|  | ||||
| 	cmds.AddCommand(cmdconfig.NewCmdConfig(out)) | ||||
| 	cmds.AddCommand(NewCmdNamespace(out)) | ||||
| 	cmds.AddCommand(f.NewCmdLog(out)) | ||||
| 	cmds.AddCommand(f.NewCmdRollingUpdate(out)) | ||||
| @@ -213,7 +215,7 @@ func DefaultClientConfig(flags *pflag.FlagSet) clientcmd.ClientConfig { | ||||
| 	flags.StringVar(&loadingRules.CommandLinePath, "kubeconfig", "", "Path to the kubeconfig file to use for CLI requests.") | ||||
|  | ||||
| 	overrides := &clientcmd.ConfigOverrides{} | ||||
| 	overrides.BindFlags(flags, clientcmd.RecommendedConfigOverrideFlags("")) | ||||
| 	clientcmd.BindOverrideFlags(overrides, flags, clientcmd.RecommendedConfigOverrideFlags("")) | ||||
| 	clientConfig := clientcmd.NewInteractiveDeferredLoadingClientConfig(loadingRules, overrides, os.Stdin) | ||||
|  | ||||
| 	return clientConfig | ||||
|   | ||||
							
								
								
									
										111
									
								
								pkg/kubectl/cmd/config/config.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								pkg/kubectl/cmd/config/config.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,111 @@ | ||||
| /* | ||||
| 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 config | ||||
|  | ||||
| import ( | ||||
| 	"io" | ||||
| 	"os" | ||||
| 	"strconv" | ||||
|  | ||||
| 	"github.com/golang/glog" | ||||
| 	"github.com/spf13/cobra" | ||||
|  | ||||
| 	"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd" | ||||
| 	clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api" | ||||
| ) | ||||
|  | ||||
| type pathOptions struct { | ||||
| 	local         bool | ||||
| 	global        bool | ||||
| 	specifiedFile string | ||||
| } | ||||
|  | ||||
| func NewCmdConfig(out io.Writer) *cobra.Command { | ||||
| 	pathOptions := &pathOptions{} | ||||
|  | ||||
| 	cmd := &cobra.Command{ | ||||
| 		Use:   "config <subcommand>", | ||||
| 		Short: "config modifies .kubeconfig files", | ||||
| 		Long:  `config modifies .kubeconfig files using subcommands like "kubectl config set current-context my-context"`, | ||||
| 		Run: func(cmd *cobra.Command, args []string) { | ||||
| 			cmd.Help() | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	// file paths are common to all sub commands | ||||
| 	cmd.PersistentFlags().BoolVar(&pathOptions.local, "local", true, "use the .kubeconfig in the currect directory") | ||||
| 	cmd.PersistentFlags().BoolVar(&pathOptions.global, "global", false, "use the .kubeconfig from "+os.Getenv("HOME")) | ||||
| 	cmd.PersistentFlags().StringVar(&pathOptions.specifiedFile, "kubeconfig", "", "use a particular .kubeconfig file") | ||||
|  | ||||
| 	cmd.AddCommand(NewCmdConfigView(out, pathOptions)) | ||||
| 	cmd.AddCommand(NewCmdConfigSetCluster(out, pathOptions)) | ||||
| 	cmd.AddCommand(NewCmdConfigSetAuthInfo(out, pathOptions)) | ||||
| 	cmd.AddCommand(NewCmdConfigSetContext(out, pathOptions)) | ||||
| 	cmd.AddCommand(NewCmdConfigSet(out, pathOptions)) | ||||
| 	cmd.AddCommand(NewCmdConfigUnset(out, pathOptions)) | ||||
| 	cmd.AddCommand(NewCmdConfigUseContext(out, pathOptions)) | ||||
|  | ||||
| 	return cmd | ||||
| } | ||||
|  | ||||
| func (o *pathOptions) getStartingConfig() (*clientcmdapi.Config, string, error) { | ||||
| 	filename := "" | ||||
| 	config := clientcmdapi.NewConfig() | ||||
| 	switch { | ||||
| 	case o.global: | ||||
| 		filename = os.Getenv("HOME") + "/.kube/.kubeconfig" | ||||
| 		config = getConfigFromFileOrDie(filename) | ||||
|  | ||||
| 	case len(o.specifiedFile) > 0: | ||||
| 		filename = o.specifiedFile | ||||
| 		config = getConfigFromFileOrDie(filename) | ||||
|  | ||||
| 	case o.local: | ||||
| 		filename = ".kubeconfig" | ||||
| 		config = getConfigFromFileOrDie(filename) | ||||
| 	} | ||||
|  | ||||
| 	return config, filename, nil | ||||
| } | ||||
|  | ||||
| // getConfigFromFileOrDie tries to read a kubeconfig file and if it can't, it calls exit.  One exception, missing files result in empty configs, not an exit | ||||
| func getConfigFromFileOrDie(filename string) *clientcmdapi.Config { | ||||
| 	var err error | ||||
| 	config, err := clientcmd.LoadFromFile(filename) | ||||
| 	if err != nil && !os.IsNotExist(err) { | ||||
| 		glog.FatalDepth(1, err) | ||||
| 	} | ||||
|  | ||||
| 	if config == nil { | ||||
| 		config = clientcmdapi.NewConfig() | ||||
| 	} | ||||
|  | ||||
| 	return config | ||||
| } | ||||
|  | ||||
| func toBool(propertyValue string) (bool, error) { | ||||
| 	boolValue := false | ||||
| 	if len(propertyValue) != 0 { | ||||
| 		var err error | ||||
| 		boolValue, err = strconv.ParseBool(propertyValue) | ||||
| 		if err != nil { | ||||
| 			return false, err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return boolValue, nil | ||||
| } | ||||
							
								
								
									
										374
									
								
								pkg/kubectl/cmd/config/config_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										374
									
								
								pkg/kubectl/cmd/config/config_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,374 @@ | ||||
| /* | ||||
| 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 config | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"io/ioutil" | ||||
| 	"os" | ||||
| 	"reflect" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd" | ||||
| 	clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api" | ||||
| 	"github.com/GoogleCloudPlatform/kubernetes/pkg/util" | ||||
| ) | ||||
|  | ||||
| func newRedFederalCowHammerConfig() clientcmdapi.Config { | ||||
| 	return clientcmdapi.Config{ | ||||
| 		AuthInfos: map[string]clientcmdapi.AuthInfo{ | ||||
| 			"red-user": {Token: "red-token"}}, | ||||
| 		Clusters: map[string]clientcmdapi.Cluster{ | ||||
| 			"cow-cluster": {Server: "http://cow.org:8080"}}, | ||||
| 		Contexts: map[string]clientcmdapi.Context{ | ||||
| 			"federal-context": {AuthInfo: "red-user", Cluster: "cow-cluster", Namespace: "hammer-ns"}}, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| type configCommandTest struct { | ||||
| 	args           []string | ||||
| 	startingConfig clientcmdapi.Config | ||||
| 	expectedConfig clientcmdapi.Config | ||||
| } | ||||
|  | ||||
| func TestSetCurrentContext(t *testing.T) { | ||||
| 	expectedConfig := newRedFederalCowHammerConfig() | ||||
| 	expectedConfig.CurrentContext = "the-new-context" | ||||
| 	test := configCommandTest{ | ||||
| 		args:           []string{"use-context", "the-new-context"}, | ||||
| 		startingConfig: newRedFederalCowHammerConfig(), | ||||
| 		expectedConfig: expectedConfig, | ||||
| 	} | ||||
|  | ||||
| 	test.run(t) | ||||
| } | ||||
|  | ||||
| func TestSetIntoExistingStruct(t *testing.T) { | ||||
| 	expectedConfig := newRedFederalCowHammerConfig() | ||||
| 	a := expectedConfig.AuthInfos["red-user"] | ||||
| 	authInfo := &a | ||||
| 	authInfo.AuthPath = "new-path-value" | ||||
| 	expectedConfig.AuthInfos["red-user"] = *authInfo | ||||
| 	test := configCommandTest{ | ||||
| 		args:           []string{"set", "users.red-user.auth-path", "new-path-value"}, | ||||
| 		startingConfig: newRedFederalCowHammerConfig(), | ||||
| 		expectedConfig: expectedConfig, | ||||
| 	} | ||||
|  | ||||
| 	test.run(t) | ||||
| } | ||||
|  | ||||
| func TestUnsetStruct(t *testing.T) { | ||||
| 	expectedConfig := newRedFederalCowHammerConfig() | ||||
| 	delete(expectedConfig.AuthInfos, "red-user") | ||||
| 	test := configCommandTest{ | ||||
| 		args:           []string{"unset", "users.red-user"}, | ||||
| 		startingConfig: newRedFederalCowHammerConfig(), | ||||
| 		expectedConfig: expectedConfig, | ||||
| 	} | ||||
|  | ||||
| 	test.run(t) | ||||
| } | ||||
|  | ||||
| func TestUnsetField(t *testing.T) { | ||||
| 	expectedConfig := newRedFederalCowHammerConfig() | ||||
| 	expectedConfig.AuthInfos["red-user"] = *clientcmdapi.NewAuthInfo() | ||||
| 	test := configCommandTest{ | ||||
| 		args:           []string{"unset", "users.red-user.token"}, | ||||
| 		startingConfig: newRedFederalCowHammerConfig(), | ||||
| 		expectedConfig: expectedConfig, | ||||
| 	} | ||||
|  | ||||
| 	test.run(t) | ||||
| } | ||||
|  | ||||
| func TestSetIntoNewStruct(t *testing.T) { | ||||
| 	expectedConfig := newRedFederalCowHammerConfig() | ||||
| 	cluster := clientcmdapi.NewCluster() | ||||
| 	cluster.Server = "new-server-value" | ||||
| 	expectedConfig.Clusters["big-cluster"] = *cluster | ||||
| 	test := configCommandTest{ | ||||
| 		args:           []string{"set", "clusters.big-cluster.server", "new-server-value"}, | ||||
| 		startingConfig: newRedFederalCowHammerConfig(), | ||||
| 		expectedConfig: expectedConfig, | ||||
| 	} | ||||
|  | ||||
| 	test.run(t) | ||||
| } | ||||
|  | ||||
| func TestSetBoolean(t *testing.T) { | ||||
| 	expectedConfig := newRedFederalCowHammerConfig() | ||||
| 	cluster := clientcmdapi.NewCluster() | ||||
| 	cluster.InsecureSkipTLSVerify = true | ||||
| 	expectedConfig.Clusters["big-cluster"] = *cluster | ||||
| 	test := configCommandTest{ | ||||
| 		args:           []string{"set", "clusters.big-cluster.insecure-skip-tls-verify", "true"}, | ||||
| 		startingConfig: newRedFederalCowHammerConfig(), | ||||
| 		expectedConfig: expectedConfig, | ||||
| 	} | ||||
|  | ||||
| 	test.run(t) | ||||
| } | ||||
|  | ||||
| func TestSetIntoNewConfig(t *testing.T) { | ||||
| 	expectedConfig := *clientcmdapi.NewConfig() | ||||
| 	context := clientcmdapi.NewContext() | ||||
| 	context.AuthInfo = "fake-user" | ||||
| 	expectedConfig.Contexts["new-context"] = *context | ||||
| 	test := configCommandTest{ | ||||
| 		args:           []string{"set", "contexts.new-context.user", "fake-user"}, | ||||
| 		startingConfig: *clientcmdapi.NewConfig(), | ||||
| 		expectedConfig: expectedConfig, | ||||
| 	} | ||||
|  | ||||
| 	test.run(t) | ||||
| } | ||||
|  | ||||
| func TestNewEmptyAuth(t *testing.T) { | ||||
| 	expectedConfig := *clientcmdapi.NewConfig() | ||||
| 	expectedConfig.AuthInfos["the-user-name"] = *clientcmdapi.NewAuthInfo() | ||||
| 	test := configCommandTest{ | ||||
| 		args:           []string{"set-credentials", "the-user-name"}, | ||||
| 		startingConfig: *clientcmdapi.NewConfig(), | ||||
| 		expectedConfig: expectedConfig, | ||||
| 	} | ||||
|  | ||||
| 	test.run(t) | ||||
| } | ||||
|  | ||||
| func TestAdditionalAuth(t *testing.T) { | ||||
| 	expectedConfig := newRedFederalCowHammerConfig() | ||||
| 	authInfo := clientcmdapi.NewAuthInfo() | ||||
| 	authInfo.AuthPath = "auth-path" | ||||
| 	authInfo.ClientKey = "client-key" | ||||
| 	authInfo.Token = "token" | ||||
| 	expectedConfig.AuthInfos["another-user"] = *authInfo | ||||
| 	test := configCommandTest{ | ||||
| 		args:           []string{"set-credentials", "another-user", "--" + clientcmd.FlagAuthPath + "=auth-path", "--" + clientcmd.FlagKeyFile + "=client-key", "--" + clientcmd.FlagBearerToken + "=token"}, | ||||
| 		startingConfig: newRedFederalCowHammerConfig(), | ||||
| 		expectedConfig: expectedConfig, | ||||
| 	} | ||||
|  | ||||
| 	test.run(t) | ||||
| } | ||||
|  | ||||
| func TestOverwriteExistingAuth(t *testing.T) { | ||||
| 	expectedConfig := newRedFederalCowHammerConfig() | ||||
| 	authInfo := clientcmdapi.NewAuthInfo() | ||||
| 	authInfo.AuthPath = "auth-path" | ||||
| 	expectedConfig.AuthInfos["red-user"] = *authInfo | ||||
| 	test := configCommandTest{ | ||||
| 		args:           []string{"set-credentials", "red-user", "--" + clientcmd.FlagAuthPath + "=auth-path"}, | ||||
| 		startingConfig: newRedFederalCowHammerConfig(), | ||||
| 		expectedConfig: expectedConfig, | ||||
| 	} | ||||
|  | ||||
| 	test.run(t) | ||||
| } | ||||
|  | ||||
| func TestNewEmptyCluster(t *testing.T) { | ||||
| 	expectedConfig := *clientcmdapi.NewConfig() | ||||
| 	expectedConfig.Clusters["new-cluster"] = *clientcmdapi.NewCluster() | ||||
| 	test := configCommandTest{ | ||||
| 		args:           []string{"set-cluster", "new-cluster"}, | ||||
| 		startingConfig: *clientcmdapi.NewConfig(), | ||||
| 		expectedConfig: expectedConfig, | ||||
| 	} | ||||
|  | ||||
| 	test.run(t) | ||||
| } | ||||
|  | ||||
| func TestAdditionalCluster(t *testing.T) { | ||||
| 	expectedConfig := newRedFederalCowHammerConfig() | ||||
| 	cluster := *clientcmdapi.NewCluster() | ||||
| 	cluster.APIVersion = "v1beta1" | ||||
| 	cluster.CertificateAuthority = "ca-location" | ||||
| 	cluster.InsecureSkipTLSVerify = true | ||||
| 	cluster.Server = "serverlocation" | ||||
| 	expectedConfig.Clusters["different-cluster"] = cluster | ||||
| 	test := configCommandTest{ | ||||
| 		args:           []string{"set-cluster", "different-cluster", "--" + clientcmd.FlagAPIServer + "=serverlocation", "--" + clientcmd.FlagInsecure + "=true", "--" + clientcmd.FlagCAFile + "=ca-location", "--" + clientcmd.FlagAPIVersion + "=v1beta1"}, | ||||
| 		startingConfig: newRedFederalCowHammerConfig(), | ||||
| 		expectedConfig: expectedConfig, | ||||
| 	} | ||||
|  | ||||
| 	test.run(t) | ||||
| } | ||||
|  | ||||
| func TestOverwriteExistingCluster(t *testing.T) { | ||||
| 	expectedConfig := newRedFederalCowHammerConfig() | ||||
| 	cluster := *clientcmdapi.NewCluster() | ||||
| 	cluster.Server = "serverlocation" | ||||
| 	expectedConfig.Clusters["cow-cluster"] = cluster | ||||
|  | ||||
| 	test := configCommandTest{ | ||||
| 		args:           []string{"set-cluster", "cow-cluster", "--" + clientcmd.FlagAPIServer + "=serverlocation"}, | ||||
| 		startingConfig: newRedFederalCowHammerConfig(), | ||||
| 		expectedConfig: expectedConfig, | ||||
| 	} | ||||
|  | ||||
| 	test.run(t) | ||||
| } | ||||
|  | ||||
| func TestNewEmptyContext(t *testing.T) { | ||||
| 	expectedConfig := *clientcmdapi.NewConfig() | ||||
| 	expectedConfig.Contexts["new-context"] = *clientcmdapi.NewContext() | ||||
| 	test := configCommandTest{ | ||||
| 		args:           []string{"set-context", "new-context"}, | ||||
| 		startingConfig: *clientcmdapi.NewConfig(), | ||||
| 		expectedConfig: expectedConfig, | ||||
| 	} | ||||
|  | ||||
| 	test.run(t) | ||||
| } | ||||
|  | ||||
| func TestAdditionalContext(t *testing.T) { | ||||
| 	expectedConfig := newRedFederalCowHammerConfig() | ||||
| 	context := *clientcmdapi.NewContext() | ||||
| 	context.Cluster = "some-cluster" | ||||
| 	context.AuthInfo = "some-user" | ||||
| 	context.Namespace = "different-namespace" | ||||
| 	expectedConfig.Contexts["different-context"] = context | ||||
| 	test := configCommandTest{ | ||||
| 		args:           []string{"set-context", "different-context", "--" + clientcmd.FlagClusterName + "=some-cluster", "--" + clientcmd.FlagAuthInfoName + "=some-user", "--" + clientcmd.FlagNamespace + "=different-namespace"}, | ||||
| 		startingConfig: newRedFederalCowHammerConfig(), | ||||
| 		expectedConfig: expectedConfig, | ||||
| 	} | ||||
|  | ||||
| 	test.run(t) | ||||
| } | ||||
|  | ||||
| func TestOverwriteExistingContext(t *testing.T) { | ||||
| 	expectedConfig := newRedFederalCowHammerConfig() | ||||
| 	context := *clientcmdapi.NewContext() | ||||
| 	context.Cluster = "clustername" | ||||
| 	expectedConfig.Contexts["federal-context"] = context | ||||
|  | ||||
| 	test := configCommandTest{ | ||||
| 		args:           []string{"set-context", "federal-context", "--" + clientcmd.FlagClusterName + "=clustername"}, | ||||
| 		startingConfig: newRedFederalCowHammerConfig(), | ||||
| 		expectedConfig: expectedConfig, | ||||
| 	} | ||||
|  | ||||
| 	test.run(t) | ||||
| } | ||||
|  | ||||
| func TestToBool(t *testing.T) { | ||||
| 	type test struct { | ||||
| 		in  string | ||||
| 		out bool | ||||
| 		err string | ||||
| 	} | ||||
|  | ||||
| 	tests := []test{ | ||||
| 		{"", false, ""}, | ||||
| 		{"true", true, ""}, | ||||
| 		{"on", false, `strconv.ParseBool: parsing "on": invalid syntax`}, | ||||
| 	} | ||||
|  | ||||
| 	for _, curr := range tests { | ||||
| 		b, err := toBool(curr.in) | ||||
| 		if (len(curr.err) != 0) && err == nil { | ||||
| 			t.Errorf("Expected error: %v, but got nil", curr.err) | ||||
| 		} | ||||
| 		if (len(curr.err) == 0) && err != nil { | ||||
| 			t.Errorf("Unexpected error: %v", err) | ||||
| 		} | ||||
| 		if (err != nil) && (err.Error() != curr.err) { | ||||
| 			t.Errorf("Expected %v, got %v", curr.err, err) | ||||
|  | ||||
| 		} | ||||
| 		if b != curr.out { | ||||
| 			t.Errorf("Expected %v, got %v", curr.out, b) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| func testConfigCommand(args []string, startingConfig clientcmdapi.Config) (string, clientcmdapi.Config) { | ||||
| 	fakeKubeFile, _ := ioutil.TempFile("", "") | ||||
| 	defer os.Remove(fakeKubeFile.Name()) | ||||
| 	clientcmd.WriteToFile(startingConfig, fakeKubeFile.Name()) | ||||
|  | ||||
| 	argsToUse := make([]string, 0, 2+len(args)) | ||||
| 	argsToUse = append(argsToUse, "--kubeconfig="+fakeKubeFile.Name()) | ||||
| 	argsToUse = append(argsToUse, args...) | ||||
|  | ||||
| 	buf := bytes.NewBuffer([]byte{}) | ||||
|  | ||||
| 	cmd := NewCmdConfig(buf) | ||||
| 	cmd.SetArgs(argsToUse) | ||||
| 	cmd.Execute() | ||||
|  | ||||
| 	// outBytes, _ := ioutil.ReadFile(fakeKubeFile.Name()) | ||||
| 	config := getConfigFromFileOrDie(fakeKubeFile.Name()) | ||||
|  | ||||
| 	return buf.String(), *config | ||||
| } | ||||
|  | ||||
| func (test configCommandTest) run(t *testing.T) { | ||||
| 	_, actualConfig := testConfigCommand(test.args, test.startingConfig) | ||||
|  | ||||
| 	testSetNilMapsToEmpties(reflect.ValueOf(&test.expectedConfig)) | ||||
| 	testSetNilMapsToEmpties(reflect.ValueOf(&actualConfig)) | ||||
|  | ||||
| 	if !reflect.DeepEqual(test.expectedConfig, actualConfig) { | ||||
| 		t.Errorf("diff: %v", util.ObjectDiff(test.expectedConfig, actualConfig)) | ||||
| 		t.Errorf("expected: %#v\n actual:   %#v", test.expectedConfig, actualConfig) | ||||
| 	} | ||||
| } | ||||
| func testSetNilMapsToEmpties(curr reflect.Value) { | ||||
| 	actualCurrValue := curr | ||||
| 	if curr.Kind() == reflect.Ptr { | ||||
| 		actualCurrValue = curr.Elem() | ||||
| 	} | ||||
|  | ||||
| 	switch actualCurrValue.Kind() { | ||||
| 	case reflect.Map: | ||||
| 		for _, mapKey := range actualCurrValue.MapKeys() { | ||||
| 			currMapValue := actualCurrValue.MapIndex(mapKey) | ||||
|  | ||||
| 			// our maps do not hold pointers to structs, they hold the structs themselves.  This means that MapIndex returns the struct itself | ||||
| 			// That in turn means that they have kinds of type.Struct, which is not a settable type.  Because of this, we need to make new struct of that type | ||||
| 			// copy all the data from the old value into the new value, then take the .addr of the new value to modify it in the next recursion. | ||||
| 			// clear as mud | ||||
| 			modifiableMapValue := reflect.New(currMapValue.Type()).Elem() | ||||
| 			modifiableMapValue.Set(currMapValue) | ||||
|  | ||||
| 			if modifiableMapValue.Kind() == reflect.Struct { | ||||
| 				modifiableMapValue = modifiableMapValue.Addr() | ||||
| 			} | ||||
|  | ||||
| 			testSetNilMapsToEmpties(modifiableMapValue) | ||||
| 			actualCurrValue.SetMapIndex(mapKey, reflect.Indirect(modifiableMapValue)) | ||||
| 		} | ||||
|  | ||||
| 	case reflect.Struct: | ||||
| 		for fieldIndex := 0; fieldIndex < actualCurrValue.NumField(); fieldIndex++ { | ||||
| 			currFieldValue := actualCurrValue.Field(fieldIndex) | ||||
|  | ||||
| 			if currFieldValue.Kind() == reflect.Map && currFieldValue.IsNil() { | ||||
| 				newValue := reflect.MakeMap(currFieldValue.Type()) | ||||
| 				currFieldValue.Set(newValue) | ||||
| 			} else { | ||||
| 				testSetNilMapsToEmpties(currFieldValue.Addr()) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
							
								
								
									
										120
									
								
								pkg/kubectl/cmd/config/create_authinfo.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								pkg/kubectl/cmd/config/create_authinfo.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,120 @@ | ||||
| /* | ||||
| 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 config | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
|  | ||||
| 	"github.com/spf13/cobra" | ||||
|  | ||||
| 	"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd" | ||||
| 	clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api" | ||||
| ) | ||||
|  | ||||
| type createAuthInfoOptions struct { | ||||
| 	pathOptions       *pathOptions | ||||
| 	name              string | ||||
| 	authPath          string | ||||
| 	clientCertificate string | ||||
| 	clientKey         string | ||||
| 	token             string | ||||
| } | ||||
|  | ||||
| func NewCmdConfigSetAuthInfo(out io.Writer, pathOptions *pathOptions) *cobra.Command { | ||||
| 	options := &createAuthInfoOptions{pathOptions: pathOptions} | ||||
|  | ||||
| 	cmd := &cobra.Command{ | ||||
| 		Use:   "set-credentials name", | ||||
| 		Short: "Sets a user entry in .kubeconfig", | ||||
| 		Long: `Sets a user entry in .kubeconfig | ||||
|  | ||||
| 		Specifying a name that already exists overwrites that user entry. | ||||
| 		`, | ||||
| 		Run: func(cmd *cobra.Command, args []string) { | ||||
| 			if !options.complete(cmd) { | ||||
| 				return | ||||
| 			} | ||||
|  | ||||
| 			err := options.run() | ||||
| 			if err != nil { | ||||
| 				fmt.Printf("%v\n", err) | ||||
| 			} | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	cmd.Flags().StringVar(&options.authPath, clientcmd.FlagAuthPath, "", clientcmd.FlagAuthPath+" for the user entry in .kubeconfig") | ||||
| 	cmd.Flags().StringVar(&options.clientCertificate, clientcmd.FlagCertFile, "", clientcmd.FlagCertFile+" for the user entry in .kubeconfig") | ||||
| 	cmd.Flags().StringVar(&options.clientKey, clientcmd.FlagKeyFile, "", clientcmd.FlagKeyFile+" for the user entry in .kubeconfig") | ||||
| 	cmd.Flags().StringVar(&options.token, clientcmd.FlagBearerToken, "", clientcmd.FlagBearerToken+" for the user entry in .kubeconfig") | ||||
|  | ||||
| 	return cmd | ||||
| } | ||||
|  | ||||
| func (o createAuthInfoOptions) run() error { | ||||
| 	err := o.validate() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	config, filename, err := o.pathOptions.getStartingConfig() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	authInfo := o.authInfo() | ||||
| 	config.AuthInfos[o.name] = authInfo | ||||
|  | ||||
| 	err = clientcmd.WriteToFile(*config, filename) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // authInfo builds an AuthInfo object from the options | ||||
| func (o *createAuthInfoOptions) authInfo() clientcmdapi.AuthInfo { | ||||
| 	authInfo := clientcmdapi.AuthInfo{ | ||||
| 		AuthPath:          o.authPath, | ||||
| 		ClientCertificate: o.clientCertificate, | ||||
| 		ClientKey:         o.clientKey, | ||||
| 		Token:             o.token, | ||||
| 	} | ||||
|  | ||||
| 	return authInfo | ||||
| } | ||||
|  | ||||
| func (o *createAuthInfoOptions) complete(cmd *cobra.Command) bool { | ||||
| 	args := cmd.Flags().Args() | ||||
| 	if len(args) != 1 { | ||||
| 		cmd.Help() | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	o.name = args[0] | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (o createAuthInfoOptions) validate() error { | ||||
| 	if len(o.name) == 0 { | ||||
| 		return errors.New("You must specify a non-empty user name") | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										124
									
								
								pkg/kubectl/cmd/config/create_cluster.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								pkg/kubectl/cmd/config/create_cluster.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,124 @@ | ||||
| /* | ||||
| 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 config | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
|  | ||||
| 	"github.com/spf13/cobra" | ||||
|  | ||||
| 	"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd" | ||||
| 	clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api" | ||||
| ) | ||||
|  | ||||
| type createClusterOptions struct { | ||||
| 	pathOptions           *pathOptions | ||||
| 	name                  string | ||||
| 	server                string | ||||
| 	apiVersion            string | ||||
| 	insecureSkipTLSVerify bool | ||||
| 	certificateAuthority  string | ||||
| } | ||||
|  | ||||
| func NewCmdConfigSetCluster(out io.Writer, pathOptions *pathOptions) *cobra.Command { | ||||
| 	options := &createClusterOptions{pathOptions: pathOptions} | ||||
|  | ||||
| 	cmd := &cobra.Command{ | ||||
| 		Use:   "set-cluster name [server] [insecure-skip-tls-verify] [certificate-authority] [api-version]", | ||||
| 		Short: "Sets a cluster entry in .kubeconfig", | ||||
| 		Long: `Sets a cluster entry in .kubeconfig | ||||
|  | ||||
| 		Specifying a name that already exists overwrites that cluster entry. | ||||
| 		`, | ||||
| 		Run: func(cmd *cobra.Command, args []string) { | ||||
| 			if !options.complete(cmd) { | ||||
| 				return | ||||
| 			} | ||||
|  | ||||
| 			err := options.run() | ||||
| 			if err != nil { | ||||
| 				fmt.Printf("%v\n", err) | ||||
| 			} | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	cmd.Flags().StringVar(&options.server, clientcmd.FlagAPIServer, "", clientcmd.FlagAPIServer+" for the cluster entry in .kubeconfig") | ||||
| 	cmd.Flags().StringVar(&options.apiVersion, clientcmd.FlagAPIVersion, "", clientcmd.FlagAPIVersion+" for the cluster entry in .kubeconfig") | ||||
| 	cmd.Flags().BoolVar(&options.insecureSkipTLSVerify, clientcmd.FlagInsecure, false, clientcmd.FlagInsecure+" for the cluster entry in .kubeconfig") | ||||
| 	cmd.Flags().StringVar(&options.certificateAuthority, clientcmd.FlagCAFile, "", clientcmd.FlagCAFile+" for the cluster entry in .kubeconfig") | ||||
|  | ||||
| 	return cmd | ||||
| } | ||||
|  | ||||
| func (o createClusterOptions) run() error { | ||||
| 	err := o.validate() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	config, filename, err := o.pathOptions.getStartingConfig() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if config.Clusters == nil { | ||||
| 		config.Clusters = make(map[string]clientcmdapi.Cluster) | ||||
| 	} | ||||
|  | ||||
| 	cluster := o.cluster() | ||||
| 	config.Clusters[o.name] = cluster | ||||
|  | ||||
| 	err = clientcmd.WriteToFile(*config, filename) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // cluster builds a Cluster object from the options | ||||
| func (o *createClusterOptions) cluster() clientcmdapi.Cluster { | ||||
| 	cluster := clientcmdapi.Cluster{ | ||||
| 		Server:                o.server, | ||||
| 		APIVersion:            o.apiVersion, | ||||
| 		InsecureSkipTLSVerify: o.insecureSkipTLSVerify, | ||||
| 		CertificateAuthority:  o.certificateAuthority, | ||||
| 	} | ||||
|  | ||||
| 	return cluster | ||||
| } | ||||
|  | ||||
| func (o *createClusterOptions) complete(cmd *cobra.Command) bool { | ||||
| 	args := cmd.Flags().Args() | ||||
| 	if len(args) != 1 { | ||||
| 		cmd.Help() | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	o.name = args[0] | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (o createClusterOptions) validate() error { | ||||
| 	if len(o.name) == 0 { | ||||
| 		return errors.New("You must specify a non-empty cluster name") | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										116
									
								
								pkg/kubectl/cmd/config/create_context.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								pkg/kubectl/cmd/config/create_context.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,116 @@ | ||||
| /* | ||||
| 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 config | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
|  | ||||
| 	"github.com/spf13/cobra" | ||||
|  | ||||
| 	"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd" | ||||
| 	clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api" | ||||
| ) | ||||
|  | ||||
| type createContextOptions struct { | ||||
| 	pathOptions *pathOptions | ||||
| 	name        string | ||||
| 	cluster     string | ||||
| 	authInfo    string | ||||
| 	namespace   string | ||||
| } | ||||
|  | ||||
| func NewCmdConfigSetContext(out io.Writer, pathOptions *pathOptions) *cobra.Command { | ||||
| 	options := &createContextOptions{pathOptions: pathOptions} | ||||
|  | ||||
| 	cmd := &cobra.Command{ | ||||
| 		Use:   "set-context name", | ||||
| 		Short: "Sets a context entry in .kubeconfig", | ||||
| 		Long: `Sets a context entry in .kubeconfig | ||||
|  | ||||
| 		Specifying a name that already exists overwrites that context entry. | ||||
| 		`, | ||||
| 		Run: func(cmd *cobra.Command, args []string) { | ||||
| 			if !options.complete(cmd) { | ||||
| 				return | ||||
| 			} | ||||
|  | ||||
| 			err := options.run() | ||||
| 			if err != nil { | ||||
| 				fmt.Printf("%v\n", err) | ||||
| 			} | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	cmd.Flags().StringVar(&options.cluster, clientcmd.FlagClusterName, "", clientcmd.FlagClusterName+" for the context entry in .kubeconfig") | ||||
| 	cmd.Flags().StringVar(&options.authInfo, clientcmd.FlagAuthInfoName, "", clientcmd.FlagAuthInfoName+" for the context entry in .kubeconfig") | ||||
| 	cmd.Flags().StringVar(&options.namespace, clientcmd.FlagNamespace, "", clientcmd.FlagNamespace+" for the context entry in .kubeconfig") | ||||
|  | ||||
| 	return cmd | ||||
| } | ||||
|  | ||||
| func (o createContextOptions) run() error { | ||||
| 	err := o.validate() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	config, filename, err := o.pathOptions.getStartingConfig() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	context := o.context() | ||||
| 	config.Contexts[o.name] = context | ||||
|  | ||||
| 	err = clientcmd.WriteToFile(*config, filename) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (o *createContextOptions) context() clientcmdapi.Context { | ||||
| 	context := clientcmdapi.Context{ | ||||
| 		Cluster:   o.cluster, | ||||
| 		AuthInfo:  o.authInfo, | ||||
| 		Namespace: o.namespace, | ||||
| 	} | ||||
|  | ||||
| 	return context | ||||
| } | ||||
|  | ||||
| func (o *createContextOptions) complete(cmd *cobra.Command) bool { | ||||
| 	args := cmd.Flags().Args() | ||||
| 	if len(args) != 1 { | ||||
| 		cmd.Help() | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	o.name = args[0] | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (o createContextOptions) validate() error { | ||||
| 	if len(o.name) == 0 { | ||||
| 		return errors.New("You must specify a non-empty context name") | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										249
									
								
								pkg/kubectl/cmd/config/set.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										249
									
								
								pkg/kubectl/cmd/config/set.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,249 @@ | ||||
| /* | ||||
| 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 config | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"reflect" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/spf13/cobra" | ||||
|  | ||||
| 	"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	cannotHaveStepsAfterError                = "Cannot have steps after %v.  %v are remaining" | ||||
| 	additionStepRequiredUnlessUnsettingError = "Must have additional steps after %v unless you are unsetting it" | ||||
| ) | ||||
|  | ||||
| type navigationSteps []string | ||||
|  | ||||
| type setOptions struct { | ||||
| 	pathOptions   *pathOptions | ||||
| 	propertyName  string | ||||
| 	propertyValue string | ||||
| } | ||||
|  | ||||
| func NewCmdConfigSet(out io.Writer, pathOptions *pathOptions) *cobra.Command { | ||||
| 	options := &setOptions{pathOptions: pathOptions} | ||||
|  | ||||
| 	cmd := &cobra.Command{ | ||||
| 		Use:   "set property-name property-value", | ||||
| 		Short: "Sets an individual value in a .kubeconfig file", | ||||
| 		Long: `Sets an individual value in a .kubeconfig file | ||||
|  | ||||
| 		property-name is a dot delimitted name where each token represents either a attribute name or a map key.  Map keys may not contain dots. | ||||
| 		property-value is the new value you wish to set. | ||||
|  | ||||
| 		`, | ||||
| 		Run: func(cmd *cobra.Command, args []string) { | ||||
| 			if !options.complete(cmd) { | ||||
| 				return | ||||
| 			} | ||||
|  | ||||
| 			err := options.run() | ||||
| 			if err != nil { | ||||
| 				fmt.Printf("%v\n", err) | ||||
| 			} | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	return cmd | ||||
| } | ||||
|  | ||||
| func (o setOptions) run() error { | ||||
| 	err := o.validate() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	config, filename, err := o.pathOptions.getStartingConfig() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if len(filename) == 0 { | ||||
| 		return errors.New("cannot set property without using a specific file") | ||||
| 	} | ||||
|  | ||||
| 	parts := strings.Split(o.propertyName, ".") | ||||
| 	err = modifyConfig(reflect.ValueOf(config), parts, o.propertyValue, false) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	err = clientcmd.WriteToFile(*config, filename) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (o *setOptions) complete(cmd *cobra.Command) bool { | ||||
| 	endingArgs := cmd.Flags().Args() | ||||
| 	if len(endingArgs) != 2 { | ||||
| 		cmd.Help() | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	o.propertyValue = endingArgs[1] | ||||
| 	o.propertyName = endingArgs[0] | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (o setOptions) validate() error { | ||||
| 	if len(o.propertyValue) == 0 { | ||||
| 		return errors.New("You cannot use set to unset a property") | ||||
| 	} | ||||
|  | ||||
| 	if len(o.propertyName) == 0 { | ||||
| 		return errors.New("You must specify a property") | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // moreStepsRemaining just makes code read cleaner | ||||
| func moreStepsRemaining(remainder []string) bool { | ||||
| 	return len(remainder) != 0 | ||||
| } | ||||
|  | ||||
| func (s navigationSteps) nextSteps() navigationSteps { | ||||
| 	if len(s) < 2 { | ||||
| 		return make([]string, 0, 0) | ||||
| 	} else { | ||||
| 		return s[1:] | ||||
| 	} | ||||
| } | ||||
| func (s navigationSteps) moreStepsRemaining() bool { | ||||
| 	return len(s) != 0 | ||||
| } | ||||
| func (s navigationSteps) nextStep() string { | ||||
| 	return s[0] | ||||
| } | ||||
|  | ||||
| func modifyConfig(curr reflect.Value, steps navigationSteps, propertyValue string, unset bool) error { | ||||
| 	shouldUnsetNextField := !steps.nextSteps().moreStepsRemaining() && unset | ||||
| 	shouldSetThisField := !steps.moreStepsRemaining() && !unset | ||||
|  | ||||
| 	actualCurrValue := curr | ||||
| 	if curr.Kind() == reflect.Ptr { | ||||
| 		actualCurrValue = curr.Elem() | ||||
| 	} | ||||
|  | ||||
| 	switch actualCurrValue.Kind() { | ||||
| 	case reflect.Map: | ||||
| 		if shouldSetThisField { | ||||
| 			return fmt.Errorf("Can't set a map to a value: %v", actualCurrValue) | ||||
| 		} | ||||
|  | ||||
| 		mapKey := reflect.ValueOf(steps.nextStep()) | ||||
| 		mapValueType := curr.Type().Elem().Elem() | ||||
|  | ||||
| 		if shouldUnsetNextField { | ||||
| 			actualCurrValue.SetMapIndex(mapKey, reflect.Value{}) | ||||
| 			return nil | ||||
| 		} | ||||
|  | ||||
| 		currMapValue := actualCurrValue.MapIndex(mapKey) | ||||
|  | ||||
| 		needToSetNewMapValue := currMapValue.Kind() == reflect.Invalid | ||||
| 		if needToSetNewMapValue { | ||||
| 			currMapValue = reflect.New(mapValueType).Elem() | ||||
| 			actualCurrValue.SetMapIndex(mapKey, currMapValue) | ||||
| 		} | ||||
|  | ||||
| 		// our maps do not hold pointers to structs, they hold the structs themselves.  This means that MapIndex returns the struct itself | ||||
| 		// That in turn means that they have kinds of type.Struct, which is not a settable type.  Because of this, we need to make new struct of that type | ||||
| 		// copy all the data from the old value into the new value, then take the .addr of the new value to modify it in the next recursion. | ||||
| 		// clear as mud | ||||
| 		modifiableMapValue := reflect.New(currMapValue.Type()).Elem() | ||||
| 		modifiableMapValue.Set(currMapValue) | ||||
|  | ||||
| 		if modifiableMapValue.Kind() == reflect.Struct { | ||||
| 			modifiableMapValue = modifiableMapValue.Addr() | ||||
| 		} | ||||
| 		err := modifyConfig(modifiableMapValue, steps.nextSteps(), propertyValue, unset) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		actualCurrValue.SetMapIndex(mapKey, reflect.Indirect(modifiableMapValue)) | ||||
| 		return nil | ||||
|  | ||||
| 	case reflect.String: | ||||
| 		if steps.moreStepsRemaining() { | ||||
| 			return fmt.Errorf("Can't have more steps after a string. %v", steps) | ||||
| 		} | ||||
| 		actualCurrValue.SetString(propertyValue) | ||||
| 		return nil | ||||
|  | ||||
| 	case reflect.Bool: | ||||
| 		if steps.moreStepsRemaining() { | ||||
| 			return fmt.Errorf("Can't have more steps after a bool. %v", steps) | ||||
| 		} | ||||
| 		boolValue, err := toBool(propertyValue) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		actualCurrValue.SetBool(boolValue) | ||||
| 		return nil | ||||
|  | ||||
| 	case reflect.Struct: | ||||
| 		if !steps.moreStepsRemaining() { | ||||
| 			return fmt.Errorf("Can't set a struct to a value: %v", actualCurrValue) | ||||
| 		} | ||||
|  | ||||
| 		for fieldIndex := 0; fieldIndex < actualCurrValue.NumField(); fieldIndex++ { | ||||
| 			currFieldValue := actualCurrValue.Field(fieldIndex) | ||||
| 			currFieldType := actualCurrValue.Type().Field(fieldIndex) | ||||
| 			currYamlTag := currFieldType.Tag.Get("json") | ||||
| 			currFieldTypeYamlName := strings.Split(currYamlTag, ",")[0] | ||||
|  | ||||
| 			if currFieldTypeYamlName == steps.nextStep() { | ||||
| 				thisMapHasNoValue := (currFieldValue.Kind() == reflect.Map && currFieldValue.IsNil()) | ||||
|  | ||||
| 				if thisMapHasNoValue { | ||||
| 					newValue := reflect.MakeMap(currFieldValue.Type()) | ||||
| 					currFieldValue.Set(newValue) | ||||
|  | ||||
| 					if shouldUnsetNextField { | ||||
| 						return nil | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 				if shouldUnsetNextField { | ||||
| 					// if we're supposed to unset the value or if the value is a map that doesn't exist, create a new value and overwrite | ||||
| 					newValue := reflect.New(currFieldValue.Type()).Elem() | ||||
| 					currFieldValue.Set(newValue) | ||||
| 					return nil | ||||
| 				} | ||||
|  | ||||
| 				return modifyConfig(currFieldValue.Addr(), steps.nextSteps(), propertyValue, unset) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		return fmt.Errorf("Unable to locate path %v under %v", steps, actualCurrValue) | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	return fmt.Errorf("Unrecognized type: %v", actualCurrValue) | ||||
| } | ||||
							
								
								
									
										107
									
								
								pkg/kubectl/cmd/config/unset.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								pkg/kubectl/cmd/config/unset.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,107 @@ | ||||
| /* | ||||
| 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 config | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"reflect" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/spf13/cobra" | ||||
|  | ||||
| 	"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd" | ||||
| ) | ||||
|  | ||||
| type unsetOptions struct { | ||||
| 	pathOptions  *pathOptions | ||||
| 	propertyName string | ||||
| } | ||||
|  | ||||
| func NewCmdConfigUnset(out io.Writer, pathOptions *pathOptions) *cobra.Command { | ||||
| 	options := &unsetOptions{pathOptions: pathOptions} | ||||
|  | ||||
| 	cmd := &cobra.Command{ | ||||
| 		Use:   "unset property-name", | ||||
| 		Short: "Unsets an individual value in a .kubeconfig file", | ||||
| 		Long: `Unsets an individual value in a .kubeconfig file | ||||
|  | ||||
| 		property-name is a dot delimitted name where each token represents either a attribute name or a map key.  Map keys may not contain dots. | ||||
| 		`, | ||||
| 		Run: func(cmd *cobra.Command, args []string) { | ||||
| 			if !options.complete(cmd) { | ||||
| 				return | ||||
| 			} | ||||
|  | ||||
| 			err := options.run() | ||||
| 			if err != nil { | ||||
| 				fmt.Printf("%v\n", err) | ||||
| 			} | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	return cmd | ||||
| } | ||||
|  | ||||
| func (o unsetOptions) run() error { | ||||
| 	err := o.validate() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	config, filename, err := o.pathOptions.getStartingConfig() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if len(filename) == 0 { | ||||
| 		return errors.New("cannot set property without using a specific file") | ||||
| 	} | ||||
|  | ||||
| 	parts := strings.Split(o.propertyName, ".") | ||||
| 	err = modifyConfig(reflect.ValueOf(config), parts, "", true) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	err = clientcmd.WriteToFile(*config, filename) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (o *unsetOptions) complete(cmd *cobra.Command) bool { | ||||
| 	endingArgs := cmd.Flags().Args() | ||||
| 	if len(endingArgs) != 1 { | ||||
| 		cmd.Help() | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	o.propertyName = endingArgs[0] | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (o unsetOptions) validate() error { | ||||
| 	if len(o.propertyName) == 0 { | ||||
| 		return errors.New("You must specify a property") | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										98
									
								
								pkg/kubectl/cmd/config/use_context.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								pkg/kubectl/cmd/config/use_context.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,98 @@ | ||||
| /* | ||||
| 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 config | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
|  | ||||
| 	"github.com/spf13/cobra" | ||||
|  | ||||
| 	"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd" | ||||
| ) | ||||
|  | ||||
| type useContextOptions struct { | ||||
| 	pathOptions *pathOptions | ||||
| 	contextName string | ||||
| } | ||||
|  | ||||
| func NewCmdConfigUseContext(out io.Writer, pathOptions *pathOptions) *cobra.Command { | ||||
| 	options := &useContextOptions{pathOptions: pathOptions} | ||||
|  | ||||
| 	cmd := &cobra.Command{ | ||||
| 		Use:   "use-context context-name", | ||||
| 		Short: "Sets the current-context in a .kubeconfig file", | ||||
| 		Long:  `Sets the current-context in a .kubeconfig file`, | ||||
| 		Run: func(cmd *cobra.Command, args []string) { | ||||
| 			if !options.complete(cmd) { | ||||
| 				return | ||||
| 			} | ||||
|  | ||||
| 			err := options.run() | ||||
| 			if err != nil { | ||||
| 				fmt.Printf("%v\n", err) | ||||
| 			} | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	return cmd | ||||
| } | ||||
|  | ||||
| func (o useContextOptions) run() error { | ||||
| 	err := o.validate() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	config, filename, err := o.pathOptions.getStartingConfig() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if len(filename) == 0 { | ||||
| 		return errors.New("cannot set current-context without using a specific file") | ||||
| 	} | ||||
|  | ||||
| 	config.CurrentContext = o.contextName | ||||
|  | ||||
| 	err = clientcmd.WriteToFile(*config, filename) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (o *useContextOptions) complete(cmd *cobra.Command) bool { | ||||
| 	endingArgs := cmd.Flags().Args() | ||||
| 	if len(endingArgs) != 1 { | ||||
| 		cmd.Help() | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	o.contextName = endingArgs[0] | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (o useContextOptions) validate() error { | ||||
| 	if len(o.contextName) == 0 { | ||||
| 		return errors.New("You must specify a current-context") | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										99
									
								
								pkg/kubectl/cmd/config/view.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								pkg/kubectl/cmd/config/view.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,99 @@ | ||||
| /* | ||||
| 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 config | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io" | ||||
|  | ||||
| 	"github.com/ghodss/yaml" | ||||
| 	"github.com/spf13/cobra" | ||||
|  | ||||
| 	"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd" | ||||
| 	clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api" | ||||
| ) | ||||
|  | ||||
| type viewOptions struct { | ||||
| 	pathOptions *pathOptions | ||||
| 	merge       bool | ||||
| } | ||||
|  | ||||
| func NewCmdConfigView(out io.Writer, pathOptions *pathOptions) *cobra.Command { | ||||
| 	options := &viewOptions{pathOptions: pathOptions} | ||||
|  | ||||
| 	cmd := &cobra.Command{ | ||||
| 		Use:   "view", | ||||
| 		Short: "displays the specified .kubeconfig file or a merged result", | ||||
| 		Long:  `displays the specified .kubeconfig file or a merged result`, | ||||
| 		Run: func(cmd *cobra.Command, args []string) { | ||||
| 			err := options.run() | ||||
| 			if err != nil { | ||||
| 				fmt.Printf("%v\n", err) | ||||
| 			} | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	cmd.Flags().BoolVar(&options.merge, "merge", false, "merge together the full hierarchy of .kubeconfig files") | ||||
|  | ||||
| 	return cmd | ||||
| } | ||||
|  | ||||
| func (o viewOptions) run() error { | ||||
| 	err := o.validate() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	config, _, err := o.getStartingConfig() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	content, err := yaml.Marshal(config) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	fmt.Printf("%v", string(content)) | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (o viewOptions) validate() error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // getStartingConfig returns the Config object built from the sources specified by the options, the filename read (only if it was a single file), and an error if something goes wrong | ||||
| func (o *viewOptions) getStartingConfig() (*clientcmdapi.Config, string, error) { | ||||
| 	switch { | ||||
| 	case o.merge: | ||||
| 		loadingRules := clientcmd.NewClientConfigLoadingRules() | ||||
| 		loadingRules.EnvVarPath = "" | ||||
| 		loadingRules.CommandLinePath = o.pathOptions.specifiedFile | ||||
|  | ||||
| 		overrides := &clientcmd.ConfigOverrides{} | ||||
| 		clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, overrides) | ||||
|  | ||||
| 		config, err := clientConfig.RawConfig() | ||||
| 		if err != nil { | ||||
| 			return nil, "", fmt.Errorf("Error getting config: %v", err) | ||||
| 		} | ||||
| 		return &config, "", nil | ||||
| 	default: | ||||
| 		return o.pathOptions.getStartingConfig() | ||||
| 	} | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 deads2k
					deads2k