1
0
mirror of https://github.com/astaxie/beego.git synced 2024-11-25 05:30:56 +00:00

Config support get environment variable

get environment variable if config item  has prefix "$ENV_" .
e.g.
```ini
[demo]
password = $ENV_MyPWD
```
This commit is contained in:
ysqi 2016-01-27 20:46:30 +08:00
parent 4ce094a29a
commit cd31c816cc
10 changed files with 222 additions and 32 deletions

View File

@ -44,6 +44,8 @@ package config
import ( import (
"fmt" "fmt"
"os"
"strings"
) )
// Configer defines how to get and set value from configuration raw data. // 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) 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. // 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, // It accepts 1, 1.0, t, T, TRUE, true, True, YES, yes, Yes,Y, y, ON, on, On,

View File

@ -25,7 +25,15 @@ type fakeConfigContainer struct {
} }
func (c *fakeConfigContainer) getData(key string) string { 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 { 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 { func (c *fakeConfigContainer) DefaultString(key string, defaultval string) string {
v := c.getData(key) v := c.String(key)
if v == "" { if v == "" {
return defaultval return defaultval
} }
@ -46,7 +54,7 @@ func (c *fakeConfigContainer) DefaultString(key string, defaultval string) strin
} }
func (c *fakeConfigContainer) Strings(key string) []string { 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 { func (c *fakeConfigContainer) DefaultStrings(key string, defaultval []string) []string {

View File

@ -286,6 +286,11 @@ func (c *IniConfigContainer) DefaultStrings(key string, defaultval []string) []s
// GetSection returns map for the given section // GetSection returns map for the given section
func (c *IniConfigContainer) GetSection(section string) (map[string]string, error) { func (c *IniConfigContainer) GetSection(section string) (map[string]string, error) {
if v, ok := c.data[section]; ok { 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 v, nil
} }
return nil, errors.New("not exist setction") 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 v, ok := c.data[section]; ok {
if vv, ok := v[k]; ok { if vv, ok := v[k]; ok {
if env, ok := Getenv(vv); ok {
return env
}
return vv return vv
} }
} }

View File

@ -42,11 +42,13 @@ needlogin = ON
enableSession = Y enableSession = Y
enableCookie = N enableCookie = N
flag = 1 flag = 1
path = $ENV_GOROOT
[demo] [demo]
key1="asta" key1="asta"
key2 = "xie" key2 = "xie"
CaseInsensitive = true CaseInsensitive = true
peers = one;two;three peers = one;two;three
password = $ENV_GOROOT
` `
keyValue = map[string]interface{}{ keyValue = map[string]interface{}{
@ -64,10 +66,12 @@ peers = one;two;three
"enableSession": true, "enableSession": true,
"enableCookie": false, "enableCookie": false,
"flag": true, "flag": true,
"path": os.Getenv("GOROOT"),
"demo::key1": "asta", "demo::key1": "asta",
"demo::key2": "xie", "demo::key2": "xie",
"demo::CaseInsensitive": true, "demo::CaseInsensitive": true,
"demo::peers": []string{"one", "two", "three"}, "demo::peers": []string{"one", "two", "three"},
"demo::password": os.Getenv("GOROOT"),
"null": "", "null": "",
"demo2::key1": "", "demo2::key1": "",
"error": "", "error": "",
@ -140,6 +144,7 @@ httpport = 8080
# db type name # db type name
# suport mysql,sqlserver # suport mysql,sqlserver
name = mysql name = mysql
path = $ENV_GOROOT
` `
saveResult = ` saveResult = `
@ -156,6 +161,7 @@ httpport=8080
# db type name # db type name
# suport mysql,sqlserver # suport mysql,sqlserver
name=mysql name=mysql
path=$ENV_GOROOT
` `
) )
cfg, err := NewConfigData("ini", []byte(inicontext)) cfg, err := NewConfigData("ini", []byte(inicontext))

View File

@ -250,11 +250,18 @@ func (c *JSONConfigContainer) getData(key string) interface{} {
} }
} }
} }
if env, ok := Getenv(curValue); ok {
return env
}
return curValue return curValue
} }
if v, ok := c.data[key]; ok { if v, ok := c.data[key]; ok {
if env, ok := Getenv(v); ok {
return env
}
return v return v
} }
return nil return nil
} }

View File

@ -86,16 +86,18 @@ func TestJson(t *testing.T) {
"enableSession": "Y", "enableSession": "Y",
"enableCookie": "N", "enableCookie": "N",
"flag": 1, "flag": 1,
"path": "$ENV_GOROOT",
"database": { "database": {
"host": "host", "host": "host",
"port": "port", "port": "port",
"database": "database", "database": "database",
"username": "username", "username": "username",
"password": "password", "password": "$ENV_GOROOT",
"conns":{ "conns":{
"maxconnection":12, "maxconnection":12,
"autoconnect":true, "autoconnect":true,
"connectioninfo":"info" "connectioninfo":"info",
"root": "$ENV_GOROOT"
} }
} }
}` }`
@ -115,13 +117,15 @@ func TestJson(t *testing.T) {
"enableSession": true, "enableSession": true,
"enableCookie": false, "enableCookie": false,
"flag": true, "flag": true,
"path": os.Getenv("GOROOT"),
"database::host": "host", "database::host": "host",
"database::port": "port", "database::port": "port",
"database::database": "database", "database::database": "database",
"database::password": "password", "database::password": os.Getenv("GOROOT"),
"database::conns::maxconnection": 12, "database::conns::maxconnection": 12,
"database::conns::autoconnect": true, "database::conns::autoconnect": true,
"database::conns::connectioninfo": "info", "database::conns::connectioninfo": "info",
"database::conns::root": os.Getenv("GOROOT"),
"unknown": "", "unknown": "",
} }
) )

View File

@ -92,7 +92,7 @@ type ConfigContainer struct {
// Bool returns the boolean value for a given key. // Bool returns the boolean value for a given key.
func (c *ConfigContainer) Bool(key string) (bool, error) { 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 config.ParseBool(v)
} }
return false, fmt.Errorf("not exist key: %q", key) 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. // Int returns the integer value for a given key.
func (c *ConfigContainer) Int(key string) (int, error) { 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. // 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. // Int64 returns the int64 value for a given key.
func (c *ConfigContainer) Int64(key string) (int64, error) { 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. // 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. // Float returns the float value for a given key.
func (c *ConfigContainer) Float(key string) (float64, error) { 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. // 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. // String returns the string value for a given key.
func (c *ConfigContainer) String(key string) string { 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 v
} }
return "" return ""
@ -190,7 +190,28 @@ func (c *ConfigContainer) DefaultStrings(key string, defaultval []string) []stri
// GetSection returns map for the given section // GetSection returns map for the given section
func (c *ConfigContainer) GetSection(section string) (map[string]string, error) { func (c *ConfigContainer) GetSection(section string) (map[string]string, error) {
if v, ok := c.data[section]; ok { 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") 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") 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() { func init() {
config.Register("xml", &Config{}) config.Register("xml", &Config{})
} }

View File

@ -16,11 +16,14 @@ package xml
import ( import (
"os" "os"
"strings"
"testing" "testing"
"github.com/astaxie/beego/config" "github.com/astaxie/beego/config"
) )
func TestXML(t *testing.T) {
//xml parse should incluce in <config></config> tags //xml parse should incluce in <config></config> tags
var xmlcontext = `<?xml version="1.0" encoding="UTF-8"?> var xmlcontext = `<?xml version="1.0" encoding="UTF-8"?>
<config> <config>
@ -31,10 +34,24 @@ var xmlcontext = `<?xml version="1.0" encoding="UTF-8"?>
<runmode>dev</runmode> <runmode>dev</runmode>
<autorender>false</autorender> <autorender>false</autorender>
<copyrequestbody>true</copyrequestbody> <copyrequestbody>true</copyrequestbody>
<path>$ENV_GOROOT</path>
<dbinfo>
<db>beego</db>
<pwd>$ENV_GOROOT</pwd>
<url>localhost</url>
<detail>
<d1>value1</d1>
<d2>$ENV_GOROOT</d2>
<d3></d3>
</detail>
<group>
<id>001</id>
<name>gp2</name>
</group>
</dbinfo>
</config> </config>
` `
func TestXML(t *testing.T) {
f, err := os.Create("testxml.conf") f, err := os.Create("testxml.conf")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -82,4 +99,16 @@ func TestXML(t *testing.T) {
if xmlconf.String("name") != "astaxie" { if xmlconf.String("name") != "astaxie" {
t.Fatal("get name error") 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")
}
} }

View File

@ -121,10 +121,12 @@ type ConfigContainer struct {
// Bool returns the boolean value for a given key. // Bool returns the boolean value for a given key.
func (c *ConfigContainer) Bool(key string) (bool, error) { 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 config.ParseBool(v)
} }
return false, fmt.Errorf("not exist key: %q", key)
} }
// DefaultBool return the bool value if has no error // 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. // Int returns the integer value for a given key.
func (c *ConfigContainer) Int(key string) (int, error) { func (c *ConfigContainer) Int(key string) (int, error) {
if v, ok := c.data[key].(int64); ok { if v, err := c.getData(key); err != nil {
return int(v), 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") 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. // Int64 returns the int64 value for a given key.
func (c *ConfigContainer) Int64(key string) (int64, error) { func (c *ConfigContainer) Int64(key string) (int64, error) {
if v, ok := c.data[key].(int64); ok { if v, err := c.getData(key); err != nil {
return v, nil return 0, err
} else if vv, ok := v.(int64); ok {
return vv, nil
} }
return 0, errors.New("not bool value") 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. // Float returns the float value for a given key.
func (c *ConfigContainer) Float(key string) (float64, error) { func (c *ConfigContainer) Float(key string) (float64, error) {
if v, ok := c.data[key].(float64); ok { if v, err := c.getData(key); err != nil {
return v, 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") 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. // String returns the string value for a given key.
func (c *ConfigContainer) String(key string) string { func (c *ConfigContainer) String(key string) string {
if v, ok := c.data[key].(string); ok { if v, err := c.getData(key); err == nil {
return v if vv, ok := v.(string); ok {
return vv
}
} }
return "" return ""
} }
@ -228,7 +244,27 @@ func (c *ConfigContainer) DefaultStrings(key string, defaultval []string) []stri
func (c *ConfigContainer) GetSection(section string) (map[string]string, error) { func (c *ConfigContainer) GetSection(section string) (map[string]string, error) {
v, ok := c.data[section] v, ok := c.data[section]
if ok { 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") 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. // DIY returns the raw value by a given key.
func (c *ConfigContainer) DIY(key string) (v interface{}, err error) { func (c *ConfigContainer) DIY(key string) (v interface{}, err error) {
return c.getData(key)
}
func (c *ConfigContainer) getData(key string) (interface{}, error) {
if len(key) == 0 {
return nil, errors.New("key is emtpy")
}
if v, ok := c.data[key]; ok { 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, errors.New("not exist key") }
return nil, fmt.Errorf("not exist key %q", key)
} }
func init() { func init() {

View File

@ -16,12 +16,16 @@ package yaml
import ( import (
"os" "os"
"strings"
"testing" "testing"
"github.com/astaxie/beego/config" "github.com/astaxie/beego/config"
) )
var yamlcontext = ` func TestYaml(t *testing.T) {
var (
yamlcontext = `
"appname": beeapi "appname": beeapi
"httpport": 8080 "httpport": 8080
"mysqlport": 3600 "mysqlport": 3600
@ -29,9 +33,22 @@ var yamlcontext = `
"runmode": dev "runmode": dev
"autorender": false "autorender": false
"copyrequestbody": true "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") f, err := os.Create("testyaml.conf")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -47,6 +64,7 @@ func TestYaml(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if yamlconf.String("appname") != "beeapi" { if yamlconf.String("appname") != "beeapi" {
t.Fatal("appname not equal to beeapi") t.Fatal("appname not equal to beeapi")
} }
@ -79,4 +97,12 @@ func TestYaml(t *testing.T) {
if yamlconf.String("name") != "astaxie" { if yamlconf.String("name") != "astaxie" {
t.Fatal("get name error") 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")
}
} }