From cd31c816cc89b73d1ba0e5495a177411cb78bb17 Mon Sep 17 00:00:00 2001 From: ysqi Date: Wed, 27 Jan 2016 20:46:30 +0800 Subject: [PATCH 1/8] Config support get environment variable get environment variable if config item has prefix "$ENV_" . e.g. ```ini [demo] password = $ENV_MyPWD ``` --- config/config.go | 20 +++++++++++ config/fake.go | 14 ++++++-- config/ini.go | 8 +++++ config/ini_test.go | 6 ++++ config/json.go | 7 ++++ config/json_test.go | 10 ++++-- config/xml/xml.go | 45 +++++++++++++++++++---- config/xml/xml_test.go | 35 ++++++++++++++++-- config/yaml/yaml.go | 77 ++++++++++++++++++++++++++++++++-------- config/yaml/yaml_test.go | 32 +++++++++++++++-- 10 files changed, 222 insertions(+), 32 deletions(-) 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") + } } From 484ca3a64384ec0c2db6ede320ee17b456b81111 Mon Sep 17 00:00:00 2001 From: ysqi Date: Wed, 27 Jan 2016 21:13:11 +0800 Subject: [PATCH 2/8] fixed test code error --- config/xml/xml_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/xml/xml_test.go b/config/xml/xml_test.go index 29db80e9..3bb62fbe 100644 --- a/config/xml/xml_test.go +++ b/config/xml/xml_test.go @@ -100,7 +100,7 @@ func TestXML(t *testing.T) { t.Fatal("get name error") } - if xmlconf.String("path") == os.Getenv("GOROOT") { + if xmlconf.String("path") != os.Getenv("GOROOT") { t.Fatal("get path error") } From 1222c87be38c5df4321e3d4bc56193895d44afb6 Mon Sep 17 00:00:00 2001 From: ysqi Date: Thu, 28 Jan 2016 14:49:44 +0800 Subject: [PATCH 3/8] optimization code --- config/config.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/config/config.go b/config/config.go index de2fb7f0..7dfca88a 100644 --- a/config/config.go +++ b/config/config.go @@ -119,9 +119,8 @@ func Getenv(env interface{}) (string, bool) { // 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 + if envKey := strings.TrimPrefix(key, envKeySign); envKey != key { + return os.Getenv(envKey), true } } return "", false From 36f69a04a9628fc058996129f64e6ec33f46532d Mon Sep 17 00:00:00 2001 From: ysqi Date: Thu, 4 Feb 2016 20:15:37 +0800 Subject: [PATCH 4/8] remove interfaceToStr function to package config --- config/config.go | 31 +++++++++++++++++++++++++++++++ config/xml/xml.go | 23 +---------------------- config/yaml/yaml.go | 24 ++---------------------- 3 files changed, 34 insertions(+), 44 deletions(-) diff --git a/config/config.go b/config/config.go index 7dfca88a..ef7738d9 100644 --- a/config/config.go +++ b/config/config.go @@ -119,6 +119,7 @@ func Getenv(env interface{}) (string, bool) { // Onley support string key. if key, ok := env.(string); ok { + if envKey := strings.TrimPrefix(key, envKeySign); envKey != key { return os.Getenv(envKey), true } @@ -126,6 +127,36 @@ func Getenv(env interface{}) (string, bool) { return "", false } +// ConvertToStringMap convert interface to string config value only for map[string]interface{} config info. +func ConvertToStringMap(m map[string]interface{}) map[string]string { + items := make(map[string]string, len(m)) + if m == nil || len(m) == 0 { + return items + } + + var s string + for k, v := range m { + s = "" + if v == nil { + s = "" + } else if str, ok := v.(string); ok { + s = str + } else if m, ok := v.(map[string]interface{}); ok { + s = fmt.Sprintf("%+v", ConvertToStringMap(m)) + } else { + s = fmt.Sprintf("%+v", v) + } + + if len(s) > 0 { + if env, ok := Getenv(s); ok { + s = env + } + } + items[k] = s + } + return items +} + // 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/xml/xml.go b/config/xml/xml.go index d48cfd8e..7222f745 100644 --- a/config/xml/xml.go +++ b/config/xml/xml.go @@ -190,28 +190,7 @@ 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 { - - 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 config.ConvertToStringMap(v.(map[string]interface{})), nil } return nil, errors.New("not exist setction") } diff --git a/config/yaml/yaml.go b/config/yaml/yaml.go index a2ec299c..db849949 100644 --- a/config/yaml/yaml.go +++ b/config/yaml/yaml.go @@ -242,29 +242,9 @@ 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) { - v, ok := c.data[section] - if ok { - 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 + if v, ok := c.data[section]; ok { + return config.ConvertToStringMap(v.(map[string]interface{})), nil } return nil, errors.New("not exist setction") } From b2a06c5fa053cbc71caece26ff19b99201be6afd Mon Sep 17 00:00:00 2001 From: ysqi Date: Sat, 12 Mar 2016 14:32:39 +0800 Subject: [PATCH 5/8] Update config suport environment variable logic --- config/config.go | 95 +++++++++++++++++-------------- config/fake.go | 10 +--- config/ini.go | 14 ++--- config/ini_test.go | 22 +++++-- config/json.go | 10 +--- config/json_test.go | 22 +++++-- config/xml/xml.go | 42 +++++--------- config/xml/xml_test.go | 120 ++++++++++++++++++++------------------- config/yaml/yaml.go | 17 +++--- config/yaml/yaml_test.go | 107 +++++++++++++++++++--------------- 10 files changed, 240 insertions(+), 219 deletions(-) diff --git a/config/config.go b/config/config.go index ef7738d9..59cc356b 100644 --- a/config/config.go +++ b/config/config.go @@ -12,11 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package config is used to parse config +// Package config is used to parse config. // Usage: -// import( -// "github.com/astaxie/beego/config" -// ) +// import "github.com/astaxie/beego/config" +//Examples. // // cnf, err := config.NewConfig("ini", "config.conf") // @@ -38,8 +37,7 @@ // cnf.DIY(key string) (interface{}, error) // cnf.GetSection(section string) (map[string]string, error) // cnf.SaveConfigFile(filename string) error -// -// more docs http://beego.me/docs/module/config.md +//More docs http://beego.me/docs/module/config.md package config import ( @@ -109,52 +107,61 @@ 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 envKey := strings.TrimPrefix(key, envKeySign); envKey != key { - return os.Getenv(envKey), true +// ChooseRealValueForMap convert all string value with environment variable. +func ChooseRealValueForMap(m map[string]interface{}) map[string]interface{} { + for k, v := range m { + switch value := v.(type) { + case string: + m[k] = ChooseRealValue(value) + case map[string]interface{}: + m[k] = ChooseRealValueForMap(value) + case map[string]string: + for k2, v2 := range value { + value[k2] = ChooseRealValue(v2) + } + m[k] = value } } - return "", false + return m } -// ConvertToStringMap convert interface to string config value only for map[string]interface{} config info. -func ConvertToStringMap(m map[string]interface{}) map[string]string { - items := make(map[string]string, len(m)) - if m == nil || len(m) == 0 { - return items +// ChooseRealValue returns value of convert with environment variable. +// +// Return environment variable if value start with "$$". +// Return default value if environment variable is empty or not exist. +// +// It accept value formats "$$env" , "$$env||" , "$$env||defaultValue" , "defaultvalue". +// Examples: +// v1 := config.ChooseRealValue("$$GOROOT") // return the GOROOT environment variable. +// v2 := config.ChooseRealValue("$$GOAsta||/usr/local/go/") // return the default value "/usr/local/go/". +// v3 := config.ChooseRealValue("Astaxie") // return the value "Astaxie". +func ChooseRealValue(value string) (realValue string) { + realValue = value + + if value == "" { + return } - var s string - for k, v := range m { - s = "" - if v == nil { - s = "" - } else if str, ok := v.(string); ok { - s = str - } else if m, ok := v.(map[string]interface{}); ok { - s = fmt.Sprintf("%+v", ConvertToStringMap(m)) - } else { - s = fmt.Sprintf("%+v", v) - } + sign := "$$" // Environment variable identifier. + sep := "||" // Environment variable and default value separator. - if len(s) > 0 { - if env, ok := Getenv(s); ok { - s = env - } - } - items[k] = s + // Not use environment variable. + if strings.HasPrefix(value, sign) == false { + return } - return items + + sepIndex := strings.Index(value, sep) + if sepIndex == -1 { + realValue = os.Getenv(string(value[len(sign):])) + } else { + realValue = os.Getenv(string(value[len(sign):sepIndex])) + // Find defalut value. + if realValue == "" { + realValue = string(value[sepIndex+len(sep):]) + } + } + + return } // ParseBool returns the boolean value represented by the string. diff --git a/config/fake.go b/config/fake.go index 347f1cfe..f5144598 100644 --- a/config/fake.go +++ b/config/fake.go @@ -25,15 +25,7 @@ type fakeConfigContainer struct { } func (c *fakeConfigContainer) getData(key string) string { - if len(key) == 0 { - return "" - } - - v := c.data[strings.ToLower(key)] - if env, ok := Getenv(v); ok { - return env - } - return v + return c.data[strings.ToLower(key)] } func (c *fakeConfigContainer) Set(key, val string) error { diff --git a/config/ini.go b/config/ini.go index 0d8571e4..829901d8 100644 --- a/config/ini.go +++ b/config/ini.go @@ -162,7 +162,7 @@ func (ini *IniConfig) parseFile(name string) (*IniConfigContainer, error) { val = bytes.Trim(val, `"`) } - cfg.data[section][key] = string(val) + cfg.data[section][key] = ChooseRealValue(string(val)) if comment.Len() > 0 { cfg.keyComment[section+"."+key] = comment.String() comment.Reset() @@ -291,17 +291,14 @@ 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") } -// SaveConfigFile save the config into file +// SaveConfigFile save the config into file. +// +// BUG(env): The environment variable config item will be saved with real value in SaveConfigFile Funcation. func (c *IniConfigContainer) SaveConfigFile(filename string) (err error) { // Write configuration file by filename. f, err := os.Create(filename) @@ -458,9 +455,6 @@ 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 9396db5b..b3f57fbe 100644 --- a/config/ini_test.go +++ b/config/ini_test.go @@ -42,13 +42,20 @@ needlogin = ON enableSession = Y enableCookie = N flag = 1 -path = $ENV_GOROOT +path1 = $$GOROOT +path2 = $$GOROOT||/home/go +path3 = $$GOROOT$$GOPATH2||/home/go +token1 = $$TOKEN +token2 = $$TOKEN|| +token3 = $$TOKEN||astaxie +token4 = token$$TOKEN +token5 = $$TOKEN$$TOKEN||TOKEN [demo] key1="asta" key2 = "xie" CaseInsensitive = true peers = one;two;three -password = $ENV_GOROOT +password = $$GOROOT ` keyValue = map[string]interface{}{ @@ -66,7 +73,14 @@ password = $ENV_GOROOT "enableSession": true, "enableCookie": false, "flag": true, - "path": os.Getenv("GOROOT"), + "path1": os.Getenv("GOROOT"), + "path2": os.Getenv("GOROOT"), + "path3": "/home/go", + "token1": "", + "token2": "", + "token3": "astaxie", + "token4": "token$$TOKEN", + "token5": "TOKEN", "demo::key1": "asta", "demo::key2": "xie", "demo::CaseInsensitive": true, @@ -145,7 +159,6 @@ httpport = 8080 # db type name # suport mysql,sqlserver name = mysql -path = $ENV_GOROOT ` saveResult = ` @@ -162,7 +175,6 @@ 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 ecef9439..3b4569b8 100644 --- a/config/json.go +++ b/config/json.go @@ -57,6 +57,9 @@ func (js *JSONConfig) ParseData(data []byte) (Configer, error) { } x.data["rootArray"] = wrappingArray } + + x.data = ChooseRealValueForMap(x.data) + return x, nil } @@ -250,18 +253,11 @@ 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 4ac2cdfc..6ff26dba 100644 --- a/config/json_test.go +++ b/config/json_test.go @@ -86,18 +86,25 @@ func TestJson(t *testing.T) { "enableSession": "Y", "enableCookie": "N", "flag": 1, -"path": "$ENV_GOROOT", +"path1": "$$GOROOT", +"path2": "$$GOROOT||/home/go", +"path3": "$$GOROOT$$GOPATH2||/home/go", +"token1": "$$TOKEN", +"token2": "$$TOKEN||", +"token3": "$$TOKEN||astaxie", +"token4": "token$$TOKEN", +"token5": "$$TOKEN$$TOKEN||TOKEN", "database": { "host": "host", "port": "port", "database": "database", "username": "username", - "password": "$ENV_GOROOT", + "password": "$$GOROOT", "conns":{ "maxconnection":12, "autoconnect":true, "connectioninfo":"info", - "root": "$ENV_GOROOT" + "root": "$$GOROOT" } } }` @@ -117,7 +124,14 @@ func TestJson(t *testing.T) { "enableSession": true, "enableCookie": false, "flag": true, - "path": os.Getenv("GOROOT"), + "path1": os.Getenv("GOROOT"), + "path2": os.Getenv("GOROOT"), + "path3": "/home/go", + "token1": "", + "token2": "", + "token3": "astaxie", + "token4": "token$$TOKEN", + "token5": "TOKEN", "database::host": "host", "database::port": "port", "database::database": "database", diff --git a/config/xml/xml.go b/config/xml/xml.go index a9218b03..63f3cb23 100644 --- a/config/xml/xml.go +++ b/config/xml/xml.go @@ -12,21 +12,21 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package xml for config provider +// Package xml for config provider. // -// depend on github.com/beego/x2j +// depend on github.com/beego/x2j. // -// go install github.com/beego/x2j +// go install github.com/beego/x2j. // // Usage: -// import( -// _ "github.com/astaxie/beego/config/xml" -// "github.com/astaxie/beego/config" -// ) +// import( +// _ "github.com/astaxie/beego/config/xml" +// "github.com/astaxie/beego/config" +// ) // // cnf, err := config.NewConfig("xml", "config.xml") // -// more docs http://beego.me/docs/module/config.md +//More docs http://beego.me/docs/module/config.md package xml import ( @@ -69,7 +69,7 @@ func (xc *Config) Parse(filename string) (config.Configer, error) { return nil, err } - x.data = d["config"].(map[string]interface{}) + x.data = config.ChooseRealValueForMap(d["config"].(map[string]interface{})) return x, nil } @@ -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 := c.getData(key); v != nil { + if v := c.data[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.getData(key).(string)) + return strconv.Atoi(c.data[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.getData(key).(string), 10, 64) + return strconv.ParseInt(c.data[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.getData(key).(string), 64) + return strconv.ParseFloat(c.data[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.getData(key).(string); ok { + if v, ok := c.data[key].(string); ok { return v } return "" @@ -194,7 +194,7 @@ 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 config.ConvertToStringMap(v.(map[string]interface{})), nil + return v.(map[string]string), nil } return nil, errors.New("not exist setction") } @@ -231,18 +231,6 @@ 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 5ef43c9b..85c92e8b 100644 --- a/config/xml/xml_test.go +++ b/config/xml/xml_test.go @@ -15,8 +15,8 @@ package xml import ( + "fmt" "os" - "strings" "testing" "github.com/astaxie/beego/config" @@ -24,8 +24,9 @@ import ( func TestXML(t *testing.T) { - //xml parse should incluce in tags - var xmlcontext = ` + var ( + //xml parse should incluce in tags + xmlcontext = ` beeapi 8080 @@ -34,23 +35,36 @@ func TestXML(t *testing.T) { dev false true -$ENV_GOROOT - - beego - $ENV_GOROOT - localhost - - value1 - $ENV_GOROOT - - - - 001 - gp2 - - +$$GOROOT +$$GOROOT||/home/go +$$GOROOT$$GOPATH2||/home/go +$$TOKEN +$$TOKEN|| +$$TOKEN||astaxie +token$$TOKEN +$$TOKEN$$TOKEN||TOKEN ` + keyValue = map[string]interface{}{ + "appname": "beeapi", + "httpport": 8080, + "mysqlport": int64(3600), + "PI": 3.1415976, + "runmode": "dev", + "autorender": false, + "copyrequestbody": true, + "path1": os.Getenv("GOROOT"), + "path2": os.Getenv("GOROOT"), + "path3": "/home/go", + "token1": "", + "token2": "", + "token3": "astaxie", + "token4": "token$$TOKEN", + "token5": "TOKEN", + "error": "", + "emptystrings": []string{}, + } + ) f, err := os.Create("testxml.conf") if err != nil { @@ -67,50 +81,42 @@ func TestXML(t *testing.T) { if err != nil { t.Fatal(err) } - if xmlconf.String("appname") != "beeapi" { - t.Fatal("appname not equal to beeapi") - } - if port, err := xmlconf.Int("httpport"); err != nil || port != 8080 { - t.Error(port) - t.Fatal(err) - } - if port, err := xmlconf.Int64("mysqlport"); err != nil || port != 3600 { - t.Error(port) - t.Fatal(err) - } - if pi, err := xmlconf.Float("PI"); err != nil || pi != 3.1415976 { - t.Error(pi) - t.Fatal(err) - } - if xmlconf.String("runmode") != "dev" { - t.Fatal("runmode not equal to dev") - } - if v, err := xmlconf.Bool("autorender"); err != nil || v != false { - t.Error(v) - t.Fatal(err) - } - if v, err := xmlconf.Bool("copyrequestbody"); err != nil || v != true { - t.Error(v) - t.Fatal(err) + + for k, v := range keyValue { + + var ( + value interface{} + err error + ) + + switch v.(type) { + case int: + value, err = xmlconf.Int(k) + case int64: + value, err = xmlconf.Int64(k) + case float64: + value, err = xmlconf.Float(k) + case bool: + value, err = xmlconf.Bool(k) + case []string: + value = xmlconf.Strings(k) + case string: + value = xmlconf.String(k) + default: + value, err = xmlconf.DIY(k) + } + if err != nil { + t.Errorf("get key %q value fatal,%v err %s", k, v, err) + } else if fmt.Sprintf("%v", v) != fmt.Sprintf("%v", value) { + t.Errorf("get key %q value, want %v got %v .", k, v, value) + } + } + if err = xmlconf.Set("name", "astaxie"); err != nil { t.Fatal(err) } 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") - } - if xmlconf.Strings("emptystrings") != nil { - t.Fatal("get emtpy strings error") - } } diff --git a/config/yaml/yaml.go b/config/yaml/yaml.go index e1dd7012..b94adbc1 100644 --- a/config/yaml/yaml.go +++ b/config/yaml/yaml.go @@ -19,14 +19,14 @@ // go install github.com/beego/goyaml2 // // Usage: -// import( +// import( // _ "github.com/astaxie/beego/config/yaml" -// "github.com/astaxie/beego/config" -// ) +// "github.com/astaxie/beego/config" +// ) // // cnf, err := config.NewConfig("yaml", "config.yaml") // -// more docs http://beego.me/docs/module/config.md +//More docs http://beego.me/docs/module/config.md package yaml import ( @@ -110,6 +110,7 @@ func ReadYmlReader(path string) (cnf map[string]interface{}, err error) { log.Println("Not a Map? >> ", string(buf), data) cnf = nil } + cnf = config.ChooseRealValueForMap(cnf) return } @@ -248,7 +249,7 @@ func (c *ConfigContainer) DefaultStrings(key string, defaultval []string) []stri func (c *ConfigContainer) GetSection(section string) (map[string]string, error) { if v, ok := c.data[section]; ok { - return config.ConvertToStringMap(v.(map[string]interface{})), nil + return v.(map[string]string), nil } return nil, errors.New("not exist setction") } @@ -285,11 +286,7 @@ func (c *ConfigContainer) getData(key string) (interface{}, error) { } if v, ok := c.data[key]; ok { - if env, ok := config.Getenv(v); ok { - return env, nil - } else { - return v, nil - } + return v, nil } return nil, fmt.Errorf("not exist key %q", key) } diff --git a/config/yaml/yaml_test.go b/config/yaml/yaml_test.go index 4eb36619..30c79d0b 100644 --- a/config/yaml/yaml_test.go +++ b/config/yaml/yaml_test.go @@ -15,8 +15,8 @@ package yaml import ( + "fmt" "os" - "strings" "testing" "github.com/astaxie/beego/config" @@ -33,21 +33,38 @@ func TestYaml(t *testing.T) { "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 +"path1": $$GOROOT +"path2": $$GOROOT||/home/go +"path3": $$GOROOT$$GOPATH2||/home/go +"token1": $$TOKEN +"token2": $$TOKEN|| +"token3": $$TOKEN||astaxie +"token4": token$$TOKEN +"token5": $$TOKEN$$TOKEN||TOKEN "empty": "" ` + + keyValue = map[string]interface{}{ + "appname": "beeapi", + "httpport": 8080, + "mysqlport": int64(3600), + "PI": 3.1415976, + "runmode": "dev", + "autorender": false, + "copyrequestbody": true, + "PATH": "GOROOT", + "path1": os.Getenv("GOROOT"), + "path2": os.Getenv("GOROOT"), + "path3": "/home/go", + "token1": "", + "token2": "", + "token3": "astaxie", + "token4": "token$$TOKEN", + "token5": "TOKEN", + "error": "", + "emptystrings": []string{}, + } ) f, err := os.Create("testyaml.conf") if err != nil { @@ -68,29 +85,38 @@ func TestYaml(t *testing.T) { if yamlconf.String("appname") != "beeapi" { t.Fatal("appname not equal to beeapi") } - if port, err := yamlconf.Int("httpport"); err != nil || port != 8080 { - t.Error(port) - t.Fatal(err) - } - if port, err := yamlconf.Int64("mysqlport"); err != nil || port != 3600 { - t.Error(port) - t.Fatal(err) - } - if pi, err := yamlconf.Float("PI"); err != nil || pi != 3.1415976 { - t.Error(pi) - t.Fatal(err) - } - if yamlconf.String("runmode") != "dev" { - t.Fatal("runmode not equal to dev") - } - if v, err := yamlconf.Bool("autorender"); err != nil || v != false { - t.Error(v) - t.Fatal(err) - } - if v, err := yamlconf.Bool("copyrequestbody"); err != nil || v != true { - t.Error(v) - t.Fatal(err) + + for k, v := range keyValue { + + var ( + value interface{} + err error + ) + + switch v.(type) { + case int: + value, err = yamlconf.Int(k) + case int64: + value, err = yamlconf.Int64(k) + case float64: + value, err = yamlconf.Float(k) + case bool: + value, err = yamlconf.Bool(k) + case []string: + value = yamlconf.Strings(k) + case string: + value = yamlconf.String(k) + default: + value, err = yamlconf.DIY(k) + } + if err != nil { + t.Errorf("get key %q value fatal,%v err %s", k, v, err) + } else if fmt.Sprintf("%v", v) != fmt.Sprintf("%v", value) { + t.Errorf("get key %q value, want %v got %v .", k, v, value) + } + } + if err = yamlconf.Set("name", "astaxie"); err != nil { t.Fatal(err) } @@ -98,15 +124,4 @@ func TestYaml(t *testing.T) { 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") - } - - if yamlconf.Strings("emptystrings") != nil { - t.Fatal("get emtpy strings error") - } } From 9c7d95b0710a41bc715554306be43a194560e621 Mon Sep 17 00:00:00 2001 From: ysqi Date: Mon, 14 Mar 2016 19:21:09 +0800 Subject: [PATCH 6/8] go vet --- config/yaml/yaml.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/config/yaml/yaml.go b/config/yaml/yaml.go index b94adbc1..6a6f07c7 100644 --- a/config/yaml/yaml.go +++ b/config/yaml/yaml.go @@ -122,12 +122,11 @@ type ConfigContainer struct { // Bool returns the boolean value for a given key. func (c *ConfigContainer) Bool(key string) (bool, error) { - - if v, err := c.getData(key); err != nil { + v, err := c.getData(key) + if err != nil { return false, err - } else { - return config.ParseBool(v) } + return config.ParseBool(v) } // DefaultBool return the bool value if has no error From 1b04571c0b493ff61da488e357b05f84747249ce Mon Sep 17 00:00:00 2001 From: ysqi Date: Mon, 14 Mar 2016 19:22:00 +0800 Subject: [PATCH 7/8] test the env use GOPATH not GOROOT --- config/config.go | 2 +- config/ini_test.go | 14 +++++++------- config/json_test.go | 18 +++++++++--------- config/xml/xml_test.go | 10 +++++----- config/yaml/yaml_test.go | 14 +++++++------- 5 files changed, 29 insertions(+), 29 deletions(-) diff --git a/config/config.go b/config/config.go index 59cc356b..6a159f82 100644 --- a/config/config.go +++ b/config/config.go @@ -132,7 +132,7 @@ func ChooseRealValueForMap(m map[string]interface{}) map[string]interface{} { // // It accept value formats "$$env" , "$$env||" , "$$env||defaultValue" , "defaultvalue". // Examples: -// v1 := config.ChooseRealValue("$$GOROOT") // return the GOROOT environment variable. +// v1 := config.ChooseRealValue("$$GOPATH") // return the GOPATH environment variable. // v2 := config.ChooseRealValue("$$GOAsta||/usr/local/go/") // return the default value "/usr/local/go/". // v3 := config.ChooseRealValue("Astaxie") // return the value "Astaxie". func ChooseRealValue(value string) (realValue string) { diff --git a/config/ini_test.go b/config/ini_test.go index b3f57fbe..a6a66198 100644 --- a/config/ini_test.go +++ b/config/ini_test.go @@ -42,9 +42,9 @@ needlogin = ON enableSession = Y enableCookie = N flag = 1 -path1 = $$GOROOT -path2 = $$GOROOT||/home/go -path3 = $$GOROOT$$GOPATH2||/home/go +path1 = $$GOPATH +path2 = $$GOPATH||/home/go +path3 = $$GOPATH$$GOPATH2||/home/go token1 = $$TOKEN token2 = $$TOKEN|| token3 = $$TOKEN||astaxie @@ -55,7 +55,7 @@ key1="asta" key2 = "xie" CaseInsensitive = true peers = one;two;three -password = $$GOROOT +password = $$GOPATH ` keyValue = map[string]interface{}{ @@ -73,8 +73,8 @@ password = $$GOROOT "enableSession": true, "enableCookie": false, "flag": true, - "path1": os.Getenv("GOROOT"), - "path2": os.Getenv("GOROOT"), + "path1": os.Getenv("GOPATH"), + "path2": os.Getenv("GOPATH"), "path3": "/home/go", "token1": "", "token2": "", @@ -85,7 +85,7 @@ password = $$GOROOT "demo::key2": "xie", "demo::CaseInsensitive": true, "demo::peers": []string{"one", "two", "three"}, - "demo::password": os.Getenv("GOROOT"), + "demo::password": os.Getenv("GOPATH"), "null": "", "demo2::key1": "", "error": "", diff --git a/config/json_test.go b/config/json_test.go index 6ff26dba..940916a1 100644 --- a/config/json_test.go +++ b/config/json_test.go @@ -86,9 +86,9 @@ func TestJson(t *testing.T) { "enableSession": "Y", "enableCookie": "N", "flag": 1, -"path1": "$$GOROOT", -"path2": "$$GOROOT||/home/go", -"path3": "$$GOROOT$$GOPATH2||/home/go", +"path1": "$$GOPATH", +"path2": "$$GOPATH||/home/go", +"path3": "$$GOPATH$$GOPATH2||/home/go", "token1": "$$TOKEN", "token2": "$$TOKEN||", "token3": "$$TOKEN||astaxie", @@ -99,12 +99,12 @@ func TestJson(t *testing.T) { "port": "port", "database": "database", "username": "username", - "password": "$$GOROOT", + "password": "$$GOPATH", "conns":{ "maxconnection":12, "autoconnect":true, "connectioninfo":"info", - "root": "$$GOROOT" + "root": "$$GOPATH" } } }` @@ -124,8 +124,8 @@ func TestJson(t *testing.T) { "enableSession": true, "enableCookie": false, "flag": true, - "path1": os.Getenv("GOROOT"), - "path2": os.Getenv("GOROOT"), + "path1": os.Getenv("GOPATH"), + "path2": os.Getenv("GOPATH"), "path3": "/home/go", "token1": "", "token2": "", @@ -135,11 +135,11 @@ func TestJson(t *testing.T) { "database::host": "host", "database::port": "port", "database::database": "database", - "database::password": os.Getenv("GOROOT"), + "database::password": os.Getenv("GOPATH"), "database::conns::maxconnection": 12, "database::conns::autoconnect": true, "database::conns::connectioninfo": "info", - "database::conns::root": os.Getenv("GOROOT"), + "database::conns::root": os.Getenv("GOPATH"), "unknown": "", } ) diff --git a/config/xml/xml_test.go b/config/xml/xml_test.go index 85c92e8b..825f4d0d 100644 --- a/config/xml/xml_test.go +++ b/config/xml/xml_test.go @@ -35,9 +35,9 @@ func TestXML(t *testing.T) { dev false true -$$GOROOT -$$GOROOT||/home/go -$$GOROOT$$GOPATH2||/home/go +$$GOPATH +$$GOPATH||/home/go +$$GOPATH$$GOPATH2||/home/go $$TOKEN $$TOKEN|| $$TOKEN||astaxie @@ -53,8 +53,8 @@ func TestXML(t *testing.T) { "runmode": "dev", "autorender": false, "copyrequestbody": true, - "path1": os.Getenv("GOROOT"), - "path2": os.Getenv("GOROOT"), + "path1": os.Getenv("GOPATH"), + "path2": os.Getenv("GOPATH"), "path3": "/home/go", "token1": "", "token2": "", diff --git a/config/yaml/yaml_test.go b/config/yaml/yaml_test.go index 30c79d0b..0731778f 100644 --- a/config/yaml/yaml_test.go +++ b/config/yaml/yaml_test.go @@ -33,10 +33,10 @@ func TestYaml(t *testing.T) { "runmode": dev "autorender": false "copyrequestbody": true -"PATH": GOROOT -"path1": $$GOROOT -"path2": $$GOROOT||/home/go -"path3": $$GOROOT$$GOPATH2||/home/go +"PATH": GOPATH +"path1": $$GOPATH +"path2": $$GOPATH||/home/go +"path3": $$GOPATH$$GOPATH2||/home/go "token1": $$TOKEN "token2": $$TOKEN|| "token3": $$TOKEN||astaxie @@ -53,9 +53,9 @@ func TestYaml(t *testing.T) { "runmode": "dev", "autorender": false, "copyrequestbody": true, - "PATH": "GOROOT", - "path1": os.Getenv("GOROOT"), - "path2": os.Getenv("GOROOT"), + "PATH": "GOPATH", + "path1": os.Getenv("GOPATH"), + "path2": os.Getenv("GOPATH"), "path3": "/home/go", "token1": "", "token2": "", From 7e65338c871527f43ce02342588a58503f089afa Mon Sep 17 00:00:00 2001 From: ysqi Date: Tue, 29 Mar 2016 21:47:33 +0800 Subject: [PATCH 8/8] Change key format key format : ${ENV_PART||defaultValue} or ${ENV_PART} --- config/config.go | 65 +++++++++++++++++++++------------------- config/config_test.go | 55 ++++++++++++++++++++++++++++++++++ config/ini.go | 2 +- config/ini_test.go | 18 ++--------- config/json.go | 2 +- config/json_test.go | 20 +++---------- config/xml/xml.go | 2 +- config/xml/xml_test.go | 16 ++-------- config/yaml/yaml.go | 2 +- config/yaml/yaml_test.go | 16 ++-------- 10 files changed, 105 insertions(+), 93 deletions(-) create mode 100644 config/config_test.go diff --git a/config/config.go b/config/config.go index 6a159f82..9f41fb79 100644 --- a/config/config.go +++ b/config/config.go @@ -43,7 +43,6 @@ package config import ( "fmt" "os" - "strings" ) // Configer defines how to get and set value from configuration raw data. @@ -107,17 +106,17 @@ func NewConfigData(adapterName string, data []byte) (Configer, error) { return adapter.ParseData(data) } -// ChooseRealValueForMap convert all string value with environment variable. -func ChooseRealValueForMap(m map[string]interface{}) map[string]interface{} { +// ExpandValueEnvForMap convert all string value with environment variable. +func ExpandValueEnvForMap(m map[string]interface{}) map[string]interface{} { for k, v := range m { switch value := v.(type) { case string: - m[k] = ChooseRealValue(value) + m[k] = ExpandValueEnv(value) case map[string]interface{}: - m[k] = ChooseRealValueForMap(value) + m[k] = ExpandValueEnvForMap(value) case map[string]string: for k2, v2 := range value { - value[k2] = ChooseRealValue(v2) + value[k2] = ExpandValueEnv(v2) } m[k] = value } @@ -125,42 +124,48 @@ func ChooseRealValueForMap(m map[string]interface{}) map[string]interface{} { return m } -// ChooseRealValue returns value of convert with environment variable. +// ExpandValueEnv returns value of convert with environment variable. // -// Return environment variable if value start with "$$". +// Return environment variable if value start with "${" and end with "}". // Return default value if environment variable is empty or not exist. // -// It accept value formats "$$env" , "$$env||" , "$$env||defaultValue" , "defaultvalue". +// It accept value formats "${env}" , "${env||}}" , "${env||defaultValue}" , "defaultvalue". // Examples: -// v1 := config.ChooseRealValue("$$GOPATH") // return the GOPATH environment variable. -// v2 := config.ChooseRealValue("$$GOAsta||/usr/local/go/") // return the default value "/usr/local/go/". -// v3 := config.ChooseRealValue("Astaxie") // return the value "Astaxie". -func ChooseRealValue(value string) (realValue string) { +// v1 := config.ExpandValueEnv("${GOPATH}") // return the GOPATH environment variable. +// v2 := config.ExpandValueEnv("${GOAsta||/usr/local/go}") // return the default value "/usr/local/go/". +// v3 := config.ExpandValueEnv("Astaxie") // return the value "Astaxie". +func ExpandValueEnv(value string) (realValue string) { realValue = value - if value == "" { + vLen := len(value) + // 3 = ${} + if vLen < 3 { + return + } + // Need start with "${" and end with "}", then return. + if value[0] != '$' || value[1] != '{' || value[vLen-1] != '}' { return } - sign := "$$" // Environment variable identifier. - sep := "||" // Environment variable and default value separator. - - // Not use environment variable. - if strings.HasPrefix(value, sign) == false { - return - } - - sepIndex := strings.Index(value, sep) - if sepIndex == -1 { - realValue = os.Getenv(string(value[len(sign):])) - } else { - realValue = os.Getenv(string(value[len(sign):sepIndex])) - // Find defalut value. - if realValue == "" { - realValue = string(value[sepIndex+len(sep):]) + key := "" + defalutV := "" + // value start with "${" + for i := 2; i < vLen; i++ { + if value[i] == '|' && (i+1 < vLen && value[i+1] == '|') { + key = value[2:i] + defalutV = value[i+2 : vLen-1] // other string is default value. + break + } else if value[i] == '}' { + key = value[2:i] + break } } + realValue = os.Getenv(key) + if realValue == "" { + realValue = defalutV + } + return } diff --git a/config/config_test.go b/config/config_test.go new file mode 100644 index 00000000..15d6ffa6 --- /dev/null +++ b/config/config_test.go @@ -0,0 +1,55 @@ +// Copyright 2016 beego Author. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package config + +import ( + "os" + "testing" +) + +func TestExpandValueEnv(t *testing.T) { + + testCases := []struct { + item string + want string + }{ + {"", ""}, + {"$", "$"}, + {"{", "{"}, + {"{}", "{}"}, + {"${}", ""}, + {"${|}", ""}, + {"${}", ""}, + {"${{}}", ""}, + {"${{||}}", "}"}, + {"${pwd||}", ""}, + {"${pwd||}", ""}, + {"${pwd||}", ""}, + {"${pwd||}}", "}"}, + {"${pwd||{{||}}}", "{{||}}"}, + {"${GOPATH}", os.Getenv("GOPATH")}, + {"${GOPATH||}", os.Getenv("GOPATH")}, + {"${GOPATH||root}", os.Getenv("GOPATH")}, + {"${GOPATH_NOT||root}", "root"}, + {"${GOPATH_NOT||||root}", "||root"}, + } + + for _, c := range testCases { + if got := ExpandValueEnv(c.item); got != c.want { + t.Errorf("expand value error, item %q want %q, got %q", c.item, c.want, got) + } + } + +} diff --git a/config/ini.go b/config/ini.go index 1ec56238..53bd992d 100644 --- a/config/ini.go +++ b/config/ini.go @@ -166,7 +166,7 @@ func (ini *IniConfig) parseFile(name string) (*IniConfigContainer, error) { val = bytes.Trim(val, `"`) } - cfg.data[section][key] = ChooseRealValue(string(val)) + cfg.data[section][key] = ExpandValueEnv(string(val)) if comment.Len() > 0 { cfg.keyComment[section+"."+key] = comment.String() comment.Reset() diff --git a/config/ini_test.go b/config/ini_test.go index a6a66198..83ff3668 100644 --- a/config/ini_test.go +++ b/config/ini_test.go @@ -42,20 +42,14 @@ needlogin = ON enableSession = Y enableCookie = N flag = 1 -path1 = $$GOPATH -path2 = $$GOPATH||/home/go -path3 = $$GOPATH$$GOPATH2||/home/go -token1 = $$TOKEN -token2 = $$TOKEN|| -token3 = $$TOKEN||astaxie -token4 = token$$TOKEN -token5 = $$TOKEN$$TOKEN||TOKEN +path1 = ${GOPATH} +path2 = ${GOPATH||/home/go} [demo] key1="asta" key2 = "xie" CaseInsensitive = true peers = one;two;three -password = $$GOPATH +password = ${GOPATH} ` keyValue = map[string]interface{}{ @@ -75,12 +69,6 @@ password = $$GOPATH "flag": true, "path1": os.Getenv("GOPATH"), "path2": os.Getenv("GOPATH"), - "path3": "/home/go", - "token1": "", - "token2": "", - "token3": "astaxie", - "token4": "token$$TOKEN", - "token5": "TOKEN", "demo::key1": "asta", "demo::key2": "xie", "demo::CaseInsensitive": true, diff --git a/config/json.go b/config/json.go index 3b4569b8..a0d93210 100644 --- a/config/json.go +++ b/config/json.go @@ -58,7 +58,7 @@ func (js *JSONConfig) ParseData(data []byte) (Configer, error) { x.data["rootArray"] = wrappingArray } - x.data = ChooseRealValueForMap(x.data) + x.data = ExpandValueEnvForMap(x.data) return x, nil } diff --git a/config/json_test.go b/config/json_test.go index 940916a1..24ff9644 100644 --- a/config/json_test.go +++ b/config/json_test.go @@ -86,25 +86,19 @@ func TestJson(t *testing.T) { "enableSession": "Y", "enableCookie": "N", "flag": 1, -"path1": "$$GOPATH", -"path2": "$$GOPATH||/home/go", -"path3": "$$GOPATH$$GOPATH2||/home/go", -"token1": "$$TOKEN", -"token2": "$$TOKEN||", -"token3": "$$TOKEN||astaxie", -"token4": "token$$TOKEN", -"token5": "$$TOKEN$$TOKEN||TOKEN", +"path1": "${GOPATH}", +"path2": "${GOPATH||/home/go}", "database": { "host": "host", "port": "port", "database": "database", "username": "username", - "password": "$$GOPATH", + "password": "${GOPATH}", "conns":{ "maxconnection":12, "autoconnect":true, "connectioninfo":"info", - "root": "$$GOPATH" + "root": "${GOPATH}" } } }` @@ -126,12 +120,6 @@ func TestJson(t *testing.T) { "flag": true, "path1": os.Getenv("GOPATH"), "path2": os.Getenv("GOPATH"), - "path3": "/home/go", - "token1": "", - "token2": "", - "token3": "astaxie", - "token4": "token$$TOKEN", - "token5": "TOKEN", "database::host": "host", "database::port": "port", "database::database": "database", diff --git a/config/xml/xml.go b/config/xml/xml.go index 63f3cb23..0c4e4d27 100644 --- a/config/xml/xml.go +++ b/config/xml/xml.go @@ -69,7 +69,7 @@ func (xc *Config) Parse(filename string) (config.Configer, error) { return nil, err } - x.data = config.ChooseRealValueForMap(d["config"].(map[string]interface{})) + x.data = config.ExpandValueEnvForMap(d["config"].(map[string]interface{})) return x, nil } diff --git a/config/xml/xml_test.go b/config/xml/xml_test.go index 825f4d0d..d8a09a59 100644 --- a/config/xml/xml_test.go +++ b/config/xml/xml_test.go @@ -35,14 +35,8 @@ func TestXML(t *testing.T) { dev false true -$$GOPATH -$$GOPATH||/home/go -$$GOPATH$$GOPATH2||/home/go -$$TOKEN -$$TOKEN|| -$$TOKEN||astaxie -token$$TOKEN -$$TOKEN$$TOKEN||TOKEN +${GOPATH} +${GOPATH||/home/go} ` keyValue = map[string]interface{}{ @@ -55,12 +49,6 @@ func TestXML(t *testing.T) { "copyrequestbody": true, "path1": os.Getenv("GOPATH"), "path2": os.Getenv("GOPATH"), - "path3": "/home/go", - "token1": "", - "token2": "", - "token3": "astaxie", - "token4": "token$$TOKEN", - "token5": "TOKEN", "error": "", "emptystrings": []string{}, } diff --git a/config/yaml/yaml.go b/config/yaml/yaml.go index 6a6f07c7..64e25cb3 100644 --- a/config/yaml/yaml.go +++ b/config/yaml/yaml.go @@ -110,7 +110,7 @@ func ReadYmlReader(path string) (cnf map[string]interface{}, err error) { log.Println("Not a Map? >> ", string(buf), data) cnf = nil } - cnf = config.ChooseRealValueForMap(cnf) + cnf = config.ExpandValueEnvForMap(cnf) return } diff --git a/config/yaml/yaml_test.go b/config/yaml/yaml_test.go index 0731778f..49cc1d1e 100644 --- a/config/yaml/yaml_test.go +++ b/config/yaml/yaml_test.go @@ -34,14 +34,8 @@ func TestYaml(t *testing.T) { "autorender": false "copyrequestbody": true "PATH": GOPATH -"path1": $$GOPATH -"path2": $$GOPATH||/home/go -"path3": $$GOPATH$$GOPATH2||/home/go -"token1": $$TOKEN -"token2": $$TOKEN|| -"token3": $$TOKEN||astaxie -"token4": token$$TOKEN -"token5": $$TOKEN$$TOKEN||TOKEN +"path1": ${GOPATH} +"path2": ${GOPATH||/home/go} "empty": "" ` @@ -56,12 +50,6 @@ func TestYaml(t *testing.T) { "PATH": "GOPATH", "path1": os.Getenv("GOPATH"), "path2": os.Getenv("GOPATH"), - "path3": "/home/go", - "token1": "", - "token2": "", - "token3": "astaxie", - "token4": "token$$TOKEN", - "token5": "TOKEN", "error": "", "emptystrings": []string{}, }