mirror of
https://github.com/zeromicro/go-zero.git
synced 2025-02-03 00:38:40 +08:00
use env if necessary in loading config (#409)
This commit is contained in:
parent
572b32729f
commit
ebec5aafab
@ -16,13 +16,26 @@ var loaders = map[string]func([]byte, interface{}) error{
|
|||||||
".yml": LoadConfigFromYamlBytes,
|
".yml": LoadConfigFromYamlBytes,
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadConfig(file string, v interface{}) error {
|
func LoadConfig(file string, v interface{}, opts ...Option) error {
|
||||||
if content, err := ioutil.ReadFile(file); err != nil {
|
content, err := ioutil.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
return err
|
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)
|
return loader([]byte(os.ExpandEnv(string(content))), v)
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("unrecoginized file type: %s", file)
|
return loader(content, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,8 +47,8 @@ func LoadConfigFromYamlBytes(content []byte, v interface{}) error {
|
|||||||
return mapping.UnmarshalYamlBytes(content, v)
|
return mapping.UnmarshalYamlBytes(content, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func MustLoad(path string, v interface{}) {
|
func MustLoad(path string, v interface{}, opts ...Option) {
|
||||||
if err := LoadConfig(path, v); err != nil {
|
if err := LoadConfig(path, v, opts...); err != nil {
|
||||||
log.Fatalf("error: config file %s, %s", path, err.Error())
|
log.Fatalf("error: config file %s, %s", path, err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,8 @@ func TestConfigJson(t *testing.T) {
|
|||||||
text := `{
|
text := `{
|
||||||
"a": "foo",
|
"a": "foo",
|
||||||
"b": 1,
|
"b": 1,
|
||||||
"c": "${FOO}"
|
"c": "${FOO}",
|
||||||
|
"d": "abcd!@#$112"
|
||||||
}`
|
}`
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
test := test
|
test := test
|
||||||
@ -45,11 +46,49 @@ func TestConfigJson(t *testing.T) {
|
|||||||
A string `json:"a"`
|
A string `json:"a"`
|
||||||
B int `json:"b"`
|
B int `json:"b"`
|
||||||
C string `json:"c"`
|
C string `json:"c"`
|
||||||
|
D string `json:"d"`
|
||||||
}
|
}
|
||||||
MustLoad(tmpfile, &val)
|
MustLoad(tmpfile, &val)
|
||||||
assert.Equal(t, "foo", val.A)
|
assert.Equal(t, "foo", val.A)
|
||||||
assert.Equal(t, 1, val.B)
|
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, "2", val.C)
|
||||||
|
assert.Equal(t, "abcd!@# 3", val.D)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
15
core/conf/options.go
Normal file
15
core/conf/options.go
Normal file
@ -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
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@ package conf
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@ -32,12 +33,17 @@ type mapBasedProperties struct {
|
|||||||
|
|
||||||
// Loads the properties into a properties configuration instance.
|
// Loads the properties into a properties configuration instance.
|
||||||
// Returns an error that indicates if there was a problem loading the configuration.
|
// 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("#"))
|
lines, err := iox.ReadTextLines(filename, iox.WithoutBlank(), iox.OmitWithPrefix("#"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var opt options
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&opt)
|
||||||
|
}
|
||||||
|
|
||||||
raw := make(map[string]string)
|
raw := make(map[string]string)
|
||||||
for i := range lines {
|
for i := range lines {
|
||||||
pair := strings.Split(lines[i], "=")
|
pair := strings.Split(lines[i], "=")
|
||||||
@ -50,7 +56,11 @@ func LoadProperties(filename string) (Properties, error) {
|
|||||||
|
|
||||||
key := strings.TrimSpace(pair[0])
|
key := strings.TrimSpace(pair[0])
|
||||||
value := strings.TrimSpace(pair[1])
|
value := strings.TrimSpace(pair[1])
|
||||||
raw[key] = value
|
if opt.env {
|
||||||
|
raw[key] = os.ExpandEnv(value)
|
||||||
|
} else {
|
||||||
|
raw[key] = value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &mapBasedProperties{
|
return &mapBasedProperties{
|
||||||
|
@ -31,6 +31,39 @@ func TestProperties(t *testing.T) {
|
|||||||
assert.Contains(t, val, "app.threads")
|
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) {
|
func TestLoadProperties_badContent(t *testing.T) {
|
||||||
filename, err := fs.TempFilenameWithText("hello")
|
filename, err := fs.TempFilenameWithText("hello")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
@ -28,7 +28,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
BuildVersion = "1.1.3"
|
BuildVersion = "1.1.5"
|
||||||
commands = []cli.Command{
|
commands = []cli.Command{
|
||||||
{
|
{
|
||||||
Name: "upgrade",
|
Name: "upgrade",
|
||||||
|
Loading…
Reference in New Issue
Block a user