-
Notifications
You must be signed in to change notification settings - Fork 618
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Issue #79: Refactor config loading to use flag sets
* support cmdline, env vars and properties * support env vars with and without a FABIO_ prefix * support loading properties from URL * support consul agent on https Inspired by PR #63 from @doublerebel
- Loading branch information
1 parent
674536c
commit 01542f5
Showing
10 changed files
with
339 additions
and
281 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package config | ||
|
||
import ( | ||
"flag" | ||
"strings" | ||
|
||
"github.com/magiconair/properties" | ||
) | ||
|
||
type FlagSet struct { | ||
flag.FlagSet | ||
set map[string]bool | ||
} | ||
|
||
func NewFlagSet(name string, errorHandling flag.ErrorHandling) *FlagSet { | ||
fs := &FlagSet{set: make(map[string]bool)} | ||
fs.Init(name, errorHandling) | ||
return fs | ||
} | ||
|
||
// IsSet returns true if a variable was set via any mechanism. | ||
func (f *FlagSet) IsSet(name string) bool { | ||
return f.set[name] | ||
} | ||
|
||
// ParseFlags parses command line arguments and provides fallback | ||
// values from environment variables and config file values. | ||
// Environment variables are case-insensitive and can have either | ||
// of the provided prefixes. | ||
func (f *FlagSet) ParseFlags(args, environ, prefixes []string, p *properties.Properties) error { | ||
if err := f.Parse(args); err != nil { | ||
return err | ||
} | ||
|
||
if len(prefixes) == 0 { | ||
prefixes = []string{""} | ||
} | ||
|
||
// parse environment in case-insensitive way | ||
env := map[string]string{} | ||
for _, e := range environ { | ||
p := strings.Split(e, "=") | ||
env[strings.ToUpper(p[0])] = p[1] | ||
} | ||
|
||
// determine all values that were set via cmdline | ||
f.Visit(func(fl *flag.Flag) { | ||
f.set[fl.Name] = true | ||
}) | ||
|
||
// lookup the rest via environ and properties | ||
f.VisitAll(func(fl *flag.Flag) { | ||
// skip if already set | ||
if f.set[fl.Name] { | ||
return | ||
} | ||
|
||
// check environment variables | ||
for _, pfx := range prefixes { | ||
name := strings.ToUpper(pfx + strings.Replace(fl.Name, ".", "_", -1)) | ||
if val, ok := env[name]; ok { | ||
f.set[fl.Name] = true | ||
f.Set(fl.Name, val) | ||
return | ||
} | ||
} | ||
|
||
// check properties | ||
if p == nil { | ||
return | ||
} | ||
if val, ok := p.Get(fl.Name); ok { | ||
f.set[fl.Name] = true | ||
f.Set(fl.Name, val) | ||
return | ||
} | ||
}) | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package config | ||
|
||
import ( | ||
"flag" | ||
"testing" | ||
|
||
"github.com/magiconair/properties" | ||
) | ||
|
||
func TestParseFlags(t *testing.T) { | ||
props := func(s string) *properties.Properties { | ||
return properties.MustLoadString(s) | ||
} | ||
|
||
tests := []struct { | ||
desc string | ||
args []string | ||
env []string | ||
prefix []string | ||
props string | ||
v string | ||
}{ | ||
{ | ||
desc: "cmdline should win", | ||
args: []string{"-v", "cmdline"}, | ||
env: []string{"v=env"}, | ||
props: "v=props", | ||
v: "cmdline", | ||
}, | ||
{ | ||
desc: "env should win", | ||
env: []string{"v=env"}, | ||
props: "v=props", | ||
v: "env", | ||
}, | ||
{ | ||
desc: "env with prefix should win", | ||
env: []string{"v=env", "p_v=prefix"}, | ||
prefix: []string{"p_"}, | ||
props: "v=props", | ||
v: "prefix", | ||
}, | ||
{ | ||
desc: "props should win", | ||
props: "v=props", | ||
v: "props", | ||
}, | ||
} | ||
|
||
for i, tt := range tests { | ||
var v string | ||
f := NewFlagSet("test", flag.ExitOnError) | ||
f.StringVar(&v, "v", "default", "") | ||
err := f.ParseFlags(tt.args, tt.env, tt.prefix, props(tt.props)) | ||
if err != nil { | ||
t.Errorf("%d -%s: got %v want nil", i, tt.desc, err) | ||
} | ||
if got, want := v, tt.v; got != want { | ||
t.Errorf("%d - %s: got %q want %q", i, tt.desc, got, want) | ||
} | ||
} | ||
} |
Oops, something went wrong.