mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-01 02:38:12 +00:00 
			
		
		
		
	change kubeconfig loading order
This commit is contained in:
		| @@ -747,12 +747,9 @@ _kubectl_config() | ||||
|     flags_with_completion=() | ||||
|     flags_completion=() | ||||
|  | ||||
|     flags+=("--envvar") | ||||
|     flags+=("--global") | ||||
|     flags+=("--help") | ||||
|     flags+=("-h") | ||||
|     flags+=("--kubeconfig=") | ||||
|     flags+=("--local") | ||||
|  | ||||
|     must_have_one_flag=() | ||||
|     must_have_one_noun=() | ||||
|   | ||||
| @@ -84,15 +84,20 @@ If the contents of the kubernetes_auth file conflict with entries in .kubeconfig | ||||
|  | ||||
| ## Loading and merging rules | ||||
| The rules for loading and merging the .kubeconfig files are straightforward, but there are a lot of them.  The final config is built in this order: | ||||
|   1.  Merge together the kubeconfig itself.  This is done with the following hierarchy and merge rules: | ||||
|   1.  Get the kubeconfig  from disk.  This is done with the following hierarchy and merge rules: | ||||
|        | ||||
|  | ||||
|       If the CommandLineLocation (the value of the `kubeconfig` command line option) is set, use this file only.  No merging.  Only one instance of this flag is allowed. | ||||
|  | ||||
|  | ||||
|       Else, if EnvVarLocation (the value of $KUBECONFIG) is available, use it as a list of files that should be merged.   | ||||
|       Merge files together based on the following rules. | ||||
|       Empty filenames are ignored.  Files with non-deserializable content produced errors. | ||||
|       The first file to set a particular value or map key wins and the value or map key is never changed. | ||||
|       This means that the first file to set CurrentContext will have its context preserved.  It also means that if two files specify a "red-user", only values from the first file's red-user are used.  Even non-conflicting entries from the second file's "red-user" are discarded. | ||||
|       1.  CommandLineLocation - the value of the `kubeconfig` command line option | ||||
|       1.  EnvVarLocation - the value of $KUBECONFIG | ||||
|       1.  CurrentDirectoryLocation - ``pwd``/.kubeconfig | ||||
|       1.  HomeDirectoryLocation = ~/.kube/.kubeconfig | ||||
|  | ||||
|  | ||||
|       Otherwise, use HomeDirectoryLocation (~/.kube/config) with no merging. | ||||
|   1.  Determine the context to use based on the first hit in this chain | ||||
|       1.  command line argument - the value of the `context` command line option | ||||
|       1.  current-context from the merged kubeconfig file | ||||
|   | ||||
| @@ -66,4 +66,4 @@ kubectl | ||||
| * [kubectl update](kubectl_update.md)	 - Update a resource by filename or stdin. | ||||
| * [kubectl version](kubectl_version.md)	 - Print the client and server version information. | ||||
|  | ||||
| ###### Auto generated by spf13/cobra at 2015-04-14 19:53:16.641789142 +0000 UTC | ||||
| ###### Auto generated by spf13/cobra at 2015-04-16 17:04:37.392549632 +0000 UTC | ||||
|   | ||||
| @@ -50,4 +50,4 @@ kubectl api-versions | ||||
| ### SEE ALSO | ||||
| * [kubectl](kubectl.md)	 - kubectl controls the Kubernetes cluster manager | ||||
|  | ||||
| ###### Auto generated by spf13/cobra at 2015-04-14 19:53:16.641051929 +0000 UTC | ||||
| ###### Auto generated by spf13/cobra at 2015-04-16 17:04:37.39227534 +0000 UTC | ||||
|   | ||||
| @@ -50,4 +50,4 @@ kubectl cluster-info | ||||
| ### SEE ALSO | ||||
| * [kubectl](kubectl.md)	 - kubectl controls the Kubernetes cluster manager | ||||
|  | ||||
| ###### Auto generated by spf13/cobra at 2015-04-14 19:53:16.640723377 +0000 UTC | ||||
| ###### Auto generated by spf13/cobra at 2015-04-16 17:04:37.392162759 +0000 UTC | ||||
|   | ||||
| @@ -7,6 +7,12 @@ config modifies kubeconfig files | ||||
|  | ||||
| config modifies kubeconfig files using subcommands like "kubectl config set current-context my-context" | ||||
|  | ||||
| The loading order follows these rules: | ||||
|     1. If the --kubeconfig flag is set, then only that file is loaded.  The flag may only be set once and no merging takes place. | ||||
|     2. If $KUBECONFIG environment variable is set, then it is used a list of paths (normal path delimitting rules for your system).  These paths are merged together.  When a value is modified, it is modified in the file that defines the stanza.  When a value is created, it is created in the first file that exists.  If no files in the chain exist, then it creates the last file in the list. | ||||
|     3. Otherwise, ${HOME}/.kube/config is used and no merging takes place. | ||||
|  | ||||
|  | ||||
| ``` | ||||
| kubectl config SUBCOMMAND | ||||
| ``` | ||||
| @@ -14,11 +20,8 @@ kubectl config SUBCOMMAND | ||||
| ### Options | ||||
|  | ||||
| ``` | ||||
|       --envvar=false: use the kubeconfig from $KUBECONFIG | ||||
|       --global=false: use the kubeconfig from /home/username/.kube/.kubeconfig | ||||
|   -h, --help=false: help for config | ||||
|       --kubeconfig="": use a particular kubeconfig file | ||||
|       --local=false: use the kubeconfig in the current directory | ||||
| ``` | ||||
|  | ||||
| ### Options inherrited from parent commands | ||||
| @@ -60,4 +63,4 @@ kubectl config SUBCOMMAND | ||||
| * [kubectl config use-context](kubectl_config_use-context.md)	 - Sets the current-context in a kubeconfig file | ||||
| * [kubectl config view](kubectl_config_view.md)	 - displays Merged kubeconfig settings or a specified kubeconfig file. | ||||
|  | ||||
| ###### Auto generated by spf13/cobra at 2015-04-14 19:53:16.640323684 +0000 UTC | ||||
| ###### Auto generated by spf13/cobra at 2015-04-16 17:04:37.392043616 +0000 UTC | ||||
|   | ||||
| @@ -45,10 +45,7 @@ $ kubectl config set-cluster e2e --insecure-skip-tls-verify=true | ||||
|       --client-key="": Path to a client key file for TLS. | ||||
|       --cluster="": The name of the kubeconfig cluster to use | ||||
|       --context="": The name of the kubeconfig context to use | ||||
|       --envvar=false: use the kubeconfig from $KUBECONFIG | ||||
|       --global=false: use the kubeconfig from /home/username/.kube/.kubeconfig | ||||
|       --kubeconfig="": use a particular kubeconfig file | ||||
|       --local=false: use the kubeconfig in the current directory | ||||
|       --log_backtrace_at=:0: when logging hits line file:N, emit a stack trace | ||||
|       --log_dir=: If non-empty, write log files in this directory | ||||
|       --log_flush_frequency=5s: Maximum number of seconds between log flushes | ||||
| @@ -68,4 +65,4 @@ $ kubectl config set-cluster e2e --insecure-skip-tls-verify=true | ||||
| ### SEE ALSO | ||||
| * [kubectl config](kubectl_config.md)	 - config modifies kubeconfig files | ||||
|  | ||||
| ###### Auto generated by spf13/cobra at 2015-04-14 19:53:16.637680508 +0000 UTC | ||||
| ###### Auto generated by spf13/cobra at 2015-04-16 17:04:37.39119629 +0000 UTC | ||||
|   | ||||
| @@ -38,11 +38,8 @@ $ kubectl config set-context gce --user=cluster-admin | ||||
|       --client-certificate="": Path to a client key file for TLS. | ||||
|       --client-key="": Path to a client key file for TLS. | ||||
|       --context="": The name of the kubeconfig context to use | ||||
|       --envvar=false: use the kubeconfig from $KUBECONFIG | ||||
|       --global=false: use the kubeconfig from /home/username/.kube/.kubeconfig | ||||
|       --insecure-skip-tls-verify=false: If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure. | ||||
|       --kubeconfig="": use a particular kubeconfig file | ||||
|       --local=false: use the kubeconfig in the current directory | ||||
|       --log_backtrace_at=:0: when logging hits line file:N, emit a stack trace | ||||
|       --log_dir=: If non-empty, write log files in this directory | ||||
|       --log_flush_frequency=5s: Maximum number of seconds between log flushes | ||||
| @@ -61,4 +58,4 @@ $ kubectl config set-context gce --user=cluster-admin | ||||
| ### SEE ALSO | ||||
| * [kubectl config](kubectl_config.md)	 - config modifies kubeconfig files | ||||
|  | ||||
| ###### Auto generated by spf13/cobra at 2015-04-14 19:53:16.638371771 +0000 UTC | ||||
| ###### Auto generated by spf13/cobra at 2015-04-16 17:04:37.391488399 +0000 UTC | ||||
|   | ||||
| @@ -59,11 +59,8 @@ $ kubectl set-credentials cluster-admin --client-certificate=~/.kube/admin.crt - | ||||
|       --certificate-authority="": Path to a cert. file for the certificate authority. | ||||
|       --cluster="": The name of the kubeconfig cluster to use | ||||
|       --context="": The name of the kubeconfig context to use | ||||
|       --envvar=false: use the kubeconfig from $KUBECONFIG | ||||
|       --global=false: use the kubeconfig from /home/username/.kube/.kubeconfig | ||||
|       --insecure-skip-tls-verify=false: If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure. | ||||
|       --kubeconfig="": use a particular kubeconfig file | ||||
|       --local=false: use the kubeconfig in the current directory | ||||
|       --log_backtrace_at=:0: when logging hits line file:N, emit a stack trace | ||||
|       --log_dir=: If non-empty, write log files in this directory | ||||
|       --log_flush_frequency=5s: Maximum number of seconds between log flushes | ||||
| @@ -81,4 +78,4 @@ $ kubectl set-credentials cluster-admin --client-certificate=~/.kube/admin.crt - | ||||
| ### SEE ALSO | ||||
| * [kubectl config](kubectl_config.md)	 - config modifies kubeconfig files | ||||
|  | ||||
| ###### Auto generated by spf13/cobra at 2015-04-14 19:53:16.638019407 +0000 UTC | ||||
| ###### Auto generated by spf13/cobra at 2015-04-16 17:04:37.391323192 +0000 UTC | ||||
|   | ||||
| @@ -30,11 +30,8 @@ kubectl config set PROPERTY_NAME PROPERTY_VALUE | ||||
|       --client-key="": Path to a client key file for TLS. | ||||
|       --cluster="": The name of the kubeconfig cluster to use | ||||
|       --context="": The name of the kubeconfig context to use | ||||
|       --envvar=false: use the kubeconfig from $KUBECONFIG | ||||
|       --global=false: use the kubeconfig from /home/username/.kube/.kubeconfig | ||||
|       --insecure-skip-tls-verify=false: If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure. | ||||
|       --kubeconfig="": use a particular kubeconfig file | ||||
|       --local=false: use the kubeconfig in the current directory | ||||
|       --log_backtrace_at=:0: when logging hits line file:N, emit a stack trace | ||||
|       --log_dir=: If non-empty, write log files in this directory | ||||
|       --log_flush_frequency=5s: Maximum number of seconds between log flushes | ||||
| @@ -55,4 +52,4 @@ kubectl config set PROPERTY_NAME PROPERTY_VALUE | ||||
| ### SEE ALSO | ||||
| * [kubectl config](kubectl_config.md)	 - config modifies kubeconfig files | ||||
|  | ||||
| ###### Auto generated by spf13/cobra at 2015-04-14 19:53:16.638724317 +0000 UTC | ||||
| ###### Auto generated by spf13/cobra at 2015-04-16 17:04:37.391618859 +0000 UTC | ||||
|   | ||||
| @@ -29,11 +29,8 @@ kubectl config unset PROPERTY_NAME | ||||
|       --client-key="": Path to a client key file for TLS. | ||||
|       --cluster="": The name of the kubeconfig cluster to use | ||||
|       --context="": The name of the kubeconfig context to use | ||||
|       --envvar=false: use the kubeconfig from $KUBECONFIG | ||||
|       --global=false: use the kubeconfig from /home/username/.kube/.kubeconfig | ||||
|       --insecure-skip-tls-verify=false: If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure. | ||||
|       --kubeconfig="": use a particular kubeconfig file | ||||
|       --local=false: use the kubeconfig in the current directory | ||||
|       --log_backtrace_at=:0: when logging hits line file:N, emit a stack trace | ||||
|       --log_dir=: If non-empty, write log files in this directory | ||||
|       --log_flush_frequency=5s: Maximum number of seconds between log flushes | ||||
| @@ -54,4 +51,4 @@ kubectl config unset PROPERTY_NAME | ||||
| ### SEE ALSO | ||||
| * [kubectl config](kubectl_config.md)	 - config modifies kubeconfig files | ||||
|  | ||||
| ###### Auto generated by spf13/cobra at 2015-04-14 19:53:16.639325332 +0000 UTC | ||||
| ###### Auto generated by spf13/cobra at 2015-04-16 17:04:37.391735806 +0000 UTC | ||||
|   | ||||
| @@ -28,11 +28,8 @@ kubectl config use-context CONTEXT_NAME | ||||
|       --client-key="": Path to a client key file for TLS. | ||||
|       --cluster="": The name of the kubeconfig cluster to use | ||||
|       --context="": The name of the kubeconfig context to use | ||||
|       --envvar=false: use the kubeconfig from $KUBECONFIG | ||||
|       --global=false: use the kubeconfig from /home/username/.kube/.kubeconfig | ||||
|       --insecure-skip-tls-verify=false: If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure. | ||||
|       --kubeconfig="": use a particular kubeconfig file | ||||
|       --local=false: use the kubeconfig in the current directory | ||||
|       --log_backtrace_at=:0: when logging hits line file:N, emit a stack trace | ||||
|       --log_dir=: If non-empty, write log files in this directory | ||||
|       --log_flush_frequency=5s: Maximum number of seconds between log flushes | ||||
| @@ -53,4 +50,4 @@ kubectl config use-context CONTEXT_NAME | ||||
| ### SEE ALSO | ||||
| * [kubectl config](kubectl_config.md)	 - config modifies kubeconfig files | ||||
|  | ||||
| ###### Auto generated by spf13/cobra at 2015-04-14 19:53:16.639859272 +0000 UTC | ||||
| ###### Auto generated by spf13/cobra at 2015-04-16 17:04:37.391848246 +0000 UTC | ||||
|   | ||||
| @@ -50,11 +50,8 @@ $ kubectl config view -o template --template='{{range .users}}{{ if eq .name "e2 | ||||
|       --client-key="": Path to a client key file for TLS. | ||||
|       --cluster="": The name of the kubeconfig cluster to use | ||||
|       --context="": The name of the kubeconfig context to use | ||||
|       --envvar=false: use the kubeconfig from $KUBECONFIG | ||||
|       --global=false: use the kubeconfig from /home/username/.kube/.kubeconfig | ||||
|       --insecure-skip-tls-verify=false: If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure. | ||||
|       --kubeconfig="": use a particular kubeconfig file | ||||
|       --local=false: use the kubeconfig in the current directory | ||||
|       --log_backtrace_at=:0: when logging hits line file:N, emit a stack trace | ||||
|       --log_dir=: If non-empty, write log files in this directory | ||||
|       --log_flush_frequency=5s: Maximum number of seconds between log flushes | ||||
| @@ -75,4 +72,4 @@ $ kubectl config view -o template --template='{{range .users}}{{ if eq .name "e2 | ||||
| ### SEE ALSO | ||||
| * [kubectl config](kubectl_config.md)	 - config modifies kubeconfig files | ||||
|  | ||||
| ###### Auto generated by spf13/cobra at 2015-04-14 19:53:16.637312729 +0000 UTC | ||||
| ###### Auto generated by spf13/cobra at 2015-04-16 17:04:37.391073075 +0000 UTC | ||||
|   | ||||
| @@ -63,4 +63,4 @@ $ cat pod.json | kubectl create -f - | ||||
| ### SEE ALSO | ||||
| * [kubectl](kubectl.md)	 - kubectl controls the Kubernetes cluster manager | ||||
|  | ||||
| ###### Auto generated by spf13/cobra at 2015-04-14 19:53:16.629142674 +0000 UTC | ||||
| ###### Auto generated by spf13/cobra at 2015-04-16 17:04:37.388588064 +0000 UTC | ||||
|   | ||||
| @@ -81,4 +81,4 @@ $ kubectl delete pods --all | ||||
| ### SEE ALSO | ||||
| * [kubectl](kubectl.md)	 - kubectl controls the Kubernetes cluster manager | ||||
|  | ||||
| ###### Auto generated by spf13/cobra at 2015-04-14 19:53:16.630884641 +0000 UTC | ||||
| ###### Auto generated by spf13/cobra at 2015-04-16 17:04:37.389412973 +0000 UTC | ||||
|   | ||||
| @@ -53,4 +53,4 @@ kubectl describe RESOURCE ID | ||||
| ### SEE ALSO | ||||
| * [kubectl](kubectl.md)	 - kubectl controls the Kubernetes cluster manager | ||||
|  | ||||
| ###### Auto generated by spf13/cobra at 2015-04-14 19:53:16.628735136 +0000 UTC | ||||
| ###### Auto generated by spf13/cobra at 2015-04-16 17:04:37.388410556 +0000 UTC | ||||
|   | ||||
| @@ -64,4 +64,4 @@ $ kubectl exec -p 123456-7890 -c ruby-container -i -t -- bash -il | ||||
| ### SEE ALSO | ||||
| * [kubectl](kubectl.md)	 - kubectl controls the Kubernetes cluster manager | ||||
|  | ||||
| ###### Auto generated by spf13/cobra at 2015-04-14 19:53:16.632991694 +0000 UTC | ||||
| ###### Auto generated by spf13/cobra at 2015-04-16 17:04:37.390127525 +0000 UTC | ||||
|   | ||||
| @@ -82,4 +82,4 @@ $ kubectl expose streamer --port=4100 --protocol=udp --service-name=video-stream | ||||
| ### SEE ALSO | ||||
| * [kubectl](kubectl.md)	 - kubectl controls the Kubernetes cluster manager | ||||
|  | ||||
| ###### Auto generated by spf13/cobra at 2015-04-14 19:53:16.636399596 +0000 UTC | ||||
| ###### Auto generated by spf13/cobra at 2015-04-16 17:04:37.390792874 +0000 UTC | ||||
|   | ||||
| @@ -85,4 +85,4 @@ $ kubectl get rc/web service/frontend pods/web-pod-13je7 | ||||
| ### SEE ALSO | ||||
| * [kubectl](kubectl.md)	 - kubectl controls the Kubernetes cluster manager | ||||
|  | ||||
| ###### Auto generated by spf13/cobra at 2015-04-14 19:53:16.620151758 +0000 UTC | ||||
| ###### Auto generated by spf13/cobra at 2015-04-16 17:04:37.387483074 +0000 UTC | ||||
|   | ||||
| @@ -81,4 +81,4 @@ $ kubectl label pods foo bar- | ||||
| ### SEE ALSO | ||||
| * [kubectl](kubectl.md)	 - kubectl controls the Kubernetes cluster manager | ||||
|  | ||||
| ###### Auto generated by spf13/cobra at 2015-04-14 19:53:16.636842283 +0000 UTC | ||||
| ###### Auto generated by spf13/cobra at 2015-04-16 17:04:37.390937166 +0000 UTC | ||||
|   | ||||
| @@ -62,4 +62,4 @@ $ kubectl log -f 123456-7890 ruby-container | ||||
| ### SEE ALSO | ||||
| * [kubectl](kubectl.md)	 - kubectl controls the Kubernetes cluster manager | ||||
|  | ||||
| ###### Auto generated by spf13/cobra at 2015-04-14 19:53:16.631637812 +0000 UTC | ||||
| ###### Auto generated by spf13/cobra at 2015-04-16 17:04:37.389728881 +0000 UTC | ||||
|   | ||||
| @@ -53,4 +53,4 @@ kubectl namespace [namespace] | ||||
| ### SEE ALSO | ||||
| * [kubectl](kubectl.md)	 - kubectl controls the Kubernetes cluster manager | ||||
|  | ||||
| ###### Auto generated by spf13/cobra at 2015-04-14 19:53:16.63128386 +0000 UTC | ||||
| ###### Auto generated by spf13/cobra at 2015-04-16 17:04:37.389609191 +0000 UTC | ||||
|   | ||||
| @@ -68,4 +68,4 @@ $ kubectl port-forward -p mypod 0:5000 | ||||
| ### SEE ALSO | ||||
| * [kubectl](kubectl.md)	 - kubectl controls the Kubernetes cluster manager | ||||
|  | ||||
| ###### Auto generated by spf13/cobra at 2015-04-14 19:53:16.633423694 +0000 UTC | ||||
| ###### Auto generated by spf13/cobra at 2015-04-16 17:04:37.390241417 +0000 UTC | ||||
|   | ||||
| @@ -65,4 +65,4 @@ $ kubectl proxy --api-prefix=k8s-api | ||||
| ### SEE ALSO | ||||
| * [kubectl](kubectl.md)	 - kubectl controls the Kubernetes cluster manager | ||||
|  | ||||
| ###### Auto generated by spf13/cobra at 2015-04-14 19:53:16.633829265 +0000 UTC | ||||
| ###### Auto generated by spf13/cobra at 2015-04-16 17:04:37.390360738 +0000 UTC | ||||
|   | ||||
| @@ -68,4 +68,4 @@ $ kubectl resize --current-replicas=2 --replicas=3 replicationcontrollers foo | ||||
| ### SEE ALSO | ||||
| * [kubectl](kubectl.md)	 - kubectl controls the Kubernetes cluster manager | ||||
|  | ||||
| ###### Auto generated by spf13/cobra at 2015-04-14 19:53:16.632560634 +0000 UTC | ||||
| ###### Auto generated by spf13/cobra at 2015-04-16 17:04:37.389989377 +0000 UTC | ||||
|   | ||||
| @@ -68,4 +68,4 @@ $ cat frontend-v2.json | kubectl rolling-update frontend-v1 -f - | ||||
| ### SEE ALSO | ||||
| * [kubectl](kubectl.md)	 - kubectl controls the Kubernetes cluster manager | ||||
|  | ||||
| ###### Auto generated by spf13/cobra at 2015-04-14 19:53:16.632023561 +0000 UTC | ||||
| ###### Auto generated by spf13/cobra at 2015-04-16 17:04:37.38985117 +0000 UTC | ||||
|   | ||||
| @@ -78,4 +78,4 @@ $ kubectl run-container nginx --image=dockerfile/nginx --overrides='{ "apiVersio | ||||
| ### SEE ALSO | ||||
| * [kubectl](kubectl.md)	 - kubectl controls the Kubernetes cluster manager | ||||
|  | ||||
| ###### Auto generated by spf13/cobra at 2015-04-14 19:53:16.634391744 +0000 UTC | ||||
| ###### Auto generated by spf13/cobra at 2015-04-16 17:04:37.390501802 +0000 UTC | ||||
|   | ||||
| @@ -72,4 +72,4 @@ $ kubectl stop -f path/to/resources | ||||
| ### SEE ALSO | ||||
| * [kubectl](kubectl.md)	 - kubectl controls the Kubernetes cluster manager | ||||
|  | ||||
| ###### Auto generated by spf13/cobra at 2015-04-14 19:53:16.635920945 +0000 UTC | ||||
| ###### Auto generated by spf13/cobra at 2015-04-16 17:04:37.390631789 +0000 UTC | ||||
|   | ||||
| @@ -67,4 +67,4 @@ $ kubectl update pods my-pod --patch='{ "apiVersion": "v1beta1", "desiredState": | ||||
| ### SEE ALSO | ||||
| * [kubectl](kubectl.md)	 - kubectl controls the Kubernetes cluster manager | ||||
|  | ||||
| ###### Auto generated by spf13/cobra at 2015-04-14 19:53:16.630218109 +0000 UTC | ||||
| ###### Auto generated by spf13/cobra at 2015-04-16 17:04:37.388743178 +0000 UTC | ||||
|   | ||||
| @@ -51,4 +51,4 @@ kubectl version | ||||
| ### SEE ALSO | ||||
| * [kubectl](kubectl.md)	 - kubectl controls the Kubernetes cluster manager | ||||
|  | ||||
| ###### Auto generated by spf13/cobra at 2015-04-14 19:53:16.641381587 +0000 UTC | ||||
| ###### Auto generated by spf13/cobra at 2015-04-16 17:04:37.392395408 +0000 UTC | ||||
|   | ||||
| @@ -68,22 +68,10 @@ Specifying a name that already exists will merge new fields on top of existing v | ||||
| \fB\-\-context\fP="" | ||||
|     The name of the kubeconfig context to use | ||||
|  | ||||
| .PP | ||||
| \fB\-\-envvar\fP=false | ||||
|     use the kubeconfig from $KUBECONFIG | ||||
|  | ||||
| .PP | ||||
| \fB\-\-global\fP=false | ||||
|     use the kubeconfig from /home/username/.kube/.kubeconfig | ||||
|  | ||||
| .PP | ||||
| \fB\-\-kubeconfig\fP="" | ||||
|     use a particular kubeconfig file | ||||
|  | ||||
| .PP | ||||
| \fB\-\-local\fP=false | ||||
|     use the kubeconfig in the current directory | ||||
|  | ||||
| .PP | ||||
| \fB\-\-log\_backtrace\_at\fP=:0 | ||||
|     when logging hits line file:N, emit a stack trace | ||||
|   | ||||
| @@ -64,14 +64,6 @@ Specifying a name that already exists will merge new fields on top of existing v | ||||
| \fB\-\-context\fP="" | ||||
|     The name of the kubeconfig context to use | ||||
|  | ||||
| .PP | ||||
| \fB\-\-envvar\fP=false | ||||
|     use the kubeconfig from $KUBECONFIG | ||||
|  | ||||
| .PP | ||||
| \fB\-\-global\fP=false | ||||
|     use the kubeconfig from /home/username/.kube/.kubeconfig | ||||
|  | ||||
| .PP | ||||
| \fB\-\-insecure\-skip\-tls\-verify\fP=false | ||||
|     If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure. | ||||
| @@ -80,10 +72,6 @@ Specifying a name that already exists will merge new fields on top of existing v | ||||
| \fB\-\-kubeconfig\fP="" | ||||
|     use a particular kubeconfig file | ||||
|  | ||||
| .PP | ||||
| \fB\-\-local\fP=false | ||||
|     use the kubeconfig in the current directory | ||||
|  | ||||
| .PP | ||||
| \fB\-\-log\_backtrace\_at\fP=:0 | ||||
|     when logging hits line file:N, emit a stack trace | ||||
|   | ||||
| @@ -87,14 +87,6 @@ Bearer token and basic auth are mutually exclusive. | ||||
| \fB\-\-context\fP="" | ||||
|     The name of the kubeconfig context to use | ||||
|  | ||||
| .PP | ||||
| \fB\-\-envvar\fP=false | ||||
|     use the kubeconfig from $KUBECONFIG | ||||
|  | ||||
| .PP | ||||
| \fB\-\-global\fP=false | ||||
|     use the kubeconfig from /home/username/.kube/.kubeconfig | ||||
|  | ||||
| .PP | ||||
| \fB\-\-insecure\-skip\-tls\-verify\fP=false | ||||
|     If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure. | ||||
| @@ -103,10 +95,6 @@ Bearer token and basic auth are mutually exclusive. | ||||
| \fB\-\-kubeconfig\fP="" | ||||
|     use a particular kubeconfig file | ||||
|  | ||||
| .PP | ||||
| \fB\-\-local\fP=false | ||||
|     use the kubeconfig in the current directory | ||||
|  | ||||
| .PP | ||||
| \fB\-\-log\_backtrace\_at\fP=:0 | ||||
|     when logging hits line file:N, emit a stack trace | ||||
|   | ||||
| @@ -57,14 +57,6 @@ PROPERTY\_VALUE is the new value you wish to set. | ||||
| \fB\-\-context\fP="" | ||||
|     The name of the kubeconfig context to use | ||||
|  | ||||
| .PP | ||||
| \fB\-\-envvar\fP=false | ||||
|     use the kubeconfig from $KUBECONFIG | ||||
|  | ||||
| .PP | ||||
| \fB\-\-global\fP=false | ||||
|     use the kubeconfig from /home/username/.kube/.kubeconfig | ||||
|  | ||||
| .PP | ||||
| \fB\-\-insecure\-skip\-tls\-verify\fP=false | ||||
|     If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure. | ||||
| @@ -73,10 +65,6 @@ PROPERTY\_VALUE is the new value you wish to set. | ||||
| \fB\-\-kubeconfig\fP="" | ||||
|     use a particular kubeconfig file | ||||
|  | ||||
| .PP | ||||
| \fB\-\-local\fP=false | ||||
|     use the kubeconfig in the current directory | ||||
|  | ||||
| .PP | ||||
| \fB\-\-log\_backtrace\_at\fP=:0 | ||||
|     when logging hits line file:N, emit a stack trace | ||||
|   | ||||
| @@ -56,14 +56,6 @@ PROPERTY\_NAME is a dot delimited name where each token represents either a attr | ||||
| \fB\-\-context\fP="" | ||||
|     The name of the kubeconfig context to use | ||||
|  | ||||
| .PP | ||||
| \fB\-\-envvar\fP=false | ||||
|     use the kubeconfig from $KUBECONFIG | ||||
|  | ||||
| .PP | ||||
| \fB\-\-global\fP=false | ||||
|     use the kubeconfig from /home/username/.kube/.kubeconfig | ||||
|  | ||||
| .PP | ||||
| \fB\-\-insecure\-skip\-tls\-verify\fP=false | ||||
|     If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure. | ||||
| @@ -72,10 +64,6 @@ PROPERTY\_NAME is a dot delimited name where each token represents either a attr | ||||
| \fB\-\-kubeconfig\fP="" | ||||
|     use a particular kubeconfig file | ||||
|  | ||||
| .PP | ||||
| \fB\-\-local\fP=false | ||||
|     use the kubeconfig in the current directory | ||||
|  | ||||
| .PP | ||||
| \fB\-\-log\_backtrace\_at\fP=:0 | ||||
|     when logging hits line file:N, emit a stack trace | ||||
|   | ||||
| @@ -55,14 +55,6 @@ Sets the current\-context in a kubeconfig file | ||||
| \fB\-\-context\fP="" | ||||
|     The name of the kubeconfig context to use | ||||
|  | ||||
| .PP | ||||
| \fB\-\-envvar\fP=false | ||||
|     use the kubeconfig from $KUBECONFIG | ||||
|  | ||||
| .PP | ||||
| \fB\-\-global\fP=false | ||||
|     use the kubeconfig from /home/username/.kube/.kubeconfig | ||||
|  | ||||
| .PP | ||||
| \fB\-\-insecure\-skip\-tls\-verify\fP=false | ||||
|     If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure. | ||||
| @@ -71,10 +63,6 @@ Sets the current\-context in a kubeconfig file | ||||
| \fB\-\-kubeconfig\fP="" | ||||
|     use a particular kubeconfig file | ||||
|  | ||||
| .PP | ||||
| \fB\-\-local\fP=false | ||||
|     use the kubeconfig in the current directory | ||||
|  | ||||
| .PP | ||||
| \fB\-\-log\_backtrace\_at\fP=:0 | ||||
|     when logging hits line file:N, emit a stack trace | ||||
|   | ||||
| @@ -87,14 +87,6 @@ You can use \-\-output=template \-\-template=TEMPLATE to extract specific values | ||||
| \fB\-\-context\fP="" | ||||
|     The name of the kubeconfig context to use | ||||
|  | ||||
| .PP | ||||
| \fB\-\-envvar\fP=false | ||||
|     use the kubeconfig from $KUBECONFIG | ||||
|  | ||||
| .PP | ||||
| \fB\-\-global\fP=false | ||||
|     use the kubeconfig from /home/username/.kube/.kubeconfig | ||||
|  | ||||
| .PP | ||||
| \fB\-\-insecure\-skip\-tls\-verify\fP=false | ||||
|     If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure. | ||||
| @@ -103,10 +95,6 @@ You can use \-\-output=template \-\-template=TEMPLATE to extract specific values | ||||
| \fB\-\-kubeconfig\fP="" | ||||
|     use a particular kubeconfig file | ||||
|  | ||||
| .PP | ||||
| \fB\-\-local\fP=false | ||||
|     use the kubeconfig in the current directory | ||||
|  | ||||
| .PP | ||||
| \fB\-\-log\_backtrace\_at\fP=:0 | ||||
|     when logging hits line file:N, emit a stack trace | ||||
|   | ||||
| @@ -15,16 +15,14 @@ kubectl config \- config modifies kubeconfig files | ||||
| .PP | ||||
| config modifies kubeconfig files using subcommands like "kubectl config set current\-context my\-context" | ||||
|  | ||||
| .PP | ||||
| The loading order follows these rules: | ||||
|     1. If the \-\-kubeconfig flag is set, then only that file is loaded.  The flag may only be set once and no merging takes place. | ||||
|     2. If $KUBECONFIG environment variable is set, then it is used a list of paths (normal path delimitting rules for your system).  These paths are merged together.  When a value is modified, it is modified in the file that defines the stanza.  When a value is created, it is created in the first file that exists.  If no files in the chain exist, then it creates the last file in the list. | ||||
|     3. Otherwise, $\{HOME\}/.kube/config is used and no merging takes place. | ||||
|  | ||||
|  | ||||
| .SH OPTIONS | ||||
| .PP | ||||
| \fB\-\-envvar\fP=false | ||||
|     use the kubeconfig from $KUBECONFIG | ||||
|  | ||||
| .PP | ||||
| \fB\-\-global\fP=false | ||||
|     use the kubeconfig from /home/username/.kube/.kubeconfig | ||||
|  | ||||
| .PP | ||||
| \fB\-h\fP, \fB\-\-help\fP=false | ||||
|     help for config | ||||
| @@ -33,10 +31,6 @@ config modifies kubeconfig files using subcommands like "kubectl config set curr | ||||
| \fB\-\-kubeconfig\fP="" | ||||
|     use a particular kubeconfig file | ||||
|  | ||||
| .PP | ||||
| \fB\-\-local\fP=false | ||||
|     use the kubeconfig in the current directory | ||||
|  | ||||
|  | ||||
| .SH OPTIONS INHERITED FROM PARENT COMMANDS | ||||
| .PP | ||||
|   | ||||
| @@ -18,8 +18,10 @@ package clientcmd | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
| 	"os" | ||||
| 	"path" | ||||
| 	"path/filepath" | ||||
|  | ||||
| 	"github.com/ghodss/yaml" | ||||
| @@ -33,22 +35,24 @@ import ( | ||||
| const ( | ||||
| 	RecommendedConfigPathFlag   = "kubeconfig" | ||||
| 	RecommendedConfigPathEnvVar = "KUBECONFIG" | ||||
|  | ||||
| 	DefaultEnvVarIndex     = 0 | ||||
| 	DefaultCurrentDirIndex = 1 | ||||
| 	DefaultHomeDirIndex    = 2 | ||||
| 	RecommendedHomeFileName     = "/.kube/config" | ||||
| ) | ||||
|  | ||||
| var OldRecommendedHomeFile = path.Join(os.Getenv("HOME"), "/.kube/.kubeconfig") | ||||
| var RecommendedHomeFile = path.Join(os.Getenv("HOME"), RecommendedHomeFileName) | ||||
|  | ||||
| // ClientConfigLoadingRules is an ExplicitPath and string slice of specific locations that are used for merging together a Config | ||||
| // Callers can put the chain together however they want, but we'd recommend: | ||||
| // [0] = EnvVarPath | ||||
| // [1] = CurrentDirectoryPath | ||||
| // [2] = HomeDirectoryPath | ||||
| // EnvVarPathFiles if set (a list of files if set) OR the HomeDirectoryPath | ||||
| // ExplicitPath is special, because if a user specifically requests a certain file be used and error is reported if thie file is not present | ||||
| type ClientConfigLoadingRules struct { | ||||
| 	ExplicitPath string | ||||
| 	Precedence   []string | ||||
|  | ||||
| 	// MigrationRules is a map of destination files to source files.  If a destination file is not present, then the source file is checked. | ||||
| 	// If the source file is present, then it is copied to the destination file BEFORE any further loading happens. | ||||
| 	MigrationRules map[string]string | ||||
|  | ||||
| 	// DoNotResolvePaths indicates whether or not to resolve paths with respect to the originating files.  This is phrased as a negative so | ||||
| 	// that a default object that doesn't set this will usually get the behavior it wants. | ||||
| 	DoNotResolvePaths bool | ||||
| @@ -57,14 +61,29 @@ type ClientConfigLoadingRules struct { | ||||
| // NewDefaultClientConfigLoadingRules returns a ClientConfigLoadingRules object with default fields filled in.  You are not required to | ||||
| // use this constructor | ||||
| func NewDefaultClientConfigLoadingRules() *ClientConfigLoadingRules { | ||||
| 	chain := []string{} | ||||
| 	migrationRules := map[string]string{} | ||||
|  | ||||
| 	envVarFiles := os.Getenv(RecommendedConfigPathEnvVar) | ||||
| 	if len(envVarFiles) != 0 { | ||||
| 		chain = append(chain, filepath.SplitList(envVarFiles)...) | ||||
|  | ||||
| 	} else { | ||||
| 		chain = append(chain, RecommendedHomeFile) | ||||
| 		migrationRules[RecommendedHomeFile] = OldRecommendedHomeFile | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	return &ClientConfigLoadingRules{ | ||||
| 		Precedence: []string{os.Getenv(RecommendedConfigPathEnvVar), ".kubeconfig", os.Getenv("HOME") + "/.kube/.kubeconfig"}, | ||||
| 		Precedence:     chain, | ||||
| 		MigrationRules: migrationRules, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Load takes the loading rules and merges together a Config object based on following order. | ||||
| //   1.  ExplicitPath | ||||
| //   2.  Precedence slice | ||||
| // Load starts by running the MigrationRules and then | ||||
| // takes the loading rules and returns a Config object based on following rules. | ||||
| //   if the ExplicitPath, return the unmerged explicit file | ||||
| //   Otherwise, return a merged config based on the Precedence slice | ||||
| // A missing ExplicitPath file produces an error. Empty filenames or other missing files are ignored. | ||||
| // Read errors or files with non-deserializable content produce errors. | ||||
| // The first file to set a particular map key wins and map key's value is never changed. | ||||
| @@ -75,17 +94,25 @@ func NewDefaultClientConfigLoadingRules() *ClientConfigLoadingRules { | ||||
| // Relative paths inside of the .kubeconfig files are resolved against the .kubeconfig file's parent folder | ||||
| // and only absolute file paths are returned. | ||||
| func (rules *ClientConfigLoadingRules) Load() (*clientcmdapi.Config, error) { | ||||
| 	if err := rules.Migrate(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	errlist := []error{} | ||||
|  | ||||
| 	kubeConfigFiles := []string{} | ||||
|  | ||||
| 	// Make sure a file we were explicitly told to use exists | ||||
| 	if len(rules.ExplicitPath) > 0 { | ||||
| 		if _, err := os.Stat(rules.ExplicitPath); os.IsNotExist(err) { | ||||
| 			errlist = append(errlist, fmt.Errorf("The config file %v does not exist", rules.ExplicitPath)) | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
| 		kubeConfigFiles = append(kubeConfigFiles, rules.ExplicitPath) | ||||
|  | ||||
| 	kubeConfigFiles := []string{rules.ExplicitPath} | ||||
| 	kubeConfigFiles = append(kubeConfigFiles, rules.Precedence...) | ||||
| 	} else { | ||||
| 		kubeConfigFiles = append(kubeConfigFiles, rules.Precedence...) | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	// first merge all of our maps | ||||
| 	mapConfig := clientcmdapi.NewConfig() | ||||
| @@ -120,6 +147,53 @@ func (rules *ClientConfigLoadingRules) Load() (*clientcmdapi.Config, error) { | ||||
| 	return config, errors.NewAggregate(errlist) | ||||
| } | ||||
|  | ||||
| // Migrate uses the MigrationRules map.  If a destination file is not present, then the source file is checked. | ||||
| // If the source file is present, then it is copied to the destination file BEFORE any further loading happens. | ||||
| func (rules *ClientConfigLoadingRules) Migrate() error { | ||||
| 	if rules.MigrationRules == nil { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	for destination, source := range rules.MigrationRules { | ||||
| 		if _, err := os.Stat(destination); err == nil { | ||||
| 			// if the destination already exists, do nothing | ||||
| 			continue | ||||
| 		} else if !os.IsNotExist(err) { | ||||
| 			// if we had an error other than non-existence, fail | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		if sourceInfo, err := os.Stat(source); err != nil { | ||||
| 			if os.IsNotExist(err) { | ||||
| 				// if the source file doesn't exist, there's no work to do. | ||||
| 				continue | ||||
| 			} | ||||
|  | ||||
| 			// if we had an error other than non-existence, fail | ||||
| 			return err | ||||
| 		} else if sourceInfo.IsDir() { | ||||
| 			return fmt.Errorf("cannot migrate %v to %v because it is a directory", source, destination) | ||||
| 		} | ||||
|  | ||||
| 		in, err := os.Open(source) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		defer in.Close() | ||||
| 		out, err := os.Create(destination) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		defer out.Close() | ||||
|  | ||||
| 		if _, err = io.Copy(out, in); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func mergeConfigWithFile(startingConfig *clientcmdapi.Config, filename string) error { | ||||
| 	if len(filename) == 0 { | ||||
| 		// no work to do | ||||
|   | ||||
| @@ -22,6 +22,7 @@ import ( | ||||
| 	"os" | ||||
| 	"path" | ||||
| 	"path/filepath" | ||||
| 	"reflect" | ||||
| 	"strings" | ||||
| 	"testing" | ||||
|  | ||||
| @@ -209,8 +210,7 @@ func TestResolveRelativePaths(t *testing.T) { | ||||
| 	WriteToFile(pathResolutionConfig2, configFile2) | ||||
|  | ||||
| 	loadingRules := ClientConfigLoadingRules{ | ||||
| 		ExplicitPath: configFile1, | ||||
| 		Precedence:   []string{configFile2}, | ||||
| 		Precedence: []string{configFile1, configFile2}, | ||||
| 	} | ||||
|  | ||||
| 	mergedConfig, err := loadingRules.Load() | ||||
| @@ -274,7 +274,86 @@ func TestResolveRelativePaths(t *testing.T) { | ||||
|  | ||||
| } | ||||
|  | ||||
| func ExampleMergingSomeWithConflict() { | ||||
| func TestMigratingFile(t *testing.T) { | ||||
| 	sourceFile, _ := ioutil.TempFile("", "") | ||||
| 	defer os.Remove(sourceFile.Name()) | ||||
| 	destinationFile, _ := ioutil.TempFile("", "") | ||||
| 	// delete the file so that we'll write to it | ||||
| 	os.Remove(destinationFile.Name()) | ||||
|  | ||||
| 	WriteToFile(testConfigAlfa, sourceFile.Name()) | ||||
|  | ||||
| 	loadingRules := ClientConfigLoadingRules{ | ||||
| 		MigrationRules: map[string]string{destinationFile.Name(): sourceFile.Name()}, | ||||
| 	} | ||||
|  | ||||
| 	if _, err := loadingRules.Load(); err != nil { | ||||
| 		t.Errorf("unexpected error %v", err) | ||||
| 	} | ||||
|  | ||||
| 	// the load should have recreated this file | ||||
| 	defer os.Remove(destinationFile.Name()) | ||||
|  | ||||
| 	sourceContent, err := ioutil.ReadFile(sourceFile.Name()) | ||||
| 	if err != nil { | ||||
| 		t.Errorf("unexpected error %v", err) | ||||
| 	} | ||||
| 	destinationContent, err := ioutil.ReadFile(destinationFile.Name()) | ||||
| 	if err != nil { | ||||
| 		t.Errorf("unexpected error %v", err) | ||||
| 	} | ||||
|  | ||||
| 	if !reflect.DeepEqual(sourceContent, destinationContent) { | ||||
| 		t.Errorf("source and destination do not match") | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestMigratingFileLeaveExistingFileAlone(t *testing.T) { | ||||
| 	sourceFile, _ := ioutil.TempFile("", "") | ||||
| 	defer os.Remove(sourceFile.Name()) | ||||
| 	destinationFile, _ := ioutil.TempFile("", "") | ||||
| 	defer os.Remove(destinationFile.Name()) | ||||
|  | ||||
| 	WriteToFile(testConfigAlfa, sourceFile.Name()) | ||||
|  | ||||
| 	loadingRules := ClientConfigLoadingRules{ | ||||
| 		MigrationRules: map[string]string{destinationFile.Name(): sourceFile.Name()}, | ||||
| 	} | ||||
|  | ||||
| 	if _, err := loadingRules.Load(); err != nil { | ||||
| 		t.Errorf("unexpected error %v", err) | ||||
| 	} | ||||
|  | ||||
| 	destinationContent, err := ioutil.ReadFile(destinationFile.Name()) | ||||
| 	if err != nil { | ||||
| 		t.Errorf("unexpected error %v", err) | ||||
| 	} | ||||
|  | ||||
| 	if len(destinationContent) > 0 { | ||||
| 		t.Errorf("destination should not have been touched") | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestMigratingFileSourceMissingSkip(t *testing.T) { | ||||
| 	sourceFilename := "some-missing-file" | ||||
| 	destinationFile, _ := ioutil.TempFile("", "") | ||||
| 	// delete the file so that we'll write to it | ||||
| 	os.Remove(destinationFile.Name()) | ||||
|  | ||||
| 	loadingRules := ClientConfigLoadingRules{ | ||||
| 		MigrationRules: map[string]string{destinationFile.Name(): sourceFilename}, | ||||
| 	} | ||||
|  | ||||
| 	if _, err := loadingRules.Load(); err != nil { | ||||
| 		t.Errorf("unexpected error %v", err) | ||||
| 	} | ||||
|  | ||||
| 	if _, err := os.Stat(destinationFile.Name()); !os.IsNotExist(err) { | ||||
| 		t.Errorf("destination should not exist") | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func ExampleNoMergingOnExplicitPaths() { | ||||
| 	commandLineFile, _ := ioutil.TempFile("", "") | ||||
| 	defer os.Remove(commandLineFile.Name()) | ||||
| 	envVarFile, _ := ioutil.TempFile("", "") | ||||
| @@ -299,6 +378,52 @@ func ExampleMergingSomeWithConflict() { | ||||
| 		fmt.Printf("Unexpected error: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	fmt.Printf("%v", string(output)) | ||||
| 	// Output: | ||||
| 	// apiVersion: v1 | ||||
| 	// clusters: | ||||
| 	// - cluster: | ||||
| 	//     server: http://cow.org:8080 | ||||
| 	//   name: cow-cluster | ||||
| 	// contexts: | ||||
| 	// - context: | ||||
| 	//     cluster: cow-cluster | ||||
| 	//     namespace: hammer-ns | ||||
| 	//     user: red-user | ||||
| 	//   name: federal-context | ||||
| 	// current-context: "" | ||||
| 	// kind: Config | ||||
| 	// preferences: {} | ||||
| 	// users: | ||||
| 	// - name: red-user | ||||
| 	//   user: | ||||
| 	//     token: red-token | ||||
| } | ||||
|  | ||||
| func ExampleMergingSomeWithConflict() { | ||||
| 	commandLineFile, _ := ioutil.TempFile("", "") | ||||
| 	defer os.Remove(commandLineFile.Name()) | ||||
| 	envVarFile, _ := ioutil.TempFile("", "") | ||||
| 	defer os.Remove(envVarFile.Name()) | ||||
|  | ||||
| 	WriteToFile(testConfigAlfa, commandLineFile.Name()) | ||||
| 	WriteToFile(testConfigConflictAlfa, envVarFile.Name()) | ||||
|  | ||||
| 	loadingRules := ClientConfigLoadingRules{ | ||||
| 		Precedence: []string{commandLineFile.Name(), envVarFile.Name()}, | ||||
| 	} | ||||
|  | ||||
| 	mergedConfig, err := loadingRules.Load() | ||||
|  | ||||
| 	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: | ||||
| 	// apiVersion: v1 | ||||
| @@ -344,8 +469,7 @@ func ExampleMergingEverythingNoConflicts() { | ||||
| 	WriteToFile(testConfigDelta, homeDirFile.Name()) | ||||
|  | ||||
| 	loadingRules := ClientConfigLoadingRules{ | ||||
| 		ExplicitPath: commandLineFile.Name(), | ||||
| 		Precedence:   []string{envVarFile.Name(), currentDirFile.Name(), homeDirFile.Name()}, | ||||
| 		Precedence: []string{commandLineFile.Name(), envVarFile.Name(), currentDirFile.Name(), homeDirFile.Name()}, | ||||
| 	} | ||||
|  | ||||
| 	mergedConfig, err := loadingRules.Load() | ||||
|   | ||||
| @@ -78,10 +78,7 @@ func testWriteAuthInfoFile(auth clientauth.Info, filename string) error { | ||||
| } | ||||
|  | ||||
| func testBindClientConfig(cmd *cobra.Command) ClientConfig { | ||||
| 	loadingRules := NewDefaultClientConfigLoadingRules() | ||||
| 	loadingRules.Precedence[DefaultEnvVarIndex] = "" | ||||
| 	loadingRules.Precedence[DefaultCurrentDirIndex] = "" | ||||
| 	loadingRules.Precedence[DefaultHomeDirIndex] = "" | ||||
| 	loadingRules := &ClientConfigLoadingRules{} | ||||
| 	cmd.PersistentFlags().StringVar(&loadingRules.ExplicitPath, "kubeconfig", "", "Path to the kubeconfig file to use for CLI requests.") | ||||
|  | ||||
| 	overrides := &ConfigOverrides{} | ||||
|   | ||||
| @@ -18,10 +18,10 @@ package config | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"os" | ||||
| 	"path" | ||||
| 	"path/filepath" | ||||
| 	"reflect" | ||||
| 	"strconv" | ||||
|  | ||||
| @@ -33,41 +33,54 @@ import ( | ||||
| ) | ||||
|  | ||||
| type PathOptions struct { | ||||
| 	Local     bool | ||||
| 	Global    bool | ||||
| 	UseEnvVar bool | ||||
|  | ||||
| 	LocalFile  string | ||||
| 	// GlobalFile is the full path to the file to load as the global (final) option | ||||
| 	GlobalFile string | ||||
| 	EnvVarFile string | ||||
|  | ||||
| 	EnvVar           string | ||||
| 	// EnvVar is the env var name that points to the list of kubeconfig files to load | ||||
| 	EnvVar string | ||||
| 	// ExplicitFileFlag is the name of the flag to use for prompting for the kubeconfig file | ||||
| 	ExplicitFileFlag string | ||||
|  | ||||
| 	// GlobalFileSubpath is an optional value used for displaying help | ||||
| 	GlobalFileSubpath string | ||||
|  | ||||
| 	LoadingRules *clientcmd.ClientConfigLoadingRules | ||||
| } | ||||
|  | ||||
| // ConfigAccess is used by subcommands and methods in this package to load and modify the appropriate config files | ||||
| type ConfigAccess interface { | ||||
| 	// GetLoadingPrecedence returns the slice of files that should be used for loading and inspecting the config | ||||
| 	GetLoadingPrecedence() []string | ||||
| 	// GetStartingConfig returns the config that subcommands should being operating against.  It may or may not be merged depending on loading rules | ||||
| 	GetStartingConfig() (*clientcmdapi.Config, error) | ||||
| 	// GetDefaultFilename returns the name of the file you should write into (create if necessary), if you're trying to create a new stanza as opposed to updating an existing one. | ||||
| 	GetDefaultFilename() string | ||||
| 	// IsExplicitFile indicates whether or not this command is interested in exactly one file.  This implementation only ever does that  via a flag, but implementations that handle local, global, and flags may have more | ||||
| 	IsExplicitFile() bool | ||||
| 	// GetExplicitFile returns the particular file this command is operating against.  This implementation only ever has one, but implementations that handle local, global, and flags may have more | ||||
| 	GetExplicitFile() string | ||||
| } | ||||
|  | ||||
| func NewCmdConfig(pathOptions *PathOptions, out io.Writer) *cobra.Command { | ||||
| 	if len(pathOptions.ExplicitFileFlag) == 0 { | ||||
| 		pathOptions.ExplicitFileFlag = clientcmd.RecommendedConfigPathFlag | ||||
| 	} | ||||
| 	if len(pathOptions.EnvVar) > 0 { | ||||
| 		pathOptions.EnvVarFile = os.Getenv(pathOptions.EnvVar) | ||||
| 	} | ||||
|  | ||||
| 	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"`, | ||||
| 		Long: `config modifies kubeconfig files using subcommands like "kubectl config set current-context my-context" | ||||
|  | ||||
| The loading order follows these rules: | ||||
|     1. If the --` + pathOptions.ExplicitFileFlag + ` flag is set, then only that file is loaded.  The flag may only be set once and no merging takes place. | ||||
|     2. If $` + pathOptions.EnvVar + ` environment variable is set, then it is used a list of paths (normal path delimitting rules for your system).  These paths are merged together.  When a value is modified, it is modified in the file that defines the stanza.  When a value is created, it is created in the first file that exists.  If no files in the chain exist, then it creates the last file in the list. | ||||
|     3. Otherwise, ` + path.Join("${HOME}", pathOptions.GlobalFileSubpath) + ` is used and no merging takes place. | ||||
| `, | ||||
| 		Run: func(cmd *cobra.Command, args []string) { | ||||
| 			cmd.Help() | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	// file paths are common to all sub commands | ||||
| 	cmd.PersistentFlags().BoolVar(&pathOptions.Local, "local", pathOptions.Local, "use the kubeconfig in the current directory") | ||||
| 	cmd.PersistentFlags().BoolVar(&pathOptions.Global, "global", pathOptions.Global, "use the kubeconfig from "+pathOptions.GlobalFile) | ||||
| 	cmd.PersistentFlags().BoolVar(&pathOptions.UseEnvVar, "envvar", pathOptions.UseEnvVar, "use the kubeconfig from $"+pathOptions.EnvVar) | ||||
| 	cmd.PersistentFlags().StringVar(&pathOptions.LoadingRules.ExplicitPath, pathOptions.ExplicitFileFlag, pathOptions.LoadingRules.ExplicitPath, "use a particular kubeconfig file") | ||||
|  | ||||
| 	cmd.AddCommand(NewCmdConfigView(out, pathOptions)) | ||||
| @@ -83,13 +96,12 @@ func NewCmdConfig(pathOptions *PathOptions, out io.Writer) *cobra.Command { | ||||
|  | ||||
| func NewDefaultPathOptions() *PathOptions { | ||||
| 	ret := &PathOptions{ | ||||
| 		LocalFile:  ".kubeconfig", | ||||
| 		GlobalFile: path.Join(os.Getenv("HOME"), "/.kube/.kubeconfig"), | ||||
| 		EnvVar:     clientcmd.RecommendedConfigPathEnvVar, | ||||
| 		EnvVarFile: os.Getenv(clientcmd.RecommendedConfigPathEnvVar), | ||||
|  | ||||
| 		GlobalFile:       clientcmd.RecommendedHomeFile, | ||||
| 		EnvVar:           clientcmd.RecommendedConfigPathEnvVar, | ||||
| 		ExplicitFileFlag: clientcmd.RecommendedConfigPathFlag, | ||||
|  | ||||
| 		GlobalFileSubpath: clientcmd.RecommendedHomeFileName, | ||||
|  | ||||
| 		LoadingRules: clientcmd.NewDefaultClientConfigLoadingRules(), | ||||
| 	} | ||||
| 	ret.LoadingRules.DoNotResolvePaths = true | ||||
| @@ -97,125 +109,78 @@ func NewDefaultPathOptions() *PathOptions { | ||||
| 	return ret | ||||
| } | ||||
|  | ||||
| func (o PathOptions) Validate() error { | ||||
| 	if len(o.LoadingRules.ExplicitPath) > 0 { | ||||
| 		if o.Global { | ||||
| 			return errors.New("cannot specify both --" + o.ExplicitFileFlag + " and --global") | ||||
| 		} | ||||
| 		if o.Local { | ||||
| 			return errors.New("cannot specify both --" + o.ExplicitFileFlag + " and --local") | ||||
| 		} | ||||
| 		if o.UseEnvVar { | ||||
| 			return errors.New("cannot specify both --" + o.ExplicitFileFlag + " and --envvar") | ||||
| 		} | ||||
| func (o *PathOptions) GetEnvVarFiles() []string { | ||||
| 	if len(o.EnvVar) == 0 { | ||||
| 		return []string{} | ||||
| 	} | ||||
|  | ||||
| 	if o.Global { | ||||
| 		if o.Local { | ||||
| 			return errors.New("cannot specify both --global and --local") | ||||
| 		} | ||||
| 		if o.UseEnvVar { | ||||
| 			return errors.New("cannot specify both --global and --envvar") | ||||
| 		} | ||||
| 	envVarValue := os.Getenv(o.EnvVar) | ||||
| 	if len(envVarValue) == 0 { | ||||
| 		return []string{} | ||||
| 	} | ||||
|  | ||||
| 	if o.Local { | ||||
| 		if o.UseEnvVar { | ||||
| 			return errors.New("cannot specify both --local and --envvar") | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if o.UseEnvVar { | ||||
| 		if len(o.EnvVarFile) == 0 { | ||||
| 			return fmt.Errorf("environment variable %v does not have a value", o.EnvVar) | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| 	return filepath.SplitList(envVarValue) | ||||
| } | ||||
|  | ||||
| func (o *PathOptions) getStartingConfig() (*clientcmdapi.Config, error) { | ||||
| 	if err := o.Validate(); err != nil { | ||||
| func (o *PathOptions) GetLoadingPrecedence() []string { | ||||
| 	if envVarFiles := o.GetEnvVarFiles(); len(envVarFiles) > 0 { | ||||
| 		return envVarFiles | ||||
| 	} | ||||
|  | ||||
| 	return []string{o.GlobalFile} | ||||
| } | ||||
|  | ||||
| func (o *PathOptions) GetStartingConfig() (*clientcmdapi.Config, error) { | ||||
| 	// don't mutate the original | ||||
| 	loadingRules := *o.LoadingRules | ||||
| 	loadingRules.Precedence = o.GetLoadingPrecedence() | ||||
|  | ||||
| 	clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(&loadingRules, &clientcmd.ConfigOverrides{}) | ||||
| 	rawConfig, err := clientConfig.RawConfig() | ||||
| 	if os.IsNotExist(err) { | ||||
| 		return clientcmdapi.NewConfig(), nil | ||||
| 	} | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	config := clientcmdapi.NewConfig() | ||||
|  | ||||
| 	switch { | ||||
| 	case o.Global: | ||||
| 		config = getConfigFromFileOrDie(o.GlobalFile) | ||||
|  | ||||
| 	case o.UseEnvVar: | ||||
| 		config = getConfigFromFileOrDie(o.EnvVarFile) | ||||
|  | ||||
| 	case o.Local: | ||||
| 		config = getConfigFromFileOrDie(o.LocalFile) | ||||
|  | ||||
| 		// no specific flag was set, load according to the loading rules | ||||
| 	default: | ||||
| 		clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(o.LoadingRules, &clientcmd.ConfigOverrides{}) | ||||
| 		rawConfig, err := clientConfig.RawConfig() | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		config = &rawConfig | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	return config, nil | ||||
| 	return &rawConfig, nil | ||||
| } | ||||
|  | ||||
| // GetDefaultFilename returns the name of the file you should write into (create if necessary), if you're trying to create | ||||
| // a new stanza as opposed to updating an existing one. | ||||
| func (o *PathOptions) GetDefaultFilename() string { | ||||
| 	if o.IsExplicitFile() { | ||||
| 		return o.GetExplicitFile() | ||||
| 	} | ||||
|  | ||||
| 	if len(o.EnvVarFile) > 0 { | ||||
| 		return o.EnvVarFile | ||||
| 	} | ||||
| 	if envVarFiles := o.GetEnvVarFiles(); len(envVarFiles) > 0 { | ||||
| 		if len(envVarFiles) == 1 { | ||||
| 			return envVarFiles[0] | ||||
| 		} | ||||
|  | ||||
| 	if _, err := os.Stat(o.LocalFile); err == nil { | ||||
| 		return o.LocalFile | ||||
| 		// if any of the envvar files already exists, return it | ||||
| 		for _, envVarFile := range envVarFiles { | ||||
| 			if _, err := os.Stat(envVarFile); err == nil { | ||||
| 				return envVarFile | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		// otherwise, return the last one in the list | ||||
| 		return envVarFiles[len(envVarFiles)-1] | ||||
| 	} | ||||
|  | ||||
| 	return o.GlobalFile | ||||
|  | ||||
| } | ||||
|  | ||||
| func (o *PathOptions) IsExplicitFile() bool { | ||||
| 	switch { | ||||
| 	case len(o.LoadingRules.ExplicitPath) > 0 || | ||||
| 		o.Global || | ||||
| 		o.UseEnvVar || | ||||
| 		o.Local: | ||||
| 	if len(o.LoadingRules.ExplicitPath) > 0 { | ||||
| 		return true | ||||
| 	} | ||||
|  | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| func (o *PathOptions) GetExplicitFile() string { | ||||
| 	if !o.IsExplicitFile() { | ||||
| 		return "" | ||||
| 	} | ||||
|  | ||||
| 	switch { | ||||
| 	case len(o.LoadingRules.ExplicitPath) > 0: | ||||
| 		return o.LoadingRules.ExplicitPath | ||||
|  | ||||
| 	case o.Global: | ||||
| 		return o.GlobalFile | ||||
|  | ||||
| 	case o.UseEnvVar: | ||||
| 		return o.EnvVarFile | ||||
|  | ||||
| 	case o.Local: | ||||
| 		return o.LocalFile | ||||
| 	} | ||||
|  | ||||
| 	return "" | ||||
| 	return o.LoadingRules.ExplicitPath | ||||
| } | ||||
|  | ||||
| // ModifyConfig takes a Config object, iterates through Clusters, AuthInfos, and Contexts, uses the LocationOfOrigin if specified or | ||||
| @@ -223,8 +188,8 @@ func (o *PathOptions) GetExplicitFile() string { | ||||
| // Preferences and CurrentContext should always be set in the default destination file.  Since we can't distinguish between empty and missing values | ||||
| // (no nil strings), we're forced have separate handling for them.  In all the currently known cases, newConfig should have, at most, one difference, | ||||
| // that means that this code will only write into a single file. | ||||
| func (o *PathOptions) ModifyConfig(newConfig clientcmdapi.Config) error { | ||||
| 	startingConfig, err := o.getStartingConfig() | ||||
| func ModifyConfig(configAccess ConfigAccess, newConfig clientcmdapi.Config) error { | ||||
| 	startingConfig, err := configAccess.GetStartingConfig() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| @@ -237,12 +202,12 @@ func (o *PathOptions) ModifyConfig(newConfig clientcmdapi.Config) error { | ||||
| 		// nothing to do | ||||
|  | ||||
| 	case startingConfig.CurrentContext != newConfig.CurrentContext: | ||||
| 		if err := o.writeCurrentContext(newConfig.CurrentContext); err != nil { | ||||
| 		if err := writeCurrentContext(configAccess, newConfig.CurrentContext); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 	case !reflect.DeepEqual(startingConfig.Preferences, newConfig.Preferences): | ||||
| 		if err := o.writePreferences(newConfig.Preferences); err != nil { | ||||
| 		if err := writePreferences(configAccess, newConfig.Preferences); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| @@ -253,7 +218,7 @@ func (o *PathOptions) ModifyConfig(newConfig clientcmdapi.Config) error { | ||||
| 			if !reflect.DeepEqual(cluster, startingCluster) || !exists { | ||||
| 				destinationFile := cluster.LocationOfOrigin | ||||
| 				if len(destinationFile) == 0 { | ||||
| 					destinationFile = o.GetDefaultFilename() | ||||
| 					destinationFile = configAccess.GetDefaultFilename() | ||||
| 				} | ||||
|  | ||||
| 				configToWrite := getConfigFromFileOrDie(destinationFile) | ||||
| @@ -270,7 +235,7 @@ func (o *PathOptions) ModifyConfig(newConfig clientcmdapi.Config) error { | ||||
| 			if !reflect.DeepEqual(context, startingContext) || !exists { | ||||
| 				destinationFile := context.LocationOfOrigin | ||||
| 				if len(destinationFile) == 0 { | ||||
| 					destinationFile = o.GetDefaultFilename() | ||||
| 					destinationFile = configAccess.GetDefaultFilename() | ||||
| 				} | ||||
|  | ||||
| 				configToWrite := getConfigFromFileOrDie(destinationFile) | ||||
| @@ -287,7 +252,7 @@ func (o *PathOptions) ModifyConfig(newConfig clientcmdapi.Config) error { | ||||
| 			if !reflect.DeepEqual(authInfo, startingAuthInfo) || !exists { | ||||
| 				destinationFile := authInfo.LocationOfOrigin | ||||
| 				if len(destinationFile) == 0 { | ||||
| 					destinationFile = o.GetDefaultFilename() | ||||
| 					destinationFile = configAccess.GetDefaultFilename() | ||||
| 				} | ||||
|  | ||||
| 				configToWrite := getConfigFromFileOrDie(destinationFile) | ||||
| @@ -303,7 +268,7 @@ func (o *PathOptions) ModifyConfig(newConfig clientcmdapi.Config) error { | ||||
| 			if _, exists := newConfig.Clusters[key]; !exists { | ||||
| 				destinationFile := cluster.LocationOfOrigin | ||||
| 				if len(destinationFile) == 0 { | ||||
| 					destinationFile = o.GetDefaultFilename() | ||||
| 					destinationFile = configAccess.GetDefaultFilename() | ||||
| 				} | ||||
|  | ||||
| 				configToWrite := getConfigFromFileOrDie(destinationFile) | ||||
| @@ -319,7 +284,7 @@ func (o *PathOptions) ModifyConfig(newConfig clientcmdapi.Config) error { | ||||
| 			if _, exists := newConfig.Contexts[key]; !exists { | ||||
| 				destinationFile := context.LocationOfOrigin | ||||
| 				if len(destinationFile) == 0 { | ||||
| 					destinationFile = o.GetDefaultFilename() | ||||
| 					destinationFile = configAccess.GetDefaultFilename() | ||||
| 				} | ||||
|  | ||||
| 				configToWrite := getConfigFromFileOrDie(destinationFile) | ||||
| @@ -335,7 +300,7 @@ func (o *PathOptions) ModifyConfig(newConfig clientcmdapi.Config) error { | ||||
| 			if _, exists := newConfig.AuthInfos[key]; !exists { | ||||
| 				destinationFile := authInfo.LocationOfOrigin | ||||
| 				if len(destinationFile) == 0 { | ||||
| 					destinationFile = o.GetDefaultFilename() | ||||
| 					destinationFile = configAccess.GetDefaultFilename() | ||||
| 				} | ||||
|  | ||||
| 				configToWrite := getConfigFromFileOrDie(destinationFile) | ||||
| @@ -356,15 +321,26 @@ func (o *PathOptions) ModifyConfig(newConfig clientcmdapi.Config) error { | ||||
| // If newCurrentContext is the same as the startingConfig's current context, then we exit. | ||||
| // If newCurrentContext has a value, then that value is written into the default destination file. | ||||
| // If newCurrentContext is empty, then we find the config file that is setting the CurrentContext and clear the value from that file | ||||
| func (o *PathOptions) writeCurrentContext(newCurrentContext string) error { | ||||
| 	if startingConfig, err := o.getStartingConfig(); err != nil { | ||||
| func writeCurrentContext(configAccess ConfigAccess, newCurrentContext string) error { | ||||
| 	if startingConfig, err := configAccess.GetStartingConfig(); err != nil { | ||||
| 		return err | ||||
| 	} else if startingConfig.CurrentContext == newCurrentContext { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	if configAccess.IsExplicitFile() { | ||||
| 		file := configAccess.GetExplicitFile() | ||||
| 		currConfig := getConfigFromFileOrDie(file) | ||||
| 		currConfig.CurrentContext = newCurrentContext | ||||
| 		if err := clientcmd.WriteToFile(*currConfig, file); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	if len(newCurrentContext) > 0 { | ||||
| 		destinationFile := o.GetDefaultFilename() | ||||
| 		destinationFile := configAccess.GetDefaultFilename() | ||||
| 		config := getConfigFromFileOrDie(destinationFile) | ||||
| 		config.CurrentContext = newCurrentContext | ||||
|  | ||||
| @@ -375,46 +351,34 @@ func (o *PathOptions) writeCurrentContext(newCurrentContext string) error { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	if o.IsExplicitFile() { | ||||
| 		file := o.GetExplicitFile() | ||||
| 		currConfig := getConfigFromFileOrDie(file) | ||||
| 		currConfig.CurrentContext = newCurrentContext | ||||
| 		if err := clientcmd.WriteToFile(*currConfig, file); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	// we're supposed to be clearing the current context.  We need to find the first spot in the chain that is setting it and clear it | ||||
| 	for _, file := range configAccess.GetLoadingPrecedence() { | ||||
| 		if _, err := os.Stat(file); err == nil { | ||||
| 			currConfig := getConfigFromFileOrDie(file) | ||||
|  | ||||
| 		return nil | ||||
| 	} | ||||
| 			if len(currConfig.CurrentContext) > 0 { | ||||
| 				currConfig.CurrentContext = newCurrentContext | ||||
| 				if err := clientcmd.WriteToFile(*currConfig, file); err != nil { | ||||
| 					return err | ||||
| 				} | ||||
|  | ||||
| 	filesToCheck := make([]string, 0, len(o.LoadingRules.Precedence)+1) | ||||
| 	filesToCheck = append(filesToCheck, o.LoadingRules.ExplicitPath) | ||||
| 	filesToCheck = append(filesToCheck, o.LoadingRules.Precedence...) | ||||
|  | ||||
| 	for _, file := range filesToCheck { | ||||
| 		currConfig := getConfigFromFileOrDie(file) | ||||
|  | ||||
| 		if len(currConfig.CurrentContext) > 0 { | ||||
| 			currConfig.CurrentContext = newCurrentContext | ||||
| 			if err := clientcmd.WriteToFile(*currConfig, file); err != nil { | ||||
| 				return err | ||||
| 				return nil | ||||
| 			} | ||||
|  | ||||
| 			return nil | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| 	return errors.New("no config found to write context") | ||||
| } | ||||
|  | ||||
| func (o *PathOptions) writePreferences(newPrefs clientcmdapi.Preferences) error { | ||||
| 	if startingConfig, err := o.getStartingConfig(); err != nil { | ||||
| func writePreferences(configAccess ConfigAccess, newPrefs clientcmdapi.Preferences) error { | ||||
| 	if startingConfig, err := configAccess.GetStartingConfig(); err != nil { | ||||
| 		return err | ||||
| 	} else if reflect.DeepEqual(startingConfig.Preferences, newPrefs) { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	if o.IsExplicitFile() { | ||||
| 		file := o.GetExplicitFile() | ||||
| 	if configAccess.IsExplicitFile() { | ||||
| 		file := configAccess.GetExplicitFile() | ||||
| 		currConfig := getConfigFromFileOrDie(file) | ||||
| 		currConfig.Preferences = newPrefs | ||||
| 		if err := clientcmd.WriteToFile(*currConfig, file); err != nil { | ||||
| @@ -424,11 +388,7 @@ func (o *PathOptions) writePreferences(newPrefs clientcmdapi.Preferences) error | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	filesToCheck := make([]string, 0, len(o.LoadingRules.Precedence)+1) | ||||
| 	filesToCheck = append(filesToCheck, o.LoadingRules.ExplicitPath) | ||||
| 	filesToCheck = append(filesToCheck, o.LoadingRules.Precedence...) | ||||
|  | ||||
| 	for _, file := range filesToCheck { | ||||
| 	for _, file := range configAccess.GetLoadingPrecedence() { | ||||
| 		currConfig := getConfigFromFileOrDie(file) | ||||
|  | ||||
| 		if !reflect.DeepEqual(currConfig.Preferences, newPrefs) { | ||||
| @@ -441,7 +401,7 @@ func (o *PathOptions) writePreferences(newPrefs clientcmdapi.Preferences) error | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| 	return errors.New("no config found to write preferences") | ||||
| } | ||||
|  | ||||
| // 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 | ||||
|   | ||||
| @@ -31,7 +31,7 @@ import ( | ||||
| ) | ||||
|  | ||||
| type createAuthInfoOptions struct { | ||||
| 	pathOptions       *PathOptions | ||||
| 	configAccess      ConfigAccess | ||||
| 	name              string | ||||
| 	authPath          util.StringFlag | ||||
| 	clientCertificate util.StringFlag | ||||
| @@ -67,8 +67,8 @@ $ kubectl set-credentials cluster-admin --username=admin --password=uXFGweU9l35q | ||||
| // Embed client certificate data in the "cluster-admin" entry | ||||
| $ kubectl set-credentials cluster-admin --client-certificate=~/.kube/admin.crt --embed-certs=true` | ||||
|  | ||||
| func NewCmdConfigSetAuthInfo(out io.Writer, pathOptions *PathOptions) *cobra.Command { | ||||
| 	options := &createAuthInfoOptions{pathOptions: pathOptions} | ||||
| func NewCmdConfigSetAuthInfo(out io.Writer, configAccess ConfigAccess) *cobra.Command { | ||||
| 	options := &createAuthInfoOptions{configAccess: configAccess} | ||||
|  | ||||
| 	cmd := &cobra.Command{ | ||||
| 		Use:     fmt.Sprintf("set-credentials NAME [--%v=/path/to/authfile] [--%v=path/to/certfile] [--%v=path/to/keyfile] [--%v=bearer_token] [--%v=basic_user] [--%v=basic_password]", clientcmd.FlagAuthPath, clientcmd.FlagCertFile, clientcmd.FlagKeyFile, clientcmd.FlagBearerToken, clientcmd.FlagUsername, clientcmd.FlagPassword), | ||||
| @@ -104,7 +104,7 @@ func (o createAuthInfoOptions) run() error { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	config, err := o.pathOptions.getStartingConfig() | ||||
| 	config, err := o.configAccess.GetStartingConfig() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| @@ -112,7 +112,7 @@ func (o createAuthInfoOptions) run() error { | ||||
| 	authInfo := o.modifyAuthInfo(config.AuthInfos[o.name]) | ||||
| 	config.AuthInfos[o.name] = authInfo | ||||
|  | ||||
| 	if err := o.pathOptions.ModifyConfig(*config); err != nil { | ||||
| 	if err := ModifyConfig(o.configAccess, *config); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| @@ -225,5 +225,5 @@ func (o createAuthInfoOptions) validate() error { | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return o.pathOptions.Validate() | ||||
| 	return nil | ||||
| } | ||||
|   | ||||
| @@ -30,7 +30,7 @@ import ( | ||||
| ) | ||||
|  | ||||
| type createClusterOptions struct { | ||||
| 	pathOptions           *PathOptions | ||||
| 	configAccess          ConfigAccess | ||||
| 	name                  string | ||||
| 	server                util.StringFlag | ||||
| 	apiVersion            util.StringFlag | ||||
| @@ -52,8 +52,8 @@ $ kubectl config set-cluster e2e --certificate-authority=~/.kube/e2e/kubernetes. | ||||
| $ kubectl config set-cluster e2e --insecure-skip-tls-verify=true` | ||||
| ) | ||||
|  | ||||
| func NewCmdConfigSetCluster(out io.Writer, pathOptions *PathOptions) *cobra.Command { | ||||
| 	options := &createClusterOptions{pathOptions: pathOptions} | ||||
| func NewCmdConfigSetCluster(out io.Writer, configAccess ConfigAccess) *cobra.Command { | ||||
| 	options := &createClusterOptions{configAccess: configAccess} | ||||
|  | ||||
| 	cmd := &cobra.Command{ | ||||
| 		Use:     fmt.Sprintf("set-cluster NAME [--%v=server] [--%v=path/to/certficate/authority] [--%v=apiversion] [--%v=true]", clientcmd.FlagAPIServer, clientcmd.FlagCAFile, clientcmd.FlagAPIVersion, clientcmd.FlagInsecure), | ||||
| @@ -89,7 +89,7 @@ func (o createClusterOptions) run() error { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	config, err := o.pathOptions.getStartingConfig() | ||||
| 	config, err := o.configAccess.GetStartingConfig() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| @@ -97,7 +97,7 @@ func (o createClusterOptions) run() error { | ||||
| 	cluster := o.modifyCluster(config.Clusters[o.name]) | ||||
| 	config.Clusters[o.name] = cluster | ||||
|  | ||||
| 	if err := o.pathOptions.ModifyConfig(*config); err != nil { | ||||
| 	if err := ModifyConfig(o.configAccess, *config); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| @@ -169,5 +169,5 @@ func (o createClusterOptions) validate() error { | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return o.pathOptions.Validate() | ||||
| 	return nil | ||||
| } | ||||
|   | ||||
| @@ -29,11 +29,11 @@ import ( | ||||
| ) | ||||
|  | ||||
| type createContextOptions struct { | ||||
| 	pathOptions *PathOptions | ||||
| 	name        string | ||||
| 	cluster     util.StringFlag | ||||
| 	authInfo    util.StringFlag | ||||
| 	namespace   util.StringFlag | ||||
| 	configAccess ConfigAccess | ||||
| 	name         string | ||||
| 	cluster      util.StringFlag | ||||
| 	authInfo     util.StringFlag | ||||
| 	namespace    util.StringFlag | ||||
| } | ||||
|  | ||||
| const ( | ||||
| @@ -43,8 +43,8 @@ Specifying a name that already exists will merge new fields on top of existing v | ||||
| $ kubectl config set-context gce --user=cluster-admin` | ||||
| ) | ||||
|  | ||||
| func NewCmdConfigSetContext(out io.Writer, pathOptions *PathOptions) *cobra.Command { | ||||
| 	options := &createContextOptions{pathOptions: pathOptions} | ||||
| func NewCmdConfigSetContext(out io.Writer, configAccess ConfigAccess) *cobra.Command { | ||||
| 	options := &createContextOptions{configAccess: configAccess} | ||||
|  | ||||
| 	cmd := &cobra.Command{ | ||||
| 		Use:     fmt.Sprintf("set-context NAME [--%v=cluster_nickname] [--%v=user_nickname] [--%v=namespace]", clientcmd.FlagClusterName, clientcmd.FlagAuthInfoName, clientcmd.FlagNamespace), | ||||
| @@ -76,7 +76,7 @@ func (o createContextOptions) run() error { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	config, err := o.pathOptions.getStartingConfig() | ||||
| 	config, err := o.configAccess.GetStartingConfig() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| @@ -84,7 +84,7 @@ func (o createContextOptions) run() error { | ||||
| 	context := o.modifyContext(config.Contexts[o.name]) | ||||
| 	config.Contexts[o.name] = context | ||||
|  | ||||
| 	if err := o.pathOptions.ModifyConfig(*config); err != nil { | ||||
| 	if err := ModifyConfig(o.configAccess, *config); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| @@ -123,5 +123,5 @@ func (o createContextOptions) validate() error { | ||||
| 		return errors.New("You must specify a non-empty context name") | ||||
| 	} | ||||
|  | ||||
| 	return o.pathOptions.Validate() | ||||
| 	return nil | ||||
| } | ||||
|   | ||||
| @@ -32,7 +32,7 @@ const ( | ||||
| ) | ||||
|  | ||||
| type setOptions struct { | ||||
| 	pathOptions   *PathOptions | ||||
| 	configAccess  ConfigAccess | ||||
| 	propertyName  string | ||||
| 	propertyValue string | ||||
| } | ||||
| @@ -41,8 +41,8 @@ const set_long = `Sets an individual value in a kubeconfig file | ||||
| PROPERTY_NAME is a dot delimited 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.` | ||||
|  | ||||
| func NewCmdConfigSet(out io.Writer, pathOptions *PathOptions) *cobra.Command { | ||||
| 	options := &setOptions{pathOptions: pathOptions} | ||||
| func NewCmdConfigSet(out io.Writer, configAccess ConfigAccess) *cobra.Command { | ||||
| 	options := &setOptions{configAccess: configAccess} | ||||
|  | ||||
| 	cmd := &cobra.Command{ | ||||
| 		Use:   "set PROPERTY_NAME PROPERTY_VALUE", | ||||
| @@ -69,7 +69,7 @@ func (o setOptions) run() error { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	config, err := o.pathOptions.getStartingConfig() | ||||
| 	config, err := o.configAccess.GetStartingConfig() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| @@ -82,7 +82,7 @@ func (o setOptions) run() error { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if err := o.pathOptions.ModifyConfig(*config); err != nil { | ||||
| 	if err := ModifyConfig(o.configAccess, *config); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| @@ -110,7 +110,7 @@ func (o setOptions) validate() error { | ||||
| 		return errors.New("You must specify a property") | ||||
| 	} | ||||
|  | ||||
| 	return o.pathOptions.Validate() | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func modifyConfig(curr reflect.Value, steps *navigationSteps, propertyValue string, unset bool) error { | ||||
|   | ||||
| @@ -26,15 +26,15 @@ import ( | ||||
| ) | ||||
|  | ||||
| type unsetOptions struct { | ||||
| 	pathOptions  *PathOptions | ||||
| 	configAccess ConfigAccess | ||||
| 	propertyName string | ||||
| } | ||||
|  | ||||
| const unset_long = `Unsets an individual value in a kubeconfig file | ||||
| PROPERTY_NAME is a dot delimited name where each token represents either a attribute name or a map key.  Map keys may not contain dots.` | ||||
|  | ||||
| func NewCmdConfigUnset(out io.Writer, pathOptions *PathOptions) *cobra.Command { | ||||
| 	options := &unsetOptions{pathOptions: pathOptions} | ||||
| func NewCmdConfigUnset(out io.Writer, configAccess ConfigAccess) *cobra.Command { | ||||
| 	options := &unsetOptions{configAccess: configAccess} | ||||
|  | ||||
| 	cmd := &cobra.Command{ | ||||
| 		Use:   "unset PROPERTY_NAME", | ||||
| @@ -61,7 +61,7 @@ func (o unsetOptions) run() error { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	config, err := o.pathOptions.getStartingConfig() | ||||
| 	config, err := o.configAccess.GetStartingConfig() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| @@ -75,7 +75,7 @@ func (o unsetOptions) run() error { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if err := o.pathOptions.ModifyConfig(*config); err != nil { | ||||
| 	if err := ModifyConfig(o.configAccess, *config); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| @@ -98,5 +98,5 @@ func (o unsetOptions) validate() error { | ||||
| 		return errors.New("You must specify a property") | ||||
| 	} | ||||
|  | ||||
| 	return o.pathOptions.Validate() | ||||
| 	return nil | ||||
| } | ||||
|   | ||||
| @@ -25,12 +25,12 @@ import ( | ||||
| ) | ||||
|  | ||||
| type useContextOptions struct { | ||||
| 	pathOptions *PathOptions | ||||
| 	contextName string | ||||
| 	configAccess ConfigAccess | ||||
| 	contextName  string | ||||
| } | ||||
|  | ||||
| func NewCmdConfigUseContext(out io.Writer, pathOptions *PathOptions) *cobra.Command { | ||||
| 	options := &useContextOptions{pathOptions: pathOptions} | ||||
| func NewCmdConfigUseContext(out io.Writer, configAccess ConfigAccess) *cobra.Command { | ||||
| 	options := &useContextOptions{configAccess: configAccess} | ||||
|  | ||||
| 	cmd := &cobra.Command{ | ||||
| 		Use:   "use-context CONTEXT_NAME", | ||||
| @@ -57,14 +57,14 @@ func (o useContextOptions) run() error { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	config, err := o.pathOptions.getStartingConfig() | ||||
| 	config, err := o.configAccess.GetStartingConfig() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	config.CurrentContext = o.contextName | ||||
|  | ||||
| 	if err := o.pathOptions.ModifyConfig(*config); err != nil { | ||||
| 	if err := ModifyConfig(o.configAccess, *config); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| @@ -87,5 +87,5 @@ func (o useContextOptions) validate() error { | ||||
| 		return errors.New("You must specify a current-context") | ||||
| 	} | ||||
|  | ||||
| 	return o.pathOptions.Validate() | ||||
| 	return nil | ||||
| } | ||||
|   | ||||
| @@ -32,10 +32,10 @@ import ( | ||||
| ) | ||||
|  | ||||
| type ViewOptions struct { | ||||
| 	PathOptions *PathOptions | ||||
| 	Merge       util.BoolFlag | ||||
| 	Flatten     bool | ||||
| 	Minify      bool | ||||
| 	ConfigAccess ConfigAccess | ||||
| 	Merge        util.BoolFlag | ||||
| 	Flatten      bool | ||||
| 	Minify       bool | ||||
| } | ||||
|  | ||||
| const ( | ||||
| @@ -52,8 +52,8 @@ $ kubectl config view --local | ||||
| $ kubectl config view -o template --template='{{range .users}}{{ if eq .name "e2e" }}{{ index .user.password }}{{end}}{{end}}'` | ||||
| ) | ||||
|  | ||||
| func NewCmdConfigView(out io.Writer, PathOptions *PathOptions) *cobra.Command { | ||||
| 	options := &ViewOptions{PathOptions: PathOptions} | ||||
| func NewCmdConfigView(out io.Writer, ConfigAccess ConfigAccess) *cobra.Command { | ||||
| 	options := &ViewOptions{ConfigAccess: ConfigAccess} | ||||
|  | ||||
| 	cmd := &cobra.Command{ | ||||
| 		Use:     "view", | ||||
| @@ -116,7 +116,7 @@ func (o ViewOptions) Run(out io.Writer, printer kubectl.ResourcePrinter) error { | ||||
|  | ||||
| func (o *ViewOptions) Complete() bool { | ||||
| 	// if --kubeconfig, --global, or --local is specified, then merging doesn't make sense since you're declaring precise intent | ||||
| 	if o.PathOptions.Global || o.PathOptions.Local || o.PathOptions.UseEnvVar { | ||||
| 	if o.ConfigAccess.IsExplicitFile() { | ||||
| 		if !o.Merge.Provided() { | ||||
| 			o.Merge.Set("false") | ||||
| 		} | ||||
| @@ -136,32 +136,20 @@ func (o ViewOptions) loadConfig() (*clientcmdapi.Config, error) { | ||||
| } | ||||
|  | ||||
| func (o ViewOptions) Validate() error { | ||||
| 	return o.PathOptions.Validate() | ||||
| 	if !o.Merge.Value() && !o.ConfigAccess.IsExplicitFile() { | ||||
| 		return errors.New("if merge==false a precise file must to specified") | ||||
| 	} | ||||
|  | ||||
| 	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, error) { | ||||
| 	switch { | ||||
| 	case !o.Merge.Value(): | ||||
| 		switch { | ||||
| 		case len(o.PathOptions.LoadingRules.ExplicitPath) > 0: | ||||
| 			return clientcmd.LoadFromFile(o.PathOptions.LoadingRules.ExplicitPath) | ||||
|  | ||||
| 		case o.PathOptions.Global: | ||||
| 			return clientcmd.LoadFromFile(o.PathOptions.GlobalFile) | ||||
|  | ||||
| 		case o.PathOptions.UseEnvVar: | ||||
| 			return clientcmd.LoadFromFile(o.PathOptions.EnvVarFile) | ||||
|  | ||||
| 		case o.PathOptions.Local: | ||||
| 			return clientcmd.LoadFromFile(o.PathOptions.LocalFile) | ||||
|  | ||||
| 		default: | ||||
| 			return nil, errors.New("if Merge==false a precise file must to specified") | ||||
|  | ||||
| 		} | ||||
| 		return clientcmd.LoadFromFile(o.ConfigAccess.GetExplicitFile()) | ||||
|  | ||||
| 	default: | ||||
| 		return o.PathOptions.getStartingConfig() | ||||
| 		return o.ConfigAccess.GetStartingConfig() | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -286,11 +286,11 @@ func (c *clientSwaggerSchema) ValidateBytes(data []byte) error { | ||||
|  | ||||
| // DefaultClientConfig creates a clientcmd.ClientConfig with the following hierarchy: | ||||
| //   1.  Use the kubeconfig builder.  The number of merges and overrides here gets a little crazy.  Stay with me. | ||||
| //       1.  Merge together the kubeconfig itself.  This is done with the following hierarchy and merge rules: | ||||
| //           1.  CommandLineLocation - this parsed from the command line, so it must be late bound | ||||
| //           2.  EnvVarLocation | ||||
| //           3.  CurrentDirectoryLocation | ||||
| //           4.  HomeDirectoryLocation | ||||
| //       1.  Merge together the kubeconfig itself.  This is done with the following hierarchy rules: | ||||
| //           1.  CommandLineLocation - this parsed from the command line, so it must be late bound.  If you specify this, | ||||
| //               then no other kubeconfig files are merged.  This file must exist. | ||||
| //           2.  If $KUBECONFIG is set, then it is treated as a list of files that should be merged. | ||||
| //			 3.  HomeDirectoryLocation | ||||
| //           Empty filenames are ignored.  Files with non-deserializable content produced errors. | ||||
| //           The first file to set a particular value or map key wins and the value or map key is never changed. | ||||
| //           This means that the first file to set CurrentContext will have its context preserved.  It also means | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 deads2k
					deads2k