blob: 215902cf8f313150fbf2287fa36c640a0931bd7f [file] [log] [blame]
khenaidooab1f7bd2019-11-14 14:00:27 -05001// 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
15// Package flags implements command-line flag parsing.
16package flags
17
18import (
19 "flag"
20 "fmt"
21 "os"
22 "strings"
23
24 "github.com/coreos/pkg/capnslog"
25 "github.com/spf13/pflag"
26)
27
28var plog = capnslog.NewPackageLogger("go.etcd.io/etcd", "pkg/flags")
29
30// SetFlagsFromEnv parses all registered flags in the given flagset,
31// and if they are not already set it attempts to set their values from
32// environment variables. Environment variables take the name of the flag but
33// are UPPERCASE, have the given prefix and any dashes are replaced by
34// underscores - for example: some-flag => ETCD_SOME_FLAG
35func SetFlagsFromEnv(prefix string, fs *flag.FlagSet) error {
36 var err error
37 alreadySet := make(map[string]bool)
38 fs.Visit(func(f *flag.Flag) {
39 alreadySet[FlagToEnv(prefix, f.Name)] = true
40 })
41 usedEnvKey := make(map[string]bool)
42 fs.VisitAll(func(f *flag.Flag) {
43 if serr := setFlagFromEnv(fs, prefix, f.Name, usedEnvKey, alreadySet, true); serr != nil {
44 err = serr
45 }
46 })
47 verifyEnv(prefix, usedEnvKey, alreadySet)
48 return err
49}
50
51// SetPflagsFromEnv is similar to SetFlagsFromEnv. However, the accepted flagset type is pflag.FlagSet
52// and it does not do any logging.
53func SetPflagsFromEnv(prefix string, fs *pflag.FlagSet) error {
54 var err error
55 alreadySet := make(map[string]bool)
56 usedEnvKey := make(map[string]bool)
57 fs.VisitAll(func(f *pflag.Flag) {
58 if f.Changed {
59 alreadySet[FlagToEnv(prefix, f.Name)] = true
60 }
61 if serr := setFlagFromEnv(fs, prefix, f.Name, usedEnvKey, alreadySet, false); serr != nil {
62 err = serr
63 }
64 })
65 verifyEnv(prefix, usedEnvKey, alreadySet)
66 return err
67}
68
69// FlagToEnv converts flag string to upper-case environment variable key string.
70func FlagToEnv(prefix, name string) string {
71 return prefix + "_" + strings.ToUpper(strings.Replace(name, "-", "_", -1))
72}
73
74func verifyEnv(prefix string, usedEnvKey, alreadySet map[string]bool) {
75 for _, env := range os.Environ() {
76 kv := strings.SplitN(env, "=", 2)
77 if len(kv) != 2 {
78 plog.Warningf("found invalid env %s", env)
79 }
80 if usedEnvKey[kv[0]] {
81 continue
82 }
83 if alreadySet[kv[0]] {
84 plog.Fatalf("conflicting environment variable %q is shadowed by corresponding command-line flag (either unset environment variable or disable flag)", kv[0])
85 }
86 if strings.HasPrefix(env, prefix+"_") {
87 plog.Warningf("unrecognized environment variable %s", env)
88 }
89 }
90}
91
92type flagSetter interface {
93 Set(fk string, fv string) error
94}
95
96func setFlagFromEnv(fs flagSetter, prefix, fname string, usedEnvKey, alreadySet map[string]bool, log bool) error {
97 key := FlagToEnv(prefix, fname)
98 if !alreadySet[key] {
99 val := os.Getenv(key)
100 if val != "" {
101 usedEnvKey[key] = true
102 if serr := fs.Set(fname, val); serr != nil {
103 return fmt.Errorf("invalid value %q for %s: %v", val, key, serr)
104 }
105 if log {
106 plog.Infof("recognized and used environment variable %s=%s", key, val)
107 }
108 }
109 }
110 return nil
111}
112
113func IsSet(fs *flag.FlagSet, name string) bool {
114 set := false
115 fs.Visit(func(f *flag.Flag) {
116 if f.Name == name {
117 set = true
118 }
119 })
120 return set
121}