Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use env if necessary in loading config #409

Merged
merged 1 commit into from
Jan 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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