1
0

formulas.rst 44 KB

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