You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

config.go 5.8 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. package client
  18. import (
  19. "flag"
  20. "fmt"
  21. "io/ioutil"
  22. "os"
  23. "path/filepath"
  24. "runtime"
  25. "strings"
  26. "github.com/knadh/koanf"
  27. "github.com/knadh/koanf/parsers/json"
  28. "github.com/knadh/koanf/parsers/toml"
  29. "github.com/knadh/koanf/parsers/yaml"
  30. "github.com/knadh/koanf/providers/rawbytes"
  31. "github.com/seata/seata-go/pkg/remoting/getty"
  32. "github.com/seata/seata-go/pkg/rm/tcc"
  33. "github.com/seata/seata-go/pkg/tm"
  34. "github.com/seata/seata-go/pkg/util/flagext"
  35. )
  36. const (
  37. configFileEnvKey = "SEATA_GO_CONFIG_PATH"
  38. configPrefix = "seata"
  39. )
  40. const (
  41. jsonSuffix = "json"
  42. tomlSuffix = "toml"
  43. yamlSuffix = "yaml"
  44. ymlSuffix = "yml"
  45. )
  46. type ClientConfig struct {
  47. TmConfig tm.TmConfig `yaml:"tm" json:"tm,omitempty" koanf:"tm"`
  48. }
  49. func (c *ClientConfig) RegisterFlagsWithPrefix(prefix string, f *flag.FlagSet) {
  50. // TODO: RmConf RegisterFlagsWithPrefix
  51. // TODO: Undo RegisterFlagsWithPrefix
  52. // TODO: LoadBalance RegisterFlagsWithPrefix
  53. c.TmConfig.RegisterFlagsWithPrefix(prefix+".tm", f)
  54. }
  55. type Config struct {
  56. TCCConfig tcc.Config `yaml:"tcc" json:"tcc" koanf:"tcc"`
  57. ClientConfig ClientConfig `yaml:"client" json:"client" koanf:"client"`
  58. GettyConfig getty.Config `yaml:"getty" json:"getty" koanf:"getty"`
  59. }
  60. func (c *Config) RegisterFlags(f *flag.FlagSet) {
  61. c.TCCConfig.FenceConfig.RegisterFlagsWithPrefix("tcc", f)
  62. c.ClientConfig.RegisterFlagsWithPrefix("client", f)
  63. c.GettyConfig.RegisterFlagsWithPrefix("getty", f)
  64. }
  65. type loaderConf struct {
  66. suffix string // loaderConf file extension default yaml
  67. path string // loaderConf file path default ./conf/seatago.yaml
  68. delim string // loaderConf file delim default .
  69. bytes []byte // config bytes
  70. name string // config file name
  71. }
  72. // Load parse config from user config path
  73. func LoadPath(configFilePath string) *Config {
  74. if configFilePath == "" {
  75. configFilePath = os.Getenv(configFileEnvKey)
  76. if configFilePath == "" {
  77. panic("system variable SEATA_GO_CONFIG_PATH is empty")
  78. }
  79. }
  80. var cfg Config
  81. // This sets default values from flags to the config.
  82. // It needs to be called before parsing the config file!
  83. flagext.RegisterFlags(&cfg)
  84. conf := newLoaderConf(configFilePath)
  85. koan := getConfigResolver(conf)
  86. if err := koan.UnmarshalWithConf(configPrefix, &cfg, koanf.UnmarshalConf{Tag: yamlSuffix}); err != nil {
  87. panic(err)
  88. }
  89. return &cfg
  90. }
  91. // Load parse config from json bytes
  92. func LoadJson(bytes []byte) *Config {
  93. var cfg Config
  94. // This sets default values from flags to the config.
  95. // It needs to be called before parsing the config file!
  96. flagext.RegisterFlags(&cfg)
  97. koan := getJsonConfigResolver(bytes)
  98. if err := koan.Unmarshal("", &cfg); err != nil {
  99. panic(err)
  100. }
  101. return &cfg
  102. }
  103. // getJsonConfigResolver get json config resolver
  104. func getJsonConfigResolver(bytes []byte) *koanf.Koanf {
  105. k := koanf.New(".")
  106. if err := k.Load(rawbytes.Provider(bytes), json.Parser()); err != nil {
  107. panic(err)
  108. }
  109. return k
  110. }
  111. // resolverFilePath resolver file path
  112. // eg: give a ./conf/seatago.yaml return seatago and yaml
  113. func resolverFilePath(path string) (name, suffix string) {
  114. paths := strings.Split(path, "/")
  115. fileName := strings.Split(paths[len(paths)-1], ".")
  116. if len(fileName) < 2 {
  117. return fileName[0], yamlSuffix
  118. }
  119. return fileName[0], fileName[1]
  120. }
  121. // getConfigResolver get config resolver
  122. func getConfigResolver(conf *loaderConf) *koanf.Koanf {
  123. var (
  124. k *koanf.Koanf
  125. err error
  126. )
  127. if len(conf.suffix) <= 0 {
  128. conf.suffix = yamlSuffix
  129. }
  130. if len(conf.delim) <= 0 {
  131. conf.delim = "."
  132. }
  133. bytes := conf.bytes
  134. if len(bytes) <= 0 {
  135. panic(fmt.Errorf("bytes is nil,please set bytes or file path"))
  136. }
  137. k = koanf.New(conf.delim)
  138. switch conf.suffix {
  139. case yamlSuffix, ymlSuffix:
  140. err = k.Load(rawbytes.Provider(bytes), yaml.Parser())
  141. case jsonSuffix:
  142. err = k.Load(rawbytes.Provider(bytes), json.Parser())
  143. case tomlSuffix:
  144. err = k.Load(rawbytes.Provider(bytes), toml.Parser())
  145. default:
  146. err = fmt.Errorf("no support %s file suffix", conf.suffix)
  147. }
  148. if err != nil {
  149. panic(err)
  150. }
  151. return k
  152. }
  153. func newLoaderConf(configFilePath string) *loaderConf {
  154. name, suffix := resolverFilePath(configFilePath)
  155. conf := &loaderConf{
  156. suffix: suffix,
  157. path: absolutePath(configFilePath),
  158. delim: ".",
  159. name: name,
  160. }
  161. if len(conf.bytes) <= 0 {
  162. if bytes, err := ioutil.ReadFile(conf.path); err != nil {
  163. panic(err)
  164. } else {
  165. conf.bytes = bytes
  166. }
  167. }
  168. return conf
  169. }
  170. // absolutePath get absolut path
  171. func absolutePath(inPath string) string {
  172. if inPath == "$HOME" || strings.HasPrefix(inPath, "$HOME"+string(os.PathSeparator)) {
  173. inPath = userHomeDir() + inPath[5:]
  174. }
  175. if filepath.IsAbs(inPath) {
  176. return filepath.Clean(inPath)
  177. }
  178. p, err := filepath.Abs(inPath)
  179. if err == nil {
  180. return filepath.Clean(p)
  181. }
  182. return ""
  183. }
  184. // userHomeDir get gopath
  185. func userHomeDir() string {
  186. if runtime.GOOS == "windows" {
  187. home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
  188. if home == "" {
  189. home = os.Getenv("USERPROFILE")
  190. }
  191. return home
  192. }
  193. return os.Getenv("HOME")
  194. }