-
Notifications
You must be signed in to change notification settings - Fork 96
/
tpl_parse.go
356 lines (308 loc) · 8.33 KB
/
tpl_parse.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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
package confgo
import (
"bytes"
"encoding/json"
"fmt"
"net/url"
"strings"
"github.com/douyu/juno/pkg/model/db"
"github.com/douyu/juno/pkg/model/view"
"github.com/douyu/juno/pkg/util"
"github.com/douyu/jupiter/pkg/store/gorm"
"github.com/douyu/jupiter/pkg/xlog"
"github.com/spf13/viper"
"go.uber.org/zap"
)
var (
CmcIp = "{{IP}}"
CmcPort = "{{PORT}}"
CmcAddr = "{{ADDR}}"
CmcDsn = "{{DSN}}"
CmcUrl = "{{URL}}"
CmcUserName = "{{USERNAME}}"
CmcPassword = "{{PASSWORD}}"
CmcAll = "{{ALL}}" // 全部解析
CmcArr = []string{CmcIp, CmcPort, CmcUserName, CmcPassword, CmcAddr, CmcDsn, CmcUrl, CmcAll}
)
const (
SEP_POINT = "."
TPL_SYMBOL = "{{}}"
)
type CmcParse interface {
ParseConfig() (output []*CmcInfo, err error)
}
type CmcToml struct {
db *gorm.DB
Viper *viper.Viper
view.RespOneConfig
}
type CmcInfo struct {
Key string `json:"key"`
Ip string `json:"ip"`
Port string `json:"port"`
UserName string `json:"user_name"`
Password string `json:"password"`
Scheme string `json:"scheme"`
DbName string `json:"db_name"`
Type string `gorm:"not null;" json:"type"`
view.RespOneConfig
}
func InitCmcToml(db *gorm.DB, value view.RespOneConfig) *CmcToml {
obj := &CmcToml{
db: db,
Viper: viper.GetViper(),
RespOneConfig: value,
}
return obj
}
func GetAllTpl(dbConn *gorm.DB) (resp []db.CmcTpl, err error) {
resp = make([]db.CmcTpl, 0)
err = dbConn.Find(&resp).Error
if err != nil {
return
}
return
}
type tplData struct {
Key string `json:"key"`
TplType string `json:"tplType"`
TplMap map[string]string `json:"tplMap"`
}
// 所有模板
func ParseTpl(tplList []db.CmcTpl) (allTplMap map[string]tplData, err error) {
// 所有模板存储的结果
allTplMap = make(map[string]tplData)
for _, v := range tplList {
// 单条模板内容map -- 模板字段映射真实的key
jsonMap := make(map[string]string)
err = json.Unmarshal([]byte(v.Content), &jsonMap)
if err != nil {
xlog.Error("ParseTpl", zap.Error(err))
continue
}
tplKey, ok := jsonMap["key"]
// 所有模板数据必须要有key字段,否则无效
if !ok {
xlog.Error("ParseTpl", zap.String("err", "no key"), zap.Int("id", v.Id))
continue
}
// 已经存在该模板key了
if _, ok := allTplMap[tplKey]; ok {
continue
}
// 单个模板存储的结构
tplMap := make(map[string]string)
for key, field := range jsonMap {
// key字段忽略,只是为了标识模板key
if key == "key" {
continue
}
// 让每一个元素都遍历到
for _, cmcTplValue := range CmcArr {
// 配置的模板与定义的模板一致
if strings.TrimSpace(field) == cmcTplValue {
tplMap[cmcTplValue] = key
}
}
}
allTplMap[tplKey] = tplData{
Key: tplKey,
TplType: v.TplType,
TplMap: tplMap,
}
}
return
}
func (c *CmcToml) ParseConfig() (output []*CmcInfo, err error) {
// 其他格式 todo
if c.RespOneConfig.Format != "toml" {
err = fmt.Errorf("暂时只支持解析toml文件")
return
}
// 读取配置文件
c.Viper.SetConfigType("toml")
err = c.Viper.ReadConfig(bytes.NewReader([]byte(c.RespOneConfig.Content)))
if err != nil {
xlog.Error("ParseConfig", zap.Error(err), zap.String("msg", "viper.ReadConfig"), zap.String("value", c.RespOneConfig.Content))
}
// 拿到所有的模板
allTpl, err := GetAllTpl(c.db)
if err != nil {
xlog.Error("ParseConfig", zap.Error(err), zap.String("msg", "GetAllTpl"), zap.String("value", c.RespOneConfig.Content))
}
// 将所有的模板根据配置的模板key组装成map
allTplMap, err := ParseTpl(allTpl)
if err != nil {
xlog.Error("ParseConfig", zap.Error(err), zap.String("msg", "ParseTpl"), zap.String("value", c.RespOneConfig.Content))
}
var (
needParseKeyMap = make(map[string]string, 0) // 需要解析的内容
allKey = make(map[string]int) // 所有的key
)
// 处理配置中所有的item
for _, item := range c.Viper.AllKeys() {
allKey[item] = 1
// 只有一层的item不考虑
if !strings.Contains(item, SEP_POINT) {
continue
}
// 匹配模板与当前item flag:匹配成功 depKey:当前的key eg jupiter.mysql.juno tplKey:jupiter.mysql.{{}}
depKey, tplKey, flag := matchTpl(item, allTplMap)
if !flag {
continue
}
// 已经找到
if _, ok := needParseKeyMap[depKey]; ok {
continue
}
needParseKeyMap[depKey] = tplKey
}
for depKey, tplKey := range needParseKeyMap {
tplValue := allTplMap[tplKey]
cmcInfo := CmcInfo{
Type: tplValue.TplType,
RespOneConfig: view.RespOneConfig{
Env: c.Env,
ZoneCode: c.ZoneCode,
Content: c.Content,
AppName: c.AppName,
Aid: c.Aid,
Format: c.Format,
FileName: c.FileName,
},
}
// 解析模板中的每一个字段
for cmcTpl, field := range tplValue.TplMap {
item := fmt.Sprintf("%s%s%s", depKey, SEP_POINT, field)
// 没有则不需要解析
if _, ok := allKey[item]; !ok {
continue
}
// 考虑为数组的情况
var (
valArray = c.Viper.GetStringSlice(item)
val = c.Viper.GetString(item)
)
if val == "" {
if len(valArray) == 0 {
continue
}
// 只有直接获取不到值且数组长度大于0才为真正的数组
for _, addr := range valArray {
cmcInfoTmp := CmcInfo{}
err := util.DeepCopy(&cmcInfoTmp, &cmcInfo)
if err != nil {
xlog.Info("ParseConfig", zap.String("msg", "DeepCopy"), zap.String("tplKey", tplKey), zap.String("item", item))
continue
}
// 处理各种配置模板的解析
err = handleParse(&cmcInfoTmp, tplValue.TplType, depKey, cmcTpl, addr)
if err != nil {
xlog.Info("ParseConfig", zap.String("msg", "handleParse array"), zap.String("tplKey", tplKey), zap.String("item", item))
continue
}
output = append(output, &cmcInfoTmp)
}
continue
}
// 处理各种配置模板的解析
err := handleParse(&cmcInfo, tplValue.TplType, depKey, cmcTpl, val)
if err != nil {
xlog.Info("ParseConfig", zap.String("msg", "handleParse"), zap.String("tplKey", tplKey), zap.String("item", item))
continue
}
}
// 只添加有内容的
if cmcInfo.Key != "" {
output = append(output, &cmcInfo)
}
}
return
}
// item与模板匹配
func matchTpl(item string, allTplMap map[string]tplData) (depKey, tplKey string, flag bool) {
// 只有一层的item不考虑
if !strings.Contains(item, SEP_POINT) {
return
}
var (
itemArray = strings.Split(item, SEP_POINT)
lenArr = len(itemArray) - 1 // lenArr必然大于等于1
)
depKey = strings.Join(itemArray[0:lenArr], SEP_POINT)
// 考虑特殊情况
if lenArr == 1 {
depKey = itemArray[0]
}
// 优先考虑绝对路径匹配成功的
for k := range allTplMap {
// 绝对路径匹配成功
if k == depKey {
flag = true
tplKey = k
return
}
}
// 匹配
for key := range allTplMap {
tplKeyArray := strings.Split(key, SEP_POINT)
// 长度不一致,不考虑
if len(tplKeyArray) != lenArr {
continue
}
// 拆分后逐个匹配
flag = true
for index := range tplKeyArray {
// 当模板字段不为{{}},且二者不相同,说明匹配失败
if tplKeyArray[index] != TPL_SYMBOL && tplKeyArray[index] != itemArray[index] {
flag = false
break
}
}
// 匹配成功
if flag {
tplKey = key
return
}
}
// 没有找到匹配的
return
}
func handleParse(cmcInfo *CmcInfo, tplType, key, parseTpl string, value string) (err error) {
cmcInfo.Key = key
switch parseTpl {
case CmcIp:
cmcInfo.Ip = value
case CmcPort:
cmcInfo.Port = value
case CmcAddr:
cmcInfo.Ip, cmcInfo.Port, cmcInfo.UserName, cmcInfo.Password = util.ParseAddr(tplType, value)
case CmcUserName:
cmcInfo.UserName = value
case CmcPassword:
cmcInfo.Password = value
case CmcDsn:
dsnData, e := util.ParseDSN(value)
if e != nil {
err = e
return
}
cmcInfo.Ip, cmcInfo.Port, cmcInfo.UserName, cmcInfo.Password = util.ParseAddr(tplType, dsnData.Addr)
cmcInfo.UserName = dsnData.User
cmcInfo.Password = dsnData.Passwd
cmcInfo.Scheme = dsnData.Net
cmcInfo.DbName = dsnData.DBName
case CmcUrl:
urlData, e := url.Parse(value)
if e != nil {
err = e
return
}
cmcInfo.Ip = urlData.Hostname()
cmcInfo.Port = urlData.Port()
cmcInfo.Scheme = urlData.Scheme
cmcInfo.UserName = urlData.User.Username()
cmcInfo.Password, _ = urlData.User.Password()
}
return
}