Skip to content

Commit f4407a1

Browse files
committed
Allow to set flags from the environment.
A flag named "foo_bar" will be set from the environment variable DOORMAN_FOO_BAR.
1 parent d1f4718 commit f4407a1

File tree

2 files changed

+105
-0
lines changed

2 files changed

+105
-0
lines changed

go/flagenv/flagenv.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Package flagenv allows setting flags from the command line.
2+
package flagenv
3+
4+
import (
5+
"flag"
6+
"fmt"
7+
"os"
8+
"strings"
9+
10+
log "github.com/golang/glog"
11+
)
12+
13+
// NOTE(ryszard): This code is heavily inspired by
14+
// https://github.com/coreos/etcd/blob/master/pkg/flags/flag.go.
15+
16+
// Populate sets the flags in set from the environment. The
17+
// environment value used will be the name of the flag in upper case,
18+
// with '-' changed to '_', and (if prefixis not the empty string)
19+
// prepended with prefix and an underscore. So, if prefix is
20+
// "DOORMAN", and the flag's name "foo-bar", the environment variable
21+
// DOORMAN_FOO_BAR will be used.
22+
func Populate(set *flag.FlagSet, prefix string) error {
23+
var (
24+
setThroughFlags = make(map[string]bool)
25+
knownEnv = make(map[string]bool)
26+
err error
27+
)
28+
set.Visit(func(f *flag.Flag) {
29+
setThroughFlags[f.Name] = true
30+
})
31+
32+
set.VisitAll(func(f *flag.Flag) {
33+
key := flagToEnv(prefix, f.Name)
34+
knownEnv[key] = true
35+
val := os.Getenv(key)
36+
if val == "" {
37+
return
38+
}
39+
40+
if setThroughFlags[f.Name] {
41+
log.Warningf("Recognized environment variable %v, but shadowed by flag %v: won't be used.", key, f.Name)
42+
return
43+
}
44+
if e := set.Set(f.Name, val); e != nil {
45+
err = fmt.Errorf("Invalid value %q for %v.", val, key)
46+
return
47+
}
48+
})
49+
50+
for _, env := range os.Environ() {
51+
kv := strings.SplitN(env, "=", 2)
52+
if len(kv) < 1 {
53+
continue
54+
}
55+
if name := kv[0]; strings.HasPrefix(name, prefix) && !knownEnv[env] {
56+
log.Warningf("Unrecognized environment variable %s", name)
57+
}
58+
}
59+
60+
return err
61+
}
62+
63+
func flagToEnv(prefix, name string) string {
64+
rest := strings.ToUpper(strings.Replace(name, "-", "_", -1))
65+
if prefix != "" {
66+
return prefix + "_" + rest
67+
}
68+
return rest
69+
}

go/flagenv/flagenv_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package flagenv
2+
3+
import (
4+
"flag"
5+
"os"
6+
"testing"
7+
)
8+
9+
func TestPopulate(t *testing.T) {
10+
11+
var (
12+
fs = flag.NewFlagSet("testing", flag.ExitOnError)
13+
foo = fs.String("foo", "", "")
14+
bar = fs.String("bar", "", "")
15+
baz = fs.String("baz", "", "")
16+
)
17+
fs.Parse([]string{"-foo=foo", "-baz=baz"})
18+
19+
os.Clearenv()
20+
os.Setenv("DOORMAN_BAR", "bar")
21+
os.Setenv("DOORMAN_BAZ", "baz from env")
22+
23+
if err := Populate(fs, "DOORMAN"); err != nil {
24+
t.Fatal(err)
25+
}
26+
27+
if got, want := *foo, "foo"; got != want {
28+
t.Errorf("foo=%q; want %q", got, want)
29+
}
30+
if got, want := *bar, "bar"; got != want {
31+
t.Errorf("bar=%q; want %q", got, want)
32+
}
33+
if got, want := *baz, "baz"; got != want {
34+
t.Errorf("baz=%q; want %q", got, want)
35+
}
36+
}

0 commit comments

Comments
 (0)