Merge pull request #3768 from deads2k/deads-make-kubectl-config-easier-alt

make kubectl config set-cluster easier to use
This commit is contained in:
Jeff Lowdermilk 2015-01-26 13:13:26 -08:00
commit 6ba8b7dfb2
7 changed files with 246 additions and 88 deletions

View File

@ -344,13 +344,13 @@ Usage:
kubectl config [command]
Available Commands:
view displays the specified .kubeconfig file or a merged result
set-cluster name [server] [insecure-skip-tls-verify] [certificate-authority] [api-version] Sets a cluster entry in .kubeconfig
set-credentials name Sets a user entry in .kubeconfig
set-context name Sets a context entry in .kubeconfig
set property-name property-value Sets an individual value in a .kubeconfig file
unset property-name Unsets an individual value in a .kubeconfig file
use-context context-name Sets the current-context in a .kubeconfig file
view displays the specified .kubeconfig file or a merged result
set-cluster name [--server=server] [--certificate-authority=path/to/certficate/authority] [--api-version=apiversion] [--insecure-skip-tls-verify=true] Sets a cluster entry in .kubeconfig
set-credentials name [--auth-path=path/to/auth/file] [--client-certificate=path/to/certficate/file] [--client-key=path/to/key/file] [--token=bearer_token_string] Sets a user entry in .kubeconfig
set-context name [--cluster=cluster-nickname] [--user=user-nickname] [--namespace=namespace] Sets a context entry in .kubeconfig
set property-name property-value Sets an individual value in a .kubeconfig file
unset property-name Unsets an individual value in a .kubeconfig file
use-context context-name Sets the current-context in a .kubeconfig file
Available Flags:
--alsologtostderr=false: log to standard error as well as files
@ -440,19 +440,21 @@ Usage:
#### config set-cluster
Sets a cluster entry in .kubeconfig
Specifying a name that already exists overwrites that cluster entry.
Specifying a name that already exists will merge new fields on top of existing values for those fields.
e.g.
kubectl config set-cluster e2e --certificate-authority=~/.kube/e2e/.kubernetes.ca.cert
only sets the certificate-authority field on the e2e cluster entry without touching other values.
Usage:
```
kubectl config set-cluster name [server] [insecure-skip-tls-verify] [certificate-authority] [api-version] [flags]
kubectl config set-cluster name [--server=server] [--certificate-authority=path/to/certficate/authority] [--api-version=apiversion] [--insecure-skip-tls-verify=true] [flags]
Available Flags:
--alsologtostderr=false: log to standard error as well as files
--api-version="": api-version for the cluster entry in .kubeconfig
--api-version=: api-version for the cluster entry in .kubeconfig
-a, --auth-path="": Path to the auth info file. If missing, prompt the user. Only used if using https.
--certificate-authority="": certificate-authority for the cluster entry in .kubeconfig
--certificate-authority=: certificate-authority for the cluster entry in .kubeconfig
--client-certificate="": Path to a client key file for TLS.
--client-key="": Path to a client key file for TLS.
--cluster="": The name of the kubeconfig cluster to use
@ -469,7 +471,7 @@ Usage:
--match-server-version=false: Require server version to match client version
--namespace="": If present, the namespace scope for this CLI request.
--ns-path="": Path to the namespace info file that holds the namespace context to use for CLI requests.
--server="": server for the cluster entry in .kubeconfig
--server=: server for the cluster entry in .kubeconfig
--stderrthreshold=2: logs at or above this threshold go to stderr
--token="": Bearer token for authentication to the API server.
--user="": The name of the kubeconfig user to use
@ -481,21 +483,23 @@ Usage:
#### config set-credentials
Sets a user entry in .kubeconfig
Specifying a name that already exists overwrites that user entry.
Specifying a name that already exists will merge new fields on top of existing values for those fields.
e.g.
kubectl config set-credentials cluster-admin --client-key=~/.kube/cluster-admin/.kubecfg.key
only sets the client-key field on the cluster-admin user entry without touching other values.
Usage:
```
kubectl config set-credentials name [flags]
kubectl config set-credentials name [--auth-path=path/to/auth/file] [--client-certificate=path/to/certficate/file] [--client-key=path/to/key/file] [--token=bearer_token_string] [flags]
Available Flags:
--alsologtostderr=false: log to standard error as well as files
--api-version="": The API version to use when talking to the server
--auth-path="": auth-path for the user entry in .kubeconfig
--auth-path=: auth-path for the user entry in .kubeconfig
--certificate-authority="": Path to a cert. file for the certificate authority.
--client-certificate="": client-certificate for the user entry in .kubeconfig
--client-key="": client-key for the user entry in .kubeconfig
--client-certificate=: client-certificate for the user entry in .kubeconfig
--client-key=: client-key for the user entry in .kubeconfig
--cluster="": The name of the kubeconfig cluster to use
--context="": The name of the kubeconfig context to use
--global=false: use the .kubeconfig from /home/username
@ -512,7 +516,7 @@ Usage:
--ns-path="": Path to the namespace info file that holds the namespace context to use for CLI requests.
-s, --server="": The address of the Kubernetes API server
--stderrthreshold=2: logs at or above this threshold go to stderr
--token="": token for the user entry in .kubeconfig
--token=: token for the user entry in .kubeconfig
--user="": The name of the kubeconfig user to use
--v=0: log level for V logs
--validate=false: If true, use a schema to validate the input before sending it
@ -522,13 +526,15 @@ Usage:
#### config set-context
Sets a context entry in .kubeconfig
Specifying a name that already exists overwrites that context entry.
Specifying a name that already exists will merge new fields on top of existing values for those fields.
e.g.
kubectl config set-context gce --user=cluster-admin
only sets the user field on the gce context entry without touching other values.
Usage:
```
kubectl config set-context name [flags]
kubectl config set-context name [--cluster=cluster-nickname] [--user=user-nickname] [--namespace=namespace] [flags]
Available Flags:
--alsologtostderr=false: log to standard error as well as files
@ -537,7 +543,7 @@ Usage:
--certificate-authority="": Path to a cert. file for the certificate authority.
--client-certificate="": Path to a client key file for TLS.
--client-key="": Path to a client key file for TLS.
--cluster="": cluster for the context entry in .kubeconfig
--cluster=: cluster for the context entry in .kubeconfig
--context="": The name of the kubeconfig context to use
--global=false: use the .kubeconfig from /home/username
-h, --help=false: help for set-context
@ -549,12 +555,12 @@ Usage:
--log_flush_frequency=5s: Maximum number of seconds between log flushes
--logtostderr=true: log to standard error instead of files
--match-server-version=false: Require server version to match client version
--namespace="": namespace for the context entry in .kubeconfig
--namespace=: namespace for the context entry in .kubeconfig
--ns-path="": Path to the namespace info file that holds the namespace context to use for CLI requests.
-s, --server="": The address of the Kubernetes API server
--stderrthreshold=2: logs at or above this threshold go to stderr
--token="": Bearer token for authentication to the API server.
--user="": user for the context entry in .kubeconfig
--user=: user for the context entry in .kubeconfig
--v=0: log level for V logs
--validate=false: If true, use a schema to validate the input before sending it
--vmodule=: comma-separated list of pattern=N settings for file-filtered logging

View File

@ -35,7 +35,7 @@ func newRedFederalCowHammerConfig() clientcmdapi.Config {
Clusters: map[string]clientcmdapi.Cluster{
"cow-cluster": {Server: "http://cow.org:8080"}},
Contexts: map[string]clientcmdapi.Context{
"federal-context": {AuthInfo: "red-user", Cluster: "cow-cluster", Namespace: "hammer-ns"}},
"federal-context": {AuthInfo: "red-user", Cluster: "cow-cluster"}},
}
}
@ -166,11 +166,11 @@ func TestAdditionalAuth(t *testing.T) {
test.run(t)
}
func TestOverwriteExistingAuth(t *testing.T) {
func TestMergeExistingAuth(t *testing.T) {
expectedConfig := newRedFederalCowHammerConfig()
authInfo := clientcmdapi.NewAuthInfo()
authInfo := expectedConfig.AuthInfos["red-user"]
authInfo.AuthPath = "auth-path"
expectedConfig.AuthInfos["red-user"] = *authInfo
expectedConfig.AuthInfos["red-user"] = authInfo
test := configCommandTest{
args: []string{"set-credentials", "red-user", "--" + clientcmd.FlagAuthPath + "=auth-path"},
startingConfig: newRedFederalCowHammerConfig(),
@ -252,14 +252,14 @@ func TestAdditionalContext(t *testing.T) {
test.run(t)
}
func TestOverwriteExistingContext(t *testing.T) {
func TestMergeExistingContext(t *testing.T) {
expectedConfig := newRedFederalCowHammerConfig()
context := *clientcmdapi.NewContext()
context.Cluster = "clustername"
context := expectedConfig.Contexts["federal-context"]
context.Namespace = "hammer"
expectedConfig.Contexts["federal-context"] = context
test := configCommandTest{
args: []string{"set-context", "federal-context", "--" + clientcmd.FlagClusterName + "=clustername"},
args: []string{"set-context", "federal-context", "--" + clientcmd.FlagNamespace + "=hammer"},
startingConfig: newRedFederalCowHammerConfig(),
expectedConfig: expectedConfig,
}

View File

@ -25,26 +25,29 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
)
type createAuthInfoOptions struct {
pathOptions *pathOptions
name string
authPath string
clientCertificate string
clientKey string
token string
authPath util.StringFlag
clientCertificate util.StringFlag
clientKey util.StringFlag
token util.StringFlag
}
func NewCmdConfigSetAuthInfo(out io.Writer, pathOptions *pathOptions) *cobra.Command {
options := &createAuthInfoOptions{pathOptions: pathOptions}
cmd := &cobra.Command{
Use: "set-credentials name",
Use: fmt.Sprintf("set-credentials name [--%v=path/to/auth/file] [--%v=path/to/certficate/file] [--%v=path/to/key/file] [--%v=bearer_token_string]", clientcmd.FlagAuthPath, clientcmd.FlagCertFile, clientcmd.FlagKeyFile, clientcmd.FlagBearerToken),
Short: "Sets a user entry in .kubeconfig",
Long: `Sets a user entry in .kubeconfig
Specifying a name that already exists overwrites that user entry.
Specifying a name that already exists will merge new fields on top of existing values for those fields.
e.g.
kubectl config set-credentials cluster-admin --client-key=~/.kube/cluster-admin/.kubecfg.key
only sets the client-key field on the cluster-admin user entry without touching other values.
`,
Run: func(cmd *cobra.Command, args []string) {
if !options.complete(cmd) {
@ -58,10 +61,10 @@ func NewCmdConfigSetAuthInfo(out io.Writer, pathOptions *pathOptions) *cobra.Com
},
}
cmd.Flags().StringVar(&options.authPath, clientcmd.FlagAuthPath, "", clientcmd.FlagAuthPath+" for the user entry in .kubeconfig")
cmd.Flags().StringVar(&options.clientCertificate, clientcmd.FlagCertFile, "", clientcmd.FlagCertFile+" for the user entry in .kubeconfig")
cmd.Flags().StringVar(&options.clientKey, clientcmd.FlagKeyFile, "", clientcmd.FlagKeyFile+" for the user entry in .kubeconfig")
cmd.Flags().StringVar(&options.token, clientcmd.FlagBearerToken, "", clientcmd.FlagBearerToken+" for the user entry in .kubeconfig")
cmd.Flags().Var(&options.authPath, clientcmd.FlagAuthPath, clientcmd.FlagAuthPath+" for the user entry in .kubeconfig")
cmd.Flags().Var(&options.clientCertificate, clientcmd.FlagCertFile, clientcmd.FlagCertFile+" for the user entry in .kubeconfig")
cmd.Flags().Var(&options.clientKey, clientcmd.FlagKeyFile, clientcmd.FlagKeyFile+" for the user entry in .kubeconfig")
cmd.Flags().Var(&options.token, clientcmd.FlagBearerToken, clientcmd.FlagBearerToken+" for the user entry in .kubeconfig")
return cmd
}
@ -77,7 +80,7 @@ func (o createAuthInfoOptions) run() error {
return err
}
authInfo := o.authInfo()
authInfo := o.modifyAuthInfo(config.AuthInfos[o.name])
config.AuthInfos[o.name] = authInfo
err = clientcmd.WriteToFile(*config, filename)
@ -89,15 +92,23 @@ func (o createAuthInfoOptions) run() error {
}
// authInfo builds an AuthInfo object from the options
func (o *createAuthInfoOptions) authInfo() clientcmdapi.AuthInfo {
authInfo := clientcmdapi.AuthInfo{
AuthPath: o.authPath,
ClientCertificate: o.clientCertificate,
ClientKey: o.clientKey,
Token: o.token,
func (o *createAuthInfoOptions) modifyAuthInfo(existingAuthInfo clientcmdapi.AuthInfo) clientcmdapi.AuthInfo {
modifiedAuthInfo := existingAuthInfo
if o.authPath.Provided() {
modifiedAuthInfo.AuthPath = o.authPath.Value()
}
if o.clientCertificate.Provided() {
modifiedAuthInfo.ClientCertificate = o.clientCertificate.Value()
}
if o.clientKey.Provided() {
modifiedAuthInfo.ClientKey = o.clientKey.Value()
}
if o.token.Provided() {
modifiedAuthInfo.Token = o.token.Value()
}
return authInfo
return modifiedAuthInfo
}
func (o *createAuthInfoOptions) complete(cmd *cobra.Command) bool {

View File

@ -25,26 +25,29 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
)
type createClusterOptions struct {
pathOptions *pathOptions
name string
server string
apiVersion string
insecureSkipTLSVerify bool
certificateAuthority string
server util.StringFlag
apiVersion util.StringFlag
insecureSkipTLSVerify util.BoolFlag
certificateAuthority util.StringFlag
}
func NewCmdConfigSetCluster(out io.Writer, pathOptions *pathOptions) *cobra.Command {
options := &createClusterOptions{pathOptions: pathOptions}
cmd := &cobra.Command{
Use: "set-cluster name [server] [insecure-skip-tls-verify] [certificate-authority] [api-version]",
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",
Long: `Sets a cluster entry in .kubeconfig
Specifying a name that already exists overwrites that cluster entry.
Specifying a name that already exists will merge new fields on top of existing values for those fields.
e.g.
kubectl config set-cluster e2e --certificate-authority=~/.kube/e2e/.kubernetes.ca.cert
only sets the certificate-authority field on the e2e cluster entry without touching other values.
`,
Run: func(cmd *cobra.Command, args []string) {
if !options.complete(cmd) {
@ -58,10 +61,12 @@ func NewCmdConfigSetCluster(out io.Writer, pathOptions *pathOptions) *cobra.Comm
},
}
cmd.Flags().StringVar(&options.server, clientcmd.FlagAPIServer, "", clientcmd.FlagAPIServer+" for the cluster entry in .kubeconfig")
cmd.Flags().StringVar(&options.apiVersion, clientcmd.FlagAPIVersion, "", clientcmd.FlagAPIVersion+" for the cluster entry in .kubeconfig")
cmd.Flags().BoolVar(&options.insecureSkipTLSVerify, clientcmd.FlagInsecure, false, clientcmd.FlagInsecure+" for the cluster entry in .kubeconfig")
cmd.Flags().StringVar(&options.certificateAuthority, clientcmd.FlagCAFile, "", clientcmd.FlagCAFile+" for the cluster entry in .kubeconfig")
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, clientcmd.FlagCAFile+" for the cluster entry in .kubeconfig")
return cmd
}
@ -81,7 +86,7 @@ func (o createClusterOptions) run() error {
config.Clusters = make(map[string]clientcmdapi.Cluster)
}
cluster := o.cluster()
cluster := o.modifyCluster(config.Clusters[o.name])
config.Clusters[o.name] = cluster
err = clientcmd.WriteToFile(*config, filename)
@ -93,15 +98,23 @@ func (o createClusterOptions) run() error {
}
// cluster builds a Cluster object from the options
func (o *createClusterOptions) cluster() clientcmdapi.Cluster {
cluster := clientcmdapi.Cluster{
Server: o.server,
APIVersion: o.apiVersion,
InsecureSkipTLSVerify: o.insecureSkipTLSVerify,
CertificateAuthority: o.certificateAuthority,
func (o *createClusterOptions) modifyCluster(existingCluster clientcmdapi.Cluster) clientcmdapi.Cluster {
modifiedCluster := existingCluster
if o.server.Provided() {
modifiedCluster.Server = o.server.Value()
}
if o.apiVersion.Provided() {
modifiedCluster.APIVersion = o.apiVersion.Value()
}
if o.insecureSkipTLSVerify.Provided() {
modifiedCluster.InsecureSkipTLSVerify = o.insecureSkipTLSVerify.Value()
}
if o.certificateAuthority.Provided() {
modifiedCluster.CertificateAuthority = o.certificateAuthority.Value()
}
return cluster
return modifiedCluster
}
func (o *createClusterOptions) complete(cmd *cobra.Command) bool {

View File

@ -25,26 +25,30 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
)
type createContextOptions struct {
pathOptions *pathOptions
name string
cluster string
authInfo string
namespace string
cluster util.StringFlag
authInfo util.StringFlag
namespace util.StringFlag
}
func NewCmdConfigSetContext(out io.Writer, pathOptions *pathOptions) *cobra.Command {
options := &createContextOptions{pathOptions: pathOptions}
cmd := &cobra.Command{
Use: "set-context name",
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",
Long: `Sets a context entry in .kubeconfig
Specifying a name that already exists overwrites that context entry.
Specifying a name that already exists will merge new fields on top of existing values for those fields.
e.g.
kubectl config set-context gce --user=cluster-admin
only sets the user field on the gce context entry without touching other values.
`,
Run: func(cmd *cobra.Command, args []string) {
if !options.complete(cmd) {
return
@ -57,9 +61,9 @@ func NewCmdConfigSetContext(out io.Writer, pathOptions *pathOptions) *cobra.Comm
},
}
cmd.Flags().StringVar(&options.cluster, clientcmd.FlagClusterName, "", clientcmd.FlagClusterName+" for the context entry in .kubeconfig")
cmd.Flags().StringVar(&options.authInfo, clientcmd.FlagAuthInfoName, "", clientcmd.FlagAuthInfoName+" for the context entry in .kubeconfig")
cmd.Flags().StringVar(&options.namespace, clientcmd.FlagNamespace, "", clientcmd.FlagNamespace+" for the context entry in .kubeconfig")
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
}
@ -75,7 +79,7 @@ func (o createContextOptions) run() error {
return err
}
context := o.context()
context := o.modifyContext(config.Contexts[o.name])
config.Contexts[o.name] = context
err = clientcmd.WriteToFile(*config, filename)
@ -86,14 +90,20 @@ func (o createContextOptions) run() error {
return nil
}
func (o *createContextOptions) context() clientcmdapi.Context {
context := clientcmdapi.Context{
Cluster: o.cluster,
AuthInfo: o.authInfo,
Namespace: o.namespace,
func (o *createContextOptions) modifyContext(existingContext clientcmdapi.Context) clientcmdapi.Context {
modifiedContext := existingContext
if o.cluster.Provided() {
modifiedContext.Cluster = o.cluster.Value()
}
if o.authInfo.Provided() {
modifiedContext.AuthInfo = o.authInfo.Value()
}
if o.namespace.Provided() {
modifiedContext.Namespace = o.namespace.Value()
}
return context
return modifiedContext
}
func (o *createContextOptions) complete(cmd *cobra.Command) bool {

64
pkg/util/bool_flag.go Normal file
View File

@ -0,0 +1,64 @@
/*
Copyright 2014 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"fmt"
"strconv"
)
// BoolFlag is a boolean flag compatible with flags and pflags that keeps track of whether it had a value supplied or not.
// Beware! If you use this type, you must actually specify --flag-name=true, you cannot leave it as --flag-name and still have
// the value set
type BoolFlag struct {
// If Set has been invoked this value is true
provided bool
// The exact value provided on the flag
value bool
}
func (f *BoolFlag) Default(value bool) {
f.value = value
}
func (f BoolFlag) String() string {
return fmt.Sprintf("%t", f.value)
}
func (f BoolFlag) Value() bool {
return f.value
}
func (f *BoolFlag) Set(value string) error {
boolVal, err := strconv.ParseBool(value)
if err != nil {
return err
}
f.value = boolVal
f.provided = true
return nil
}
func (f BoolFlag) Provided() bool {
return f.provided
}
func (f *BoolFlag) Type() string {
return "bool"
}

54
pkg/util/string_flag.go Normal file
View File

@ -0,0 +1,54 @@
/*
Copyright 2014 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import ()
// StringFlag is a string flag compatible with flags and pflags that keeps track of whether it had a value supplied or not.
type StringFlag struct {
// If Set has been invoked this value is true
provided bool
// The exact value provided on the flag
value string
}
func (f *StringFlag) Default(value string) {
f.value = value
}
func (f StringFlag) String() string {
return f.value
}
func (f StringFlag) Value() string {
return f.value
}
func (f *StringFlag) Set(value string) error {
f.value = value
f.provided = true
return nil
}
func (f StringFlag) Provided() bool {
return f.provided
}
func (f *StringFlag) Type() string {
return "string"
}