Saltstack statefiles

From wikinotes
Revision as of 21:38, 20 March 2021 by Will (talk | contribs) (→‎universal state parameters (requisites))
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

States are where you define a set of instructions to apply to a machine.

Structure

Basics

For a state to be callable, it must exist on your salt-master configuration's file_roots. States are written in YAML.


Statefiles are structured similarly to the pillar, but work a slightly differently. Unlike the pillar, all hosts have access to all of it’s saltmaster’s statefiles. You can manually run any state file using the command state.apply (ex: sudo salt-call state.apply progs.editors.vim).


Adding a state-file to states/top.sls makes that state a part of that hostname’s highstate. A system’s highstate is the default configuration for that system. You can apply a system’s highstate (installing/updating all programs, and configurations) using the command: sudo salt-call state.highstate .


Finally, inspired by python’s package-structure, statefiles called init.sls located within a directory are referred to as the directory’s name. This is useful if you want to bundle extra configuration files alongside it’s statefile.

states/progs/mystate.sls         #  both considered equivalent, and called using:
states/progs/mystate/init.sls    #     state.apply  progs.mystate


name description location
state-files .sls files under the states subdirectory are YAML files that let you define variables, and their values. {project}/states/*.sls
states/top.sls A special sls file that contains hostnames, and under each the path to a state-file. Adding a state-file to a hostname makes it a part of that hostname's highstate. Unlike the pillar, all statefiles are accessible to all machines using state.apply. {project}/pillar/top.sls


top.sls

base:
    'dev-* or beta-*':
        - match: compound
        - progs.editors.vim

    'os_family:(Arch|FreeBSD|Debian)':
        - match: grain_pcre
        - progs.editors.vim

    '* not os_family(MacOS)':
        - match: grain_pcre

    # regex match against minion
    '^{{grains['id]}}(-w\w+)*$':
        - match: pcre
        - hosts.{{grains['id']}}

statefile format

Every salt statefile is written using the following pattern:

#!stateconf

.description of task:
    Class.method:
        - param1:
        - param2:
        # ...

.description of another task:
    Class.method:
        - param1: ...

including states

States can include other states (calling them if they haven't been called yet):

#!stateconf

include:
    - progs.vim
    - progs.git

templating, and dynamic logic

States can also use the jinja2 templating engine to perform simple logic like loops.

#!stateconf

{% set personal_users = ['will', 'alex'] %}
{% for user in personal_users %}
./home/{{user}}/.bashrc:
    file.managed:
        - source: salt://progs/shell/bash/files/bashrc
        - user:   {{user}}
        - group:  {{user}}
        - mode:   644
{% endfor %}

Salt exposes the pillar, and salt-modules this way.

#!stateconf

# pillar
{% set password = pillar['passwd']['will']['unix'] %}

# grains
{% set fam = grains['os_family'] %}

# salt.modules.*
{% if not salt.file.file_exists( '/home/will' ) %}
# ... do something ...
{% endif %}

universal state parameters (requisites)

Some state arguments can be applied to ALL states. Here is a list of the params I use the most frequently:

#!stateconf

.your state description:
   your.state_module:
      - name:      # usually the filepath, package-name, or line-to-be-run on commandline
      - unless:    # if this shell command succeeds, do not run the state
      - onlyif:    # if this shell command succeeds, run state, otherwise do not
      - watch:
        - file: /etc/nginx/nginx.conf  # run when this file changes

See Also: https://docs.saltstack.com/en/latest/ref/states/requisites.html



applying states

States are applied either as a part of the system-highstate, or individually.

salt-call state.highstate                  # apply all states in highstate
salt-call state.apply  progs.shell.bash    # apply only {file_roots}/progs/shell/bash/init.sls

recipes

Here are some patterns that I find myself using quite a lot

iterate homedirs

NOTE:

I have also encountered the use of '%h' which expands to a user's homedirectory. I need to look into the semantics of how this works, and what other types of paths this might work for.

{% set home_root = pillar['system']['home_root'] %}   # 'C:/Users', '/home', '/Users'

{% for user in salt.user.list_users() %}
    {% for homedir in [ home_root + user ] %}
        {% if salt.file.directory_exists( homedir ) %}

.your description:
   your.state:
      -name: blah

        {% endif %}
    {% endfor %}
{% endfor %}