pkg/client/config.go (195 lines of code) (raw):
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package client
import (
"flag"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"runtime"
"strings"
"github.com/knadh/koanf/parsers/json"
"github.com/knadh/koanf/parsers/toml"
"github.com/knadh/koanf/parsers/yaml"
"github.com/knadh/koanf/providers/rawbytes"
koanf "github.com/knadh/koanf/v2"
"seata.apache.org/seata-go/pkg/discovery"
"seata.apache.org/seata-go/pkg/datasource/sql"
"seata.apache.org/seata-go/pkg/datasource/sql/undo"
remoteConfig "seata.apache.org/seata-go/pkg/remoting/config"
"seata.apache.org/seata-go/pkg/rm"
"seata.apache.org/seata-go/pkg/rm/tcc"
"seata.apache.org/seata-go/pkg/tm"
"seata.apache.org/seata-go/pkg/util/flagext"
)
const (
configFileEnvKey = "SEATA_GO_CONFIG_PATH"
configPrefix = "seata"
)
const (
jsonSuffix = "json"
tomlSuffix = "toml"
yamlSuffix = "yaml"
ymlSuffix = "yml"
)
type ClientConfig struct {
TmConfig tm.TmConfig `yaml:"tm" json:"tm,omitempty" koanf:"tm"`
RmConfig rm.Config `yaml:"rm" json:"rm,omitempty" koanf:"rm"`
UndoConfig undo.Config `yaml:"undo" json:"undo,omitempty" koanf:"undo"`
XaConfig sql.XAConfig `yaml:"xa" json:"xa" koanf:"xa"`
}
func (c *ClientConfig) RegisterFlagsWithPrefix(prefix string, f *flag.FlagSet) {
c.TmConfig.RegisterFlagsWithPrefix(prefix+".tm", f)
c.RmConfig.RegisterFlagsWithPrefix(prefix+".rm", f)
c.UndoConfig.RegisterFlagsWithPrefix(prefix+".undo", f)
c.XaConfig.RegisterFlagsWithPrefix(prefix+".xa", f)
}
type Config struct {
Enabled bool `yaml:"enabled" json:"enabled,omitempty" koanf:"enabled"`
ApplicationID string `yaml:"application-id" json:"application-id,omitempty" koanf:"application-id"`
TxServiceGroup string `yaml:"tx-service-group" json:"tx-service-group,omitempty" koanf:"tx-service-group"`
AccessKey string `yaml:"access-key" json:"access-key,omitempty" koanf:"access-key"`
SecretKey string `yaml:"secret-key" json:"secret-key,omitempty" koanf:"secret-key"`
EnableAutoDataSourceProxy bool `yaml:"enable-auto-data-source-proxy" json:"enable-auto-data-source-proxy,omitempty" koanf:"enable-auto-data-source-proxy"`
DataSourceProxyMode string `yaml:"data-source-proxy-mode" json:"data-source-proxy-mode,omitempty" koanf:"data-source-proxy-mode"`
AsyncWorkerConfig sql.AsyncWorkerConfig `yaml:"async" json:"async" koanf:"async"`
TCCConfig tcc.Config `yaml:"tcc" json:"tcc" koanf:"tcc"`
ClientConfig ClientConfig `yaml:"client" json:"client" koanf:"client"`
GettyConfig remoteConfig.Config `yaml:"getty" json:"getty" koanf:"getty"`
TransportConfig remoteConfig.TransportConfig `yaml:"transport" json:"transport" koanf:"transport"`
ServiceConfig discovery.ServiceConfig `yaml:"service" json:"service" koanf:"service"`
RegistryConfig discovery.RegistryConfig `yaml:"registry" json:"registry" koanf:"registry"`
}
func (c *Config) RegisterFlags(f *flag.FlagSet) {
f.BoolVar(&c.Enabled, "enabled", true, "Whether enable auto configuration.")
f.StringVar(&c.ApplicationID, "application-id", "seata-go", "Application id.")
f.StringVar(&c.TxServiceGroup, "tx-service-group", "default_tx_group", "Transaction service group.")
f.StringVar(&c.AccessKey, "access-key", "", "Used for aliyun accessKey.")
f.StringVar(&c.SecretKey, "secret-key", "", "Used for aliyun secretKey.")
f.BoolVar(&c.EnableAutoDataSourceProxy, "enable-auto-data-source-proxy", true, "Whether enable auto proxying of datasource bean.")
f.StringVar(&c.DataSourceProxyMode, "data-source-proxy-mode", "AT", "Data source proxy mode.")
c.AsyncWorkerConfig.RegisterFlagsWithPrefix("async-worker", f)
c.TCCConfig.RegisterFlagsWithPrefix("tcc", f)
c.ClientConfig.RegisterFlagsWithPrefix("client", f)
c.GettyConfig.RegisterFlagsWithPrefix("getty", f)
c.TransportConfig.RegisterFlagsWithPrefix("transport", f)
c.RegistryConfig.RegisterFlagsWithPrefix("registry", f)
c.ServiceConfig.RegisterFlagsWithPrefix("service", f)
}
type loaderConf struct {
suffix string // loaderConf file extension default yaml
path string // loaderConf file path default ./conf/seatago.yaml
delim string // loaderConf file delim default .
bytes []byte // config bytes
name string // config file name
}
// Load parse config from user config path
func LoadPath(configFilePath string) *Config {
if configFilePath == "" {
configFilePath = os.Getenv(configFileEnvKey)
if configFilePath == "" {
panic("system variable SEATA_GO_CONFIG_PATH is empty")
}
}
var cfg Config
// This sets default values from flags to the config.
// It needs to be called before parsing the config file!
flagext.RegisterFlags(&cfg)
conf := newLoaderConf(configFilePath)
koan := getConfigResolver(conf)
if err := koan.UnmarshalWithConf(configPrefix, &cfg, koanf.UnmarshalConf{Tag: yamlSuffix}); err != nil {
panic(err)
}
return &cfg
}
// Load parse config from json bytes
func LoadJson(bytes []byte) *Config {
var cfg Config
// This sets default values from flags to the config.
// It needs to be called before parsing the config file!
flagext.RegisterFlags(&cfg)
koan := getJsonConfigResolver(bytes)
if err := koan.Unmarshal("", &cfg); err != nil {
panic(err)
}
return &cfg
}
// getJsonConfigResolver get json config resolver
func getJsonConfigResolver(bytes []byte) *koanf.Koanf {
k := koanf.New(".")
if err := k.Load(rawbytes.Provider(bytes), json.Parser()); err != nil {
panic(err)
}
return k
}
// resolverFilePath resolver file path
// eg: give a ./conf/seatago.yaml return seatago and yaml
func resolverFilePath(path string) (name, suffix string) {
paths := strings.Split(path, "/")
fileName := strings.Split(paths[len(paths)-1], ".")
if len(fileName) < 2 {
return fileName[0], yamlSuffix
}
return fileName[0], fileName[1]
}
// getConfigResolver get config resolver
func getConfigResolver(conf *loaderConf) *koanf.Koanf {
var (
k *koanf.Koanf
err error
)
if len(conf.suffix) <= 0 {
conf.suffix = yamlSuffix
}
if len(conf.delim) <= 0 {
conf.delim = "."
}
bytes := conf.bytes
if len(bytes) <= 0 {
panic(fmt.Errorf("bytes is nil,please set bytes or file path"))
}
k = koanf.New(conf.delim)
switch conf.suffix {
case yamlSuffix, ymlSuffix:
err = k.Load(rawbytes.Provider(bytes), yaml.Parser())
case jsonSuffix:
err = k.Load(rawbytes.Provider(bytes), json.Parser())
case tomlSuffix:
err = k.Load(rawbytes.Provider(bytes), toml.Parser())
default:
err = fmt.Errorf("no support %s file suffix", conf.suffix)
}
if err != nil {
panic(err)
}
return k
}
func newLoaderConf(configFilePath string) *loaderConf {
name, suffix := resolverFilePath(configFilePath)
conf := &loaderConf{
suffix: suffix,
path: absolutePath(configFilePath),
delim: ".",
name: name,
}
if len(conf.bytes) <= 0 {
if bytes, err := ioutil.ReadFile(conf.path); err != nil {
panic(err)
} else {
conf.bytes = bytes
}
}
return conf
}
// absolutePath get absolut path
func absolutePath(inPath string) string {
if inPath == "$HOME" || strings.HasPrefix(inPath, "$HOME"+string(os.PathSeparator)) {
inPath = userHomeDir() + inPath[5:]
}
if filepath.IsAbs(inPath) {
return filepath.Clean(inPath)
}
p, err := filepath.Abs(inPath)
if err == nil {
return filepath.Clean(p)
}
return ""
}
// userHomeDir get gopath
func userHomeDir() string {
if runtime.GOOS == "windows" {
home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
if home == "" {
home = os.Getenv("USERPROFILE")
}
return home
}
return os.Getenv("HOME")
}