Skip to content

Commit

Permalink
use env if necessary in loading config (zeromicro#409)
Browse files Browse the repository at this point in the history
  • Loading branch information
kevwan authored Jan 21, 2021
1 parent 572b327 commit ebec5aa
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 10 deletions.
25 changes: 19 additions & 6 deletions core/conf/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,26 @@ var loaders = map[string]func([]byte, interface{}) error{
".yml": LoadConfigFromYamlBytes,
}

func LoadConfig(file string, v interface{}) error {
if content, err := ioutil.ReadFile(file); err != nil {
func LoadConfig(file string, v interface{}, opts ...Option) error {
content, err := ioutil.ReadFile(file)
if err != nil {
return err
} else if loader, ok := loaders[path.Ext(file)]; ok {
}

loader, ok := loaders[path.Ext(file)]
if !ok {
return fmt.Errorf("unrecoginized file type: %s", file)
}

var opt options
for _, o := range opts {
o(&opt)
}

if opt.env {
return loader([]byte(os.ExpandEnv(string(content))), v)
} else {
return fmt.Errorf("unrecoginized file type: %s", file)
return loader(content, v)
}
}

Expand All @@ -34,8 +47,8 @@ func LoadConfigFromYamlBytes(content []byte, v interface{}) error {
return mapping.UnmarshalYamlBytes(content, v)
}

func MustLoad(path string, v interface{}) {
if err := LoadConfig(path, v); err != nil {
func MustLoad(path string, v interface{}, opts ...Option) {
if err := LoadConfig(path, v, opts...); err != nil {
log.Fatalf("error: config file %s, %s", path, err.Error())
}
}
41 changes: 40 additions & 1 deletion core/conf/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ func TestConfigJson(t *testing.T) {
text := `{
"a": "foo",
"b": 1,
"c": "${FOO}"
"c": "${FOO}",
"d": "abcd!@#$112"
}`
for _, test := range tests {
test := test
Expand All @@ -45,11 +46,49 @@ func TestConfigJson(t *testing.T) {
A string `json:"a"`
B int `json:"b"`
C string `json:"c"`
D string `json:"d"`
}
MustLoad(tmpfile, &val)
assert.Equal(t, "foo", val.A)
assert.Equal(t, 1, val.B)
assert.Equal(t, "${FOO}", val.C)
assert.Equal(t, "abcd!@#$112", val.D)
})
}
}

func TestConfigJsonEnv(t *testing.T) {
tests := []string{
".json",
".yaml",
".yml",
}
text := `{
"a": "foo",
"b": 1,
"c": "${FOO}",
"d": "abcd!@#$a12 3"
}`
for _, test := range tests {
test := test
t.Run(test, func(t *testing.T) {
os.Setenv("FOO", "2")
defer os.Unsetenv("FOO")
tmpfile, err := createTempFile(test, text)
assert.Nil(t, err)
defer os.Remove(tmpfile)

var val struct {
A string `json:"a"`
B int `json:"b"`
C string `json:"c"`
D string `json:"d"`
}
MustLoad(tmpfile, &val, UseEnv())
assert.Equal(t, "foo", val.A)
assert.Equal(t, 1, val.B)
assert.Equal(t, "2", val.C)
assert.Equal(t, "abcd!@# 3", val.D)
})
}
}
Expand Down
15 changes: 15 additions & 0 deletions core/conf/options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package conf

type (
Option func(opt *options)

options struct {
env bool
}
)

func UseEnv() Option {
return func(opt *options) {
opt.env = true
}
}
14 changes: 12 additions & 2 deletions core/conf/properties.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package conf

import (
"fmt"
"os"
"strconv"
"strings"
"sync"
Expand Down Expand Up @@ -32,12 +33,17 @@ type mapBasedProperties struct {

// Loads the properties into a properties configuration instance.
// Returns an error that indicates if there was a problem loading the configuration.
func LoadProperties(filename string) (Properties, error) {
func LoadProperties(filename string, opts ...Option) (Properties, error) {
lines, err := iox.ReadTextLines(filename, iox.WithoutBlank(), iox.OmitWithPrefix("#"))
if err != nil {
return nil, err
}

var opt options
for _, o := range opts {
o(&opt)
}

raw := make(map[string]string)
for i := range lines {
pair := strings.Split(lines[i], "=")
Expand All @@ -50,7 +56,11 @@ func LoadProperties(filename string) (Properties, error) {

key := strings.TrimSpace(pair[0])
value := strings.TrimSpace(pair[1])
raw[key] = value
if opt.env {
raw[key] = os.ExpandEnv(value)
} else {
raw[key] = value
}
}

return &mapBasedProperties{
Expand Down
33 changes: 33 additions & 0 deletions core/conf/properties_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,39 @@ func TestProperties(t *testing.T) {
assert.Contains(t, val, "app.threads")
}

func TestPropertiesEnv(t *testing.T) {
text := `app.name = test
app.program=app
app.env1 = ${FOO}
app.env2 = $none
# this is comment
app.threads = 5`
tmpfile, err := fs.TempFilenameWithText(text)
assert.Nil(t, err)
defer os.Remove(tmpfile)

os.Setenv("FOO", "2")
defer os.Unsetenv("FOO")

props, err := LoadProperties(tmpfile, UseEnv())
assert.Nil(t, err)
assert.Equal(t, "test", props.GetString("app.name"))
assert.Equal(t, "app", props.GetString("app.program"))
assert.Equal(t, 5, props.GetInt("app.threads"))
assert.Equal(t, "2", props.GetString("app.env1"))
assert.Equal(t, "", props.GetString("app.env2"))

val := props.ToString()
assert.Contains(t, val, "app.name")
assert.Contains(t, val, "app.program")
assert.Contains(t, val, "app.threads")
assert.Contains(t, val, "app.env1")
assert.Contains(t, val, "app.env2")
}

func TestLoadProperties_badContent(t *testing.T) {
filename, err := fs.TempFilenameWithText("hello")
assert.Nil(t, err)
Expand Down
2 changes: 1 addition & 1 deletion tools/goctl/goctl.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import (
)

var (
BuildVersion = "1.1.3"
BuildVersion = "1.1.5"
commands = []cli.Command{
{
Name: "upgrade",
Expand Down

0 comments on commit ebec5aa

Please sign in to comment.