1
0

formulas.rst 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354
  1. .. _conventions-formula:
  2. =============
  3. Salt Formulas
  4. =============
  5. Formulas are pre-written Salt States. They are as open-ended as Salt States
  6. themselves and can be used for tasks such as installing a package, configuring,
  7. and starting a service, setting up users or permissions, and many other common
  8. tasks.
  9. All official Salt Formulas are found as separate Git repositories in the
  10. "saltstack-formulas" organization on GitHub:
  11. https://github.com/saltstack-formulas
  12. As a simple example, to install the popular Apache web server (using the normal
  13. defaults for the underlying distro) simply include the
  14. :formula_url:`apache-formula` from a top file:
  15. .. code-block:: yaml
  16. base:
  17. 'web*':
  18. - apache
  19. Installation
  20. ============
  21. Each Salt Formula is an individual Git repository designed as a drop-in
  22. addition to an existing Salt State tree. Formulas can be installed in the
  23. following ways.
  24. Adding a Formula as a GitFS remote
  25. ----------------------------------
  26. One design goal of Salt's GitFS fileserver backend was to facilitate reusable
  27. States. GitFS is a quick and natural way to use Formulas.
  28. 1. :ref:`Install any necessary dependencies and configure GitFS
  29. <tutorial-gitfs>`.
  30. 2. Add one or more Formula repository URLs as remotes in the
  31. :conf_master:`gitfs_remotes` list in the Salt Master configuration file:
  32. .. code-block:: yaml
  33. gitfs_remotes:
  34. - https://github.com/saltstack-formulas/apache-formula
  35. - https://github.com/saltstack-formulas/memcached-formula
  36. **We strongly recommend forking a formula repository** into your own GitHub
  37. account to avoid unexpected changes to your infrastructure.
  38. Many Salt Formulas are highly active repositories so pull new changes with
  39. care. Plus any additions you make to your fork can be easily sent back
  40. upstream with a quick pull request!
  41. 3. Restart the Salt master.
  42. Beginning with the 2018.3.0 release, using formulas with GitFS is now much more
  43. convenient for deployments which use many different fileserver environments
  44. (i.e. saltenvs). Using the :ref:`all_saltenvs <gitfs-global-remotes>`
  45. parameter, files from a single git branch/tag will appear in all environments.
  46. See :ref:`here <gitfs-global-remotes>` for more information on this feature.
  47. Adding a Formula directory manually
  48. -----------------------------------
  49. Formulas are simply directories that can be copied onto the local file system
  50. by using Git to clone the repository or by downloading and expanding a tarball
  51. or zip file of the repository. The directory structure is designed to work with
  52. :conf_master:`file_roots` in the Salt master configuration.
  53. 1. Clone or download the repository into a directory:
  54. .. code-block:: bash
  55. mkdir -p /srv/formulas
  56. cd /srv/formulas
  57. git clone https://github.com/saltstack-formulas/apache-formula.git
  58. # or
  59. mkdir -p /srv/formulas
  60. cd /srv/formulas
  61. wget https://github.com/saltstack-formulas/apache-formula/archive/master.tar.gz
  62. tar xf apache-formula-master.tar.gz
  63. 2. Add the new directory to :conf_master:`file_roots`:
  64. .. code-block:: yaml
  65. file_roots:
  66. base:
  67. - /srv/salt
  68. - /srv/formulas/apache-formula
  69. 3. Restart the Salt Master.
  70. Usage
  71. =====
  72. Each Formula is intended to be immediately usable with sane defaults without
  73. any additional configuration. Many formulas are also configurable by including
  74. data in Pillar; see the :file:`pillar.example` file in each Formula repository
  75. for available options.
  76. Including a Formula in an existing State tree
  77. ---------------------------------------------
  78. Formula may be included in an existing ``sls`` file. This is often useful when
  79. a state you are writing needs to ``require`` or ``extend`` a state defined in
  80. the formula.
  81. Here is an example of a state that uses the :formula_url:`epel-formula` in a
  82. ``require`` declaration which directs Salt to not install the ``python26``
  83. package until after the EPEL repository has also been installed:
  84. .. code-block:: yaml
  85. include:
  86. - epel
  87. python26:
  88. pkg.installed:
  89. - require:
  90. - pkg: epel
  91. Including a Formula from a Top File
  92. -----------------------------------
  93. Some Formula perform completely standalone installations that are not
  94. referenced from other state files. It is usually cleanest to include these
  95. Formula directly from a Top File.
  96. For example the easiest way to set up an OpenStack deployment on a single
  97. machine is to include the :formula_url:`openstack-standalone-formula` directly from
  98. a :file:`top.sls` file:
  99. .. code-block:: yaml
  100. base:
  101. 'myopenstackmaster':
  102. - openstack
  103. Quickly deploying OpenStack across several dedicated machines could also be
  104. done directly from a Top File and may look something like this:
  105. .. code-block:: yaml
  106. base:
  107. 'controller':
  108. - openstack.horizon
  109. - openstack.keystone
  110. 'hyper-*':
  111. - openstack.nova
  112. - openstack.glance
  113. 'storage-*':
  114. - openstack.swift
  115. Configuring Formula using Pillar
  116. --------------------------------
  117. Salt Formulas are designed to work out of the box with no additional
  118. configuration. However, many Formula support additional configuration and
  119. customization through :ref:`Pillar <pillar>`. Examples of available options can
  120. be found in a file named :file:`pillar.example` in the root directory of each
  121. Formula repository.
  122. .. _extending-formulas:
  123. Using Formula with your own states
  124. ----------------------------------
  125. Remember that Formula are regular Salt States and can be used with all Salt's
  126. normal state mechanisms. Formula can be required from other States with
  127. :ref:`requisites-require` declarations, they can be modified using ``extend``,
  128. they can made to watch other states with :ref:`requisites-watch-in`.
  129. The following example uses the stock :formula_url:`apache-formula` alongside a
  130. custom state to create a vhost on a Debian/Ubuntu system and to reload the
  131. Apache service whenever the vhost is changed.
  132. .. code-block:: yaml
  133. # Include the stock, upstream apache formula.
  134. include:
  135. - apache
  136. # Use the watch_in requisite to cause the apache service state to reload
  137. # apache whenever the my-example-com-vhost state changes.
  138. my-example-com-vhost:
  139. file:
  140. - managed
  141. - name: /etc/apache2/sites-available/my-example-com
  142. - watch_in:
  143. - service: apache
  144. Don't be shy to read through the source for each Formula!
  145. Reporting problems & making additions
  146. -------------------------------------
  147. Each Formula is a separate repository on GitHub. If you encounter a bug with a
  148. Formula please file an issue in the respective repository! Send fixes and
  149. additions as a pull request. Add tips and tricks to the repository wiki.
  150. Writing Formulas
  151. ================
  152. Each Formula is a separate repository in the `saltstack-formulas`_ organization
  153. on GitHub.
  154. Get involved creating new Formulas
  155. ----------------------------------
  156. The best way to create new Formula repositories for now is to create a
  157. repository in your own account on GitHub and notify a SaltStack employee when
  158. it is ready. We will add you to the Contributors team on the
  159. `saltstack-formulas`_ organization and help you transfer the repository over.
  160. Ping a SaltStack employee on IRC (``#salt`` on Freenode), join the
  161. ``#formulas`` channel on the `salt-slack`_ or send an email to the
  162. `salt-users`_ mailing list.
  163. There are a lot of repositories in that organization! Team members can manage
  164. which repositories they are subscribed to on GitHub's watching page:
  165. https://github.com/watching.
  166. Members of the Contributors team are welcome to participate in reviewing pull
  167. requests across the Organization. Some repositories will have regular
  168. contributors and some repositories will not. As you get involved in a
  169. repository be sure to communicate with any other contributors there on pull
  170. requests that are large or have breaking changes.
  171. In general it is best to have another Contributor review and merge any pull
  172. requests that you open. Feel free to `at-mention`_ other regular contributors
  173. to a repository and request a review. However, there are a lot of formula
  174. repositories so if a repository does not yet have regular contributors or if
  175. your pull request has stayed open for more than a couple days feel free to
  176. "selfie-merge" your own pull request.
  177. .. _`at-mention`: https://help.github.com/articles/basic-writing-and-formatting-syntax/#mentioning-users-and-teams
  178. Style
  179. -----
  180. Maintainability, readability, and reusability are all marks of a good Salt sls
  181. file. This section contains several suggestions and examples.
  182. .. code-block:: jinja
  183. # Deploy the stable master branch unless version overridden by passing
  184. # Pillar at the CLI or via the Reactor.
  185. deploy_myapp:
  186. git.latest:
  187. - name: git@github.com/myco/myapp.git
  188. - version: {{ salt.pillar.get('myapp:version', 'master') }}
  189. Use a descriptive State ID
  190. ``````````````````````````
  191. The ID of a state is used as a unique identifier that may be referenced via
  192. other states in :ref:`requisites <requisites>`. It must be unique across the
  193. whole state tree (:ref:`it is a key in a dictionary <id-declaration>`, after
  194. all).
  195. In addition a state ID should be descriptive and serve as a high-level hint of
  196. what it will do, or manage, or change. For example, ``deploy_webapp``, or
  197. ``apache``, or ``reload_firewall``.
  198. Use ``module.function`` notation
  199. ````````````````````````````````
  200. So-called "short-declaration" notation is preferred for referencing state
  201. modules and state functions. It provides a consistent pattern of
  202. ``module.function`` shared between Salt States, the Reactor, Salt
  203. Mine, the Scheduler, as well as with the CLI.
  204. .. code-block:: yaml
  205. # Do
  206. apache:
  207. pkg.installed:
  208. - name: httpd
  209. # Don't
  210. apache:
  211. pkg:
  212. - installed
  213. - name: httpd
  214. Salt's state compiler will transform "short-decs" into the longer format
  215. :ref:`when compiling the human-friendly highstate structure into the
  216. machine-friendly lowstate structure <state-layers>`.
  217. Specify the ``name`` parameter
  218. ``````````````````````````````
  219. Use a unique and permanent identifier for the state ID and reserve ``name`` for
  220. data with variability.
  221. The :ref:`name declaration <name-declaration>` is a required parameter for all
  222. state functions. The state ID will implicitly be used as ``name`` if it is not
  223. explicitly set in the state.
  224. In many state functions the ``name`` parameter is used for data that varies
  225. such as OS-specific package names, OS-specific file system paths, repository
  226. addresses, etc. Any time the ID of a state changes all references to that ID
  227. must also be changed. Use a permanent ID when writing a state the first time to
  228. future-proof that state and allow for easier refactors down the road.
  229. Comment state files
  230. ```````````````````
  231. YAML allows comments at varying indentation levels. It is a good practice to
  232. comment state files. Use vertical whitespace to visually separate different
  233. concepts or actions.
  234. .. code-block:: yaml
  235. # Start with a high-level description of the current sls file.
  236. # Explain the scope of what it will do or manage.
  237. # Comment individual states as necessary.
  238. update_a_config_file:
  239. # Provide details on why an unusual choice was made. For example:
  240. #
  241. # This template is fetched from a third-party and does not fit our
  242. # company norm of using Jinja. This must be processed using Mako.
  243. file.managed:
  244. - name: /path/to/file.cfg
  245. - source: salt://path/to/file.cfg.template
  246. - template: mako
  247. # Provide a description or explanation that did not fit within the state
  248. # ID. For example:
  249. #
  250. # Update the application's last-deployed timestamp.
  251. # This is a workaround until Bob configures Jenkins to automate RPM
  252. # builds of the app.
  253. cmd.run:
  254. # FIXME: Joe needs this to run on Windows by next quarter. Switch these
  255. # from shell commands to Salt's file.managed and file.replace state
  256. # modules.
  257. - name: |
  258. touch /path/to/file_last_updated
  259. sed -e 's/foo/bar/g' /path/to/file_environment
  260. - onchanges:
  261. - file: a_config_file
  262. Be careful to use Jinja comments for commenting Jinja code and YAML comments
  263. for commenting YAML code.
  264. .. code-block:: jinja
  265. # BAD EXAMPLE
  266. # The Jinja in this YAML comment is still executed!
  267. # {% set apache_is_installed = 'apache' in salt.pkg.list_pkgs() %}
  268. # GOOD EXAMPLE
  269. # The Jinja in this Jinja comment will not be executed.
  270. {# {% set apache_is_installed = 'apache' in salt.pkg.list_pkgs() %} #}
  271. Easy on the Jinja!
  272. ------------------
  273. Jinja templating provides vast flexibility and power when building Salt sls
  274. files. It can also create an unmaintainable tangle of logic and data. Speaking
  275. broadly, Jinja is best used when kept apart from the states (as much as is
  276. possible).
  277. Below are guidelines and examples of how Jinja can be used effectively.
  278. Know the evaluation and execution order
  279. ```````````````````````````````````````
  280. High-level knowledge of how Salt states are compiled and run is useful when
  281. writing states.
  282. The default :conf_minion:`renderer` setting in Salt is Jinja piped to YAML.
  283. Each is a separate step. Each step is not aware of the previous or following
  284. step. Jinja is not YAML aware, YAML is not Jinja aware; they cannot share
  285. variables or interact.
  286. * Whatever the Jinja step produces must be valid YAML.
  287. * Whatever the YAML step produces must be a valid :ref:`highstate data
  288. structure <states-highstate-example>`. (This is also true of the final step
  289. for :ref:`any of the alternate renderers <all-salt.renderers>` in Salt.)
  290. * Highstate can be thought of as a human-friendly data structure; easy to write
  291. and easy to read.
  292. * Salt's state compiler validates the :ref:`highstate <running-highstate>` and
  293. compiles it to low state.
  294. * Low state can be thought of as a machine-friendly data structure. It is a
  295. list of dictionaries that each map directly to a function call.
  296. * Salt's state system finally starts and executes on each "chunk" in the low
  297. state. Remember that requisites are evaluated at runtime.
  298. * The return for each function call is added to the "running" dictionary which
  299. is the final output at the end of the state run.
  300. The full evaluation and execution order::
  301. Jinja -> YAML -> Highstate -> low state -> execution
  302. Avoid changing the underlying system with Jinja
  303. ```````````````````````````````````````````````
  304. Avoid calling commands from Jinja that change the underlying system. Commands
  305. run via Jinja do not respect Salt's dry-run mode (``test=True``)! This is
  306. usually in conflict with the idempotent nature of Salt states unless the
  307. command being run is also idempotent.
  308. Inspect the local system
  309. ````````````````````````
  310. A common use for Jinja in Salt states is to gather information about the
  311. underlying system. The ``grains`` dictionary available in the Jinja context is
  312. a great example of common data points that Salt itself has already gathered.
  313. Less common values are often found by running commands. For example:
  314. .. code-block:: jinja
  315. {% set is_selinux_enabled = salt.cmd.run('sestatus') == '1' %}
  316. This is usually best done with a variable assignment in order to separate the
  317. data from the state that will make use of the data.
  318. Gather external data
  319. ````````````````````
  320. One of the most common uses for Jinja is to pull external data into the state
  321. file. External data can come from anywhere like API calls or database queries,
  322. but it most commonly comes from flat files on the file system or Pillar data
  323. from the Salt Master. For example:
  324. .. code-block:: jinja
  325. {% set some_data = salt.pillar.get('some_data', {'sane default': True}) %}
  326. {# or #}
  327. {% import_yaml 'path/to/file.yaml' as some_data %}
  328. {# or #}
  329. {% import_json 'path/to/file.json' as some_data %}
  330. {# or #}
  331. {% import_text 'path/to/ssh_key.pub' as ssh_pub_key %}
  332. {# or #}
  333. {% from 'path/to/other_file.jinja' import some_data with context %}
  334. This is usually best done with a variable assignment in order to separate the
  335. data from the state that will make use of the data.
  336. Light conditionals and looping
  337. ``````````````````````````````
  338. Jinja is extremely powerful for programmatically generating Salt states. It is
  339. also easy to overuse. As a rule of thumb, if it is hard to read it will be hard
  340. to maintain!
  341. Separate Jinja control-flow statements from the states as much as is possible
  342. to create readable states. Limit Jinja within states to simple variable
  343. lookups.
  344. Below is a simple example of a readable loop:
  345. .. code-block:: jinja
  346. {% for user in salt.pillar.get('list_of_users', []) %}
  347. {# Ensure unique state IDs when looping. #}
  348. {{ user.name }}-{{ loop.index }}:
  349. user.present:
  350. - name: {{ user.name }}
  351. - shell: {{ user.shell }}
  352. {% endfor %}
  353. Avoid putting a Jinja conditionals within Salt states where possible.
  354. Readability suffers and the correct YAML indentation is difficult to see in the
  355. surrounding visual noise. Parametrization (discussed below) and variables are
  356. both useful techniques to avoid this. For example:
  357. .. code-block:: jinja
  358. {# ---- Bad example ---- #}
  359. apache:
  360. pkg.installed:
  361. {% if grains.os_family == 'RedHat' %}
  362. - name: httpd
  363. {% elif grains.os_family == 'Debian' %}
  364. - name: apache2
  365. {% endif %}
  366. {# ---- Better example ---- #}
  367. {% if grains.os_family == 'RedHat' %}
  368. {% set name = 'httpd' %}
  369. {% elif grains.os_family == 'Debian' %}
  370. {% set name = 'apache2' %}
  371. {% endif %}
  372. apache:
  373. pkg.installed:
  374. - name: {{ name }}
  375. {# ---- Good example ---- #}
  376. {% set name = {
  377. 'RedHat': 'httpd',
  378. 'Debian': 'apache2',
  379. }.get(grains.os_family) %}
  380. apache:
  381. pkg.installed:
  382. - name: {{ name }}
  383. Dictionaries are useful to effectively "namespace" a collection of variables.
  384. This is useful with parametrization (discussed below). Dictionaries are also
  385. easily combined and merged. And they can be directly serialized into YAML which
  386. is often easier than trying to create valid YAML through templating. For
  387. example:
  388. .. code-block:: jinja
  389. {# ---- Bad example ---- #}
  390. haproxy_conf:
  391. file.managed:
  392. - name: /etc/haproxy/haproxy.cfg
  393. - template: jinja
  394. {% if 'external_loadbalancer' in grains.roles %}
  395. - source: salt://haproxy/external_haproxy.cfg
  396. {% elif 'internal_loadbalancer' in grains.roles %}
  397. - source: salt://haproxy/internal_haproxy.cfg
  398. {% endif %}
  399. - context:
  400. {% if 'external_loadbalancer' in grains.roles %}
  401. ssl_termination: True
  402. {% elif 'internal_loadbalancer' in grains.roles %}
  403. ssl_termination: False
  404. {% endif %}
  405. {# ---- Better example ---- #}
  406. {% load_yaml as haproxy_defaults %}
  407. common_settings:
  408. bind_port: 80
  409. internal_loadbalancer:
  410. source: salt://haproxy/internal_haproxy.cfg
  411. settings:
  412. bind_port: 8080
  413. ssl_termination: False
  414. external_loadbalancer:
  415. source: salt://haproxy/external_haproxy.cfg
  416. settings:
  417. ssl_termination: True
  418. {% endload %}
  419. {% if 'external_loadbalancer' in grains.roles %}
  420. {% set haproxy = haproxy_defaults['external_loadbalancer'] %}
  421. {% elif 'internal_loadbalancer' in grains.roles %}
  422. {% set haproxy = haproxy_defaults['internal_loadbalancer'] %}
  423. {% endif %}
  424. {% do haproxy.settings.update(haproxy_defaults.common_settings) %}
  425. haproxy_conf:
  426. file.managed:
  427. - name: /etc/haproxy/haproxy.cfg
  428. - template: jinja
  429. - source: {{ haproxy.source }}
  430. - context: {{ haproxy.settings | yaml() }}
  431. There is still room for improvement in the above example. For example,
  432. extracting into an external file or replacing the if-elif conditional with a
  433. function call to filter the correct data more succinctly. However, the state
  434. itself is simple and legible, the data is separate and also simple and legible.
  435. And those suggested improvements can be made at some future date without
  436. altering the state at all!
  437. Avoid heavy logic and programming
  438. `````````````````````````````````
  439. Jinja is not Python. It was made by Python programmers and shares many
  440. semantics and some syntax but it does not allow for abitrary Python function
  441. calls or Python imports. Jinja is a fast and efficient templating language but
  442. the syntax can be verbose and visually noisy.
  443. Once Jinja use within an sls file becomes slightly complicated -- long chains
  444. of if-elif-elif-else statements, nested conditionals, complicated dictionary
  445. merges, wanting to use sets -- instead consider using a different Salt
  446. renderer, such as the Python renderer. As a rule of thumb, if it is hard to
  447. read it will be hard to maintain -- switch to a format that is easier to read.
  448. Using alternate renderers is very simple to do using Salt's "she-bang" syntax
  449. at the top of the file. The Python renderer must simply return the correct
  450. :ref:`highstate data structure <states-highstate-example>`. The following
  451. example is a state tree of two sls files, one simple and one complicated.
  452. ``/srv/salt/top.sls``:
  453. .. code-block:: yaml
  454. base:
  455. '*':
  456. - common_configuration
  457. - roles_configuration
  458. ``/srv/salt/common_configuration.sls``:
  459. .. code-block:: yaml
  460. common_users:
  461. user.present:
  462. - names:
  463. - larry
  464. - curly
  465. - moe
  466. ``/srv/salt/roles_configuration``:
  467. .. code-block:: python
  468. #!py
  469. def run():
  470. list_of_roles = set()
  471. # This example has the minion id in the form 'web-03-dev'.
  472. # Easily access the grains dictionary:
  473. try:
  474. app, instance_number, environment = __grains__['id'].split('-')
  475. instance_number = int(instance_number)
  476. except ValueError:
  477. app, instance_number, environment = ['Unknown', 0, 'dev']
  478. list_of_roles.add(app)
  479. if app == 'web' and environment == 'dev':
  480. list_of_roles.add('primary')
  481. list_of_roles.add('secondary')
  482. elif app == 'web' and environment == 'staging':
  483. if instance_number == 0:
  484. list_of_roles.add('primary')
  485. else:
  486. list_of_roles.add('secondary')
  487. # Easily cross-call Salt execution modules:
  488. if __salt__['myutils.query_valid_ec2_instance']():
  489. list_of_roles.add('is_ec2_instance')
  490. return {
  491. 'set_roles_grains': {
  492. 'grains.present': [
  493. {'name': 'roles'},
  494. {'value': list(list_of_roles)},
  495. ],
  496. },
  497. }
  498. Jinja Macros
  499. ````````````
  500. In Salt sls files Jinja macros are useful for one thing and one thing only:
  501. creating mini templates that can be reused and rendered on demand. Do not fall
  502. into the trap of thinking of macros as functions; Jinja is not Python (see
  503. above).
  504. Macros are useful for creating reusable, parameterized states. For example:
  505. .. code-block:: jinja
  506. {% macro user_state(state_id, user_name, shell='/bin/bash', groups=[]) %}
  507. {{ state_id }}:
  508. user.present:
  509. - name: {{ user_name }}
  510. - shell: {{ shell }}
  511. - groups: {{ groups | json() }}
  512. {% endmacro %}
  513. {% for user_info in salt.pillar.get('my_users', []) %}
  514. {{ user_state('user_number_' ~ loop.index, **user_info) }}
  515. {% endfor %}
  516. Macros are also useful for creating one-off "serializers" that can accept a
  517. data structure and write that out as a domain-specific configuration file. For
  518. example, the following macro could be used to write a php.ini config file:
  519. ``/srv/salt/php.sls``:
  520. .. code-block:: jinja
  521. php_ini:
  522. file.managed:
  523. - name: /etc/php.ini
  524. - source: salt://php.ini.tmpl
  525. - template: jinja
  526. - context:
  527. php_ini_settings: {{ salt.pillar.get('php_ini', {}) | json() }}
  528. ``/srv/pillar/php.sls``:
  529. .. code-block:: yaml
  530. php_ini:
  531. PHP:
  532. engine: 'On'
  533. short_open_tag: 'Off'
  534. error_reporting: 'E_ALL & ~E_DEPRECATED & ~E_STRICT'
  535. ``/srv/salt/php.ini.tmpl``:
  536. .. code-block:: jinja
  537. {% macro php_ini_serializer(data) %}
  538. {% for section_name, name_val_pairs in data.items() %}
  539. [{{ section_name }}]
  540. {% for name, val in name_val_pairs.items() -%}
  541. {{ name }} = "{{ val }}"
  542. {% endfor %}
  543. {% endfor %}
  544. {% endmacro %}
  545. ; File managed by Salt at <{{ source }}>.
  546. ; Your changes will be overwritten.
  547. {{ php_ini_serializer(php_ini_settings) }}
  548. Abstracting static defaults into a lookup table
  549. -----------------------------------------------
  550. Separate data that a state uses from the state itself to increases the
  551. flexibility and reusability of a state.
  552. An obvious and common example of this is platform-specific package names and
  553. file system paths. Another example is sane defaults for an application, or
  554. common settings within a company or organization. Organizing such data as a
  555. dictionary (aka hash map, lookup table, associative array) often provides a
  556. lightweight namespacing and allows for quick and easy lookups. In addition,
  557. using a dictionary allows for easily merging and overriding static values
  558. within a lookup table with dynamic values fetched from Pillar.
  559. A strong convention in Salt Formulas is to place platform-specific data, such
  560. as package names and file system paths, into a file named :file:`map.jinja`
  561. that is placed alongside the state files.
  562. The following is an example from the MySQL Formula.
  563. The :py:func:`grains.filter_by <salt.modules.grains.filter_by>` function
  564. performs a lookup on that table using the ``os_family`` grain (by default).
  565. The result is that the ``mysql`` variable is assigned to a *subset* of
  566. the lookup table for the current platform. This allows states to reference, for
  567. example, the name of a package without worrying about the underlying OS. The
  568. syntax for referencing a value is a normal dictionary lookup in Jinja, such as
  569. ``{{ mysql['service'] }}`` or the shorthand ``{{ mysql.service }}``.
  570. :file:`map.jinja`:
  571. .. code-block:: jinja
  572. {% set mysql = salt['grains.filter_by']({
  573. 'Debian': {
  574. 'server': 'mysql-server',
  575. 'client': 'mysql-client',
  576. 'service': 'mysql',
  577. 'config': '/etc/mysql/my.cnf',
  578. 'python': 'python-mysqldb',
  579. },
  580. 'RedHat': {
  581. 'server': 'mysql-server',
  582. 'client': 'mysql',
  583. 'service': 'mysqld',
  584. 'config': '/etc/my.cnf',
  585. 'python': 'MySQL-python',
  586. },
  587. 'Gentoo': {
  588. 'server': 'dev-db/mysql',
  589. 'client': 'dev-db/mysql',
  590. 'service': 'mysql',
  591. 'config': '/etc/mysql/my.cnf',
  592. 'python': 'dev-python/mysql-python',
  593. },
  594. }, merge=salt['pillar.get']('mysql:lookup')) %}
  595. Values defined in the map file can be fetched for the current platform in any
  596. state file using the following syntax:
  597. .. code-block:: jinja
  598. {% from "mysql/map.jinja" import mysql with context %}
  599. mysql-server:
  600. pkg.installed:
  601. - name: {{ mysql.server }}
  602. service.running:
  603. - name: {{ mysql.service }}
  604. Organizing Pillar data
  605. ``````````````````````
  606. It is considered a best practice to make formulas expect **all**
  607. formula-related parameters to be placed under second-level ``lookup`` key,
  608. within a main namespace designated for holding data for particular
  609. service/software/etc, managed by the formula:
  610. .. code-block:: yaml
  611. mysql:
  612. lookup:
  613. version: 5.7.11
  614. Collecting common values
  615. ````````````````````````
  616. Common values can be collected into a *base* dictionary. This
  617. minimizes repetition of identical values in each of the
  618. ``lookup_dict`` sub-dictionaries. Now only the values that are
  619. different from the base must be specified by the alternates:
  620. :file:`map.jinja`:
  621. .. code-block:: jinja
  622. {% set mysql = salt['grains.filter_by']({
  623. 'default': {
  624. 'server': 'mysql-server',
  625. 'client': 'mysql-client',
  626. 'service': 'mysql',
  627. 'config': '/etc/mysql/my.cnf',
  628. 'python': 'python-mysqldb',
  629. },
  630. 'Debian': {
  631. },
  632. 'RedHat': {
  633. 'client': 'mysql',
  634. 'service': 'mysqld',
  635. 'config': '/etc/my.cnf',
  636. 'python': 'MySQL-python',
  637. },
  638. 'Gentoo': {
  639. 'server': 'dev-db/mysql',
  640. 'client': 'dev-db/mysql',
  641. 'python': 'dev-python/mysql-python',
  642. },
  643. },
  644. merge=salt['pillar.get']('mysql:lookup'), base='default') %}
  645. Overriding values in the lookup table
  646. `````````````````````````````````````
  647. Allow static values within lookup tables to be overridden. This is a simple
  648. pattern which once again increases flexibility and reusability for state files.
  649. The ``merge`` argument in :py:func:`filter_by <salt.modules.grains.filter_by>`
  650. specifies the location of a dictionary in Pillar that can be used to override
  651. values returned from the lookup table. If the value exists in Pillar it will
  652. take precedence.
  653. This is useful when software or configuration files is installed to
  654. non-standard locations or on unsupported platforms. For example, the following
  655. Pillar would replace the ``config`` value from the call above.
  656. .. code-block:: yaml
  657. mysql:
  658. lookup:
  659. config: /usr/local/etc/mysql/my.cnf
  660. .. note:: Protecting Expansion of Content with Special Characters
  661. When templating keep in mind that YAML does have special characters for
  662. quoting, flows, and other special structure and content. When a Jinja
  663. substitution may have special characters that will be incorrectly parsed by
  664. YAML care must be taken. It is a good policy to use the ``yaml_encode`` or
  665. the ``yaml_dquote`` Jinja filters:
  666. .. code-block:: jinja
  667. {%- set foo = 7.7 %}
  668. {%- set bar = none %}
  669. {%- set baz = true %}
  670. {%- set zap = 'The word of the day is "salty".' %}
  671. {%- set zip = '"The quick brown fox . . ."' %}
  672. foo: {{ foo|yaml_encode }}
  673. bar: {{ bar|yaml_encode }}
  674. baz: {{ baz|yaml_encode }}
  675. zap: {{ zap|yaml_encode }}
  676. zip: {{ zip|yaml_dquote }}
  677. The above will be rendered as below:
  678. .. code-block:: yaml
  679. foo: 7.7
  680. bar: null
  681. baz: true
  682. zap: "The word of the day is \"salty\"."
  683. zip: "\"The quick brown fox . . .\""
  684. The :py:func:`filter_by <salt.modules.grains.filter_by>` function performs a
  685. simple dictionary lookup but also allows for fetching data from Pillar and
  686. overriding data stored in the lookup table. That same workflow can be easily
  687. performed without using ``filter_by``; other dictionaries besides data from
  688. Pillar can also be used.
  689. .. code-block:: jinja
  690. {% set lookup_table = {...} %}
  691. {% do lookup_table.update(salt.pillar.get('my:custom:data')) %}
  692. When to use lookup tables
  693. `````````````````````````
  694. The ``map.jinja`` file is only a convention within Salt Formulas. This greater
  695. pattern is useful for a wide variety of data in a wide variety of workflows.
  696. This pattern is not limited to pulling data from a single file or data source.
  697. This pattern is useful in States, Pillar and the Reactor, for example.
  698. Working with a data structure instead of, say, a config file allows the data to
  699. be cobbled together from multiple sources (local files, remote Pillar, database
  700. queries, etc), combined, overridden, and searched.
  701. Below are a few examples of what lookup tables may be useful for and how they
  702. may be used and represented.
  703. Platform-specific information
  704. .............................
  705. An obvious pattern and one used heavily in Salt Formulas is extracting
  706. platform-specific information such as package names and file system paths in
  707. a file named ``map.jinja``. The pattern is explained in detail above.
  708. Sane defaults
  709. .............
  710. Application settings can be a good fit for this pattern. Store default
  711. settings along with the states themselves and keep overrides and sensitive
  712. settings in Pillar. Combine both into a single dictionary and then write the
  713. application config or settings file.
  714. The example below stores most of the Apache Tomcat ``server.xml`` file
  715. alongside the Tomcat states and then allows values to be updated or augmented
  716. via Pillar. (This example uses the BadgerFish format for transforming JSON to
  717. XML.)
  718. ``/srv/salt/tomcat/defaults.yaml``:
  719. .. code-block:: yaml
  720. Server:
  721. '@port': '8005'
  722. '@shutdown': SHUTDOWN
  723. GlobalNamingResources:
  724. Resource:
  725. '@auth': Container
  726. '@description': User database that can be updated and saved
  727. '@factory': org.apache.catalina.users.MemoryUserDatabaseFactory
  728. '@name': UserDatabase
  729. '@pathname': conf/tomcat-users.xml
  730. '@type': org.apache.catalina.UserDatabase
  731. # <...snip...>
  732. ``/srv/pillar/tomcat.sls``:
  733. .. code-block:: yaml
  734. appX:
  735. server_xml_overrides:
  736. Server:
  737. Service:
  738. '@name': Catalina
  739. Connector:
  740. '@port': '8009'
  741. '@protocol': AJP/1.3
  742. '@redirectPort': '8443'
  743. # <...snip...>
  744. ``/srv/salt/tomcat/server_xml.sls``:
  745. .. code-block:: jinja
  746. {% import_yaml 'tomcat/defaults.yaml' as server_xml_defaults %}
  747. {% set server_xml_final_values = salt.pillar.get(
  748. 'appX:server_xml_overrides',
  749. default=server_xml_defaults,
  750. merge=True)
  751. %}
  752. appX_server_xml:
  753. file.serialize:
  754. - name: /etc/tomcat/server.xml
  755. - dataset: {{ server_xml_final_values | json() }}
  756. - formatter: xml_badgerfish
  757. The :py:func:`file.serialize <salt.states.file.serialize>` state can provide a
  758. shorthand for creating some files from data structures. There are also many
  759. examples within Salt Formulas of creating one-off "serializers" (often as Jinja
  760. macros) that reformat a data structure to a specific config file format. For
  761. example, look at the`Nginx vhosts`_ states or the `php.ini`_ file template.
  762. .. _`Nginx vhosts`: https://github.com/saltstack-formulas/nginx-formula/blob/5cad4512/nginx/ng/vhosts_config.sls
  763. .. _`php.ini`: https://github.com/saltstack-formulas/php-formula/blob/82e2cd3a/php/ng/files/php.ini
  764. Environment specific information
  765. ................................
  766. A single state can be reused when it is parameterized as described in the
  767. section below, by separating the data the state will use from the state that
  768. performs the work. This can be the difference between deploying *Application X*
  769. and *Application Y*, or the difference between production and development. For
  770. example:
  771. ``/srv/salt/app/deploy.sls``:
  772. .. code-block:: jinja
  773. {# Load the map file. #}
  774. {% import_yaml 'app/defaults.yaml' as app_defaults %}
  775. {# Extract the relevant subset for the app configured on the current
  776. machine (configured via a grain in this example). #}
  777. {% app = app_defaults.get(salt.grains.get('role')) %}
  778. {# Allow values from Pillar to (optionally) update values from the lookup
  779. table. #}
  780. {% do app_defaults.update(salt.pillar.get('myapp', {})) %}
  781. deploy_application:
  782. git.latest:
  783. - name: {{ app.repo_url }}
  784. - version: {{ app.version }}
  785. - target: {{ app.deploy_dir }}
  786. myco/myapp/deployed:
  787. event.send:
  788. - data:
  789. version: {{ app.version }}
  790. - onchanges:
  791. - git: deploy_application
  792. ``/srv/salt/app/defaults.yaml``:
  793. .. code-block:: yaml
  794. appX:
  795. repo_url: git@github.com/myco/appX.git
  796. target: /var/www/appX
  797. version: master
  798. appY:
  799. repo_url: git@github.com/myco/appY.git
  800. target: /var/www/appY
  801. version: v1.2.3.4
  802. Single-purpose SLS files
  803. ------------------------
  804. Each sls file in a Formula should strive to do a single thing. This increases
  805. the reusability of this file by keeping unrelated tasks from getting coupled
  806. together.
  807. As an example, the base Apache formula should only install the Apache httpd
  808. server and start the httpd service. This is the basic, expected behavior when
  809. installing Apache. It should not perform additional changes such as set the
  810. Apache configuration file or create vhosts.
  811. If a formula is single-purpose as in the example above, other formulas, and
  812. also other states can ``include`` and use that formula with :ref:`requisites`
  813. without also including undesirable or unintended side-effects.
  814. The following is a best-practice example for a reusable Apache formula. (This
  815. skips platform-specific options for brevity. See the full
  816. :formula_url:`apache-formula` for more.)
  817. .. code-block:: text
  818. # apache/init.sls
  819. apache:
  820. pkg.installed:
  821. [...]
  822. service.running:
  823. [...]
  824. # apache/mod_wsgi.sls
  825. include:
  826. - apache
  827. mod_wsgi:
  828. pkg.installed:
  829. [...]
  830. - require:
  831. - pkg: apache
  832. # apache/conf.sls
  833. include:
  834. - apache
  835. apache_conf:
  836. file.managed:
  837. [...]
  838. - watch_in:
  839. - service: apache
  840. To illustrate a bad example, say the above Apache formula installed Apache and
  841. also created a default vhost. The mod_wsgi state would not be able to include
  842. the Apache formula to create that dependency tree without also installing the
  843. unneeded default vhost.
  844. :ref:`Formulas should be reusable <extending-formulas>`. Avoid coupling
  845. unrelated actions together.
  846. .. _conventions-formula-parameterization:
  847. Parameterization
  848. ----------------
  849. *Parameterization is a key feature of Salt Formulas* and also for Salt
  850. States. Parameterization allows a single Formula to be reused across many
  851. operating systems; to be reused across production, development, or staging
  852. environments; and to be reused by many people all with varying goals.
  853. Writing states, specifying ordering and dependencies is the part that takes the
  854. longest to write and to test. Filling those states out with data such as users
  855. or package names or file locations is the easy part. How many users, what those
  856. users are named, or where the files live are all implementation details that
  857. **should be parameterized**. This separation between a state and the data that
  858. populates a state creates a reusable formula.
  859. In the example below the data that populates the state can come from anywhere
  860. -- it can be hard-coded at the top of the state, it can come from an external
  861. file, it can come from Pillar, it can come from an execution function call, or
  862. it can come from a database query. The state itself doesn't change regardless
  863. of where the data comes from. Production data will vary from development data
  864. will vary from data from one company to another, however the state itself stays
  865. the same.
  866. .. code-block:: jinja
  867. {% set user_list = [
  868. {'name': 'larry', 'shell': 'bash'},
  869. {'name': 'curly', 'shell': 'bash'},
  870. {'name': 'moe', 'shell': 'zsh'},
  871. ] %}
  872. {# or #}
  873. {% set user_list = salt['pillar.get']('user_list') %}
  874. {# or #}
  875. {% load_json "default_users.json" as user_list %}
  876. {# or #}
  877. {% set user_list = salt['acme_utils.get_user_list']() %}
  878. {% for user in list_list %}
  879. {{ user.name }}:
  880. user.present:
  881. - name: {{ user.name }}
  882. - shell: {{ user.shell }}
  883. {% endfor %}
  884. Configuration
  885. -------------
  886. Formulas should strive to use the defaults of the underlying platform, followed
  887. by defaults from the upstream project, followed by sane defaults for the
  888. formula itself.
  889. As an example, a formula to install Apache **should not** change the default
  890. Apache configuration file installed by the OS package. However, the Apache
  891. formula **should** include a state to change or override the default
  892. configuration file.
  893. Pillar overrides
  894. ----------------
  895. Pillar lookups must use the safe :py:func:`~salt.modules.pillar.get`
  896. and must provide a default value. Create local variables using the Jinja
  897. ``set`` construct to increase readability and to avoid potentially hundreds or
  898. thousands of function calls across a large state tree.
  899. .. code-block:: jinja
  900. {% from "apache/map.jinja" import apache with context %}
  901. {% set settings = salt['pillar.get']('apache', {}) %}
  902. mod_status:
  903. file.managed:
  904. - name: {{ apache.conf_dir }}
  905. - source: {{ settings.get('mod_status_conf', 'salt://apache/mod_status.conf') }}
  906. - template: {{ settings.get('template_engine', 'jinja') }}
  907. Any default values used in the Formula must also be documented in the
  908. :file:`pillar.example` file in the root of the repository. Comments should be
  909. used liberally to explain the intent of each configuration value. In addition,
  910. users should be able copy-and-paste the contents of this file into their own
  911. Pillar to make any desired changes.
  912. Scripting
  913. ---------
  914. Remember that both State files and Pillar files can easily call out to Salt
  915. :ref:`execution modules <all-salt.modules>` and have access to all the system
  916. grains as well.
  917. .. code-block:: jinja
  918. {% if '/storage' in salt['mount.active']() %}
  919. /usr/local/etc/myfile.conf:
  920. file:
  921. - symlink
  922. - target: /storage/myfile.conf
  923. {% endif %}
  924. Jinja macros to encapsulate logic or conditionals are discouraged in favor of
  925. :ref:`writing custom execution modules <writing-execution-modules>` in Python.
  926. Repository structure
  927. ====================
  928. A basic Formula repository should have the following layout:
  929. .. code-block:: text
  930. foo-formula
  931. |-- foo/
  932. | |-- map.jinja
  933. | |-- init.sls
  934. | `-- bar.sls
  935. |-- CHANGELOG.rst
  936. |-- LICENSE
  937. |-- pillar.example
  938. |-- README.rst
  939. `-- VERSION
  940. .. seealso:: :formula_url:`template-formula`
  941. The :formula_url:`template-formula` repository has a pre-built layout that
  942. serves as the basic structure for a new formula repository. Just copy the
  943. files from there and edit them.
  944. ``README.rst``
  945. --------------
  946. The README should detail each available ``.sls`` file by explaining what it
  947. does, whether it has any dependencies on other formulas, whether it has a
  948. target platform, and any other installation or usage instructions or tips.
  949. A sample skeleton for the ``README.rst`` file:
  950. .. code-block:: restructuredtext
  951. ===
  952. foo
  953. ===
  954. Install and configure the FOO service.
  955. **NOTE**
  956. See the full `Salt Formulas installation and usage instructions
  957. <https://docs.saltstack.com/en/latest/topics/development/conventions/formulas.html>`_.
  958. Available states
  959. ================
  960. .. contents::
  961. :local:
  962. ``foo``
  963. -------
  964. Install the ``foo`` package and enable the service.
  965. ``foo.bar``
  966. -----------
  967. Install the ``bar`` package.
  968. ``CHANGELOG.rst``
  969. -----------------
  970. The ``CHANGELOG.rst`` file should detail the individual versions, their
  971. release date and a set of bullet points for each version highlighting the
  972. overall changes in a given version of the formula.
  973. A sample skeleton for the `CHANGELOG.rst` file:
  974. :file:`CHANGELOG.rst`:
  975. .. code-block:: restructuredtext
  976. foo formula
  977. ===========
  978. 0.0.2 (2013-01-01)
  979. - Re-organized formula file layout
  980. - Fixed filename used for upstart logger template
  981. - Allow for pillar message to have default if none specified
  982. Versioning
  983. ----------
  984. Formula are versioned according to Semantic Versioning, http://semver.org/.
  985. .. note::
  986. Given a version number MAJOR.MINOR.PATCH, increment the:
  987. #. MAJOR version when you make incompatible API changes,
  988. #. MINOR version when you add functionality in a backwards-compatible manner, and
  989. #. PATCH version when you make backwards-compatible bug fixes.
  990. Additional labels for pre-release and build metadata are available as extensions
  991. to the MAJOR.MINOR.PATCH format.
  992. Formula versions are tracked using Git tags as well as the ``VERSION`` file
  993. in the formula repository. The ``VERSION`` file should contain the currently
  994. released version of the particular formula.
  995. Testing Formulas
  996. ================
  997. A smoke-test for invalid Jinja, invalid YAML, or an invalid Salt state
  998. structure can be performed by with the :py:func:`state.show_sls
  999. <salt.modules.state.show_sls>` function:
  1000. .. code-block:: bash
  1001. salt '*' state.show_sls apache
  1002. Salt Formulas can then be tested by running each ``.sls`` file via
  1003. :py:func:`state.apply <salt.modules.state.apply_>` and checking the output for
  1004. the success or failure of each state in the Formula. This should be done for
  1005. each supported platform.
  1006. .. ............................................................................
  1007. .. _`saltstack-formulas`: https://github.com/saltstack-formulas