From 05bc508a13bc4c0540390928adb76fbe04b527ef Mon Sep 17 00:00:00 2001 From: deads2k Date: Fri, 23 Jan 2015 14:18:25 -0500 Subject: [PATCH] make all kubectl config set-* easier to use --- docs/kubectl.md | 58 +++++++++++--------- pkg/kubectl/cmd/config/config_test.go | 16 +++--- pkg/kubectl/cmd/config/create_authinfo.go | 49 ++++++++++------- pkg/kubectl/cmd/config/create_cluster.go | 51 +++++++++++------- pkg/kubectl/cmd/config/create_context.go | 42 +++++++++------ pkg/util/bool_flag.go | 64 +++++++++++++++++++++++ pkg/util/string_flag.go | 54 +++++++++++++++++++ 7 files changed, 246 insertions(+), 88 deletions(-) create mode 100644 pkg/util/bool_flag.go create mode 100644 pkg/util/string_flag.go diff --git a/docs/kubectl.md b/docs/kubectl.md index 3251005ce5c..3423bd87464 100644 --- a/docs/kubectl.md +++ b/docs/kubectl.md @@ -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 diff --git a/pkg/kubectl/cmd/config/config_test.go b/pkg/kubectl/cmd/config/config_test.go index 069760872cb..60949928e5a 100644 --- a/pkg/kubectl/cmd/config/config_test.go +++ b/pkg/kubectl/cmd/config/config_test.go @@ -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, } diff --git a/pkg/kubectl/cmd/config/create_authinfo.go b/pkg/kubectl/cmd/config/create_authinfo.go index 813390d177f..814988bfb1a 100644 --- a/pkg/kubectl/cmd/config/create_authinfo.go +++ b/pkg/kubectl/cmd/config/create_authinfo.go @@ -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 { diff --git a/pkg/kubectl/cmd/config/create_cluster.go b/pkg/kubectl/cmd/config/create_cluster.go index ee9b49b3a26..933aafb3602 100644 --- a/pkg/kubectl/cmd/config/create_cluster.go +++ b/pkg/kubectl/cmd/config/create_cluster.go @@ -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 { diff --git a/pkg/kubectl/cmd/config/create_context.go b/pkg/kubectl/cmd/config/create_context.go index c77fba7c0f3..2da9b859c91 100644 --- a/pkg/kubectl/cmd/config/create_context.go +++ b/pkg/kubectl/cmd/config/create_context.go @@ -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 { diff --git a/pkg/util/bool_flag.go b/pkg/util/bool_flag.go new file mode 100644 index 00000000000..fef6b3f7fd8 --- /dev/null +++ b/pkg/util/bool_flag.go @@ -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" +} diff --git a/pkg/util/string_flag.go b/pkg/util/string_flag.go new file mode 100644 index 00000000000..6aa67f419a1 --- /dev/null +++ b/pkg/util/string_flag.go @@ -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" +}