Browse Source

Merge pull request #4867 from xiang90/ctl_env

etcdctlv3: accept evn for global configuration flags
Xiang Li 9 years ago
parent
commit
36db6cd982

+ 2 - 0
etcdctlv3/command/ep_health_command.go

@@ -20,6 +20,7 @@ import (
 	"time"
 
 	"github.com/coreos/etcd/clientv3"
+	"github.com/coreos/etcd/pkg/flags"
 	"github.com/spf13/cobra"
 )
 
@@ -35,6 +36,7 @@ func NewEpHealthCommand() *cobra.Command {
 
 // epHealthCommandFunc executes the "endpoint-health" command.
 func epHealthCommandFunc(cmd *cobra.Command, args []string) {
+	flags.SetPflagsFromEnv("ETCDCTL", cmd.InheritedFlags())
 	endpoints, err := cmd.Flags().GetStringSlice("endpoints")
 	if err != nil {
 		ExitWithError(ExitError, err)

+ 3 - 0
etcdctlv3/command/global.go

@@ -22,6 +22,7 @@ import (
 	"time"
 
 	"github.com/coreos/etcd/clientv3"
+	"github.com/coreos/etcd/pkg/flags"
 	"github.com/coreos/etcd/pkg/transport"
 	"github.com/spf13/cobra"
 )
@@ -51,6 +52,8 @@ type secureCfg struct {
 var display printer = &simplePrinter{}
 
 func mustClientFromCmd(cmd *cobra.Command) *clientv3.Client {
+	flags.SetPflagsFromEnv("ETCDCTL", cmd.InheritedFlags())
+
 	endpoints, err := cmd.Flags().GetStringSlice("endpoints")
 	if err != nil {
 		ExitWithError(ExitError, err)

+ 1 - 1
etcdmain/config.go

@@ -265,7 +265,7 @@ func (cfg *config) Parse(arguments []string) error {
 		os.Exit(0)
 	}
 
-	err := flags.SetFlagsFromEnv(cfg.FlagSet)
+	err := flags.SetFlagsFromEnv("ETCD", cfg.FlagSet)
 	if err != nil {
 		plog.Fatalf("%v", err)
 	}

+ 50 - 17
pkg/flags/flag.go

@@ -24,6 +24,7 @@ import (
 
 	"github.com/coreos/etcd/pkg/transport"
 	"github.com/coreos/pkg/capnslog"
+	"github.com/spf13/pflag"
 )
 
 var (
@@ -68,29 +69,46 @@ func (f *IgnoredFlag) String() string {
 // SetFlagsFromEnv parses all registered flags in the given flagset,
 // and if they are not already set it attempts to set their values from
 // environment variables. Environment variables take the name of the flag but
-// are UPPERCASE, have the prefix "ETCD_", and any dashes are replaced by
+// are UPPERCASE, have the given prefix  and any dashes are replaced by
 // underscores - for example: some-flag => ETCD_SOME_FLAG
-func SetFlagsFromEnv(fs *flag.FlagSet) error {
+func SetFlagsFromEnv(prefix string, fs *flag.FlagSet) error {
 	var err error
 	alreadySet := make(map[string]bool)
 	fs.Visit(func(f *flag.Flag) {
-		alreadySet[flagToEnv(f.Name)] = true
+		alreadySet[flagToEnv(prefix, f.Name)] = true
 	})
 	usedEnvKey := make(map[string]bool)
 	fs.VisitAll(func(f *flag.Flag) {
-		key := flagToEnv(f.Name)
-		if !alreadySet[key] {
-			val := os.Getenv(key)
-			if val != "" {
-				usedEnvKey[key] = true
-				if serr := fs.Set(f.Name, val); serr != nil {
-					err = fmt.Errorf("invalid value %q for %s: %v", val, key, serr)
-				}
-				plog.Infof("recognized and used environment variable %s=%s", key, val)
-			}
+		err = setFlagFromEnv(fs, prefix, f.Name, usedEnvKey, alreadySet, true)
+	})
+
+	verifyEnv(prefix, usedEnvKey, alreadySet)
+
+	return err
+}
+
+// SetPflagsFromEnv is similar to SetFlagsFromEnv. However, the accepted flagset type is pflag.FlagSet
+// and it does not do any logging.
+func SetPflagsFromEnv(prefix string, fs *pflag.FlagSet) error {
+	var err error
+	alreadySet := make(map[string]bool)
+	usedEnvKey := make(map[string]bool)
+	fs.VisitAll(func(f *pflag.Flag) {
+		if f.Changed {
+			alreadySet[flagToEnv(prefix, f.Name)] = true
+		}
+		if serr := setFlagFromEnv(fs, prefix, f.Name, usedEnvKey, alreadySet, false); serr != nil {
+			err = serr
 		}
 	})
+	return err
+}
+
+func flagToEnv(prefix, name string) string {
+	return prefix + "_" + strings.ToUpper(strings.Replace(name, "-", "_", -1))
+}
 
+func verifyEnv(prefix string, usedEnvKey, alreadySet map[string]bool) {
 	for _, env := range os.Environ() {
 		kv := strings.SplitN(env, "=", 2)
 		if len(kv) != 2 {
@@ -103,16 +121,31 @@ func SetFlagsFromEnv(fs *flag.FlagSet) error {
 			plog.Infof("recognized environment variable %s, but unused: shadowed by corresponding flag ", kv[0])
 			continue
 		}
-		if strings.HasPrefix(env, "ETCD_") {
+		if strings.HasPrefix(env, prefix) {
 			plog.Warningf("unrecognized environment variable %s", env)
 		}
 	}
+}
 
-	return err
+type flagSetter interface {
+	Set(fk string, fv string) error
 }
 
-func flagToEnv(name string) string {
-	return "ETCD_" + strings.ToUpper(strings.Replace(name, "-", "_", -1))
+func setFlagFromEnv(fs flagSetter, prefix, fname string, usedEnvKey, alreadySet map[string]bool, log bool) error {
+	key := flagToEnv(prefix, fname)
+	if !alreadySet[key] {
+		val := os.Getenv(key)
+		if val != "" {
+			usedEnvKey[key] = true
+			if serr := fs.Set(fname, val); serr != nil {
+				return fmt.Errorf("invalid value %q for %s: %v", val, key, serr)
+			}
+			if log {
+				plog.Infof("recognized and used environment variable %s=%s", key, val)
+			}
+		}
+	}
+	return nil
 }
 
 // SetBindAddrFromAddr sets the value of bindAddr flag from the value

+ 2 - 2
pkg/flags/flag_test.go

@@ -56,7 +56,7 @@ func TestSetFlagsFromEnv(t *testing.T) {
 	}
 
 	// now read the env and verify flags were updated as expected
-	err := SetFlagsFromEnv(fs)
+	err := SetFlagsFromEnv("ETCD", fs)
 	if err != nil {
 		t.Errorf("err=%v, want nil", err)
 	}
@@ -76,7 +76,7 @@ func TestSetFlagsFromEnvBad(t *testing.T) {
 	fs := flag.NewFlagSet("testing", flag.ExitOnError)
 	fs.Int("x", 0, "")
 	os.Setenv("ETCD_X", "not_a_number")
-	if err := SetFlagsFromEnv(fs); err == nil {
+	if err := SetFlagsFromEnv("ETCD", fs); err == nil {
 		t.Errorf("err=nil, want != nil")
 	}
 }