diff --git a/docs/kubectl-config-set-cluster.md b/docs/kubectl-config-set-cluster.md index fde779c9af0..a13b4f01063 100644 --- a/docs/kubectl-config-set-cluster.md +++ b/docs/kubectl-config-set-cluster.md @@ -1,11 +1,11 @@ ## kubectl config set-cluster -Sets a cluster entry in .kubeconfig +Sets a cluster entry in kubeconfig ### Synopsis -Sets a cluster entry in .kubeconfig. +Sets a cluster entry in kubeconfig. Specifying a name that already exists will merge new fields on top of existing values for those fields. ``` @@ -28,12 +28,12 @@ $ kubectl config set-cluster e2e --insecure-skip-tls-verify=true ### Options ``` - --api-version=: api-version for the cluster entry in .kubeconfig - --certificate-authority=: path to certificate-authority for the cluster entry in .kubeconfig - --embed-certs=false: embed-certs for the cluster entry in .kubeconfig + --api-version=: api-version for the cluster entry in kubeconfig + --certificate-authority=: path to certificate-authority for the cluster entry in kubeconfig + --embed-certs=false: embed-certs for the cluster entry in kubeconfig -h, --help=false: help for set-cluster - --insecure-skip-tls-verify=false: insecure-skip-tls-verify for the cluster entry in .kubeconfig - --server=: server for the cluster entry in .kubeconfig + --insecure-skip-tls-verify=false: insecure-skip-tls-verify for the cluster entry in kubeconfig + --server=: server for the cluster entry in kubeconfig ``` ### Options inherrited from parent commands @@ -45,10 +45,10 @@ $ 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 - --kubeconfig="": use a particular .kubeconfig file - --local=false: use the .kubeconfig in the current directory + --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 diff --git a/docs/kubectl-config-set-context.md b/docs/kubectl-config-set-context.md index 7b271f4ec6c..b68df1e8c8a 100644 --- a/docs/kubectl-config-set-context.md +++ b/docs/kubectl-config-set-context.md @@ -1,11 +1,11 @@ ## kubectl config set-context -Sets a context entry in .kubeconfig +Sets a context entry in kubeconfig ### Synopsis -Sets a context entry in .kubeconfig +Sets a context entry in kubeconfig Specifying a name that already exists will merge new fields on top of existing values for those fields. ``` @@ -22,10 +22,10 @@ $ kubectl config set-context gce --user=cluster-admin ### Options ``` - --cluster=: cluster for the context entry in .kubeconfig + --cluster=: cluster for the context entry in kubeconfig -h, --help=false: help for set-context - --namespace=: namespace for the context entry in .kubeconfig - --user=: user for the context entry in .kubeconfig + --namespace=: namespace for the context entry in kubeconfig + --user=: user for the context entry in kubeconfig ``` ### Options inherrited from parent commands @@ -38,11 +38,11 @@ $ 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 + --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 + --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 diff --git a/docs/kubectl-config-set-credentials.md b/docs/kubectl-config-set-credentials.md index b316a976ce6..69aa859557d 100644 --- a/docs/kubectl-config-set-credentials.md +++ b/docs/kubectl-config-set-credentials.md @@ -1,11 +1,11 @@ ## kubectl config set-credentials -Sets a user entry in .kubeconfig +Sets a user entry in kubeconfig ### Synopsis -Sets a user entry in .kubeconfig +Sets a user entry in kubeconfig Specifying a name that already exists will merge new fields on top of existing values. Client-certificate flags: @@ -41,14 +41,14 @@ $ kubectl set-credentials cluster-admin --client-certificate=~/.kube/admin.crt - ### Options ``` - --auth-path=: auth-path for the user entry in .kubeconfig - --client-certificate=: path to client-certificate for the user entry in .kubeconfig - --client-key=: path to client-key for the user entry in .kubeconfig - --embed-certs=false: embed client cert/key for the user entry in .kubeconfig + --auth-path=: auth-path for the user entry in kubeconfig + --client-certificate=: path to client-certificate for the user entry in kubeconfig + --client-key=: path to client-key for the user entry in kubeconfig + --embed-certs=false: embed client cert/key for the user entry in kubeconfig -h, --help=false: help for set-credentials - --password=: password for the user entry in .kubeconfig - --token=: token for the user entry in .kubeconfig - --username=: username for the user entry in .kubeconfig + --password=: password for the user entry in kubeconfig + --token=: token for the user entry in kubeconfig + --username=: username for the user entry in kubeconfig ``` ### Options inherrited from parent commands @@ -59,11 +59,11 @@ $ 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 + --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 + --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 diff --git a/docs/kubectl-config-set.md b/docs/kubectl-config-set.md index 16e970735dc..9480ca9c35d 100644 --- a/docs/kubectl-config-set.md +++ b/docs/kubectl-config-set.md @@ -1,11 +1,11 @@ ## kubectl config set -Sets an individual value in a .kubeconfig file +Sets an individual value in a kubeconfig file ### Synopsis -Sets an individual value in a .kubeconfig file +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. @@ -30,11 +30,11 @@ 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 + --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 + --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 diff --git a/docs/kubectl-config-unset.md b/docs/kubectl-config-unset.md index ca6f75e2f50..cbf5bddae8e 100644 --- a/docs/kubectl-config-unset.md +++ b/docs/kubectl-config-unset.md @@ -1,11 +1,11 @@ ## kubectl config unset -Unsets an individual value in a .kubeconfig file +Unsets an individual value in a kubeconfig file ### Synopsis -Unsets an individual value in a .kubeconfig file +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. ``` @@ -29,11 +29,11 @@ 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 + --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 + --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 diff --git a/docs/kubectl-config-use-context.md b/docs/kubectl-config-use-context.md index ee24b9c650e..af49950653c 100644 --- a/docs/kubectl-config-use-context.md +++ b/docs/kubectl-config-use-context.md @@ -1,11 +1,11 @@ ## kubectl config use-context -Sets the current-context in a .kubeconfig file +Sets the current-context in a kubeconfig file ### Synopsis -Sets the current-context in a .kubeconfig file +Sets the current-context in a kubeconfig file ``` kubectl config use-context CONTEXT_NAME @@ -28,11 +28,11 @@ 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 + --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 + --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 diff --git a/docs/kubectl-config-view.md b/docs/kubectl-config-view.md index 784220bd815..1db8654ec34 100644 --- a/docs/kubectl-config-view.md +++ b/docs/kubectl-config-view.md @@ -1,11 +1,11 @@ ## kubectl config view -displays merged .kubeconfig settings or a specified .kubeconfig file. +displays merged kubeconfig settings or a specified kubeconfig file. ### Synopsis -displays merged .kubeconfig settings or a specified .kubeconfig file. +displays merged kubeconfig settings or a specified kubeconfig file. You can use --output=template --template=TEMPLATE to extract specific values. @@ -16,10 +16,10 @@ kubectl config view ### Examples ``` -// Show merged .kubeconfig settings. +// Show merged kubeconfig settings. $ kubectl config view -// Show only local ./.kubeconfig settings +// Show only local kubeconfig settings $ kubectl config view --local // Get the password for the e2e user @@ -30,7 +30,7 @@ $ kubectl config view -o template --template='{{range .users}}{{ if eq .name "e2 ``` -h, --help=false: help for view - --merge=true: merge together the full hierarchy of .kubeconfig files + --merge=true: merge together the full hierarchy of kubeconfig files --no-headers=false: When using the default output, don't print headers. -o, --output="": Output format. One of: json|yaml|template|templatefile. --output-version="": Output the formatted object with the given version (default api-version). @@ -48,11 +48,11 @@ $ 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 + --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 + --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 diff --git a/docs/kubectl-config.md b/docs/kubectl-config.md index b5e9fdb4aec..3df9970f13d 100644 --- a/docs/kubectl-config.md +++ b/docs/kubectl-config.md @@ -1,11 +1,11 @@ ## kubectl config -config modifies .kubeconfig files +config modifies kubeconfig files ### Synopsis -config modifies .kubeconfig files using subcommands like "kubectl config set current-context my-context" +config modifies kubeconfig files using subcommands like "kubectl config set current-context my-context" ``` kubectl config SUBCOMMAND @@ -14,11 +14,11 @@ kubectl config SUBCOMMAND ### Options ``` - --envvar=false: use the .kubeconfig from $KUBECONFIG - --global=false: use the .kubeconfig from /home/username + --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 + --kubeconfig="": use a particular kubeconfig file + --local=false: use the kubeconfig in the current directory ``` ### Options inherrited from parent commands diff --git a/docs/man/man1/kubectl-config-set-cluster.1 b/docs/man/man1/kubectl-config-set-cluster.1 index 7eec68fd944..ccaf234dcec 100644 --- a/docs/man/man1/kubectl-config-set-cluster.1 +++ b/docs/man/man1/kubectl-config-set-cluster.1 @@ -3,7 +3,7 @@ .SH NAME .PP -kubectl config set\-cluster \- Sets a cluster entry in .kubeconfig +kubectl config set\-cluster \- Sets a cluster entry in kubeconfig .SH SYNOPSIS @@ -13,22 +13,22 @@ kubectl config set\-cluster \- Sets a cluster entry in .kubeconfig .SH DESCRIPTION .PP -Sets a cluster entry in .kubeconfig. +Sets a cluster entry in kubeconfig. Specifying a name that already exists will merge new fields on top of existing values for those fields. .SH OPTIONS .PP \fB\-\-api\-version\fP="" - api\-version for the cluster entry in .kubeconfig + api\-version for the cluster entry in kubeconfig .PP \fB\-\-certificate\-authority\fP="" - path to certificate\-authority for the cluster entry in .kubeconfig + path to certificate\-authority for the cluster entry in kubeconfig .PP \fB\-\-embed\-certs\fP=false - embed\-certs for the cluster entry in .kubeconfig + embed\-certs for the cluster entry in kubeconfig .PP \fB\-h\fP, \fB\-\-help\fP=false @@ -36,11 +36,11 @@ Specifying a name that already exists will merge new fields on top of existing v .PP \fB\-\-insecure\-skip\-tls\-verify\fP=false - insecure\-skip\-tls\-verify for the cluster entry in .kubeconfig + insecure\-skip\-tls\-verify for the cluster entry in kubeconfig .PP \fB\-\-server\fP="" - server for the cluster entry in .kubeconfig + server for the cluster entry in kubeconfig .SH OPTIONS INHERITED FROM PARENT COMMANDS @@ -70,19 +70,19 @@ Specifying a name that already exists will merge new fields on top of existing v .PP \fB\-\-envvar\fP=false - use the .kubeconfig from $KUBECONFIG + use the kubeconfig from $KUBECONFIG .PP \fB\-\-global\fP=false - use the .kubeconfig from /home/username + use the kubeconfig from /home/username/.kube/.kubeconfig .PP \fB\-\-kubeconfig\fP="" - use a particular .kubeconfig file + use a particular kubeconfig file .PP \fB\-\-local\fP=false - use the .kubeconfig in the current directory + use the kubeconfig in the current directory .PP \fB\-\-log\_backtrace\_at\fP=:0 diff --git a/docs/man/man1/kubectl-config-set-context.1 b/docs/man/man1/kubectl-config-set-context.1 index e8636e4bde6..822a8c248ae 100644 --- a/docs/man/man1/kubectl-config-set-context.1 +++ b/docs/man/man1/kubectl-config-set-context.1 @@ -3,7 +3,7 @@ .SH NAME .PP -kubectl config set\-context \- Sets a context entry in .kubeconfig +kubectl config set\-context \- Sets a context entry in kubeconfig .SH SYNOPSIS @@ -13,14 +13,14 @@ kubectl config set\-context \- Sets a context entry in .kubeconfig .SH DESCRIPTION .PP -Sets a context entry in .kubeconfig +Sets a context entry in kubeconfig Specifying a name that already exists will merge new fields on top of existing values for those fields. .SH OPTIONS .PP \fB\-\-cluster\fP="" - cluster for the context entry in .kubeconfig + cluster for the context entry in kubeconfig .PP \fB\-h\fP, \fB\-\-help\fP=false @@ -28,11 +28,11 @@ Specifying a name that already exists will merge new fields on top of existing v .PP \fB\-\-namespace\fP="" - namespace for the context entry in .kubeconfig + namespace for the context entry in kubeconfig .PP \fB\-\-user\fP="" - user for the context entry in .kubeconfig + user for the context entry in kubeconfig .SH OPTIONS INHERITED FROM PARENT COMMANDS @@ -66,11 +66,11 @@ Specifying a name that already exists will merge new fields on top of existing v .PP \fB\-\-envvar\fP=false - use the .kubeconfig from $KUBECONFIG + use the kubeconfig from $KUBECONFIG .PP \fB\-\-global\fP=false - use the .kubeconfig from /home/username + use the kubeconfig from /home/username/.kube/.kubeconfig .PP \fB\-\-insecure\-skip\-tls\-verify\fP=false @@ -78,11 +78,11 @@ Specifying a name that already exists will merge new fields on top of existing v .PP \fB\-\-kubeconfig\fP="" - use a particular .kubeconfig file + use a particular kubeconfig file .PP \fB\-\-local\fP=false - use the .kubeconfig in the current directory + use the kubeconfig in the current directory .PP \fB\-\-log\_backtrace\_at\fP=:0 diff --git a/docs/man/man1/kubectl-config-set-credentials.1 b/docs/man/man1/kubectl-config-set-credentials.1 index 039bbc9d4d2..6136aa476c4 100644 --- a/docs/man/man1/kubectl-config-set-credentials.1 +++ b/docs/man/man1/kubectl-config-set-credentials.1 @@ -3,7 +3,7 @@ .SH NAME .PP -kubectl config set\-credentials \- Sets a user entry in .kubeconfig +kubectl config set\-credentials \- Sets a user entry in kubeconfig .SH SYNOPSIS @@ -13,7 +13,7 @@ kubectl config set\-credentials \- Sets a user entry in .kubeconfig .SH DESCRIPTION .PP -Sets a user entry in .kubeconfig +Sets a user entry in kubeconfig Specifying a name that already exists will merge new fields on top of existing values. .PP @@ -35,19 +35,19 @@ Bearer token and basic auth are mutually exclusive. .SH OPTIONS .PP \fB\-\-auth\-path\fP="" - auth\-path for the user entry in .kubeconfig + auth\-path for the user entry in kubeconfig .PP \fB\-\-client\-certificate\fP="" - path to client\-certificate for the user entry in .kubeconfig + path to client\-certificate for the user entry in kubeconfig .PP \fB\-\-client\-key\fP="" - path to client\-key for the user entry in .kubeconfig + path to client\-key for the user entry in kubeconfig .PP \fB\-\-embed\-certs\fP=false - embed client cert/key for the user entry in .kubeconfig + embed client cert/key for the user entry in kubeconfig .PP \fB\-h\fP, \fB\-\-help\fP=false @@ -55,15 +55,15 @@ Bearer token and basic auth are mutually exclusive. .PP \fB\-\-password\fP="" - password for the user entry in .kubeconfig + password for the user entry in kubeconfig .PP \fB\-\-token\fP="" - token for the user entry in .kubeconfig + token for the user entry in kubeconfig .PP \fB\-\-username\fP="" - username for the user entry in .kubeconfig + username for the user entry in kubeconfig .SH OPTIONS INHERITED FROM PARENT COMMANDS @@ -89,11 +89,11 @@ Bearer token and basic auth are mutually exclusive. .PP \fB\-\-envvar\fP=false - use the .kubeconfig from $KUBECONFIG + use the kubeconfig from $KUBECONFIG .PP \fB\-\-global\fP=false - use the .kubeconfig from /home/username + use the kubeconfig from /home/username/.kube/.kubeconfig .PP \fB\-\-insecure\-skip\-tls\-verify\fP=false @@ -101,11 +101,11 @@ Bearer token and basic auth are mutually exclusive. .PP \fB\-\-kubeconfig\fP="" - use a particular .kubeconfig file + use a particular kubeconfig file .PP \fB\-\-local\fP=false - use the .kubeconfig in the current directory + use the kubeconfig in the current directory .PP \fB\-\-log\_backtrace\_at\fP=:0 diff --git a/docs/man/man1/kubectl-config-set.1 b/docs/man/man1/kubectl-config-set.1 index 5c9782b27cf..fed5524d0ee 100644 --- a/docs/man/man1/kubectl-config-set.1 +++ b/docs/man/man1/kubectl-config-set.1 @@ -3,7 +3,7 @@ .SH NAME .PP -kubectl config set \- Sets an individual value in a .kubeconfig file +kubectl config set \- Sets an individual value in a kubeconfig file .SH SYNOPSIS @@ -13,7 +13,7 @@ kubectl config set \- Sets an individual value in a .kubeconfig file .SH DESCRIPTION .PP -Sets an individual value in a .kubeconfig file +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. @@ -59,11 +59,11 @@ PROPERTY\_VALUE is the new value you wish to set. .PP \fB\-\-envvar\fP=false - use the .kubeconfig from $KUBECONFIG + use the kubeconfig from $KUBECONFIG .PP \fB\-\-global\fP=false - use the .kubeconfig from /home/username + use the kubeconfig from /home/username/.kube/.kubeconfig .PP \fB\-\-insecure\-skip\-tls\-verify\fP=false @@ -71,11 +71,11 @@ PROPERTY\_VALUE is the new value you wish to set. .PP \fB\-\-kubeconfig\fP="" - use a particular .kubeconfig file + use a particular kubeconfig file .PP \fB\-\-local\fP=false - use the .kubeconfig in the current directory + use the kubeconfig in the current directory .PP \fB\-\-log\_backtrace\_at\fP=:0 diff --git a/docs/man/man1/kubectl-config-unset.1 b/docs/man/man1/kubectl-config-unset.1 index 61786eb63d7..1a3c6f6a37a 100644 --- a/docs/man/man1/kubectl-config-unset.1 +++ b/docs/man/man1/kubectl-config-unset.1 @@ -3,7 +3,7 @@ .SH NAME .PP -kubectl config unset \- Unsets an individual value in a .kubeconfig file +kubectl config unset \- Unsets an individual value in a kubeconfig file .SH SYNOPSIS @@ -13,7 +13,7 @@ kubectl config unset \- Unsets an individual value in a .kubeconfig file .SH DESCRIPTION .PP -Unsets an individual value in a .kubeconfig file +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. @@ -58,11 +58,11 @@ PROPERTY\_NAME is a dot delimited name where each token represents either a attr .PP \fB\-\-envvar\fP=false - use the .kubeconfig from $KUBECONFIG + use the kubeconfig from $KUBECONFIG .PP \fB\-\-global\fP=false - use the .kubeconfig from /home/username + use the kubeconfig from /home/username/.kube/.kubeconfig .PP \fB\-\-insecure\-skip\-tls\-verify\fP=false @@ -70,11 +70,11 @@ PROPERTY\_NAME is a dot delimited name where each token represents either a attr .PP \fB\-\-kubeconfig\fP="" - use a particular .kubeconfig file + use a particular kubeconfig file .PP \fB\-\-local\fP=false - use the .kubeconfig in the current directory + use the kubeconfig in the current directory .PP \fB\-\-log\_backtrace\_at\fP=:0 diff --git a/docs/man/man1/kubectl-config-use-context.1 b/docs/man/man1/kubectl-config-use-context.1 index f0c92ebecd4..febd1fe0a35 100644 --- a/docs/man/man1/kubectl-config-use-context.1 +++ b/docs/man/man1/kubectl-config-use-context.1 @@ -3,7 +3,7 @@ .SH NAME .PP -kubectl config use\-context \- Sets the current\-context in a .kubeconfig file +kubectl config use\-context \- Sets the current\-context in a kubeconfig file .SH SYNOPSIS @@ -13,7 +13,7 @@ kubectl config use\-context \- Sets the current\-context in a .kubeconfig file .SH DESCRIPTION .PP -Sets the current\-context in a .kubeconfig file +Sets the current\-context in a kubeconfig file .SH OPTIONS @@ -57,11 +57,11 @@ Sets the current\-context in a .kubeconfig file .PP \fB\-\-envvar\fP=false - use the .kubeconfig from $KUBECONFIG + use the kubeconfig from $KUBECONFIG .PP \fB\-\-global\fP=false - use the .kubeconfig from /home/username + use the kubeconfig from /home/username/.kube/.kubeconfig .PP \fB\-\-insecure\-skip\-tls\-verify\fP=false @@ -69,11 +69,11 @@ Sets the current\-context in a .kubeconfig file .PP \fB\-\-kubeconfig\fP="" - use a particular .kubeconfig file + use a particular kubeconfig file .PP \fB\-\-local\fP=false - use the .kubeconfig in the current directory + use the kubeconfig in the current directory .PP \fB\-\-log\_backtrace\_at\fP=:0 diff --git a/docs/man/man1/kubectl-config-view.1 b/docs/man/man1/kubectl-config-view.1 index 86d4d063e76..f929f6b40b2 100644 --- a/docs/man/man1/kubectl-config-view.1 +++ b/docs/man/man1/kubectl-config-view.1 @@ -3,7 +3,7 @@ .SH NAME .PP -kubectl config view \- displays merged .kubeconfig settings or a specified .kubeconfig file. +kubectl config view \- displays merged kubeconfig settings or a specified kubeconfig file. .SH SYNOPSIS @@ -13,7 +13,7 @@ kubectl config view \- displays merged .kubeconfig settings or a specified .kube .SH DESCRIPTION .PP -displays merged .kubeconfig settings or a specified .kubeconfig file. +displays merged kubeconfig settings or a specified kubeconfig file. .PP You can use \-\-output=template \-\-template=TEMPLATE to extract specific values. @@ -26,7 +26,7 @@ You can use \-\-output=template \-\-template=TEMPLATE to extract specific values .PP \fB\-\-merge\fP=true - merge together the full hierarchy of .kubeconfig files + merge together the full hierarchy of kubeconfig files .PP \fB\-\-no\-headers\fP=false @@ -81,11 +81,11 @@ You can use \-\-output=template \-\-template=TEMPLATE to extract specific values .PP \fB\-\-envvar\fP=false - use the .kubeconfig from $KUBECONFIG + use the kubeconfig from $KUBECONFIG .PP \fB\-\-global\fP=false - use the .kubeconfig from /home/username + use the kubeconfig from /home/username/.kube/.kubeconfig .PP \fB\-\-insecure\-skip\-tls\-verify\fP=false @@ -93,11 +93,11 @@ You can use \-\-output=template \-\-template=TEMPLATE to extract specific values .PP \fB\-\-kubeconfig\fP="" - use a particular .kubeconfig file + use a particular kubeconfig file .PP \fB\-\-local\fP=false - use the .kubeconfig in the current directory + use the kubeconfig in the current directory .PP \fB\-\-log\_backtrace\_at\fP=:0 @@ -165,10 +165,10 @@ You can use \-\-output=template \-\-template=TEMPLATE to extract specific values .RS .nf -// Show merged .kubeconfig settings. +// Show merged kubeconfig settings. $ kubectl config view -// Show only local ./.kubeconfig settings +// Show only local kubeconfig settings $ kubectl config view \-\-local // Get the password for the e2e user diff --git a/docs/man/man1/kubectl-config.1 b/docs/man/man1/kubectl-config.1 index 0b2d55b16e7..8dc96aa6f06 100644 --- a/docs/man/man1/kubectl-config.1 +++ b/docs/man/man1/kubectl-config.1 @@ -3,7 +3,7 @@ .SH NAME .PP -kubectl config \- config modifies .kubeconfig files +kubectl config \- config modifies kubeconfig files .SH SYNOPSIS @@ -13,17 +13,17 @@ kubectl config \- config modifies .kubeconfig files .SH DESCRIPTION .PP -config modifies .kubeconfig files using subcommands like "kubectl config set current\-context my\-context" +config modifies kubeconfig files using subcommands like "kubectl config set current\-context my\-context" .SH OPTIONS .PP \fB\-\-envvar\fP=false - use the .kubeconfig from $KUBECONFIG + use the kubeconfig from $KUBECONFIG .PP \fB\-\-global\fP=false - use the .kubeconfig from /home/username + use the kubeconfig from /home/username/.kube/.kubeconfig .PP \fB\-h\fP, \fB\-\-help\fP=false @@ -31,11 +31,11 @@ config modifies .kubeconfig files using subcommands like "kubectl config set cur .PP \fB\-\-kubeconfig\fP="" - use a particular .kubeconfig file + use a particular kubeconfig file .PP \fB\-\-local\fP=false - use the .kubeconfig in the current directory + use the kubeconfig in the current directory .SH OPTIONS INHERITED FROM PARENT COMMANDS diff --git a/pkg/client/clientcmd/api/types.go b/pkg/client/clientcmd/api/types.go index ab126071a49..ec4216aec87 100644 --- a/pkg/client/clientcmd/api/types.go +++ b/pkg/client/clientcmd/api/types.go @@ -49,6 +49,8 @@ type Preferences struct { // Cluster contains information about how to communicate with a kubernetes cluster type Cluster struct { + // LocationOfOrigin indicates where this object came from. It is used for round tripping config post-merge, but never serialized. + LocationOfOrigin string // Server is the address of the kubernetes cluster (https://hostname:port). Server string `json:"server"` // APIVersion is the preferred api version for communicating with the kubernetes cluster (v1beta1, v1beta2, v1beta3, etc). @@ -65,6 +67,8 @@ type Cluster struct { // AuthInfo contains information that describes identity information. This is use to tell the kubernetes cluster who you are. type AuthInfo struct { + // LocationOfOrigin indicates where this object came from. It is used for round tripping config post-merge, but never serialized. + LocationOfOrigin string // AuthPath is the path to a kubernetes auth file (~/.kubernetes_auth). If you provide an AuthPath, the other options specified are ignored AuthPath string `json:"auth-path,omitempty"` // ClientCertificate is the path to a client cert file for TLS. @@ -87,6 +91,8 @@ type AuthInfo struct { // Context is a tuple of references to a cluster (how do I communicate with a kubernetes cluster), a user (how do I identify myself), and a namespace (what subset of resources do I want to work with) type Context struct { + // LocationOfOrigin indicates where this object came from. It is used for round tripping config post-merge, but never serialized. + LocationOfOrigin string // Cluster is the name of the cluster for this context Cluster string `json:"cluster"` // AuthInfo is the name of the authInfo for this context diff --git a/pkg/client/clientcmd/api/types_test.go b/pkg/client/clientcmd/api/types_test.go index 1376d4f9666..0bbade0ac6b 100644 --- a/pkg/client/clientcmd/api/types_test.go +++ b/pkg/client/clientcmd/api/types_test.go @@ -88,22 +88,27 @@ func ExampleOfOptionsConfig() { // Output: // clusters: // alfa: + // LocationOfOrigin: "" // api-version: v1beta2 // certificate-authority: path/to/my/cert-ca-filename // insecure-skip-tls-verify: true // server: https://alfa.org:8080 // bravo: + // LocationOfOrigin: "" // api-version: v1beta1 // server: https://bravo.org:8080 // contexts: // alfa-as-black-mage: + // LocationOfOrigin: "" // cluster: alfa // namespace: zulu // user: black-mage-via-file // alfa-as-white-mage: + // LocationOfOrigin: "" // cluster: alfa // user: white-mage-via-cert // bravo-as-black-mage: + // LocationOfOrigin: "" // cluster: bravo // namespace: yankee // user: black-mage-via-file @@ -112,10 +117,13 @@ func ExampleOfOptionsConfig() { // colors: true // users: // black-mage-via-file: + // LocationOfOrigin: "" // auth-path: path/to/my/.kubernetes_auth // red-mage-via-token: + // LocationOfOrigin: "" // token: my-secret-token // white-mage-via-cert: + // LocationOfOrigin: "" // client-certificate: path/to/my/client-cert-filename // client-key: path/to/my/client-key-filename } diff --git a/pkg/client/clientcmd/api/v1/conversion.go b/pkg/client/clientcmd/api/v1/conversion.go index a646b673312..1a42b376b23 100644 --- a/pkg/client/clientcmd/api/v1/conversion.go +++ b/pkg/client/clientcmd/api/v1/conversion.go @@ -26,6 +26,31 @@ import ( func init() { err := newer.Scheme.AddConversionFuncs( + func(in *Cluster, out *newer.Cluster, s conversion.Scope) error { + return s.DefaultConvert(in, out, conversion.IgnoreMissingFields) + }, + func(in *newer.Cluster, out *Cluster, s conversion.Scope) error { + return s.DefaultConvert(in, out, conversion.IgnoreMissingFields) + }, + func(in *Preferences, out *newer.Preferences, s conversion.Scope) error { + return s.DefaultConvert(in, out, conversion.IgnoreMissingFields) + }, + func(in *newer.Preferences, out *Preferences, s conversion.Scope) error { + return s.DefaultConvert(in, out, conversion.IgnoreMissingFields) + }, + func(in *AuthInfo, out *newer.AuthInfo, s conversion.Scope) error { + return s.DefaultConvert(in, out, conversion.IgnoreMissingFields) + }, + func(in *newer.AuthInfo, out *AuthInfo, s conversion.Scope) error { + return s.DefaultConvert(in, out, conversion.IgnoreMissingFields) + }, + func(in *Context, out *newer.Context, s conversion.Scope) error { + return s.DefaultConvert(in, out, conversion.IgnoreMissingFields) + }, + func(in *newer.Context, out *Context, s conversion.Scope) error { + return s.DefaultConvert(in, out, conversion.IgnoreMissingFields) + }, + func(in *Config, out *newer.Config, s conversion.Scope) error { out.CurrentContext = in.CurrentContext if err := s.Convert(&in.Preferences, &out.Preferences, 0); err != nil { diff --git a/pkg/client/clientcmd/loader.go b/pkg/client/clientcmd/loader.go index 3eff8857756..2b7fe12b41a 100644 --- a/pkg/client/clientcmd/loader.go +++ b/pkg/client/clientcmd/loader.go @@ -48,6 +48,10 @@ const ( type ClientConfigLoadingRules struct { ExplicitPath string Precedence []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 } // NewDefaultClientConfigLoadingRules returns a ClientConfigLoadingRules object with default fields filled in. You are not required to @@ -71,7 +75,6 @@ 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) { - errlist := []error{} // Make sure a file we were explicitly told to use exists @@ -90,8 +93,10 @@ func (rules *ClientConfigLoadingRules) Load() (*clientcmdapi.Config, error) { if err := mergeConfigWithFile(mapConfig, file); err != nil { errlist = append(errlist, err) } - if err := ResolveLocalPaths(file, mapConfig); err != nil { - errlist = append(errlist, err) + if rules.ResolvePaths() { + if err := ResolveLocalPaths(file, mapConfig); err != nil { + errlist = append(errlist, err) + } } } @@ -101,7 +106,9 @@ func (rules *ClientConfigLoadingRules) Load() (*clientcmdapi.Config, error) { for i := len(kubeConfigFiles) - 1; i >= 0; i-- { file := kubeConfigFiles[i] mergeConfigWithFile(nonMapConfig, file) - ResolveLocalPaths(file, nonMapConfig) + if rules.ResolvePaths() { + ResolveLocalPaths(file, nonMapConfig) + } } // since values are overwritten, but maps values are not, we can merge the non-map config on top of the map config and @@ -182,7 +189,26 @@ func LoadFromFile(filename string) (*clientcmdapi.Config, error) { if err != nil { return nil, err } - return Load(kubeconfigBytes) + config, err := Load(kubeconfigBytes) + if err != nil { + return nil, err + } + + // set LocationOfOrigin on every Cluster, User, and Context + for key, obj := range config.AuthInfos { + obj.LocationOfOrigin = filename + config.AuthInfos[key] = obj + } + for key, obj := range config.Clusters { + obj.LocationOfOrigin = filename + config.Clusters[key] = obj + } + for key, obj := range config.Contexts { + obj.LocationOfOrigin = filename + config.Contexts[key] = obj + } + + return config, nil } // Load takes a byte slice and deserializes the contents into Config object. @@ -226,3 +252,7 @@ func Write(config clientcmdapi.Config) ([]byte, error) { } return content, nil } + +func (rules ClientConfigLoadingRules) ResolvePaths() bool { + return !rules.DoNotResolvePaths +} diff --git a/pkg/kubectl/cmd/cmd.go b/pkg/kubectl/cmd/cmd.go index fee7c5ec361..736a5dbcbfd 100644 --- a/pkg/kubectl/cmd/cmd.go +++ b/pkg/kubectl/cmd/cmd.go @@ -61,7 +61,7 @@ Find more information at https://github.com/GoogleCloudPlatform/kubernetes.`, cmds.AddCommand(NewCmdLabel(f, out)) - cmds.AddCommand(cmdconfig.NewCmdConfig(f, out)) + cmds.AddCommand(cmdconfig.NewCmdConfig(cmdconfig.NewDefaultPathOptions(), out)) cmds.AddCommand(NewCmdClusterInfo(f, out)) cmds.AddCommand(NewCmdApiVersions(f, out)) cmds.AddCommand(NewCmdVersion(f, out)) diff --git a/pkg/kubectl/cmd/config/config.go b/pkg/kubectl/cmd/config/config.go index 88f12a7b942..203095e46c4 100644 --- a/pkg/kubectl/cmd/config/config.go +++ b/pkg/kubectl/cmd/config/config.go @@ -17,9 +17,12 @@ limitations under the License. package config import ( + "errors" "fmt" "io" "os" + "path" + "reflect" "strconv" "github.com/golang/glog" @@ -27,33 +30,45 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd" clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api" - cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" ) -type pathOptions struct { - local bool - global bool - envvar bool - specifiedFile string +type PathOptions struct { + Local bool + Global bool + UseEnvVar bool + + LocalFile string + GlobalFile string + EnvVarFile string + + EnvVar string + ExplicitFileFlag string + + LoadingRules *clientcmd.ClientConfigLoadingRules } -func NewCmdConfig(f *cmdutil.Factory, out io.Writer) *cobra.Command { - pathOptions := &pathOptions{} +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"`, + Short: "config modifies kubeconfig files", + Long: `config modifies kubeconfig files using subcommands like "kubectl config set current-context my-context"`, Run: func(cmd *cobra.Command, args []string) { cmd.Help() }, } // file paths are common to all sub commands - cmd.PersistentFlags().BoolVar(&pathOptions.local, "local", false, "use the .kubeconfig in the current directory") - cmd.PersistentFlags().BoolVar(&pathOptions.global, "global", false, "use the .kubeconfig from "+os.Getenv("HOME")) - cmd.PersistentFlags().BoolVar(&pathOptions.envvar, "envvar", false, "use the .kubeconfig from $KUBECONFIG") - cmd.PersistentFlags().StringVar(&pathOptions.specifiedFile, "kubeconfig", "", "use a particular .kubeconfig file") + 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)) cmd.AddCommand(NewCmdConfigSetCluster(out, pathOptions)) @@ -66,59 +81,367 @@ func NewCmdConfig(f *cmdutil.Factory, out io.Writer) *cobra.Command { return cmd } -func (o *pathOptions) getStartingConfig() (*clientcmdapi.Config, string, error) { - filename := "" +func NewDefaultPathOptions() *PathOptions { + ret := &PathOptions{ + LocalFile: ".kubeconfig", + GlobalFile: path.Join(os.Getenv("HOME"), "/.kube/.kubeconfig"), + EnvVar: clientcmd.RecommendedConfigPathEnvVar, + EnvVarFile: os.Getenv(clientcmd.RecommendedConfigPathEnvVar), + + ExplicitFileFlag: clientcmd.RecommendedConfigPathFlag, + + LoadingRules: clientcmd.NewDefaultClientConfigLoadingRules(), + } + ret.LoadingRules.DoNotResolvePaths = true + + 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") + } + } + + 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") + } + } + + 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 +} + +func (o *PathOptions) getStartingConfig() (*clientcmdapi.Config, error) { + if err := o.Validate(); err != nil { + return nil, err + } + config := clientcmdapi.NewConfig() - if len(o.specifiedFile) > 0 { - filename = o.specifiedFile - config = getConfigFromFileOrDie(filename) - } + switch { + case o.Global: + config = getConfigFromFileOrDie(o.GlobalFile) - if o.global { - if len(filename) > 0 { - return nil, "", fmt.Errorf("already loading from %v, cannot specify global as well", filename) + 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 } - - filename = os.Getenv("HOME") + "/.kube/.kubeconfig" - config = getConfigFromFileOrDie(filename) - } - - if o.envvar { - if len(filename) > 0 { - return nil, "", fmt.Errorf("already loading from %v, cannot specify global as well", filename) - } - - filename = os.Getenv(clientcmd.RecommendedConfigPathEnvVar) - if len(filename) == 0 { - return nil, "", fmt.Errorf("environment variable %v does not have a value", clientcmd.RecommendedConfigPathEnvVar) - } - - config = getConfigFromFileOrDie(filename) - } - - if o.local { - if len(filename) > 0 { - return nil, "", fmt.Errorf("already loading from %v, cannot specify global as well", filename) - } - - filename = ".kubeconfig" - config = getConfigFromFileOrDie(filename) + config = &rawConfig } - // no specific flag was set, first attempt to use the envvar, then use local - if len(filename) == 0 { - if len(os.Getenv(clientcmd.RecommendedConfigPathEnvVar)) > 0 { - filename = os.Getenv(clientcmd.RecommendedConfigPathEnvVar) - config = getConfigFromFileOrDie(filename) - } else { - filename = ".kubeconfig" - config = getConfigFromFileOrDie(filename) + 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 _, err := os.Stat(o.LocalFile); err == nil { + return o.LocalFile + } + + return o.GlobalFile + +} + +func (o *PathOptions) IsExplicitFile() bool { + switch { + case len(o.LoadingRules.ExplicitPath) > 0 || + o.Global || + o.UseEnvVar || + o.Local: + 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 +// uses the default destination file to write the results into. This results in multiple file reads, but it's very easy to follow. +// 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() + if err != nil { + return err + } + + // at this point, config and startingConfig should have, at most, one difference. We need to chase the difference until we find it + // then we'll build a partial config object to call write upon. Special case the test for current context and preferences since those + // always write to the default file. + switch { + case reflect.DeepEqual(*startingConfig, newConfig): + // nothing to do + + case startingConfig.CurrentContext != newConfig.CurrentContext: + if err := o.writeCurrentContext(newConfig.CurrentContext); err != nil { + return err + } + + case !reflect.DeepEqual(startingConfig.Preferences, newConfig.Preferences): + if err := o.writePreferences(newConfig.Preferences); err != nil { + return err + } + + default: + // something is different. Search every cluster, authInfo, and context. First from new to old for differences, then from old to new for deletions + for key, cluster := range newConfig.Clusters { + startingCluster, exists := startingConfig.Clusters[key] + if !reflect.DeepEqual(cluster, startingCluster) || !exists { + destinationFile := cluster.LocationOfOrigin + if len(destinationFile) == 0 { + destinationFile = o.GetDefaultFilename() + } + + configToWrite := getConfigFromFileOrDie(destinationFile) + configToWrite.Clusters[key] = cluster + + if err := clientcmd.WriteToFile(*configToWrite, destinationFile); err != nil { + return err + } + } + } + + for key, context := range newConfig.Contexts { + startingContext, exists := startingConfig.Contexts[key] + if !reflect.DeepEqual(context, startingContext) || !exists { + destinationFile := context.LocationOfOrigin + if len(destinationFile) == 0 { + destinationFile = o.GetDefaultFilename() + } + + configToWrite := getConfigFromFileOrDie(destinationFile) + configToWrite.Contexts[key] = context + + if err := clientcmd.WriteToFile(*configToWrite, destinationFile); err != nil { + return err + } + } + } + + for key, authInfo := range newConfig.AuthInfos { + startingAuthInfo, exists := startingConfig.AuthInfos[key] + if !reflect.DeepEqual(authInfo, startingAuthInfo) || !exists { + destinationFile := authInfo.LocationOfOrigin + if len(destinationFile) == 0 { + destinationFile = o.GetDefaultFilename() + } + + configToWrite := getConfigFromFileOrDie(destinationFile) + configToWrite.AuthInfos[key] = authInfo + + if err := clientcmd.WriteToFile(*configToWrite, destinationFile); err != nil { + return err + } + } + } + + for key, cluster := range startingConfig.Clusters { + if _, exists := newConfig.Clusters[key]; !exists { + destinationFile := cluster.LocationOfOrigin + if len(destinationFile) == 0 { + destinationFile = o.GetDefaultFilename() + } + + configToWrite := getConfigFromFileOrDie(destinationFile) + delete(configToWrite.Clusters, key) + + if err := clientcmd.WriteToFile(*configToWrite, destinationFile); err != nil { + return err + } + } + } + + for key, context := range startingConfig.Contexts { + if _, exists := newConfig.Contexts[key]; !exists { + destinationFile := context.LocationOfOrigin + if len(destinationFile) == 0 { + destinationFile = o.GetDefaultFilename() + } + + configToWrite := getConfigFromFileOrDie(destinationFile) + delete(configToWrite.Contexts, key) + + if err := clientcmd.WriteToFile(*configToWrite, destinationFile); err != nil { + return err + } + } + } + + for key, authInfo := range startingConfig.AuthInfos { + if _, exists := newConfig.AuthInfos[key]; !exists { + destinationFile := authInfo.LocationOfOrigin + if len(destinationFile) == 0 { + destinationFile = o.GetDefaultFilename() + } + + configToWrite := getConfigFromFileOrDie(destinationFile) + delete(configToWrite.AuthInfos, key) + + if err := clientcmd.WriteToFile(*configToWrite, destinationFile); err != nil { + return err + } + } + } + + } + + return nil +} + +// writeCurrentContext takes three possible paths. +// 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 { + return err + } else if startingConfig.CurrentContext == newCurrentContext { + return nil + } + + if len(newCurrentContext) > 0 { + destinationFile := o.GetDefaultFilename() + config := getConfigFromFileOrDie(destinationFile) + config.CurrentContext = newCurrentContext + + if err := clientcmd.WriteToFile(*config, destinationFile); err != nil { + return err + } + + 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 { + currConfig := getConfigFromFileOrDie(file) + + if len(currConfig.CurrentContext) > 0 { + currConfig.CurrentContext = newCurrentContext + if err := clientcmd.WriteToFile(*currConfig, file); err != nil { + return err + } + + return nil } } - return config, filename, nil + return nil +} + +func (o *PathOptions) writePreferences(newPrefs clientcmdapi.Preferences) error { + if startingConfig, err := o.getStartingConfig(); err != nil { + return err + } else if reflect.DeepEqual(startingConfig.Preferences, newPrefs) { + return nil + } + + if o.IsExplicitFile() { + file := o.GetExplicitFile() + currConfig := getConfigFromFileOrDie(file) + currConfig.Preferences = newPrefs + 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 { + currConfig := getConfigFromFileOrDie(file) + + if !reflect.DeepEqual(currConfig.Preferences, newPrefs) { + currConfig.Preferences = newPrefs + if err := clientcmd.WriteToFile(*currConfig, file); err != nil { + return err + } + + return nil + } + } + + return nil } // getConfigFromFileOrDie tries to read a kubeconfig file and if it can't, it calls exit. One exception, missing files result in empty configs, not an exit diff --git a/pkg/kubectl/cmd/config/config_test.go b/pkg/kubectl/cmd/config/config_test.go index 23b92ab0806..fcbd0dd06b1 100644 --- a/pkg/kubectl/cmd/config/config_test.go +++ b/pkg/kubectl/cmd/config/config_test.go @@ -27,7 +27,6 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd" clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api" - cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" ) @@ -650,9 +649,8 @@ func testConfigCommand(args []string, startingConfig clientcmdapi.Config) (strin argsToUse = append(argsToUse, args...) buf := bytes.NewBuffer([]byte{}) - f := cmdutil.NewFactory(nil) - cmd := NewCmdConfig(f, buf) + cmd := NewCmdConfig(NewDefaultPathOptions(), buf) cmd.SetArgs(argsToUse) cmd.Execute() @@ -667,6 +665,7 @@ func (test configCommandTest) run(t *testing.T) string { testSetNilMapsToEmpties(reflect.ValueOf(&test.expectedConfig)) testSetNilMapsToEmpties(reflect.ValueOf(&actualConfig)) + testClearLocationOfOrigin(&actualConfig) if !reflect.DeepEqual(test.expectedConfig, actualConfig) { t.Errorf("diff: %v", util.ObjectDiff(test.expectedConfig, actualConfig)) @@ -681,6 +680,20 @@ func (test configCommandTest) run(t *testing.T) string { return out } +func testClearLocationOfOrigin(config *clientcmdapi.Config) { + for key, obj := range config.AuthInfos { + obj.LocationOfOrigin = "" + config.AuthInfos[key] = obj + } + for key, obj := range config.Clusters { + obj.LocationOfOrigin = "" + config.Clusters[key] = obj + } + for key, obj := range config.Contexts { + obj.LocationOfOrigin = "" + config.Contexts[key] = obj + } +} func testSetNilMapsToEmpties(curr reflect.Value) { actualCurrValue := curr if curr.Kind() == reflect.Ptr { diff --git a/pkg/kubectl/cmd/config/create_authinfo.go b/pkg/kubectl/cmd/config/create_authinfo.go index 8253e350a5a..47f7a8e9324 100644 --- a/pkg/kubectl/cmd/config/create_authinfo.go +++ b/pkg/kubectl/cmd/config/create_authinfo.go @@ -31,7 +31,7 @@ import ( ) type createAuthInfoOptions struct { - pathOptions *pathOptions + pathOptions *PathOptions name string authPath util.StringFlag clientCertificate util.StringFlag @@ -42,7 +42,7 @@ type createAuthInfoOptions struct { embedCertData util.BoolFlag } -var create_authinfo_long = fmt.Sprintf(`Sets a user entry in .kubeconfig +var create_authinfo_long = fmt.Sprintf(`Sets a user entry in kubeconfig Specifying a name that already exists will merge new fields on top of existing values. Client-certificate flags: @@ -67,12 +67,12 @@ $ 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 { +func NewCmdConfigSetAuthInfo(out io.Writer, pathOptions *PathOptions) *cobra.Command { options := &createAuthInfoOptions{pathOptions: pathOptions} 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), - Short: "Sets a user entry in .kubeconfig", + Short: "Sets a user entry in kubeconfig", Long: create_authinfo_long, Example: create_authinfo_example, Run: func(cmd *cobra.Command, args []string) { @@ -87,13 +87,13 @@ func NewCmdConfigSetAuthInfo(out io.Writer, pathOptions *pathOptions) *cobra.Com }, } - cmd.Flags().Var(&options.authPath, clientcmd.FlagAuthPath, clientcmd.FlagAuthPath+" for the user entry in .kubeconfig") - cmd.Flags().Var(&options.clientCertificate, clientcmd.FlagCertFile, "path to "+clientcmd.FlagCertFile+" for the user entry in .kubeconfig") - cmd.Flags().Var(&options.clientKey, clientcmd.FlagKeyFile, "path to "+clientcmd.FlagKeyFile+" for the user entry in .kubeconfig") - cmd.Flags().Var(&options.token, clientcmd.FlagBearerToken, clientcmd.FlagBearerToken+" for the user entry in .kubeconfig") - cmd.Flags().Var(&options.username, clientcmd.FlagUsername, clientcmd.FlagUsername+" for the user entry in .kubeconfig") - cmd.Flags().Var(&options.password, clientcmd.FlagPassword, clientcmd.FlagPassword+" for the user entry in .kubeconfig") - cmd.Flags().Var(&options.embedCertData, clientcmd.FlagEmbedCerts, "embed client cert/key for the user entry in .kubeconfig") + cmd.Flags().Var(&options.authPath, clientcmd.FlagAuthPath, clientcmd.FlagAuthPath+" for the user entry in kubeconfig") + cmd.Flags().Var(&options.clientCertificate, clientcmd.FlagCertFile, "path to "+clientcmd.FlagCertFile+" for the user entry in kubeconfig") + cmd.Flags().Var(&options.clientKey, clientcmd.FlagKeyFile, "path to "+clientcmd.FlagKeyFile+" for the user entry in kubeconfig") + cmd.Flags().Var(&options.token, clientcmd.FlagBearerToken, clientcmd.FlagBearerToken+" for the user entry in kubeconfig") + cmd.Flags().Var(&options.username, clientcmd.FlagUsername, clientcmd.FlagUsername+" for the user entry in kubeconfig") + cmd.Flags().Var(&options.password, clientcmd.FlagPassword, clientcmd.FlagPassword+" for the user entry in kubeconfig") + cmd.Flags().Var(&options.embedCertData, clientcmd.FlagEmbedCerts, "embed client cert/key for the user entry in kubeconfig") return cmd } @@ -104,7 +104,7 @@ func (o createAuthInfoOptions) run() error { return err } - config, filename, err := o.pathOptions.getStartingConfig() + config, err := o.pathOptions.getStartingConfig() if err != nil { return err } @@ -112,8 +112,7 @@ func (o createAuthInfoOptions) run() error { authInfo := o.modifyAuthInfo(config.AuthInfos[o.name]) config.AuthInfos[o.name] = authInfo - err = clientcmd.WriteToFile(*config, filename) - if err != nil { + if err := o.pathOptions.ModifyConfig(*config); err != nil { return err } @@ -226,5 +225,5 @@ func (o createAuthInfoOptions) validate() error { } } - return nil + return o.pathOptions.Validate() } diff --git a/pkg/kubectl/cmd/config/create_cluster.go b/pkg/kubectl/cmd/config/create_cluster.go index 08c38361b20..389c4429cad 100644 --- a/pkg/kubectl/cmd/config/create_cluster.go +++ b/pkg/kubectl/cmd/config/create_cluster.go @@ -30,7 +30,7 @@ import ( ) type createClusterOptions struct { - pathOptions *pathOptions + pathOptions *PathOptions name string server util.StringFlag apiVersion util.StringFlag @@ -40,7 +40,7 @@ type createClusterOptions struct { } const ( - create_cluster_long = `Sets a cluster entry in .kubeconfig. + create_cluster_long = `Sets a cluster entry in kubeconfig. Specifying a name that already exists will merge new fields on top of existing values for those fields.` create_cluster_example = `// Set only the server field on the e2e cluster entry without touching other values. $ kubectl config set-cluster e2e --server=https://1.2.3.4 @@ -52,12 +52,12 @@ $ 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 { +func NewCmdConfigSetCluster(out io.Writer, pathOptions *PathOptions) *cobra.Command { options := &createClusterOptions{pathOptions: pathOptions} 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), - Short: "Sets a cluster entry in .kubeconfig", + Short: "Sets a cluster entry in kubeconfig", Long: create_cluster_long, Example: create_cluster_example, Run: func(cmd *cobra.Command, args []string) { @@ -74,11 +74,11 @@ func NewCmdConfigSetCluster(out io.Writer, pathOptions *pathOptions) *cobra.Comm options.insecureSkipTLSVerify.Default(false) - cmd.Flags().Var(&options.server, clientcmd.FlagAPIServer, clientcmd.FlagAPIServer+" for the cluster entry in .kubeconfig") - cmd.Flags().Var(&options.apiVersion, clientcmd.FlagAPIVersion, clientcmd.FlagAPIVersion+" for the cluster entry in .kubeconfig") - cmd.Flags().Var(&options.insecureSkipTLSVerify, clientcmd.FlagInsecure, clientcmd.FlagInsecure+" for the cluster entry in .kubeconfig") - cmd.Flags().Var(&options.certificateAuthority, clientcmd.FlagCAFile, "path to "+clientcmd.FlagCAFile+" for the cluster entry in .kubeconfig") - cmd.Flags().Var(&options.embedCAData, clientcmd.FlagEmbedCerts, clientcmd.FlagEmbedCerts+" for the cluster entry in .kubeconfig") + cmd.Flags().Var(&options.server, clientcmd.FlagAPIServer, clientcmd.FlagAPIServer+" for the cluster entry in kubeconfig") + cmd.Flags().Var(&options.apiVersion, clientcmd.FlagAPIVersion, clientcmd.FlagAPIVersion+" for the cluster entry in kubeconfig") + cmd.Flags().Var(&options.insecureSkipTLSVerify, clientcmd.FlagInsecure, clientcmd.FlagInsecure+" for the cluster entry in kubeconfig") + cmd.Flags().Var(&options.certificateAuthority, clientcmd.FlagCAFile, "path to "+clientcmd.FlagCAFile+" for the cluster entry in kubeconfig") + cmd.Flags().Var(&options.embedCAData, clientcmd.FlagEmbedCerts, clientcmd.FlagEmbedCerts+" for the cluster entry in kubeconfig") return cmd } @@ -89,20 +89,15 @@ func (o createClusterOptions) run() error { return err } - config, filename, err := o.pathOptions.getStartingConfig() + config, err := o.pathOptions.getStartingConfig() if err != nil { return err } - if config.Clusters == nil { - config.Clusters = make(map[string]clientcmdapi.Cluster) - } - cluster := o.modifyCluster(config.Clusters[o.name]) config.Clusters[o.name] = cluster - err = clientcmd.WriteToFile(*config, filename) - if err != nil { + if err := o.pathOptions.ModifyConfig(*config); err != nil { return err } @@ -174,5 +169,5 @@ func (o createClusterOptions) validate() error { } } - return nil + return o.pathOptions.Validate() } diff --git a/pkg/kubectl/cmd/config/create_context.go b/pkg/kubectl/cmd/config/create_context.go index 3869e28cf55..49f7d66f802 100644 --- a/pkg/kubectl/cmd/config/create_context.go +++ b/pkg/kubectl/cmd/config/create_context.go @@ -29,7 +29,7 @@ import ( ) type createContextOptions struct { - pathOptions *pathOptions + pathOptions *PathOptions name string cluster util.StringFlag authInfo util.StringFlag @@ -37,18 +37,18 @@ type createContextOptions struct { } const ( - create_context_long = `Sets a context entry in .kubeconfig + create_context_long = `Sets a context entry in kubeconfig Specifying a name that already exists will merge new fields on top of existing values for those fields.` create_context_example = `// Set the user field on the gce context entry without touching other values $ kubectl config set-context gce --user=cluster-admin` ) -func NewCmdConfigSetContext(out io.Writer, pathOptions *pathOptions) *cobra.Command { +func NewCmdConfigSetContext(out io.Writer, pathOptions *PathOptions) *cobra.Command { options := &createContextOptions{pathOptions: pathOptions} cmd := &cobra.Command{ Use: fmt.Sprintf("set-context NAME [--%v=cluster_nickname] [--%v=user_nickname] [--%v=namespace]", clientcmd.FlagClusterName, clientcmd.FlagAuthInfoName, clientcmd.FlagNamespace), - Short: "Sets a context entry in .kubeconfig", + Short: "Sets a context entry in kubeconfig", Long: create_context_long, Example: create_context_example, Run: func(cmd *cobra.Command, args []string) { @@ -63,9 +63,9 @@ func NewCmdConfigSetContext(out io.Writer, pathOptions *pathOptions) *cobra.Comm }, } - cmd.Flags().Var(&options.cluster, clientcmd.FlagClusterName, clientcmd.FlagClusterName+" for the context entry in .kubeconfig") - cmd.Flags().Var(&options.authInfo, clientcmd.FlagAuthInfoName, clientcmd.FlagAuthInfoName+" for the context entry in .kubeconfig") - cmd.Flags().Var(&options.namespace, clientcmd.FlagNamespace, clientcmd.FlagNamespace+" for the context entry in .kubeconfig") + cmd.Flags().Var(&options.cluster, clientcmd.FlagClusterName, clientcmd.FlagClusterName+" for the context entry in kubeconfig") + cmd.Flags().Var(&options.authInfo, clientcmd.FlagAuthInfoName, clientcmd.FlagAuthInfoName+" for the context entry in kubeconfig") + cmd.Flags().Var(&options.namespace, clientcmd.FlagNamespace, clientcmd.FlagNamespace+" for the context entry in kubeconfig") return cmd } @@ -76,7 +76,7 @@ func (o createContextOptions) run() error { return err } - config, filename, err := o.pathOptions.getStartingConfig() + config, err := o.pathOptions.getStartingConfig() if err != nil { return err } @@ -84,8 +84,7 @@ func (o createContextOptions) run() error { context := o.modifyContext(config.Contexts[o.name]) config.Contexts[o.name] = context - err = clientcmd.WriteToFile(*config, filename) - if err != nil { + if err := o.pathOptions.ModifyConfig(*config); err != nil { return err } @@ -124,5 +123,5 @@ func (o createContextOptions) validate() error { return errors.New("You must specify a non-empty context name") } - return nil + return o.pathOptions.Validate() } diff --git a/pkg/kubectl/cmd/config/set.go b/pkg/kubectl/cmd/config/set.go index b65a00f9d91..1eaf69b1ca2 100644 --- a/pkg/kubectl/cmd/config/set.go +++ b/pkg/kubectl/cmd/config/set.go @@ -24,8 +24,6 @@ import ( "strings" "github.com/spf13/cobra" - - "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd" ) const ( @@ -34,21 +32,21 @@ const ( ) type setOptions struct { - pathOptions *pathOptions + pathOptions *PathOptions propertyName string propertyValue string } -const set_long = `Sets an individual value in a .kubeconfig file +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 { +func NewCmdConfigSet(out io.Writer, pathOptions *PathOptions) *cobra.Command { options := &setOptions{pathOptions: pathOptions} cmd := &cobra.Command{ Use: "set PROPERTY_NAME PROPERTY_VALUE", - Short: "Sets an individual value in a .kubeconfig file", + Short: "Sets an individual value in a kubeconfig file", Long: set_long, Run: func(cmd *cobra.Command, args []string) { if !options.complete(cmd) { @@ -71,15 +69,10 @@ func (o setOptions) run() error { return err } - config, filename, err := o.pathOptions.getStartingConfig() + config, err := o.pathOptions.getStartingConfig() if err != nil { return err } - - if len(filename) == 0 { - return errors.New("cannot set property without using a specific file") - } - steps, err := newNavigationSteps(o.propertyName) if err != nil { return err @@ -89,8 +82,7 @@ func (o setOptions) run() error { return err } - err = clientcmd.WriteToFile(*config, filename) - if err != nil { + if err := o.pathOptions.ModifyConfig(*config); err != nil { return err } @@ -118,7 +110,7 @@ func (o setOptions) validate() error { return errors.New("You must specify a property") } - return nil + return o.pathOptions.Validate() } func modifyConfig(curr reflect.Value, steps *navigationSteps, propertyValue string, unset bool) error { diff --git a/pkg/kubectl/cmd/config/unset.go b/pkg/kubectl/cmd/config/unset.go index d0fda6a3e56..59057b1e874 100644 --- a/pkg/kubectl/cmd/config/unset.go +++ b/pkg/kubectl/cmd/config/unset.go @@ -23,24 +23,22 @@ import ( "reflect" "github.com/spf13/cobra" - - "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd" ) type unsetOptions struct { - pathOptions *pathOptions + pathOptions *PathOptions propertyName string } -const unset_long = `Unsets an individual value in a .kubeconfig file +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 { +func NewCmdConfigUnset(out io.Writer, pathOptions *PathOptions) *cobra.Command { options := &unsetOptions{pathOptions: pathOptions} cmd := &cobra.Command{ Use: "unset PROPERTY_NAME", - Short: "Unsets an individual value in a .kubeconfig file", + Short: "Unsets an individual value in a kubeconfig file", Long: unset_long, Run: func(cmd *cobra.Command, args []string) { if !options.complete(cmd) { @@ -63,15 +61,11 @@ func (o unsetOptions) run() error { return err } - config, filename, err := o.pathOptions.getStartingConfig() + config, err := o.pathOptions.getStartingConfig() if err != nil { return err } - if len(filename) == 0 { - return errors.New("cannot set property without using a specific file") - } - steps, err := newNavigationSteps(o.propertyName) if err != nil { return err @@ -81,8 +75,7 @@ func (o unsetOptions) run() error { return err } - err = clientcmd.WriteToFile(*config, filename) - if err != nil { + if err := o.pathOptions.ModifyConfig(*config); err != nil { return err } @@ -105,5 +98,5 @@ func (o unsetOptions) validate() error { return errors.New("You must specify a property") } - return nil + return o.pathOptions.Validate() } diff --git a/pkg/kubectl/cmd/config/use_context.go b/pkg/kubectl/cmd/config/use_context.go index 0508fd1b707..98a50167e0d 100644 --- a/pkg/kubectl/cmd/config/use_context.go +++ b/pkg/kubectl/cmd/config/use_context.go @@ -22,22 +22,20 @@ import ( "io" "github.com/spf13/cobra" - - "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd" ) type useContextOptions struct { - pathOptions *pathOptions + pathOptions *PathOptions contextName string } -func NewCmdConfigUseContext(out io.Writer, pathOptions *pathOptions) *cobra.Command { +func NewCmdConfigUseContext(out io.Writer, pathOptions *PathOptions) *cobra.Command { options := &useContextOptions{pathOptions: pathOptions} cmd := &cobra.Command{ Use: "use-context CONTEXT_NAME", - Short: "Sets the current-context in a .kubeconfig file", - Long: `Sets the current-context in a .kubeconfig file`, + Short: "Sets the current-context in a kubeconfig file", + Long: `Sets the current-context in a kubeconfig file`, Run: func(cmd *cobra.Command, args []string) { if !options.complete(cmd) { return @@ -59,19 +57,14 @@ func (o useContextOptions) run() error { return err } - config, filename, err := o.pathOptions.getStartingConfig() + config, err := o.pathOptions.getStartingConfig() if err != nil { return err } - if len(filename) == 0 { - return errors.New("cannot set current-context without using a specific file") - } - config.CurrentContext = o.contextName - err = clientcmd.WriteToFile(*config, filename) - if err != nil { + if err := o.pathOptions.ModifyConfig(*config); err != nil { return err } @@ -94,5 +87,5 @@ func (o useContextOptions) validate() error { return errors.New("You must specify a current-context") } - return nil + return o.pathOptions.Validate() } diff --git a/pkg/kubectl/cmd/config/view.go b/pkg/kubectl/cmd/config/view.go index 9b198c8e031..41b69bb9a60 100644 --- a/pkg/kubectl/cmd/config/view.go +++ b/pkg/kubectl/cmd/config/view.go @@ -17,7 +17,7 @@ limitations under the License. package config import ( - "fmt" + "errors" "io" "github.com/golang/glog" @@ -32,30 +32,30 @@ import ( ) type viewOptions struct { - pathOptions *pathOptions + pathOptions *PathOptions merge util.BoolFlag } const ( - view_long = `displays merged .kubeconfig settings or a specified .kubeconfig file. + view_long = `displays merged kubeconfig settings or a specified kubeconfig file. You can use --output=template --template=TEMPLATE to extract specific values.` - view_example = `// Show merged .kubeconfig settings. + view_example = `// Show merged kubeconfig settings. $ kubectl config view -// Show only local ./.kubeconfig settings +// Show only local kubeconfig settings $ kubectl config view --local // Get the password for the e2e user $ 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 { +func NewCmdConfigView(out io.Writer, pathOptions *PathOptions) *cobra.Command { options := &viewOptions{pathOptions: pathOptions} cmd := &cobra.Command{ Use: "view", - Short: "displays merged .kubeconfig settings or a specified .kubeconfig file.", + Short: "displays merged kubeconfig settings or a specified kubeconfig file.", Long: view_long, Example: view_example, Run: func(cmd *cobra.Command, args []string) { @@ -85,13 +85,13 @@ func NewCmdConfigView(out io.Writer, pathOptions *pathOptions) *cobra.Command { cmd.Flags().Set("output", "yaml") options.merge.Default(true) - cmd.Flags().Var(&options.merge, "merge", "merge together the full hierarchy of .kubeconfig files") + cmd.Flags().Var(&options.merge, "merge", "merge together the full hierarchy of kubeconfig files") return cmd } 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 (len(o.pathOptions.specifiedFile) > 0) || o.pathOptions.global || o.pathOptions.local { + if o.pathOptions.Global || o.pathOptions.Local || o.pathOptions.UseEnvVar { if !o.merge.Provided() { o.merge.Set("false") } @@ -106,29 +106,36 @@ func (o viewOptions) loadConfig() (*clientcmdapi.Config, error) { return nil, err } - config, _, err := o.getStartingConfig() + config, err := o.getStartingConfig() return config, err } func (o viewOptions) validate() error { - return nil + return o.pathOptions.Validate() } // getStartingConfig returns the Config object built from the sources specified by the options, the filename read (only if it was a single file), and an error if something goes wrong -func (o *viewOptions) getStartingConfig() (*clientcmdapi.Config, string, error) { +func (o *viewOptions) getStartingConfig() (*clientcmdapi.Config, error) { switch { - case o.merge.Value(): - loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() - loadingRules.ExplicitPath = o.pathOptions.specifiedFile + case !o.merge.Value(): + switch { + case len(o.pathOptions.LoadingRules.ExplicitPath) > 0: + return clientcmd.LoadFromFile(o.pathOptions.LoadingRules.ExplicitPath) - overrides := &clientcmd.ConfigOverrides{} - clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, overrides) + 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") - config, err := clientConfig.RawConfig() - if err != nil { - return nil, "", fmt.Errorf("Error getting config: %v", err) } - return &config, "", nil + default: return o.pathOptions.getStartingConfig() }