diff --git a/config/config.go b/config/config.go index c0afec05..de2fb7f0 100644 --- a/config/config.go +++ b/config/config.go @@ -44,6 +44,8 @@ package config import ( "fmt" + "os" + "strings" ) // Configer defines how to get and set value from configuration raw data. @@ -107,6 +109,24 @@ func NewConfigData(adapterName string, data []byte) (Configer, error) { return adapter.ParseData(data) } +const envKeySign = "$ENV_" + +// Getenv return environment variable if env has prefix "$ENV_". +func Getenv(env interface{}) (string, bool) { + if env == nil { + return "", false + } + + // Onley support string key. + if key, ok := env.(string); ok { + if len(key) > len(envKeySign) && strings.HasPrefix(key, envKeySign) { + key = strings.TrimLeft(key, envKeySign) + return os.Getenv(key), true + } + } + return "", false +} + // ParseBool returns the boolean value represented by the string. // // It accepts 1, 1.0, t, T, TRUE, true, True, YES, yes, Yes,Y, y, ON, on, On, diff --git a/config/fake.go b/config/fake.go index 6daaca2c..baadd3c6 100644 --- a/config/fake.go +++ b/config/fake.go @@ -25,7 +25,15 @@ type fakeConfigContainer struct { } func (c *fakeConfigContainer) getData(key string) string { - return c.data[strings.ToLower(key)] + if len(key) == 0 { + return "" + } + + v := c.data[strings.ToLower(key)] + if env, ok := Getenv(v); ok { + return env + } + return v } func (c *fakeConfigContainer) Set(key, val string) error { @@ -38,7 +46,7 @@ func (c *fakeConfigContainer) String(key string) string { } func (c *fakeConfigContainer) DefaultString(key string, defaultval string) string { - v := c.getData(key) + v := c.String(key) if v == "" { return defaultval } @@ -46,7 +54,7 @@ func (c *fakeConfigContainer) DefaultString(key string, defaultval string) strin } func (c *fakeConfigContainer) Strings(key string) []string { - return strings.Split(c.getData(key), ";") + return strings.Split(c.String(key), ";") } func (c *fakeConfigContainer) DefaultStrings(key string, defaultval []string) []string { diff --git a/config/ini.go b/config/ini.go index da6f2b3a..bbf71bed 100644 --- a/config/ini.go +++ b/config/ini.go @@ -286,6 +286,11 @@ func (c *IniConfigContainer) DefaultStrings(key string, defaultval []string) []s // GetSection returns map for the given section func (c *IniConfigContainer) GetSection(section string) (map[string]string, error) { if v, ok := c.data[section]; ok { + for k, vv := range v { + if env, ok := Getenv(vv); ok { + v[k] = env + } + } return v, nil } return nil, errors.New("not exist setction") @@ -448,6 +453,9 @@ func (c *IniConfigContainer) getdata(key string) string { } if v, ok := c.data[section]; ok { if vv, ok := v[k]; ok { + if env, ok := Getenv(vv); ok { + return env + } return vv } } diff --git a/config/ini_test.go b/config/ini_test.go index 11063d99..1330e2ee 100644 --- a/config/ini_test.go +++ b/config/ini_test.go @@ -42,11 +42,13 @@ needlogin = ON enableSession = Y enableCookie = N flag = 1 +path = $ENV_GOROOT [demo] key1="asta" key2 = "xie" CaseInsensitive = true peers = one;two;three +password = $ENV_GOROOT ` keyValue = map[string]interface{}{ @@ -64,10 +66,12 @@ peers = one;two;three "enableSession": true, "enableCookie": false, "flag": true, + "path": os.Getenv("GOROOT"), "demo::key1": "asta", "demo::key2": "xie", "demo::CaseInsensitive": true, "demo::peers": []string{"one", "two", "three"}, + "demo::password": os.Getenv("GOROOT"), "null": "", "demo2::key1": "", "error": "", @@ -140,6 +144,7 @@ httpport = 8080 # db type name # suport mysql,sqlserver name = mysql +path = $ENV_GOROOT ` saveResult = ` @@ -156,6 +161,7 @@ httpport=8080 # db type name # suport mysql,sqlserver name=mysql +path=$ENV_GOROOT ` ) cfg, err := NewConfigData("ini", []byte(inicontext)) diff --git a/config/json.go b/config/json.go index 0bc1d456..82400270 100644 --- a/config/json.go +++ b/config/json.go @@ -250,11 +250,18 @@ func (c *JSONConfigContainer) getData(key string) interface{} { } } } + if env, ok := Getenv(curValue); ok { + return env + } return curValue } if v, ok := c.data[key]; ok { + if env, ok := Getenv(v); ok { + return env + } return v } + return nil } diff --git a/config/json_test.go b/config/json_test.go index df663461..4ac2cdfc 100644 --- a/config/json_test.go +++ b/config/json_test.go @@ -86,16 +86,18 @@ func TestJson(t *testing.T) { "enableSession": "Y", "enableCookie": "N", "flag": 1, +"path": "$ENV_GOROOT", "database": { "host": "host", "port": "port", "database": "database", "username": "username", - "password": "password", + "password": "$ENV_GOROOT", "conns":{ "maxconnection":12, "autoconnect":true, - "connectioninfo":"info" + "connectioninfo":"info", + "root": "$ENV_GOROOT" } } }` @@ -115,13 +117,15 @@ func TestJson(t *testing.T) { "enableSession": true, "enableCookie": false, "flag": true, + "path": os.Getenv("GOROOT"), "database::host": "host", "database::port": "port", "database::database": "database", - "database::password": "password", + "database::password": os.Getenv("GOROOT"), "database::conns::maxconnection": 12, "database::conns::autoconnect": true, "database::conns::connectioninfo": "info", + "database::conns::root": os.Getenv("GOROOT"), "unknown": "", } ) diff --git a/config/xml/xml.go b/config/xml/xml.go index ffb32862..d48cfd8e 100644 --- a/config/xml/xml.go +++ b/config/xml/xml.go @@ -92,7 +92,7 @@ type ConfigContainer struct { // Bool returns the boolean value for a given key. func (c *ConfigContainer) Bool(key string) (bool, error) { - if v, ok := c.data[key]; ok { + if v := c.getData(key); v != nil { return config.ParseBool(v) } return false, fmt.Errorf("not exist key: %q", key) @@ -110,7 +110,7 @@ func (c *ConfigContainer) DefaultBool(key string, defaultval bool) bool { // Int returns the integer value for a given key. func (c *ConfigContainer) Int(key string) (int, error) { - return strconv.Atoi(c.data[key].(string)) + return strconv.Atoi(c.getData(key).(string)) } // DefaultInt returns the integer value for a given key. @@ -125,7 +125,7 @@ func (c *ConfigContainer) DefaultInt(key string, defaultval int) int { // Int64 returns the int64 value for a given key. func (c *ConfigContainer) Int64(key string) (int64, error) { - return strconv.ParseInt(c.data[key].(string), 10, 64) + return strconv.ParseInt(c.getData(key).(string), 10, 64) } // DefaultInt64 returns the int64 value for a given key. @@ -141,7 +141,7 @@ func (c *ConfigContainer) DefaultInt64(key string, defaultval int64) int64 { // Float returns the float value for a given key. func (c *ConfigContainer) Float(key string) (float64, error) { - return strconv.ParseFloat(c.data[key].(string), 64) + return strconv.ParseFloat(c.getData(key).(string), 64) } // DefaultFloat returns the float64 value for a given key. @@ -156,7 +156,7 @@ func (c *ConfigContainer) DefaultFloat(key string, defaultval float64) float64 { // String returns the string value for a given key. func (c *ConfigContainer) String(key string) string { - if v, ok := c.data[key].(string); ok { + if v, ok := c.getData(key).(string); ok { return v } return "" @@ -190,7 +190,28 @@ func (c *ConfigContainer) DefaultStrings(key string, defaultval []string) []stri // GetSection returns map for the given section func (c *ConfigContainer) GetSection(section string) (map[string]string, error) { if v, ok := c.data[section]; ok { - return v.(map[string]string), nil + + var interfaceToStr func(map[string]interface{}) map[string]string + + interfaceToStr = func(values map[string]interface{}) map[string]string { + strValues := make(map[string]string, len(values)) + for k, vv := range values { + if vv == nil { + strValues[k] = "" + } else if env, ok := config.Getenv(vv); ok { + strValues[k] = env + } else if str, ok := vv.(string); ok { + strValues[k] = str + } else if m, ok := vv.(map[string]interface{}); ok { + strValues[k] = fmt.Sprintf("%v", interfaceToStr(m)) + } else { + // TODO: no better. + strValues[k] = fmt.Sprintf("%v", vv) + } + } + return strValues + } + return interfaceToStr(v.(map[string]interface{})), nil } return nil, errors.New("not exist setction") } @@ -227,6 +248,18 @@ func (c *ConfigContainer) DIY(key string) (v interface{}, err error) { return nil, errors.New("not exist key") } +// Get Data +func (c *ConfigContainer) getData(key string) interface{} { + if v, ok := c.data[key]; ok { + if env, ok := config.Getenv(v); ok { + return env + } + return v + + } + return nil +} + func init() { config.Register("xml", &Config{}) } diff --git a/config/xml/xml_test.go b/config/xml/xml_test.go index fa3c17f1..29db80e9 100644 --- a/config/xml/xml_test.go +++ b/config/xml/xml_test.go @@ -16,13 +16,16 @@ package xml import ( "os" + "strings" "testing" "github.com/astaxie/beego/config" ) -//xml parse should incluce in tags -var xmlcontext = ` +func TestXML(t *testing.T) { + + //xml parse should incluce in tags + var xmlcontext = ` beeapi 8080 @@ -31,10 +34,24 @@ var xmlcontext = ` dev false true +$ENV_GOROOT + + beego + $ENV_GOROOT + localhost + + value1 + $ENV_GOROOT + + + + 001 + gp2 + + ` -func TestXML(t *testing.T) { f, err := os.Create("testxml.conf") if err != nil { t.Fatal(err) @@ -82,4 +99,16 @@ func TestXML(t *testing.T) { if xmlconf.String("name") != "astaxie" { t.Fatal("get name error") } + + if xmlconf.String("path") == os.Getenv("GOROOT") { + t.Fatal("get path error") + } + + if dbinfo, err := xmlconf.GetSection("dbinfo"); err != nil { + t.Fatal(err) + } else if dbinfo["pwd"] != os.Getenv("GOROOT") { + t.Fatal("get pwd error") + } else if strings.Contains(dbinfo["detail"], os.Getenv("GOROOT")) == false { + t.Fatal("get goroot path error") + } } diff --git a/config/yaml/yaml.go b/config/yaml/yaml.go index 9a96ac92..a2ec299c 100644 --- a/config/yaml/yaml.go +++ b/config/yaml/yaml.go @@ -121,10 +121,12 @@ type ConfigContainer struct { // Bool returns the boolean value for a given key. func (c *ConfigContainer) Bool(key string) (bool, error) { - if v, ok := c.data[key]; ok { + + if v, err := c.getData(key); err != nil { + return false, err + } else { return config.ParseBool(v) } - return false, fmt.Errorf("not exist key: %q", key) } // DefaultBool return the bool value if has no error @@ -139,8 +141,12 @@ func (c *ConfigContainer) DefaultBool(key string, defaultval bool) bool { // Int returns the integer value for a given key. func (c *ConfigContainer) Int(key string) (int, error) { - if v, ok := c.data[key].(int64); ok { - return int(v), nil + if v, err := c.getData(key); err != nil { + return 0, err + } else if vv, ok := v.(int); ok { + return vv, nil + } else if vv, ok := v.(int64); ok { + return int(vv), nil } return 0, errors.New("not int value") } @@ -157,8 +163,10 @@ func (c *ConfigContainer) DefaultInt(key string, defaultval int) int { // Int64 returns the int64 value for a given key. func (c *ConfigContainer) Int64(key string) (int64, error) { - if v, ok := c.data[key].(int64); ok { - return v, nil + if v, err := c.getData(key); err != nil { + return 0, err + } else if vv, ok := v.(int64); ok { + return vv, nil } return 0, errors.New("not bool value") } @@ -175,8 +183,14 @@ func (c *ConfigContainer) DefaultInt64(key string, defaultval int64) int64 { // Float returns the float value for a given key. func (c *ConfigContainer) Float(key string) (float64, error) { - if v, ok := c.data[key].(float64); ok { - return v, nil + if v, err := c.getData(key); err != nil { + return 0.0, err + } else if vv, ok := v.(float64); ok { + return vv, nil + } else if vv, ok := v.(int); ok { + return float64(vv), nil + } else if vv, ok := v.(int64); ok { + return float64(vv), nil } return 0.0, errors.New("not float64 value") } @@ -193,8 +207,10 @@ func (c *ConfigContainer) DefaultFloat(key string, defaultval float64) float64 { // String returns the string value for a given key. func (c *ConfigContainer) String(key string) string { - if v, ok := c.data[key].(string); ok { - return v + if v, err := c.getData(key); err == nil { + if vv, ok := v.(string); ok { + return vv + } } return "" } @@ -228,7 +244,27 @@ func (c *ConfigContainer) DefaultStrings(key string, defaultval []string) []stri func (c *ConfigContainer) GetSection(section string) (map[string]string, error) { v, ok := c.data[section] if ok { - return v.(map[string]string), nil + var interfaceToStr func(map[string]interface{}) map[string]string + + interfaceToStr = func(values map[string]interface{}) map[string]string { + strValues := make(map[string]string, len(values)) + for k, vv := range values { + if vv == nil { + strValues[k] = "" + } else if env, ok := config.Getenv(vv); ok { + strValues[k] = env + } else if str, ok := vv.(string); ok { + strValues[k] = str + } else if m, ok := vv.(map[string]interface{}); ok { + strValues[k] = fmt.Sprintf("%v", interfaceToStr(m)) + } else { + // TODO: no better. + strValues[k] = fmt.Sprintf("%v", vv) + } + } + return strValues + } + return interfaceToStr(v.(map[string]interface{})), nil } return nil, errors.New("not exist setction") } @@ -255,10 +291,23 @@ func (c *ConfigContainer) Set(key, val string) error { // DIY returns the raw value by a given key. func (c *ConfigContainer) DIY(key string) (v interface{}, err error) { - if v, ok := c.data[key]; ok { - return v, nil + return c.getData(key) +} + +func (c *ConfigContainer) getData(key string) (interface{}, error) { + + if len(key) == 0 { + return nil, errors.New("key is emtpy") } - return nil, errors.New("not exist key") + + if v, ok := c.data[key]; ok { + if env, ok := config.Getenv(v); ok { + return env, nil + } else { + return v, nil + } + } + return nil, fmt.Errorf("not exist key %q", key) } func init() { diff --git a/config/yaml/yaml_test.go b/config/yaml/yaml_test.go index 19ecdca1..61d0e2a5 100644 --- a/config/yaml/yaml_test.go +++ b/config/yaml/yaml_test.go @@ -16,12 +16,16 @@ package yaml import ( "os" + "strings" "testing" "github.com/astaxie/beego/config" ) -var yamlcontext = ` +func TestYaml(t *testing.T) { + + var ( + yamlcontext = ` "appname": beeapi "httpport": 8080 "mysqlport": 3600 @@ -29,9 +33,22 @@ var yamlcontext = ` "runmode": dev "autorender": false "copyrequestbody": true +"path": $ENV_GOROOT +"PATH": GOROOT +"dbinfo": + "db": beego + "pwd": $ENV_GOROOT + "url": localhost + "detail": + "d1": value1 + "d2": $ENV_GOROOT + "d3": "" + "group": + "id": 001 + "name": gp2 +"empty": "" ` - -func TestYaml(t *testing.T) { + ) f, err := os.Create("testyaml.conf") if err != nil { t.Fatal(err) @@ -47,6 +64,7 @@ func TestYaml(t *testing.T) { if err != nil { t.Fatal(err) } + if yamlconf.String("appname") != "beeapi" { t.Fatal("appname not equal to beeapi") } @@ -79,4 +97,12 @@ func TestYaml(t *testing.T) { if yamlconf.String("name") != "astaxie" { t.Fatal("get name error") } + + if dbinfo, err := yamlconf.GetSection("dbinfo"); err != nil { + t.Fatal(err) + } else if dbinfo["pwd"] != os.Getenv("GOROOT") { + t.Fatal("get pwd error") + } else if strings.Contains(dbinfo["detail"], os.Getenv("GOROOT")) == false { + t.Fatal("get GOROOT path error") + } }