复制项目
This commit is contained in:
337
internal/api/admin/config_manager.go
Normal file
337
internal/api/admin/config_manager.go
Normal file
@@ -0,0 +1,337 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"git.imall.cloud/openim/chat/pkg/common/apistruct"
|
||||
"git.imall.cloud/openim/chat/pkg/common/config"
|
||||
"git.imall.cloud/openim/chat/pkg/common/kdisc"
|
||||
"git.imall.cloud/openim/chat/pkg/common/kdisc/etcd"
|
||||
"git.imall.cloud/openim/chat/version"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/openimsdk/tools/apiresp"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
"github.com/openimsdk/tools/utils/runtimeenv"
|
||||
clientv3 "go.etcd.io/etcd/client/v3"
|
||||
)
|
||||
|
||||
const (
|
||||
// wait for Restart http call return
|
||||
waitHttp = time.Millisecond * 200
|
||||
)
|
||||
|
||||
type ConfigManager struct {
|
||||
config *config.AllConfig
|
||||
client *clientv3.Client
|
||||
configPath string
|
||||
runtimeEnv string
|
||||
}
|
||||
|
||||
func NewConfigManager(cfg *config.AllConfig, client *clientv3.Client, configPath string, runtimeEnv string) *ConfigManager {
|
||||
return &ConfigManager{
|
||||
config: cfg,
|
||||
client: client,
|
||||
configPath: configPath,
|
||||
runtimeEnv: runtimeEnv,
|
||||
}
|
||||
}
|
||||
|
||||
func (cm *ConfigManager) GetConfig(c *gin.Context) {
|
||||
var req apistruct.GetConfigReq
|
||||
if err := c.BindJSON(&req); err != nil {
|
||||
apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap())
|
||||
return
|
||||
}
|
||||
conf := cm.config.Name2Config(req.ConfigName)
|
||||
if conf == nil {
|
||||
apiresp.GinError(c, errs.ErrArgs.WithDetail("config name not found").Wrap())
|
||||
return
|
||||
}
|
||||
b, err := json.Marshal(conf)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
apiresp.GinSuccess(c, string(b))
|
||||
}
|
||||
|
||||
func (cm *ConfigManager) GetConfigList(c *gin.Context) {
|
||||
var resp apistruct.GetConfigListResp
|
||||
resp.ConfigNames = cm.config.GetConfigNames()
|
||||
resp.Environment = runtimeenv.PrintRuntimeEnvironment()
|
||||
resp.Version = version.Version
|
||||
|
||||
apiresp.GinSuccess(c, resp)
|
||||
}
|
||||
|
||||
func (cm *ConfigManager) SetConfig(c *gin.Context) {
|
||||
if cm.config.Discovery.Enable != kdisc.ETCDCONST {
|
||||
apiresp.GinError(c, errs.New("only etcd support set config").Wrap())
|
||||
return
|
||||
}
|
||||
var req apistruct.SetConfigReq
|
||||
if err := c.BindJSON(&req); err != nil {
|
||||
apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap())
|
||||
return
|
||||
}
|
||||
var err error
|
||||
switch req.ConfigName {
|
||||
case config.DiscoveryConfigFileName:
|
||||
err = compareAndSave[config.Discovery](c, cm.config.Name2Config(req.ConfigName), &req, cm.client)
|
||||
case config.LogConfigFileName:
|
||||
err = compareAndSave[config.Log](c, cm.config.Name2Config(req.ConfigName), &req, cm.client)
|
||||
case config.MongodbConfigFileName:
|
||||
err = compareAndSave[config.Mongo](c, cm.config.Name2Config(req.ConfigName), &req, cm.client)
|
||||
case config.ChatAPIAdminCfgFileName:
|
||||
err = compareAndSave[config.API](c, cm.config.Name2Config(req.ConfigName), &req, cm.client)
|
||||
case config.ChatAPIChatCfgFileName:
|
||||
err = compareAndSave[config.API](c, cm.config.Name2Config(req.ConfigName), &req, cm.client)
|
||||
case config.ChatRPCAdminCfgFileName:
|
||||
err = compareAndSave[config.Admin](c, cm.config.Name2Config(req.ConfigName), &req, cm.client)
|
||||
case config.ChatRPCChatCfgFileName:
|
||||
err = compareAndSave[config.Chat](c, cm.config.Name2Config(req.ConfigName), &req, cm.client)
|
||||
case config.ShareFileName:
|
||||
err = compareAndSave[config.Share](c, cm.config.Name2Config(req.ConfigName), &req, cm.client)
|
||||
case config.RedisConfigFileName:
|
||||
err = compareAndSave[config.Redis](c, cm.config.Name2Config(req.ConfigName), &req, cm.client)
|
||||
default:
|
||||
apiresp.GinError(c, errs.ErrArgs.Wrap())
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap())
|
||||
return
|
||||
}
|
||||
apiresp.GinSuccess(c, nil)
|
||||
}
|
||||
|
||||
func (cm *ConfigManager) SetConfigs(c *gin.Context) {
|
||||
if cm.config.Discovery.Enable != kdisc.ETCDCONST {
|
||||
apiresp.GinError(c, errs.New("only etcd support set config").Wrap())
|
||||
return
|
||||
}
|
||||
var req apistruct.SetConfigsReq
|
||||
if err := c.BindJSON(&req); err != nil {
|
||||
apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap())
|
||||
return
|
||||
}
|
||||
var (
|
||||
err error
|
||||
ops []*clientv3.Op
|
||||
)
|
||||
|
||||
for _, cf := range req.Configs {
|
||||
var op *clientv3.Op
|
||||
switch cf.ConfigName {
|
||||
case config.DiscoveryConfigFileName:
|
||||
op, err = compareAndOp[config.Discovery](c, cm.config.Name2Config(cf.ConfigName), &cf, cm.client)
|
||||
case config.LogConfigFileName:
|
||||
op, err = compareAndOp[config.Log](c, cm.config.Name2Config(cf.ConfigName), &cf, cm.client)
|
||||
case config.MongodbConfigFileName:
|
||||
op, err = compareAndOp[config.Mongo](c, cm.config.Name2Config(cf.ConfigName), &cf, cm.client)
|
||||
case config.ChatAPIAdminCfgFileName:
|
||||
op, err = compareAndOp[config.API](c, cm.config.Name2Config(cf.ConfigName), &cf, cm.client)
|
||||
case config.ChatAPIChatCfgFileName:
|
||||
op, err = compareAndOp[config.API](c, cm.config.Name2Config(cf.ConfigName), &cf, cm.client)
|
||||
case config.ChatRPCAdminCfgFileName:
|
||||
op, err = compareAndOp[config.Admin](c, cm.config.Name2Config(cf.ConfigName), &cf, cm.client)
|
||||
case config.ChatRPCChatCfgFileName:
|
||||
op, err = compareAndOp[config.Chat](c, cm.config.Name2Config(cf.ConfigName), &cf, cm.client)
|
||||
case config.ShareFileName:
|
||||
op, err = compareAndOp[config.Share](c, cm.config.Name2Config(cf.ConfigName), &cf, cm.client)
|
||||
case config.RedisConfigFileName:
|
||||
op, err = compareAndOp[config.Redis](c, cm.config.Name2Config(cf.ConfigName), &cf, cm.client)
|
||||
default:
|
||||
apiresp.GinError(c, errs.ErrArgs.Wrap())
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap())
|
||||
return
|
||||
}
|
||||
if op != nil {
|
||||
ops = append(ops, op)
|
||||
}
|
||||
}
|
||||
if len(ops) > 0 {
|
||||
tx := cm.client.Txn(c)
|
||||
if _, err = tx.Then(datautil.Batch(func(op *clientv3.Op) clientv3.Op { return *op }, ops)...).Commit(); err != nil {
|
||||
apiresp.GinError(c, errs.WrapMsg(err, "save to etcd failed"))
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
apiresp.GinSuccess(c, nil)
|
||||
}
|
||||
|
||||
func compareAndOp[T any](c *gin.Context, old any, req *apistruct.SetConfigReq, client *clientv3.Client) (*clientv3.Op, error) {
|
||||
conf := new(T)
|
||||
err := json.Unmarshal([]byte(req.Data), &conf)
|
||||
if err != nil {
|
||||
return nil, errs.ErrArgs.WithDetail(err.Error()).Wrap()
|
||||
}
|
||||
eq := reflect.DeepEqual(old, conf)
|
||||
if eq {
|
||||
return nil, nil
|
||||
}
|
||||
data, err := json.Marshal(conf)
|
||||
if err != nil {
|
||||
return nil, errs.ErrArgs.WithDetail(err.Error()).Wrap()
|
||||
}
|
||||
op := clientv3.OpPut(etcd.BuildKey(req.ConfigName), string(data))
|
||||
return &op, nil
|
||||
}
|
||||
|
||||
func compareAndSave[T any](c *gin.Context, old any, req *apistruct.SetConfigReq, client *clientv3.Client) error {
|
||||
conf := new(T)
|
||||
err := json.Unmarshal([]byte(req.Data), &conf)
|
||||
if err != nil {
|
||||
return errs.ErrArgs.WithDetail(err.Error()).Wrap()
|
||||
}
|
||||
eq := reflect.DeepEqual(old, conf)
|
||||
if eq {
|
||||
return nil
|
||||
}
|
||||
data, err := json.Marshal(conf)
|
||||
if err != nil {
|
||||
return errs.ErrArgs.WithDetail(err.Error()).Wrap()
|
||||
}
|
||||
_, err = client.Put(c, etcd.BuildKey(req.ConfigName), string(data))
|
||||
if err != nil {
|
||||
return errs.WrapMsg(err, "save to etcd failed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cm *ConfigManager) ResetConfig(c *gin.Context) {
|
||||
go func() {
|
||||
if err := cm.resetConfig(c, true); err != nil {
|
||||
log.ZError(c, "reset config err", err)
|
||||
}
|
||||
}()
|
||||
apiresp.GinSuccess(c, nil)
|
||||
}
|
||||
|
||||
func (cm *ConfigManager) resetConfig(c *gin.Context, checkChange bool, ops ...clientv3.Op) error {
|
||||
txn := cm.client.Txn(c)
|
||||
type initConf struct {
|
||||
old any
|
||||
new any
|
||||
}
|
||||
configMap := map[string]*initConf{
|
||||
config.DiscoveryConfigFileName: {old: &cm.config.Discovery, new: new(config.Discovery)},
|
||||
config.LogConfigFileName: {old: &cm.config.Log, new: new(config.Log)},
|
||||
config.MongodbConfigFileName: {old: &cm.config.Mongo, new: new(config.Mongo)},
|
||||
config.ChatAPIAdminCfgFileName: {old: &cm.config.AdminAPI, new: new(config.API)},
|
||||
config.ChatAPIChatCfgFileName: {old: &cm.config.ChatAPI, new: new(config.API)},
|
||||
config.ChatRPCAdminCfgFileName: {old: &cm.config.Admin, new: new(config.Admin)},
|
||||
config.ChatRPCChatCfgFileName: {old: &cm.config.Chat, new: new(config.Chat)},
|
||||
config.RedisConfigFileName: {old: &cm.config.Redis, new: new(config.Redis)},
|
||||
config.ShareFileName: {old: &cm.config.Share, new: new(config.Share)},
|
||||
}
|
||||
|
||||
changedKeys := make([]string, 0, len(configMap))
|
||||
for k, v := range configMap {
|
||||
err := config.Load(
|
||||
cm.configPath,
|
||||
k,
|
||||
config.EnvPrefixMap[k],
|
||||
cm.runtimeEnv,
|
||||
v.new,
|
||||
)
|
||||
if err != nil {
|
||||
log.ZError(c, "load config failed", err)
|
||||
continue
|
||||
}
|
||||
equal := reflect.DeepEqual(v.old, v.new)
|
||||
if !checkChange || !equal {
|
||||
changedKeys = append(changedKeys, k)
|
||||
}
|
||||
}
|
||||
|
||||
for _, k := range changedKeys {
|
||||
data, err := json.Marshal(configMap[k].new)
|
||||
if err != nil {
|
||||
log.ZError(c, "marshal config failed", err)
|
||||
continue
|
||||
}
|
||||
ops = append(ops, clientv3.OpPut(etcd.BuildKey(k), string(data)))
|
||||
}
|
||||
if len(ops) > 0 {
|
||||
txn.Then(ops...)
|
||||
_, err := txn.Commit()
|
||||
if err != nil {
|
||||
return errs.WrapMsg(err, "commit etcd txn failed")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cm *ConfigManager) Restart(c *gin.Context) {
|
||||
go cm.restart(c)
|
||||
apiresp.GinSuccess(c, nil)
|
||||
}
|
||||
|
||||
func (cm *ConfigManager) restart(c *gin.Context) {
|
||||
time.Sleep(waitHttp) // wait for Restart http call return
|
||||
t := time.Now().Unix()
|
||||
_, err := cm.client.Put(c, etcd.BuildKey(etcd.RestartKey), strconv.Itoa(int(t)))
|
||||
if err != nil {
|
||||
log.ZError(c, "restart etcd put key failed", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (cm *ConfigManager) SetEnableConfigManager(c *gin.Context) {
|
||||
var req apistruct.SetEnableConfigManagerReq
|
||||
if err := c.BindJSON(&req); err != nil {
|
||||
apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap())
|
||||
return
|
||||
}
|
||||
var enableStr string
|
||||
if req.Enable {
|
||||
enableStr = etcd.Enable
|
||||
} else {
|
||||
enableStr = etcd.Disable
|
||||
}
|
||||
resp, err := cm.client.Get(c, etcd.BuildKey(etcd.EnableConfigCenterKey))
|
||||
if err != nil {
|
||||
apiresp.GinError(c, errs.WrapMsg(err, "getEnableConfigManager failed"))
|
||||
return
|
||||
}
|
||||
if !(resp.Count > 0 && string(resp.Kvs[0].Value) == etcd.Enable) && req.Enable {
|
||||
go func() {
|
||||
time.Sleep(waitHttp) // wait for Restart http call return
|
||||
err := cm.resetConfig(c, false, clientv3.OpPut(etcd.BuildKey(etcd.EnableConfigCenterKey), enableStr))
|
||||
if err != nil {
|
||||
log.ZError(c, "writeAllConfig failed", err)
|
||||
}
|
||||
}()
|
||||
} else {
|
||||
_, err = cm.client.Put(c, etcd.BuildKey(etcd.EnableConfigCenterKey), enableStr)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, errs.WrapMsg(err, "setEnableConfigManager failed"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
apiresp.GinSuccess(c, nil)
|
||||
}
|
||||
|
||||
func (cm *ConfigManager) GetEnableConfigManager(c *gin.Context) {
|
||||
resp, err := cm.client.Get(c, etcd.BuildKey(etcd.EnableConfigCenterKey))
|
||||
if err != nil {
|
||||
apiresp.GinError(c, errs.WrapMsg(err, "getEnableConfigManager failed"))
|
||||
return
|
||||
}
|
||||
var enable bool
|
||||
if resp.Count > 0 && string(resp.Kvs[0].Value) == etcd.Enable {
|
||||
enable = true
|
||||
}
|
||||
apiresp.GinSuccess(c, &apistruct.GetEnableConfigManagerResp{Enable: enable})
|
||||
}
|
||||
Reference in New Issue
Block a user