359 lines
8.8 KiB
Go
359 lines
8.8 KiB
Go
package bootstrap
|
||
|
||
import (
|
||
"os"
|
||
"path/filepath"
|
||
"strings"
|
||
|
||
"google.golang.org/grpc"
|
||
|
||
"github.com/go-kratos/kratos/v2/config"
|
||
"github.com/go-kratos/kratos/v2/log"
|
||
|
||
// file
|
||
fileKratos "github.com/go-kratos/kratos/v2/config/file"
|
||
|
||
// etcd
|
||
etcdKratos "github.com/go-kratos/kratos/contrib/config/etcd/v2"
|
||
etcdClient "go.etcd.io/etcd/client/v3"
|
||
|
||
// consul
|
||
consulKratos "github.com/go-kratos/kratos/contrib/config/consul/v2"
|
||
consulApi "github.com/hashicorp/consul/api"
|
||
|
||
// nacos
|
||
nacosKratos "github.com/go-kratos/kratos/contrib/config/nacos/v2"
|
||
nacosClients "github.com/nacos-group/nacos-sdk-go/clients"
|
||
nacosConstant "github.com/nacos-group/nacos-sdk-go/common/constant"
|
||
nacosVo "github.com/nacos-group/nacos-sdk-go/vo"
|
||
|
||
// apollo
|
||
apolloKratos "github.com/go-kratos/kratos/contrib/config/apollo/v2"
|
||
|
||
// kubernetes
|
||
k8sKratos "github.com/go-kratos/kratos/contrib/config/kubernetes/v2"
|
||
k8sUtil "k8s.io/client-go/util/homedir"
|
||
|
||
conf "github.com/tx7do/kratos-bootstrap/gen/api/go/conf/v1"
|
||
)
|
||
|
||
var commonConfig = &conf.Bootstrap{}
|
||
var configList []interface{}
|
||
|
||
const remoteConfigSourceConfigFile = "remote.yaml"
|
||
|
||
// RegisterConfig 注册配置
|
||
func RegisterConfig(c interface{}) {
|
||
initBootstrapConfig()
|
||
configList = append(configList, c)
|
||
}
|
||
|
||
func initBootstrapConfig() {
|
||
if len(configList) > 0 {
|
||
return
|
||
}
|
||
|
||
configList = append(configList, commonConfig)
|
||
|
||
if commonConfig.Server == nil {
|
||
commonConfig.Server = &conf.Server{}
|
||
configList = append(configList, commonConfig.Server)
|
||
}
|
||
|
||
if commonConfig.Client == nil {
|
||
commonConfig.Client = &conf.Client{}
|
||
configList = append(configList, commonConfig.Client)
|
||
}
|
||
|
||
if commonConfig.Data == nil {
|
||
commonConfig.Data = &conf.Data{}
|
||
configList = append(configList, commonConfig.Data)
|
||
}
|
||
|
||
if commonConfig.Trace == nil {
|
||
commonConfig.Trace = &conf.Tracer{}
|
||
configList = append(configList, commonConfig.Trace)
|
||
}
|
||
|
||
if commonConfig.Logger == nil {
|
||
commonConfig.Logger = &conf.Logger{}
|
||
configList = append(configList, commonConfig.Logger)
|
||
}
|
||
|
||
if commonConfig.Registry == nil {
|
||
commonConfig.Registry = &conf.Registry{}
|
||
configList = append(configList, commonConfig.Registry)
|
||
}
|
||
|
||
if commonConfig.Oss == nil {
|
||
commonConfig.Oss = &conf.OSS{}
|
||
configList = append(configList, commonConfig.Oss)
|
||
}
|
||
|
||
if commonConfig.Notify == nil {
|
||
commonConfig.Notify = &conf.Notification{}
|
||
configList = append(configList, commonConfig.Notify)
|
||
}
|
||
}
|
||
|
||
// NewConfigProvider 创建一个配置
|
||
func NewConfigProvider(configPath string) config.Config {
|
||
err, rc := LoadRemoteConfigSourceConfigs(configPath)
|
||
if err != nil {
|
||
log.Error("LoadRemoteConfigSourceConfigs: ", err.Error())
|
||
}
|
||
if rc != nil {
|
||
return config.New(
|
||
config.WithSource(
|
||
NewFileConfigSource(configPath),
|
||
NewRemoteConfigSource(rc),
|
||
),
|
||
)
|
||
} else {
|
||
return config.New(
|
||
config.WithSource(
|
||
NewFileConfigSource(configPath),
|
||
),
|
||
)
|
||
}
|
||
}
|
||
|
||
// LoadBootstrapConfig 加载程序引导配置
|
||
func LoadBootstrapConfig(configPath string) error {
|
||
cfg := NewConfigProvider(configPath)
|
||
|
||
var err error
|
||
|
||
if err = cfg.Load(); err != nil {
|
||
return err
|
||
}
|
||
|
||
initBootstrapConfig()
|
||
|
||
if err = scanConfigs(cfg); err != nil {
|
||
return err
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func scanConfigs(cfg config.Config) error {
|
||
initBootstrapConfig()
|
||
|
||
for _, c := range configList {
|
||
if err := cfg.Scan(c); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func pathExists(path string) bool {
|
||
_, err := os.Stat(path)
|
||
if err == nil {
|
||
return true
|
||
}
|
||
if os.IsNotExist(err) {
|
||
return false
|
||
}
|
||
return false
|
||
}
|
||
|
||
// LoadRemoteConfigSourceConfigs 加载远程配置源的本地配置
|
||
func LoadRemoteConfigSourceConfigs(configPath string) (error, *conf.RemoteConfig) {
|
||
configPath = configPath + "/" + remoteConfigSourceConfigFile
|
||
if !pathExists(configPath) {
|
||
return nil, nil
|
||
}
|
||
|
||
cfg := config.New(
|
||
config.WithSource(
|
||
NewFileConfigSource(configPath),
|
||
),
|
||
)
|
||
defer func(cfg config.Config) {
|
||
if err := cfg.Close(); err != nil {
|
||
panic(err)
|
||
}
|
||
}(cfg)
|
||
|
||
var err error
|
||
|
||
if err = cfg.Load(); err != nil {
|
||
return err, nil
|
||
}
|
||
|
||
if err = scanConfigs(cfg); err != nil {
|
||
return err, nil
|
||
}
|
||
|
||
return nil, commonConfig.Config
|
||
}
|
||
|
||
type ConfigType string
|
||
|
||
const (
|
||
ConfigTypeLocalFile ConfigType = "file"
|
||
ConfigTypeNacos ConfigType = "nacos"
|
||
ConfigTypeConsul ConfigType = "consul"
|
||
ConfigTypeEtcd ConfigType = "etcd"
|
||
ConfigTypeApollo ConfigType = "apollo"
|
||
ConfigTypeKubernetes ConfigType = "kubernetes"
|
||
ConfigTypePolaris ConfigType = "polaris"
|
||
)
|
||
|
||
// NewRemoteConfigSource 创建一个远程配置源
|
||
func NewRemoteConfigSource(c *conf.RemoteConfig) config.Source {
|
||
switch ConfigType(c.Type) {
|
||
default:
|
||
fallthrough
|
||
case ConfigTypeLocalFile:
|
||
return nil
|
||
case ConfigTypeNacos:
|
||
return NewNacosConfigSource(c)
|
||
case ConfigTypeConsul:
|
||
return NewConsulConfigSource(c)
|
||
case ConfigTypeEtcd:
|
||
return NewEtcdConfigSource(c)
|
||
case ConfigTypeApollo:
|
||
return NewApolloConfigSource(c)
|
||
case ConfigTypeKubernetes:
|
||
return NewKubernetesConfigSource(c)
|
||
case ConfigTypePolaris:
|
||
return NewPolarisConfigSource(c)
|
||
}
|
||
}
|
||
|
||
// getConfigKey 获取合法的配置名
|
||
func getConfigKey(configKey string, useBackslash bool) string {
|
||
if useBackslash {
|
||
return strings.Replace(configKey, `.`, `/`, -1)
|
||
} else {
|
||
return configKey
|
||
}
|
||
}
|
||
|
||
// NewFileConfigSource 创建一个本地文件配置源
|
||
func NewFileConfigSource(filePath string) config.Source {
|
||
return fileKratos.NewSource(filePath)
|
||
}
|
||
|
||
// NewNacosConfigSource 创建一个远程配置源 - Nacos
|
||
func NewNacosConfigSource(c *conf.RemoteConfig) config.Source {
|
||
srvConf := []nacosConstant.ServerConfig{
|
||
*nacosConstant.NewServerConfig(c.Nacos.Address, c.Nacos.Port),
|
||
}
|
||
|
||
cliConf := nacosConstant.ClientConfig{
|
||
TimeoutMs: 10 * 1000, // http请求超时时间,单位毫秒
|
||
BeatInterval: 5 * 1000, // 心跳间隔时间,单位毫秒
|
||
UpdateThreadNum: 20, // 更新服务的线程数
|
||
LogLevel: "debug",
|
||
CacheDir: "../../configs/cache", // 缓存目录
|
||
LogDir: "../../configs/log", // 日志目录
|
||
NotLoadCacheAtStart: true, // 在启动时不读取本地缓存数据,true--不读取,false--读取
|
||
UpdateCacheWhenEmpty: true, // 当服务列表为空时是否更新本地缓存,true--更新,false--不更新
|
||
}
|
||
|
||
nacosClient, err := nacosClients.NewConfigClient(
|
||
nacosVo.NacosClientParam{
|
||
ClientConfig: &cliConf,
|
||
ServerConfigs: srvConf,
|
||
},
|
||
)
|
||
if err != nil {
|
||
log.Fatal(err)
|
||
}
|
||
|
||
return nacosKratos.NewConfigSource(nacosClient,
|
||
nacosKratos.WithGroup(getConfigKey(c.Nacos.Key, false)),
|
||
nacosKratos.WithDataID("bootstrap.yaml"),
|
||
)
|
||
}
|
||
|
||
// NewEtcdConfigSource 创建一个远程配置源 - Etcd
|
||
func NewEtcdConfigSource(c *conf.RemoteConfig) config.Source {
|
||
cfg := etcdClient.Config{
|
||
Endpoints: c.Etcd.Endpoints,
|
||
DialTimeout: c.Etcd.Timeout.AsDuration(),
|
||
DialOptions: []grpc.DialOption{grpc.WithBlock()},
|
||
}
|
||
|
||
cli, err := etcdClient.New(cfg)
|
||
if err != nil {
|
||
panic(err)
|
||
}
|
||
|
||
source, err := etcdKratos.New(cli, etcdKratos.WithPath(getConfigKey(c.Etcd.Key, true)))
|
||
if err != nil {
|
||
log.Fatal(err)
|
||
}
|
||
|
||
return source
|
||
}
|
||
|
||
// NewConsulConfigSource 创建一个远程配置源 - Consul
|
||
func NewConsulConfigSource(c *conf.RemoteConfig) config.Source {
|
||
cfg := consulApi.DefaultConfig()
|
||
cfg.Address = c.Consul.Address
|
||
cfg.Scheme = c.Consul.Scheme
|
||
|
||
cli, err := consulApi.NewClient(cfg)
|
||
if err != nil {
|
||
log.Fatal(err)
|
||
}
|
||
|
||
source, err := consulKratos.New(cli,
|
||
consulKratos.WithPath(getConfigKey(c.Consul.Key, true)),
|
||
)
|
||
if err != nil {
|
||
log.Fatal(err)
|
||
}
|
||
|
||
return source
|
||
}
|
||
|
||
// NewApolloConfigSource 创建一个远程配置源 - Apollo
|
||
func NewApolloConfigSource(c *conf.RemoteConfig) config.Source {
|
||
source := apolloKratos.NewSource(
|
||
apolloKratos.WithAppID(c.Apollo.AppId),
|
||
apolloKratos.WithCluster(c.Apollo.Cluster),
|
||
apolloKratos.WithEndpoint(c.Apollo.Endpoint),
|
||
apolloKratos.WithNamespace(c.Apollo.Namespace),
|
||
apolloKratos.WithSecret(c.Apollo.Secret),
|
||
apolloKratos.WithEnableBackup(),
|
||
)
|
||
return source
|
||
}
|
||
|
||
// NewKubernetesConfigSource 创建一个远程配置源 - Kubernetes
|
||
func NewKubernetesConfigSource(c *conf.RemoteConfig) config.Source {
|
||
source := k8sKratos.NewSource(
|
||
k8sKratos.Namespace(c.Kubernetes.Namespace),
|
||
k8sKratos.LabelSelector(""),
|
||
k8sKratos.KubeConfig(filepath.Join(k8sUtil.HomeDir(), ".kube", "config")),
|
||
)
|
||
return source
|
||
}
|
||
|
||
// NewPolarisConfigSource 创建一个远程配置源 - Polaris
|
||
func NewPolarisConfigSource(_ *conf.RemoteConfig) config.Source {
|
||
//configApi, err := polarisApi.NewConfigAPI()
|
||
//if err != nil {
|
||
// log.Fatal(err)
|
||
//}
|
||
//
|
||
//var opts []polarisKratos.Option
|
||
//opts = append(opts, polarisKratos.WithNamespace("default"))
|
||
//opts = append(opts, polarisKratos.WithFileGroup("default"))
|
||
//opts = append(opts, polarisKratos.WithFileName("default.yaml"))
|
||
//
|
||
//source, err := polarisKratos.New(configApi, opts...)
|
||
//if err != nil {
|
||
// log.Fatal(err)
|
||
//}
|
||
//
|
||
//return source
|
||
return nil
|
||
}
|