Skip to content

Commit

Permalink
Merge b19c75b into f1d6c5f
Browse files Browse the repository at this point in the history
  • Loading branch information
Hasan Ozgan committed May 7, 2021
2 parents f1d6c5f + b19c75b commit a543fa5
Show file tree
Hide file tree
Showing 14 changed files with 156 additions and 7 deletions.
57 changes: 57 additions & 0 deletions fig.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"strings"
"time"

"github.com/imdario/mergo"
"github.com/mitchellh/mapstructure"
"github.com/pelletier/go-toml"
"gopkg.in/yaml.v2"
Expand Down Expand Up @@ -76,6 +77,8 @@ type fig struct {
timeLayout string
useEnv bool
envPrefix string
useProfile bool
profile string
}

func (f *fig) Load(cfg interface{}) error {
Expand All @@ -93,13 +96,41 @@ func (f *fig) Load(cfg interface{}) error {
return err
}

if f.useProfile {
profileFile, err := f.findProfileCfgFile()
if err != nil {
return err
}

profileVals, err := f.decodeFile(profileFile)
if err != nil {
return err
}

if err := mergo.Merge(&vals, profileVals, mergo.WithOverride); err != nil {
return err
}
}

if err := f.decodeMap(vals, cfg); err != nil {
return err
}

return f.processCfg(cfg)
}

func (f *fig) findProfileCfgFile() (path string, err error) {
parts := strings.Split(f.filename, ".")
profileFileName := fmt.Sprintf("%s.%s.%s", parts[0], f.profile, parts[1])
for _, dir := range f.dirs {
path = filepath.Join(dir, profileFileName)
if fileExists(path) {
return
}
}
return "", fmt.Errorf("%s: %w", profileFileName, ErrFileNotFound)
}

