|
|
@ -7,7 +7,10 @@ import ( |
|
|
|
"github.com/go-kratos/kratos/v2/log" |
|
|
|
"github.com/go-kratos/kratos/v2/log" |
|
|
|
|
|
|
|
|
|
|
|
"github.com/apolloconfig/agollo/v4" |
|
|
|
"github.com/apolloconfig/agollo/v4" |
|
|
|
|
|
|
|
"github.com/apolloconfig/agollo/v4/constant" |
|
|
|
apolloConfig "github.com/apolloconfig/agollo/v4/env/config" |
|
|
|
apolloConfig "github.com/apolloconfig/agollo/v4/env/config" |
|
|
|
|
|
|
|
"github.com/apolloconfig/agollo/v4/extension" |
|
|
|
|
|
|
|
"github.com/go-kratos/kratos/v2/encoding" |
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
type apollo struct { |
|
|
|
type apollo struct { |
|
|
@ -15,6 +18,13 @@ type apollo struct { |
|
|
|
opt *options |
|
|
|
opt *options |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const ( |
|
|
|
|
|
|
|
yaml = "yaml" |
|
|
|
|
|
|
|
yml = "yml" |
|
|
|
|
|
|
|
json = "json" |
|
|
|
|
|
|
|
properties = "properties" |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
// Option is apollo option
|
|
|
|
// Option is apollo option
|
|
|
|
type Option func(*options) |
|
|
|
type Option func(*options) |
|
|
|
|
|
|
|
|
|
|
@ -26,6 +36,7 @@ type options struct { |
|
|
|
namespace string |
|
|
|
namespace string |
|
|
|
isBackupConfig bool |
|
|
|
isBackupConfig bool |
|
|
|
backupPath string |
|
|
|
backupPath string |
|
|
|
|
|
|
|
originConfig bool |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// WithAppID with apollo config app id
|
|
|
|
// WithAppID with apollo config app id
|
|
|
@ -84,6 +95,16 @@ func WithBackupPath(backupPath string) Option { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// WithOriginalConfig use the original configuration file without parse processing
|
|
|
|
|
|
|
|
func WithOriginalConfig() Option { |
|
|
|
|
|
|
|
return func(o *options) { |
|
|
|
|
|
|
|
extension.AddFormatParser(constant.JSON, &jsonExtParser{}) |
|
|
|
|
|
|
|
extension.AddFormatParser(constant.YAML, &yamlExtParser{}) |
|
|
|
|
|
|
|
extension.AddFormatParser(constant.YML, &yamlExtParser{}) |
|
|
|
|
|
|
|
o.originConfig = true |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func NewSource(opts ...Option) config.Source { |
|
|
|
func NewSource(opts ...Option) config.Source { |
|
|
|
op := options{} |
|
|
|
op := options{} |
|
|
|
for _, o := range opts { |
|
|
|
for _, o := range opts { |
|
|
@ -108,31 +129,78 @@ func NewSource(opts ...Option) config.Source { |
|
|
|
|
|
|
|
|
|
|
|
func format(ns string) string { |
|
|
|
func format(ns string) string { |
|
|
|
arr := strings.Split(ns, ".") |
|
|
|
arr := strings.Split(ns, ".") |
|
|
|
if len(arr) <= 1 { |
|
|
|
if len(arr) <= 1 || arr[len(arr)-1] == properties { |
|
|
|
return "json" |
|
|
|
return json |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return arr[len(arr)-1] |
|
|
|
return arr[len(arr)-1] |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (e *apollo) load() []*config.KeyValue { |
|
|
|
func (e *apollo) load() []*config.KeyValue { |
|
|
|
kv := make([]*config.KeyValue, 0) |
|
|
|
kvs := make([]*config.KeyValue, 0) |
|
|
|
namespaces := strings.Split(e.opt.namespace, ",") |
|
|
|
namespaces := strings.Split(e.opt.namespace, ",") |
|
|
|
|
|
|
|
|
|
|
|
for _, ns := range namespaces { |
|
|
|
for _, ns := range namespaces { |
|
|
|
|
|
|
|
if !e.opt.originConfig { |
|
|
|
|
|
|
|
kv, err := e.getConfig(ns) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
log.Errorf("apollo get config failed,err:%v", err) |
|
|
|
|
|
|
|
continue |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
kvs = append(kvs, kv) |
|
|
|
|
|
|
|
continue |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if strings.Contains(ns, ".") && !strings.Contains(ns, properties) && |
|
|
|
|
|
|
|
(format(ns) == yaml || format(ns) == yml || format(ns) == json) { |
|
|
|
|
|
|
|
kv, err := e.getOriginConfig(ns) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
log.Errorf("apollo get config failed,err:%v", err) |
|
|
|
|
|
|
|
continue |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
kvs = append(kvs, kv) |
|
|
|
|
|
|
|
continue |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
kv, err := e.getConfig(ns) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
log.Errorf("apollo get config failed,err:%v", err) |
|
|
|
|
|
|
|
continue |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
kvs = append(kvs, kv) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return kvs |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (e *apollo) getConfig(ns string) (*config.KeyValue, error) { |
|
|
|
|
|
|
|
next := map[string]interface{}{} |
|
|
|
|
|
|
|
e.client.GetConfigCache(ns).Range(func(key, value interface{}) bool { |
|
|
|
|
|
|
|
// all values are out properties format
|
|
|
|
|
|
|
|
resolve(genKey(ns, key.(string)), value, next) |
|
|
|
|
|
|
|
return true |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
f := format(ns) |
|
|
|
|
|
|
|
codec := encoding.GetCodec(f) |
|
|
|
|
|
|
|
val, err := codec.Marshal(next) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return nil, err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return &config.KeyValue{ |
|
|
|
|
|
|
|
Key: ns, |
|
|
|
|
|
|
|
Value: val, |
|
|
|
|
|
|
|
Format: f, |
|
|
|
|
|
|
|
}, nil |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (e apollo) getOriginConfig(ns string) (*config.KeyValue, error) { |
|
|
|
value, err := e.client.GetConfigCache(ns).Get("content") |
|
|
|
value, err := e.client.GetConfigCache(ns).Get("content") |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
log.Warnw("apollo get config failed", "err", err) |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
} |
|
|
|
// serialize the namespace content KeyValue into bytes.
|
|
|
|
// serialize the namespace content KeyValue into bytes.
|
|
|
|
kv = append(kv, &config.KeyValue{ |
|
|
|
return &config.KeyValue{ |
|
|
|
Key: ns, |
|
|
|
Key: ns, |
|
|
|
Value: []byte(value.(string)), |
|
|
|
Value: []byte(value.(string)), |
|
|
|
Format: format(ns), |
|
|
|
Format: format(ns), |
|
|
|
}) |
|
|
|
}, nil |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return kv |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (e *apollo) Load() (kv []*config.KeyValue, err error) { |
|
|
|
func (e *apollo) Load() (kv []*config.KeyValue, err error) { |
|
|
@ -146,3 +214,54 @@ func (e *apollo) Watch() (config.Watcher, error) { |
|
|
|
} |
|
|
|
} |
|
|
|
return w, nil |
|
|
|
return w, nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// resolve convert kv pair into one map[string]interface{} by split key into different
|
|
|
|
|
|
|
|
// map level. such as: app.name = "application" => map[app][name] = "application"
|
|
|
|
|
|
|
|
func resolve(key string, value interface{}, target map[string]interface{}) { |
|
|
|
|
|
|
|
// expand key "aaa.bbb" into map[aaa]map[bbb]interface{}
|
|
|
|
|
|
|
|
keys := strings.Split(key, ".") |
|
|
|
|
|
|
|
last := len(keys) - 1 |
|
|
|
|
|
|
|
cursor := target |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for i, k := range keys { |
|
|
|
|
|
|
|
if i == last { |
|
|
|
|
|
|
|
cursor[k] = value |
|
|
|
|
|
|
|
break |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// not the last key, be deeper
|
|
|
|
|
|
|
|
v, ok := cursor[k] |
|
|
|
|
|
|
|
if !ok { |
|
|
|
|
|
|
|
// create a new map
|
|
|
|
|
|
|
|
deeper := make(map[string]interface{}) |
|
|
|
|
|
|
|
cursor[k] = deeper |
|
|
|
|
|
|
|
cursor = deeper |
|
|
|
|
|
|
|
continue |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// current exists, then check existing value type, if it's not map
|
|
|
|
|
|
|
|
// that means duplicate keys, and at least one is not map instance.
|
|
|
|
|
|
|
|
if cursor, ok = v.(map[string]interface{}); !ok { |
|
|
|
|
|
|
|
log.Warnf("duplicate key: %v\n", strings.Join(keys[:i+1], ".")) |
|
|
|
|
|
|
|
break |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// genKey got the key of config.KeyValue pair.
|
|
|
|
|
|
|
|
// eg: namespace.ext with subKey got namespace.subKey
|
|
|
|
|
|
|
|
func genKey(ns, sub string) string { |
|
|
|
|
|
|
|
arr := strings.Split(ns, ".") |
|
|
|
|
|
|
|
if len(arr) < 1 { |
|
|
|
|
|
|
|
return sub |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if len(arr) == 1 { |
|
|
|
|
|
|
|
if ns == "" { |
|
|
|
|
|
|
|
return sub |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return ns + "." + sub |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return strings.Join(arr[:len(arr)-1], ".") + "." + sub |
|
|
|
|
|
|
|
} |
|
|
|