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

311 lines
9.6KB

  1. {#- -*- coding: utf-8 -*- #}
  2. {#- vim: ft=jinja #}
  3. {#- Get the `tplroot` from `tpldir` #}
  4. {%- set tplroot = tpldir.split("/")[0] %}
  5. {%- from tplroot ~ "/libmatchers.jinja" import parse_matchers, query_map with context %}
  6. {%- set _default_config_dirs = [
  7. "parameters/",
  8. tplroot ~ "/parameters"
  9. ] %}
  10. {%- macro mapstack(
  11. matchers,
  12. defaults=None,
  13. dirs=_default_config_dirs,
  14. log_prefix="libmapstack: "
  15. ) %}
  16. {#-
  17. Load configuration in the order of `matchers` and merge
  18. successively the values with `defaults`.
  19. The `matchers` are processed using `libmatchers.jinja` to select
  20. the configuration sources from where the values are loaded.
  21. Parameters:
  22. - `matchers`: list of matchers in the form
  23. `[<TYPE>[:<OPTION>[:<DELIMITER>]]@]<QUERY>`
  24. - `defaults`: dictionary of default values to start the merging,
  25. they are considered built-ins. It must conform to the same
  26. layout as the YAML files: a mandatory `values` key and two
  27. optional `strategy` and `merge_lists` keys.
  28. - `dirs`: list of directory where to look-up the configuration
  29. file matching the matchers, by default a global `salt://parameters/`
  30. and a per formula `salt://<tplroot>/parameters`
  31. - `log_prefix`: prefix used in the log outputs, by default it is
  32. `libmapstack: `
  33. Example: On a Debian system with `roles=["nginx/server", "telegraf"]`
  34. {%- set settings = mapstack(
  35. matchers=[
  36. "Y:G@os_family",
  37. "I@" ~ tplroot,
  38. "Y:C@roles",
  39. ],
  40. dirs=["defaults", tplroot ~ "/parameters"],
  41. )
  42. | load_yaml %}
  43. This will merge the values:
  44. - starting with the default empty dictionary `{}` (no
  45. `defaults` parameter)
  46. - from the YAML files
  47. - `salt://defaults/os_family/Debian.yaml`
  48. - `salt://{{ tplroot }}/parameters/os_family/Debian.yaml`
  49. - from the pillar `salt["pillar.get"](tplroot)`
  50. - from the `nginx/server` YAML files:
  51. - `salt://defaults/roles/nginx/server.yaml`
  52. - `salt://{{ tplroot }}/parameters/roles/nginx/server.yaml`
  53. - from the `telegraf` YAML files:
  54. - `salt://defaults/roles/telegraf.yaml`
  55. - `salt://{{ tplroot }}/parameters/roles/telegraf.yaml`
  56. Each YAML file and the `defaults` parameters must conform to the
  57. following layout:
  58. - a mandatory `values` key to store the configuration values
  59. - two optional keys to configure the use of `salt.slsutil.merge`
  60. - an optional `strategy` key to configure the merging
  61. strategy, for example `strategy: 'recurse'`, the default is
  62. `smart`
  63. - an optional `merge_lists` key to configure if lists should
  64. be merged or overridden for the `recurse` and `overwrite`
  65. strategies, for example `merge_lists: 'true'`
  66. #}
  67. {%- set stack = defaults | default({"values": {} }, boolean=True) %}
  68. {#- Build configuration file names based on matchers #}
  69. {%- set config_get_strategy = salt["config.get"](tplroot ~ ":strategy", None) %}
  70. {%- set matchers = parse_matchers(
  71. matchers,
  72. config_get_strategy=config_get_strategy,
  73. log_prefix=log_prefix
  74. )
  75. | load_yaml %}
  76. {%- do salt["log.debug"](
  77. log_prefix
  78. ~ "built-in configuration:\n"
  79. ~ {"values": defaults | traverse("values")}
  80. | yaml(False)
  81. ) %}
  82. {%- for param_dir in dirs %}
  83. {%- for matcher in matchers %}
  84. {#- `slsutil.merge` options from #}
  85. {#- 1. the `value` #}
  86. {#- 2. the `defaults` #}
  87. {#- 3. the built-in #}
  88. {%- set strategy = matcher.value
  89. | traverse(
  90. "strategy",
  91. defaults
  92. | traverse(
  93. "strategy",
  94. "smart"
  95. )
  96. ) %}
  97. {%- set merge_lists = matcher.value
  98. | traverse(
  99. "merge_lists",
  100. defaults
  101. | traverse(
  102. "merge_lists",
  103. False
  104. )
  105. )
  106. | to_bool %}
  107. {%- if matcher.type in query_map.keys() %}
  108. {#- No value is an empty list, must be a dict for `stack.update` #}
  109. {%- set normalized_value = matcher.value | default({}, boolean=True) %}
  110. {#- Merge in `mapdata.<query>` instead of directly in `mapdata` #}
  111. {%- set is_sub_key = matcher.option | default(False) == "SUB" %}
  112. {%- if is_sub_key %}
  113. {#- Merge values with `mapdata.<key>`, `<key>` and `<key>:lookup` are merged together #}
  114. {%- set value = { matcher.query | regex_replace(":lookup$", ""): normalized_value } %}
  115. {%- else %}
  116. {%- set value = normalized_value %}
  117. {%- endif %}
  118. {%- do salt["log.debug"](
  119. log_prefix
  120. ~ "merge "
  121. ~ "sub key " * is_sub_key
  122. ~ "'"
  123. ~ matcher.query
  124. ~ "' retrieved with '"
  125. ~ matcher.query_method
  126. ~ "', merge: strategy='"
  127. ~ strategy
  128. ~ "', lists='"
  129. ~ merge_lists
  130. ~ "':\n"
  131. ~ value
  132. | yaml(False)
  133. ) %}
  134. {%- do stack.update(
  135. {
  136. "values": salt["slsutil.merge"](
  137. stack["values"],
  138. value,
  139. strategy=strategy,
  140. merge_lists=merge_lists,
  141. )
  142. }
  143. ) %}
  144. {%- else %}
  145. {#- Load YAML file matching the grain/pillar/... #}
  146. {#- Fallback to use the source name as a direct filename #}
  147. {%- if matcher.value | length == 0 %}
  148. {#- Mangle `matcher.value` to use it as literal path #}
  149. {%- set query_parts = matcher.query.split("/") %}
  150. {%- set yaml_dirname = query_parts[0:-1] | join("/") %}
  151. {%- set yaml_names = query_parts[-1] %}
  152. {%- else %}
  153. {%- set yaml_dirname = matcher.query %}
  154. {%- set yaml_names = matcher.value %}
  155. {%- endif %}
  156. {#- Some configuration return list #}
  157. {%- if yaml_names is string %}
  158. {%- set yaml_names = [yaml_names] %}
  159. {%- endif %}
  160. {#- Try to load a `.yaml.jinja` file for each `.yaml` file #}
  161. {%- set all_yaml_names = [] %}
  162. {%- for name in yaml_names %}
  163. {%- set extension = name.rpartition(".")[2] %}
  164. {%- if extension not in ["yaml", "jinja"] %}
  165. {%- do all_yaml_names.extend([name ~ ".yaml", name ~ ".yaml.jinja"]) %}
  166. {%- elif extension == "yaml" %}
  167. {%- do all_yaml_names.extend([name, name ~ ".jinja"]) %}
  168. {%- else %}
  169. {%- do all_yaml_names.append(name) %}
  170. {%- endif %}
  171. {%- endfor %}
  172. {#- `yaml_dirname` can be an empty string with literal path like `myconf.yaml` #}
  173. {%- set yaml_dir = [
  174. param_dir,
  175. yaml_dirname
  176. ]
  177. | select
  178. | join("/") %}
  179. {%- for yaml_name in all_yaml_names %}
  180. {%- set yaml_filename = [
  181. yaml_dir.rstrip("/"),
  182. yaml_name
  183. ]
  184. | select
  185. | join("/") %}
  186. {%- do salt["log.debug"](
  187. log_prefix
  188. ~ "load configuration values from "
  189. ~ yaml_filename
  190. ) %}
  191. {%- load_yaml as yaml_values %}
  192. {%- include yaml_filename ignore missing %}
  193. {%- endload %}
  194. {%- if yaml_values %}
  195. {%- do salt["log.debug"](
  196. log_prefix
  197. ~ "loaded configuration values from "
  198. ~ yaml_filename
  199. ~ ":\n"
  200. ~ yaml_values
  201. | yaml(False)
  202. ) %}
  203. {#- `slsutil.merge` options from #}
  204. {#- 1. the `value` #}
  205. {#- 2. the `defaults` #}
  206. {#- 3. the built-in #}
  207. {%- set strategy = yaml_values
  208. | traverse(
  209. "strategy",
  210. defaults
  211. | traverse(
  212. "strategy",
  213. "smart"
  214. )
  215. ) %}
  216. {%- set merge_lists = yaml_values
  217. | traverse(
  218. "merge_lists",
  219. defaults
  220. | traverse(
  221. "merge_lists",
  222. False
  223. )
  224. )
  225. | to_bool %}
  226. {%- do stack.update(
  227. {
  228. "values": salt["slsutil.merge"](
  229. stack["values"],
  230. yaml_values
  231. | traverse("values", {}),
  232. strategy=strategy,
  233. merge_lists=merge_lists,
  234. )
  235. }
  236. ) %}
  237. {%- do salt["log.debug"](
  238. log_prefix
  239. ~ "merged configuration values from "
  240. ~ yaml_filename
  241. ~ ", merge: strategy='"
  242. ~ strategy
  243. ~ "', merge_lists='"
  244. ~ merge_lists
  245. ~ "':\n"
  246. ~ {"values": stack["values"]}
  247. | yaml(False)
  248. ) %}
  249. {%- endif %}
  250. {%- endfor %}
  251. {%- endif %}
  252. {%- endfor %}
  253. {%- endfor %}
  254. {%- do salt["log.debug"](
  255. log_prefix
  256. ~ "final configuration values:\n"
  257. ~ {"values": stack["values"]}
  258. | yaml(False)
  259. ) %}
  260. {#- Output stack as YAML, caller should use with something like #}
  261. {#- `{%- set config = mapstack(matchers=["foo"]) | load_yaml %}` #}
  262. {{ stack | yaml }}
  263. {%- endmacro %}