load.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  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. "github.com/mitchellh/mapstructure"
  33. "gopkg.in/yaml.v3"
  34. "io/ioutil"
  35. "os"
  36. "path"
  37. "path/filepath"
  38. "strconv"
  39. "strings"
  40. )
  41. const Separator = "__"
  42. func LoadConfig(c interface{}) error {
  43. return LoadConfigByName(ConfFileName, c)
  44. }
  45. func LoadConfigByName(name string, c interface{}) error {
  46. dir, err := GetConfLoc()
  47. if err != nil {
  48. return err
  49. }
  50. p := path.Join(dir, name)
  51. return LoadConfigFromPath(p, c)
  52. }
  53. func LoadConfigFromPath(p string, c interface{}) error {
  54. prefix := getPrefix(p)
  55. b, err := ioutil.ReadFile(p)
  56. if err != nil {
  57. return err
  58. }
  59. configMap := make(map[string]interface{})
  60. err = yaml.Unmarshal(b, &configMap)
  61. if err != nil {
  62. return err
  63. }
  64. configs := normalize(configMap)
  65. err = process(configs, os.Environ(), prefix)
  66. if err != nil {
  67. return err
  68. }
  69. if _, success := c.(*map[string]interface{}); success {
  70. names, err := extractKeysFromJsonIfExists(p)
  71. if err != nil {
  72. return err
  73. }
  74. applyKeys(configs, names)
  75. }
  76. return mapstructure.Decode(configs, c)
  77. }
  78. func CorrectsConfigKeysByJson(configs map[string]interface{}, jsonFilePath string) error {
  79. dir, err := GetConfLoc()
  80. if err != nil {
  81. return err
  82. }
  83. path := path.Join(dir, jsonFilePath)
  84. m, err := loadJsonForYaml(path)
  85. if err != nil {
  86. return err
  87. }
  88. names, err := extractNamesFromProperties(m)
  89. if err != nil {
  90. return err
  91. }
  92. applyKeys(configs, names)
  93. return nil
  94. }
  95. func getPrefix(p string) string {
  96. _, file := path.Split(p)
  97. return strings.ToUpper(strings.TrimSuffix(file, filepath.Ext(file)))
  98. }
  99. func process(configMap map[string]interface{}, variables []string, prefix string) error {
  100. for _, e := range variables {
  101. if !strings.HasPrefix(e, prefix) {
  102. continue
  103. }
  104. pair := strings.SplitN(e, "=", 2)
  105. if len(pair) != 2 {
  106. return fmt.Errorf("wrong format of variable")
  107. }
  108. keys := nameToKeys(trimPrefix(pair[0], prefix))
  109. handle(configMap, keys, pair[1])
  110. printableK := strings.Join(keys, ".")
  111. printableV := pair[1]
  112. if strings.Contains(strings.ToLower(printableK), "password") {
  113. printableV = "*"
  114. }
  115. Log.Infof("Set config '%s.%s' to '%s' by environment variable", strings.ToLower(prefix), printableK, printableV)
  116. }
  117. return nil
  118. }
  119. func handle(conf map[string]interface{}, keysLeft []string, val string) {
  120. key := getConfigKey(keysLeft[0])
  121. if len(keysLeft) == 1 {
  122. conf[key] = getValueType(val)
  123. } else if len(keysLeft) >= 2 {
  124. if v, ok := conf[key]; ok {
  125. if casted, castSuccess := v.(map[string]interface{}); castSuccess {
  126. handle(casted, keysLeft[1:], val)
  127. } else {
  128. panic("not expected type")
  129. }
  130. } else {
  131. next := make(map[string]interface{})
  132. conf[key] = next
  133. handle(next, keysLeft[1:], val)
  134. }
  135. }
  136. }
  137. func trimPrefix(key string, prefix string) string {
  138. p := fmt.Sprintf("%s%s", prefix, Separator)
  139. return strings.TrimPrefix(key, p)
  140. }
  141. func nameToKeys(key string) []string {
  142. return strings.Split(strings.ToLower(key), Separator)
  143. }
  144. func getConfigKey(key string) string {
  145. return strings.ToLower(key)
  146. }
  147. func getValueType(val string) interface{} {
  148. val = strings.Trim(val, " ")
  149. if strings.HasPrefix(val, "[") && strings.HasSuffix(val, "]") {
  150. val = strings.ReplaceAll(val, "[", "")
  151. val = strings.ReplaceAll(val, "]", "")
  152. vals := strings.Split(val, ",")
  153. var ret []interface{}
  154. for _, v := range vals {
  155. if i, err := strconv.ParseInt(v, 10, 64); err == nil {
  156. ret = append(ret, i)
  157. } else if b, err := strconv.ParseBool(v); err == nil {
  158. ret = append(ret, b)
  159. } else if f, err := strconv.ParseFloat(v, 64); err == nil {
  160. ret = append(ret, f)
  161. } else {
  162. ret = append(ret, v)
  163. }
  164. }
  165. return ret
  166. } else if i, err := strconv.ParseInt(val, 10, 64); err == nil {
  167. return i
  168. } else if b, err := strconv.ParseBool(val); err == nil {
  169. return b
  170. } else if f, err := strconv.ParseFloat(val, 64); err == nil {
  171. return f
  172. }
  173. return val
  174. }
  175. func normalize(m map[string]interface{}) map[string]interface{} {
  176. res := make(map[string]interface{})
  177. for k, v := range m {
  178. lowered := strings.ToLower(k)
  179. if casted, success := v.(map[string]interface{}); success {
  180. node := normalize(casted)
  181. res[lowered] = node
  182. } else {
  183. res[lowered] = v
  184. }
  185. }
  186. return res
  187. }
  188. func applyKeys(m map[string]interface{}, list []string) {
  189. for _, k := range list {
  190. applyKey(m, k)
  191. }
  192. }
  193. func applyKey(m map[string]interface{}, key string) {
  194. for k, v := range m {
  195. if casted, ok := v.(map[string]interface{}); ok {
  196. applyKey(casted, key)
  197. }
  198. if key != k && strings.EqualFold(key, k) {
  199. m[key] = v
  200. delete(m, k)
  201. }
  202. }
  203. }
  204. func extractKeysFromJsonIfExists(yamlPath string) ([]string, error) {
  205. jsonFilePath := jsonPathForFile(yamlPath)
  206. _, err := os.Stat(jsonFilePath)
  207. if err != nil {
  208. if errors.Is(err, os.ErrNotExist) {
  209. return make([]string, 0), nil
  210. } else {
  211. return nil, err
  212. }
  213. }
  214. m, err := loadJsonForYaml(jsonFilePath)
  215. if err != nil {
  216. return nil, err
  217. }
  218. return extractNamesFromProperties(m)
  219. }
  220. func loadJsonForYaml(filePath string) (map[string]interface{}, error) {
  221. data, err := ioutil.ReadFile(filePath)
  222. if err != nil {
  223. return nil, err
  224. }
  225. m := make(map[string]interface{})
  226. err = json.Unmarshal(data, &m)
  227. if err != nil {
  228. return nil, err
  229. }
  230. return m, nil
  231. }
  232. func jsonPathForFile(yamlPath string) string {
  233. p := strings.TrimSuffix(yamlPath, filepath.Ext(yamlPath))
  234. return fmt.Sprintf("%s.json", p)
  235. }
  236. func extractNamesFromProperties(jsonMap map[string]interface{}) ([]string, error) {
  237. result := make([]string, 0)
  238. properties, contains := jsonMap["properties"]
  239. if !contains {
  240. return nil, fmt.Errorf("json map does not have properties value")
  241. }
  242. if propertiesAsMap, success := properties.(map[string]interface{}); success {
  243. re := extractNamesFromElement(propertiesAsMap)
  244. result = append(result, re...)
  245. } else {
  246. return nil, fmt.Errorf("failed to cast to list of properties")
  247. }
  248. return result, nil
  249. }
  250. func extractNamesFromElement(jsonMap map[string]interface{}) []string {
  251. result := make([]string, 0)
  252. list := jsonMap["default"]
  253. if interfaceList, isList := list.([]interface{}); isList {
  254. for _, element := range interfaceList {
  255. if m, isMap := element.(map[string]interface{}); isMap {
  256. re := extractNamesFromElement(m)
  257. result = append(result, re...)
  258. }
  259. }
  260. } else {
  261. n := jsonMap["name"]
  262. if s, isString := n.(string); isString {
  263. result = append(result, s)
  264. }
  265. }
  266. return result
  267. }