func (f *fig) findCfgFile() (path string, err error) {
for _, dir := range f.dirs {
path = filepath.Join(dir, f.filename)
Expand Down Expand Up @@ -151,6 +182,7 @@ func (f *fig) decodeMap(m map[string]interface{}, result interface{}) error {
Result: result,
TagName: f.tag,
DecodeHook: mapstructure.ComposeDecodeHookFunc(
fromEnvironmentHookFunc(),
mapstructure.StringToTimeDurationHookFunc(),
mapstructure.StringToTimeHookFunc(f.timeLayout),
),
Expand All @@ -161,6 +193,31 @@ func (f *fig) decodeMap(m map[string]interface{}, result interface{}) error {
return dec.Decode(m)
}

func fromEnvironmentHookFunc() mapstructure.DecodeHookFunc {
return func(
f reflect.Type,
t reflect.Type,
data interface{}) (interface{}, error) {
if f.Kind() != reflect.String {
return data, nil
}

str := data.(string)
if !strings.HasPrefix(str, "${") || !strings.HasSuffix(str, "}") {
return data, nil
}

kv := str[2 : len(str)-1]
s := strings.Split(kv, ":")
envName, defaultVal := s[0], s[1]
if envValue, ok := os.LookupEnv(envName); ok {
return envValue, nil
} else {
return defaultVal, nil
}
}
}

// processCfg processes a cfg struct after it has been loaded from
// the config file, by validating required fields and setting defaults
// where applicable.
Expand Down
69 changes: 69 additions & 0 deletions fig_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,26 @@ func Test_fig_Load(t *testing.T) {
}
}

func Test_fig_Load_If_Env_Set_In_Conf_File(t *testing.T) {
os.Setenv("POD_NAME", "ehcache")
for _, f := range []string{"pod.yaml", "pod.json", "pod.toml"} {
t.Run(f, func(t *testing.T) {
var cfg Pod
err := Load(&cfg, File(f), Dirs(filepath.Join("testdata", "valid")))
if err != nil {
t.Fatalf("unexpected err: %v", err)
}

want := validPodConfig()
want.Metadata.Name = "ehcache"

if !reflect.DeepEqual(want, cfg) {
t.Errorf("\nwant %+v\ngot %+v", want, cfg)
}
})
}
}

func Test_fig_Load_FileNotFound(t *testing.T) {
fig := defaultFig()
fig.filename = "abrakadabra"
Expand Down Expand Up @@ -402,6 +422,55 @@ func Test_fig_Load_WithOptions(t *testing.T) {
}
}

func Test_fig_Load_Server_If_Env_Set_In_Conf_File(t *testing.T) {
os.Setenv("SERVICE_HOST", "192.168.0.128")
for _, f := range []string{"server.yaml", "server.json", "server.toml"} {
t.Run(f, func(t *testing.T) {
type Server struct {
Host string `fig:"host"`
}

var cfg Server
err := Load(&cfg, File(f), Dirs(filepath.Join("testdata", "valid")))
if err != nil {
t.Fatalf("expected err")
}

want := Server{Host: "192.168.0.128"}

if !reflect.DeepEqual(want, cfg) {
t.Errorf("\nwant %+v\ngot %+v", want, cfg)
}
})
}
}

func Test_fig_Load_Server_Profile(t *testing.T) {
for _, f := range []string{"server.yaml", "server.json", "server.toml"} {
t.Run(f, func(t *testing.T) {
type Server struct {
Host string `fig:"host"`
Logger struct {
LogLevel string `fig:"log_level" default:"info"`
}
}

var cfg Server
err := Load(&cfg, File(f), Dirs(filepath.Join("testdata", "valid")), UseProfile("test"))
if err != nil {
t.Fatalf("expected err %v", err)
}

want := Server{Host: "192.168.0.256"}
want.Logger.LogLevel = "debug"

if !reflect.DeepEqual(want, cfg) {
t.Errorf("\nwant %+v\ngot %+v", want, cfg)
}
})
}
}

func Test_fig_findCfgFile(t *testing.T) {
t.Run("finds existing file", func(t *testing.T) {
fig := defaultFig()
Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ module github.com/kkyr/fig
go 1.14

require (
github.com/imdario/mergo v0.3.12
github.com/mitchellh/mapstructure v1.1.2
github.com/pelletier/go-toml v1.6.0
gopkg.in/yaml.v2 v2.2.7
gopkg.in/yaml.v2 v2.3.0
)
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/pelletier/go-toml v1.6.0 h1:aetoXYr0Tv7xRU/V4B4IZJ2QcbtMUFoNb3ORp7TzIK4=
Expand All @@ -11,3 +13,5 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
13 changes: 13 additions & 0 deletions option.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,16 @@ func UseEnv(prefix string) Option {
f.envPrefix = prefix
}
}

// Tag returns an option that configures the tag key that fig uses
// when for the alt name struct tag key in fields.
//
// fig.Load(&cfg, fig.UseProfile("test"))
//
// If this option is not used then fig uses the tag `fig`.
func UseProfile(profile string) Option {
return func(f *fig) {
f.useProfile = true
f.profile = profile
}
}
2 changes: 1 addition & 1 deletion testdata/valid/pod.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"apiVersion": null,
"kind": "Pod",
"metadata": {
"name": "redis",
"name": "${POD_NAME:redis}",
"master": true
},
"spec": {
Expand Down
2 changes: 1 addition & 1 deletion testdata/valid/pod.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
kind = "Pod"

[metadata]
name = "redis"
name = "${POD_NAME:redis}"
master = true

[spec]
Expand Down
2 changes: 1 addition & 1 deletion testdata/valid/pod.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
apiVersion:
kind: Pod
metadata:
name: redis
name: ${POD_NAME:redis}
master: true
spec:
containers:
Expand Down
2 changes: 1 addition & 1 deletion testdata/valid/server.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"host": "0.0.0.0",
"host": "${SERVICE_HOST:0.0.0.0}",
"logger": {
"log_level": "debug"
}
Expand Down
3 changes: 3 additions & 0 deletions testdata/valid/server.test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"host": "192.168.0.256"
}
1 change: 1 addition & 0 deletions testdata/valid/server.test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
host = "192.168.0.256"
1 change: 1 addition & 0 deletions testdata/valid/server.test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
host: "192.168.0.256"
2 changes: 1 addition & 1 deletion testdata/valid/server.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
host = "0.0.0.0"
host = "${SERVICE_HOST:0.0.0.0}"

[logger]
log_level = "debug"
2 changes: 1 addition & 1 deletion testdata/valid/server.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
host: "0.0.0.0"
host: "${SERVICE_HOST:0.0.0.0}"

logger:
log_level: "debug"

0 comments on commit a543fa5

Please sign in to comment.