// Copyright (C) 2014-2018 Goodrain Co., Ltd. // RAINBOND, Application Management Platform // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. For any non-GPL usage of Rainbond, // one or multiple Commercial Licenses authorized by Goodrain Co., Ltd. // must be obtained first. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see . package config import ( "context" "fmt" "github.com/coreos/etcd/mvcc/mvccpb" "github.com/pquerna/ffjson/ffjson" "github.com/goodrain/rainbond/cmd/node/option" "github.com/goodrain/rainbond/node/api/model" "github.com/goodrain/rainbond/node/core/store" "github.com/goodrain/rainbond/util" "github.com/Sirupsen/logrus" client "github.com/coreos/etcd/clientv3" ) //DataCenterConfig 数据中心配置 type DataCenterConfig struct { config *model.GlobalConfig options *option.Conf ctx context.Context cancel context.CancelFunc //group config 不持久化 groupConfigs map[string]*GroupContext } var dataCenterConfig *DataCenterConfig //GetDataCenterConfig 获取 func GetDataCenterConfig() *DataCenterConfig { if dataCenterConfig == nil { return CreateDataCenterConfig() } return dataCenterConfig } //CreateDataCenterConfig 创建 func CreateDataCenterConfig() *DataCenterConfig { ctx, cancel := context.WithCancel(context.Background()) dataCenterConfig = &DataCenterConfig{ options: option.Config, ctx: ctx, cancel: cancel, config: &model.GlobalConfig{ Configs: make(map[string]*model.ConfigUnit), }, groupConfigs: make(map[string]*GroupContext), } res, err := store.DefalutClient.Get(dataCenterConfig.options.ConfigStoragePath+"/global", client.WithPrefix()) if err != nil { logrus.Error("load datacenter config error.", err.Error()) } if len(res.Kvs) < 1 { // dgc := model.CreateDefaultGlobalConfig() // err := dataCenterConfig.PutDataCenterConfig(dgc) // if err != nil { // logrus.Error("put datacenter config error,", err.Error()) // } dgc := &model.GlobalConfig{ Configs: make(map[string]*model.ConfigUnit), } dataCenterConfig.config = dgc } else { for _, kv := range res.Kvs { dataCenterConfig.PutConfigKV(kv) } } return dataCenterConfig } //Start 启动,监听配置变化 func (d *DataCenterConfig) Start() { go util.Exec(d.ctx, func() error { ctx, cancel := context.WithCancel(d.ctx) defer cancel() logrus.Info("datacenter config listener start") ch := store.DefalutClient.WatchByCtx(ctx, d.options.ConfigStoragePath+"/global", client.WithPrefix()) for event := range ch { for _, e := range event.Events { switch { case e.IsCreate(), e.IsModify(): d.PutConfigKV(e.Kv) case e.Type == client.EventTypeDelete: d.DeleteConfig(util.GetIDFromKey(string(e.Kv.Key))) } } } return nil }, 1) } //Stop 停止监听 func (d *DataCenterConfig) Stop() { d.cancel() logrus.Info("datacenter config listener stop") } //GetDataCenterConfig 获取配置 func (d *DataCenterConfig) GetDataCenterConfig() (*model.GlobalConfig, error) { return d.config, nil } //PutDataCenterConfig 更改配置 func (d *DataCenterConfig) PutDataCenterConfig(c *model.GlobalConfig) (err error) { if c == nil { return } for k, v := range c.Configs { d.config.Add(*v) _, err = store.DefalutClient.Put(d.options.ConfigStoragePath+"/global/"+k, v.String()) } return err } //GetConfig 获取全局配置 func (d *DataCenterConfig) GetConfig(name string) *model.ConfigUnit { return d.config.Get(name) } //CacheConfig 更新配置缓存 func (d *DataCenterConfig) CacheConfig(c *model.ConfigUnit) error { if c.Name == "" { return fmt.Errorf("config name can not be empty") } logrus.Debugf("add config %v", c) //将值类型由[]interface{} 转 []string if c.ValueType == "array" { switch c.Value.(type) { case []interface{}: var data []string for _, v := range c.Value.([]interface{}) { data = append(data, v.(string)) } c.Value = data } oldC := d.config.Get(c.Name) if oldC != nil { switch oldC.Value.(type) { case string: value := append(c.Value.([]string), oldC.Value.(string)) util.Deweight(&value) c.Value = value case []string: value := append(c.Value.([]string), oldC.Value.([]string)...) util.Deweight(&value) c.Value = value default: } } } d.config.Add(*c) return nil } //PutConfig 增加or更新配置 func (d *DataCenterConfig) PutConfig(c *model.ConfigUnit) error { if c.Name == "" { return fmt.Errorf("config name can not be empty") } logrus.Debugf("add config %v", c) //将值类型由[]interface{} 转 []string if c.ValueType == "array" { switch c.Value.(type) { case []interface{}: var data []string for _, v := range c.Value.([]interface{}) { data = append(data, v.(string)) } c.Value = data } oldC := d.config.Get(c.Name) if oldC != nil { switch oldC.Value.(type) { case string: value := append(c.Value.([]string), oldC.Value.(string)) util.Deweight(&value) c.Value = value case []string: value := append(c.Value.([]string), oldC.Value.([]string)...) util.Deweight(&value) c.Value = value default: } } } d.config.Add(*c) //持久化 _, err := store.DefalutClient.Put(d.options.ConfigStoragePath+"/global/"+c.Name, c.String()) if err != nil { logrus.Error("put datacenter config to etcd error.", err.Error()) return err } return nil } //PutConfigKV 更新 func (d *DataCenterConfig) PutConfigKV(kv *mvccpb.KeyValue) { var cn model.ConfigUnit if err := ffjson.Unmarshal(kv.Value, &cn); err == nil { d.CacheConfig(&cn) } else { logrus.Errorf("parse config error,%s", err.Error()) } } //DeleteConfig 删除配置 func (d *DataCenterConfig) DeleteConfig(name string) { d.config.Delete(name) } //GetGroupConfig get group config func (d *DataCenterConfig) GetGroupConfig(groupID string) *GroupContext { if c, ok := d.groupConfigs[groupID]; ok { return c } c := NewGroupContext(groupID) d.groupConfigs[groupID] = c return c }