load.go 6.8 KB

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