load.go 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. // Copyright 2021 EMQ Technologies Co., Ltd.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. // INTECH Process Automation Ltd.
  15. //
  16. // Licensed under the Apache License, Version 2.0 (the "License");
  17. // you may not use this file except in compliance with the License.
  18. // You may obtain a copy of the License at
  19. //
  20. // http://www.apache.org/licenses/LICENSE-2.0
  21. //
  22. // Unless required by applicable law or agreed to in writing, software
  23. // distributed under the License is distributed on an "AS IS" BASIS,
  24. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  25. // See the License for the specific language governing permissions and
  26. // limitations under the License.
  27. package conf
  28. import (
  29. "encoding/json"
  30. "errors"
  31. "fmt"
  32. "os"
  33. "path"
  34. "path/filepath"
  35. "strconv"
  36. "strings"
  37. "github.com/mitchellh/mapstructure"
  38. "gopkg.in/yaml.v3"
  39. )
  40. const Separator = "__"
  41. func LoadConfig(c interface{}) error {
  42. return LoadConfigByName(ConfFileName, c)
  43. }
  44. func LoadConfigByName(name string, c interface{}) error {
  45. dir, err := GetConfLoc()
  46. if err != nil {
  47. return err
  48. }
  49. p := path.Join(dir, name)
  50. return LoadConfigFromPath(p, c)
  51. }
  52. func LoadConfigFromPath(p string, c interface{}) error {
  53. prefix := getPrefix(p)
  54. b, err := os.ReadFile(p)
  55. if err != nil {
  56. return err
  57. }
  58. configMap := make(map[string]interface{})
  59. err = yaml.Unmarshal(b, &configMap)
  60. if err != nil {
  61. return err
  62. }
  63. configs := normalize(configMap)
  64. err = process(configs, os.Environ(), prefix)
  65. if err != nil {
  66. return err
  67. }
  68. if _, success := c.(*map[string]interface{}); success {
  69. names, err := extractKeysFromJsonIfExists(p)
  70. if err != nil {
  71. return err
  72. }
  73. applyKeys(configs, names)
  74. }
  75. return mapstructure.Decode(configs, c)
  76. }
  77. func CorrectsConfigKeysByJson(configs map[string]interface{}, jsonFilePath string) error {
  78. dir, err := GetConfLoc()
  79. if err != nil {
  80. return err
  81. }
  82. path := path.Join(dir, jsonFilePath)
  83. m, err := loadJsonForYaml(path)
  84. if err != nil {
  85. return err
  86. }
  87. names, err := extractNamesFromProperties(m)
  88. if err != nil {
  89. return err
  90. }
  91. applyKeys(configs, names)
  92. return nil
  93. }
  94. func getPrefix(p string) string {
  95. _, file := path.Split(p)
  96. return strings.ToUpper(strings.TrimSuffix(file, filepath.Ext(file)))
  97. }
  98. func process(configMap map[string]interface{}, variables []string, prefix string) error {
  99. for _, e := range variables {
  100. if !strings.HasPrefix(e, prefix) {
  101. continue
  102. }
  103. pair := strings.SplitN(e, "=", 2)
  104. if len(pair) != 2 {
  105. return fmt.Errorf("wrong format of variable")
  106. }
  107. keys := nameToKeys(trimPrefix(pair[0], prefix))
  108. handle(configMap, keys, pair[1])
  109. printableK := strings.Join(keys, ".")
  110. printableV := pair[1]
  111. if strings.Contains(strings.ToLower(printableK), "password") {
  112. printableV = "*"
  113. }
  114. Log.Infof("Set config '%s.%s' to '%s' by environment variable", strings.ToLower(prefix), printableK, printableV)
  115. }
  116. return nil
  117. }
  118. func handle(conf map[string]interface{}, keysLeft []string, val string) {
  119. key := getConfigKey(keysLeft[0])
  120. if len(keysLeft) == 1 {
  121. conf[key] = getValueType(val)
  122. } else if len(keysLeft) >= 2 {
  123. if v, ok := conf[key]; ok {
  124. if casted, castSuccess := v.(map[string]interface{}); castSuccess {
  125. handle(casted, keysLeft[1:], val)
  126. } else {
  127. panic("not expected type")
  128. }
  129. } else {
  130. next := make(map[string]interface{})
  131. conf[key] = next
  132. handle(next, keysLeft[1:], val)
  133. }
  134. }
  135. }
  136. func trimPrefix(key string, prefix string) string {
  137. p := fmt.Sprintf("%s%s", prefix, Separator)
  138. return strings.TrimPrefix(key, p)
  139. }
  140. func nameToKeys(key string) []string {
  141. return strings.Split(strings.ToLower(key), Separator)
  142. }
  143. func getConfigKey(key string) string {
  144. return strings.ToLower(key)
  145. }
  146. func getValueType(val string) interface{} {
  147. val = strings.Trim(val, " ")
  148. if strings.HasPrefix(val, "[") && strings.HasSuffix(val, "]") {
  149. val = strings.ReplaceAll(val, "[", "")
  150. val = strings.ReplaceAll(val, "]", "")
  151. vals := strings.Split(val, ",")
  152. var ret []interface{}
  153. for _, v := range vals {
  154. if i, err := strconv.ParseInt(v, 10, 64); err == nil {
  155. ret = append(ret, i)
  156. } else if b, err := strconv.ParseBool(v); err == nil {
  157. ret = append(ret, b)
  158. } else if f, err := strconv.ParseFloat(v, 64); err == nil {
  159. ret = append(ret, f)
  160. } else {
  161. ret = append(ret, v)
  162. }
  163. }
  164. return ret
  165. } else if i, err := strconv.ParseInt(val, 10, 64); err == nil {
  166. return i
  167. } else if b, err := strconv.ParseBool(val); err == nil {
  168. return b
  169. } else if f, err := strconv.ParseFloat(val, 64); err == nil {
  170. return f
  171. }
  172. return val
  173. }
  174. func normalize(m map[string]interface{}) map[string]interface{} {
  175. res := make(map[string]interface{})
  176. for k, v := range m {
  177. lowered := strings.ToLower(k)
  178. if casted, success := v.(map[string]interface{}); success {
  179. node := normalize(casted)
  180. res[lowered] = node
  181. } else {
  182. res[lowered] = v
  183. }
  184. }
  185. return res
  186. }
  187. func applyKeys(m map[string]interface{}, list []string) {
  188. for _, k := range list {
  189. applyKey(m, k)
  190. }
  191. }
  192. func applyKey(m map[string]interface{}, key string) {
  193. for k, v := range m {
  194. if casted, ok := v.(map[string]interface{}); ok {
  195. applyKey(casted, key)
  196. }
  197. if key != k && strings.EqualFold(key, k) {
  198. m[key] = v
  199. delete(m, k)
  200. }
  201. }
  202. }
  203. func extractKeysFromJsonIfExists(yamlPath string) ([]string, error) {
  204. jsonFilePath := jsonPathForFile(yamlPath)
  205. _, err := os.Stat(jsonFilePath)
  206. if err != nil {
  207. if errors.Is(err, os.ErrNotExist) {
  208. return make([]string, 0), nil
  209. } else {
  210. return nil, err
  211. }
  212. }
  213. m, err := loadJsonForYaml(jsonFilePath)
  214. if err != nil {
  215. return nil, err
  216. }
  217. return extractNamesFromProperties(m)
  218. }
  219. func loadJsonForYaml(filePath string) (map[string]interface{}, error) {
  220. data, err := os.ReadFile(filePath)
  221. if err != nil {
  222. return nil, err
  223. }
  224. m := make(map[string]interface{})
  225. err = json.Unmarshal(data, &m)
  226. if err != nil {
  227. return nil, err
  228. }
  229. return m, nil
  230. }
  231. func jsonPathForFile(yamlPath string) string {
  232. p := strings.TrimSuffix(yamlPath, filepath.Ext(yamlPath))
  233. return fmt.Sprintf("%s.json", p)
  234. }
  235. func extractNamesFromProperties(jsonMap map[string]interface{}) ([]string, error) {
  236. result := make([]string, 0)
  237. properties, contains := jsonMap["properties"]
  238. if !contains {
  239. return nil, fmt.Errorf("json map does not have properties value")
  240. }
  241. if propertiesAsMap, success := properties.(map[string]interface{}); success {
  242. re := extractNamesFromElement(propertiesAsMap)
  243. result = append(result, re...)
  244. } else {
  245. return nil, fmt.Errorf("failed to cast to list of properties")
  246. }
  247. return result, nil
  248. }
  249. func extractNamesFromElement(jsonMap map[string]interface{}) []string {
  250. result := make([]string, 0)
  251. list := jsonMap["default"]
  252. if interfaceList, isList := list.([]interface{}); isList {
  253. for _, element := range interfaceList {
  254. if m, isMap := element.(map[string]interface{}); isMap {
  255. re := extractNamesFromElement(m)
  256. result = append(result, re...)
  257. }
  258. }
  259. } else {
  260. n := jsonMap["name"]
  261. if s, isString := n.(string); isString {
  262. result = append(result, s)
  263. }
  264. }
  265. return result
  266. }
  267. func Printable(m map[string]interface{}) map[string]interface{} {
  268. printableMap := make(map[string]interface{})
  269. for k, v := range m {
  270. if strings.ToLower(k) == "password" {
  271. printableMap[k] = "***"
  272. } else {
  273. if vm, ok := v.(map[string]interface{}); ok {
  274. printableMap[k] = Printable(vm)
  275. } else {
  276. printableMap[k] = v
  277. }
  278. }
  279. }
  280. return printableMap
  281. }