flag.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. // Copyright 2015 The etcd Authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. // Package flags implements command-line flag parsing.
  15. package flags
  16. import (
  17. "flag"
  18. "fmt"
  19. "os"
  20. "strings"
  21. "github.com/coreos/pkg/capnslog"
  22. "github.com/spf13/pflag"
  23. )
  24. var plog = capnslog.NewPackageLogger("go.etcd.io/etcd", "pkg/flags")
  25. // SetFlagsFromEnv parses all registered flags in the given flagset,
  26. // and if they are not already set it attempts to set their values from
  27. // environment variables. Environment variables take the name of the flag but
  28. // are UPPERCASE, have the given prefix and any dashes are replaced by
  29. // underscores - for example: some-flag => ETCD_SOME_FLAG
  30. func SetFlagsFromEnv(prefix string, fs *flag.FlagSet) error {
  31. var err error
  32. alreadySet := make(map[string]bool)
  33. fs.Visit(func(f *flag.Flag) {
  34. alreadySet[FlagToEnv(prefix, f.Name)] = true
  35. })
  36. usedEnvKey := make(map[string]bool)
  37. fs.VisitAll(func(f *flag.Flag) {
  38. if serr := setFlagFromEnv(fs, prefix, f.Name, usedEnvKey, alreadySet, true); serr != nil {
  39. err = serr
  40. }
  41. })
  42. verifyEnv(prefix, usedEnvKey, alreadySet)
  43. return err
  44. }
  45. // SetPflagsFromEnv is similar to SetFlagsFromEnv. However, the accepted flagset type is pflag.FlagSet
  46. // and it does not do any logging.
  47. func SetPflagsFromEnv(prefix string, fs *pflag.FlagSet) error {
  48. var err error
  49. alreadySet := make(map[string]bool)
  50. usedEnvKey := make(map[string]bool)
  51. fs.VisitAll(func(f *pflag.Flag) {
  52. if f.Changed {
  53. alreadySet[FlagToEnv(prefix, f.Name)] = true
  54. }
  55. if serr := setFlagFromEnv(fs, prefix, f.Name, usedEnvKey, alreadySet, false); serr != nil {
  56. err = serr
  57. }
  58. })
  59. verifyEnv(prefix, usedEnvKey, alreadySet)
  60. return err
  61. }
  62. // FlagToEnv converts flag string to upper-case environment variable key string.
  63. func FlagToEnv(prefix, name string) string {
  64. return prefix + "_" + strings.ToUpper(strings.Replace(name, "-", "_", -1))
  65. }
  66. func verifyEnv(prefix string, usedEnvKey, alreadySet map[string]bool) {
  67. for _, env := range os.Environ() {
  68. kv := strings.SplitN(env, "=", 2)
  69. if len(kv) != 2 {
  70. plog.Warningf("found invalid env %s", env)
  71. }
  72. if usedEnvKey[kv[0]] {
  73. continue
  74. }
  75. if alreadySet[kv[0]] {
  76. plog.Fatalf("conflicting environment variable %q is shadowed by corresponding command-line flag (either unset environment variable or disable flag)", kv[0])
  77. }
  78. if strings.HasPrefix(env, prefix+"_") {
  79. plog.Warningf("unrecognized environment variable %s", env)
  80. }
  81. }
  82. }
  83. type flagSetter interface {
  84. Set(fk string, fv string) error
  85. }
  86. func setFlagFromEnv(fs flagSetter, prefix, fname string, usedEnvKey, alreadySet map[string]bool, log bool) error {
  87. key := FlagToEnv(prefix, fname)
  88. if !alreadySet[key] {
  89. val := os.Getenv(key)
  90. if val != "" {
  91. usedEnvKey[key] = true
  92. if serr := fs.Set(fname, val); serr != nil {
  93. return fmt.Errorf("invalid value %q for %s: %v", val, key, serr)
  94. }
  95. if log {
  96. plog.Infof("recognized and used environment variable %s=%s", key, val)
  97. }
  98. }
  99. }
  100. return nil
  101. }
  102. func IsSet(fs *flag.FlagSet, name string) bool {
  103. set := false
  104. fs.Visit(func(f *flag.Flag) {
  105. if f.Name == name {
  106. set = true
  107. }
  108. })
  109. return set
  110. }