mirror of https://github.com/k3s-io/k3s
Merge pull request #6680 from deads2k/deads-change-kubeconfig-chain
change kubeconfig loading chainpull/6/head
commit
97e4549170
|
@ -776,12 +776,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=nginx --overrides='{ "apiVersion": "v1beta
|
|||
### 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,18 +94,26 @@ 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}
|
||||
} else {
|
||||
kubeConfigFiles = append(kubeConfigFiles, rules.Precedence...)
|
||||
|
||||
}
|
||||
|
||||
// first merge all of our maps
|
||||
mapConfig := clientcmdapi.NewConfig()
|
||||
for _, file := range kubeConfigFiles {
|
||||
|
@ -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 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"),
|
||||
GlobalFile: clientcmd.RecommendedHomeFile,
|
||||
EnvVar: clientcmd.RecommendedConfigPathEnvVar,
|
||||
EnvVarFile: os.Getenv(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")
|
||||
}
|
||||
return filepath.SplitList(envVarValue)
|
||||
}
|
||||
|
||||
if o.UseEnvVar {
|
||||
if len(o.EnvVarFile) == 0 {
|
||||
return fmt.Errorf("environment variable %v does not have a value", o.EnvVar)
|
||||
func (o *PathOptions) GetLoadingPrecedence() []string {
|
||||
if envVarFiles := o.GetEnvVarFiles(); len(envVarFiles) > 0 {
|
||||
return envVarFiles
|
||||
}
|
||||
|
||||
return []string{o.GlobalFile}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
func (o *PathOptions) GetStartingConfig() (*clientcmdapi.Config, error) {
|
||||
// don't mutate the original
|
||||
loadingRules := *o.LoadingRules
|
||||
loadingRules.Precedence = o.GetLoadingPrecedence()
|
||||
|
||||
func (o *PathOptions) getStartingConfig() (*clientcmdapi.Config, error) {
|
||||
if err := o.Validate(); 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{})
|
||||
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 = &rawConfig
|
||||
|
||||
return &rawConfig, nil
|
||||
}
|
||||
|
||||
return config, 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 ""
|
||||
}
|
||||
|
||||
// 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,22 +351,9 @@ 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
|
||||
}
|
||||
|
||||
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 {
|
||||
// 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)
|
||||
|
||||
if len(currConfig.CurrentContext) > 0 {
|
||||
|
@ -402,19 +365,20 @@ func (o *PathOptions) writeCurrentContext(newCurrentContext string) error {
|
|||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *PathOptions) writePreferences(newPrefs clientcmdapi.Preferences) error {
|
||||
if startingConfig, err := o.getStartingConfig(); err != nil {
|
||||
return errors.New("no config found to write context")
|
||||
}
|
||||
|
||||
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,7 +29,7 @@ import (
|
|||
)
|
||||
|
||||
type createContextOptions struct {
|
||||
pathOptions *PathOptions
|
||||
configAccess ConfigAccess
|
||||
name string
|
||||
cluster util.StringFlag
|
||||
authInfo util.StringFlag
|
||||
|
@ -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
|
||||
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,7 +32,7 @@ import (
|
|||
)
|
||||
|
||||
type ViewOptions struct {
|
||||
PathOptions *PathOptions
|
||||
ConfigAccess ConfigAccess
|
||||
Merge util.BoolFlag
|
||||
Flatten bool
|
||||
Minify bool
|
||||
|
@ -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)
|
||||
return clientcmd.LoadFromFile(o.ConfigAccess.GetExplicitFile())
|
||||
|
||||
default:
|
||||
return nil, errors.New("if Merge==false a precise file must to specified")
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue