/
config.go
162 lines (146 loc) · 3.53 KB
/
config.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
package config
import (
"bytes"
"flag"
"io/ioutil"
"net/url"
"os"
"strings"
"github.com/fsnotify/fsnotify"
"github.com/google/wire"
"github.com/goriller/ginny/logger"
"github.com/spf13/viper"
_ "github.com/spf13/viper/remote"
)
var (
remoteConfig string
defaultConfigPath string
// ConfigProviderSet
ConfigProviderSet = wire.NewSet(NewConfig)
)
func init() {
// 远程配置 etcd、consul
flag.StringVar(&remoteConfig, "remote", "", "remote config provider: etcd://127.0.0.1:8500/test or consul://127.0.0.1:6577/test ")
// 配置文件路径
flag.StringVar(&defaultConfigPath, "conf", "./configs/config.yaml", "uri to load config")
}
// NewConfig
func NewConfig() (*viper.Viper, error) {
var (
err error
v = viper.New()
)
flag.Parse()
v.AddConfigPath(".")
v.SetConfigFile(defaultConfigPath)
v.AutomaticEnv()
v.SetEnvPrefix("ginny")
v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
// 监听配置文件变更
v.WatchConfig()
v.OnConfigChange(func(_ fsnotify.Event) {
log := logger.Default()
log.Info("Config file updated.")
err := loadConfig(v)
if err != nil {
log.Error("Config file reload error." + err.Error())
}
})
// if err := v.ReadInConfig(); err == nil {
// log.Printf("Config %s loaded successfully...", v.ConfigFileUsed())
// } else {
// return nil, err
// }
if err := loadConfig(v); err != nil {
return nil, err
}
return v, err
}
// loadConfig
func loadConfig(v *viper.Viper) error {
log := logger.Default()
log.Info("Loading config...")
// load config from remote
p := os.Getenv("REMOTE_CONFIG")
if remoteConfig == "" {
remoteConfig = p
}
if remoteConfig != "" {
return loadConfigFromRemote(v, remoteConfig)
}
data, err := ioutil.ReadFile(v.ConfigFileUsed())
if err != nil {
return err
}
log.Info("Getting environment variables...")
conf := expandEnv(string(data))
err = v.ReadConfig(bytes.NewReader([]byte(conf)))
if err != nil {
return err
}
return nil
}
// loadConfigFromRemote
func loadConfigFromRemote(v *viper.Viper, uri string) error {
u, err := url.Parse(uri)
if err != nil {
return err
}
t := u.Query().Get("type")
if t == "" {
t = "json"
}
if err := v.AddRemoteProvider(u.Scheme, u.Host, u.Path); err != nil {
return err
}
v.SetConfigType(t)
if err := v.ReadRemoteConfig(); err != nil {
return err
}
return nil
}
// expandEnv 寻找s中的 ${var} 并替换为环境变量的值,没有则替换为空,不解析 $var
func expandEnv(s string) string {
var buf []byte
i := 0
for j := 0; j < len(s); j++ {
if s[j] == '$' && j+2 < len(s) && s[j+1] == '{' { // 只匹配${var} 不匹配$var
if buf == nil {
buf = make([]byte, 0, 2*len(s))
}
buf = append(buf, s[i:j]...)
name, w := getShellName(s[j+1:])
if name == "" && w > 0 {
// 非法匹配,去掉$
} else if name == "" {
buf = append(buf, s[j]) // 保留$
} else {
buf = append(buf, os.Getenv(name)...)
}
j += w
i = j + 1
}
}
if buf == nil {
return s
}
return string(buf) + s[i:]
}
// getShellName 获取占位符的key,即${var}里面的var内容
// 返回 key内容 和 key长度
func getShellName(s string) (string, int) {
// 匹配右括号 }
// 输入已经保证第一个字符是{,并且至少两个字符以上
for i := 1; i < len(s); i++ {
if s[i] == ' ' || s[i] == '\n' || s[i] == '"' { // "xx${xxx"
return "", 0 // 遇到上面这些字符认为没有匹配中,保留$
}
if s[i] == '}' {
if i == 1 { // ${}
return "", 2 // 去掉${}
}
return s[1:i], i + 1
}
}
return "", 0 // 没有右括号,保留$
}