mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge pull request #39736 from techhat/spmstates
Add pre and post states to SPM
This commit is contained in:
commit
2d711782e8
3 changed files with 167 additions and 2 deletions
|
@ -146,6 +146,105 @@ The first 5 of these types (``c``, ``d``, ``g``, ``l``, ``r``) will be placed in
|
|||
The last two types (``s`` and ``m``) are currently ignored, but they are
|
||||
reserved for future use.
|
||||
|
||||
Pre and Post States
|
||||
-------------------
|
||||
It is possible to run Salt states before and after installing a package by
|
||||
using pre and post states. The following sections may be declared in a
|
||||
``FORMULA``:
|
||||
|
||||
* ``pre_local_state``
|
||||
* ``pre_tgt_state``
|
||||
* ``post_local_state``
|
||||
* ``post_tgt_state``
|
||||
|
||||
Sections with ``pre`` in their name are evaluated before a package is installed
|
||||
and sections with ``post`` are evaluated after a package is installed. ``local``
|
||||
states are evaluated before ``tgt`` states.
|
||||
|
||||
Each of these sections needs to be evaluated as text, rather than as YAML.
|
||||
Consider the following block:
|
||||
|
||||
.. code-block::
|
||||
|
||||
pre_local_state: >
|
||||
echo test > /tmp/spmtest:
|
||||
cmd:
|
||||
- run
|
||||
|
||||
Note that this declaration uses ``>`` after ``pre_local_state``. This is a YAML
|
||||
marker that marks the next multi-line block as text, including newlines. It is
|
||||
important to use this marker whenever declaring ``pre`` or ``post`` states, so
|
||||
that the text following it can be evaluated properly.
|
||||
|
||||
local States
|
||||
~~~~~~~~~~~~
|
||||
``local`` states are evaluated locally; this is analagous to issuing a state
|
||||
run using a ``salt-call --local`` command. These commands will be issued on the
|
||||
local machine running the ``spm`` command, whether that machine is a master or
|
||||
a minion.
|
||||
|
||||
``local`` states do not require any special arguments, but they must still use
|
||||
the ``>`` marker to denote that the state is evaluated as text, not a data
|
||||
structure.
|
||||
|
||||
.. code-block::
|
||||
|
||||
pre_local_state: >
|
||||
echo test > /tmp/spmtest:
|
||||
cmd:
|
||||
- run
|
||||
|
||||
tgt States
|
||||
~~~~~~~~~~
|
||||
``tgt`` states are issued against a remote target. This is analogous to issuing
|
||||
a state using the ``salt`` command. As such it requires that the machine that
|
||||
the ``spm`` command is running on is a master.
|
||||
|
||||
Because ``tgt`` states require that a target be specified, their code blocks
|
||||
are a little different. Consider the following state:
|
||||
|
||||
.. code-block::
|
||||
|
||||
pre_tgt_state:
|
||||
tgt: '*'
|
||||
data: >
|
||||
echo test > /tmp/spmtest:
|
||||
cmd:
|
||||
- run
|
||||
|
||||
With ``tgt`` states, the state data is placed under a ``data`` section, inside
|
||||
the ``*_tgt_state`` code block. The target is of course specified as a ``tgt``
|
||||
and you may also optionally specify a ``tgt_type`` (the default is ``glob``).
|
||||
|
||||
You still need to use the ``>`` marker, but this time it follows the ``data``
|
||||
line, rather than the ``*_tgt_state`` line.
|
||||
|
||||
Templating States
|
||||
~~~~~~~~~~~~~~~~~
|
||||
The reason that state data must be evaluated as text rather than a data
|
||||
structure is because that state data is first processed through the rendering
|
||||
engine, as it would be with a standard state run.
|
||||
|
||||
This means that you can use Jinja or any other supported renderer inside of
|
||||
Salt. All formula variables are available to the renderer, so you can reference
|
||||
``FORMULA`` data inside your state if you need to:
|
||||
|
||||
.. code-block::
|
||||
|
||||
pre_tgt_state:
|
||||
tgt: '*'
|
||||
data: >
|
||||
echo {{ name }} > /tmp/spmtest:
|
||||
cmd:
|
||||
- run
|
||||
|
||||
You may also declare your own variables inside the ``FORMULA``. If SPM doesn't
|
||||
recognize them then it will ignore them, so there are no restrictions on
|
||||
variable names, outside of avoiding reserved words.
|
||||
|
||||
By default the renderer is set to ``yaml_jinja``. You may change this by
|
||||
changing the ``renderer`` setting in the ``FORMULA`` itself.
|
||||
|
||||
Building a Package
|
||||
------------------
|
||||
Once a ``FORMULA`` file has been created, it is placed into the root of the
|
||||
|
|
|
@ -20,6 +20,7 @@ import grp
|
|||
import sys
|
||||
|
||||
# Import Salt libs
|
||||
import salt.client
|
||||
import salt.config
|
||||
import salt.loader
|
||||
import salt.cache
|
||||
|
@ -31,6 +32,7 @@ from salt.ext.six import string_types
|
|||
from salt.ext.six.moves import input
|
||||
from salt.ext.six.moves import zip
|
||||
from salt.ext.six.moves import filter
|
||||
from salt.template import compile_template
|
||||
|
||||
# Get logging started
|
||||
log = logging.getLogger(__name__)
|
||||
|
@ -229,6 +231,10 @@ class SPMClient(object):
|
|||
if len(args) < 2:
|
||||
raise SPMInvocationError('A package must be specified')
|
||||
|
||||
caller_opts = self.opts.copy()
|
||||
caller_opts['file_client'] = 'local'
|
||||
self.caller = salt.client.Caller(mopts=caller_opts)
|
||||
self.client = salt.client.get_local_client(self.opts['conf_file'])
|
||||
cache = salt.cache.Cache(self.opts)
|
||||
|
||||
packages = args[1:]
|
||||
|
@ -468,6 +474,22 @@ class SPMClient(object):
|
|||
# We've decided to install
|
||||
self._pkgdb_fun('register_pkg', pkg_name, formula_def, self.db_conn)
|
||||
|
||||
# Run the pre_local_state script, if present
|
||||
if 'pre_local_state' in formula_def:
|
||||
high_data = self._render(formula_def['pre_local_state'], formula_def)
|
||||
ret = self.caller.cmd('state.high', data=high_data)
|
||||
if 'pre_tgt_state' in formula_def:
|
||||
log.debug('Executing pre_tgt_state script')
|
||||
high_data = self._render(formula_def['pre_tgt_state']['data'], formula_def)
|
||||
tgt = formula_def['pre_tgt_state']['tgt']
|
||||
ret = self.client.run_job(
|
||||
tgt=formula_def['pre_tgt_state']['tgt'],
|
||||
fun='state.high',
|
||||
tgt_type=formula_def['pre_tgt_state'].get('tgt_type', 'glob'),
|
||||
timout=self.opts['timeout'],
|
||||
data=high_data,
|
||||
)
|
||||
|
||||
# No defaults for this in config.py; default to the current running
|
||||
# user and group
|
||||
uid = self.opts.get('spm_uid', os.getuid())
|
||||
|
@ -505,6 +527,23 @@ class SPMClient(object):
|
|||
digest,
|
||||
self.db_conn)
|
||||
|
||||
# Run the post_local_state script, if present
|
||||
if 'post_local_state' in formula_def:
|
||||
log.debug('Executing post_local_state script')
|
||||
high_data = self._render(formula_def['post_local_state'], formula_def)
|
||||
self.caller.cmd('state.high', data=high_data)
|
||||
if 'post_tgt_state' in formula_def:
|
||||
log.debug('Executing post_tgt_state script')
|
||||
high_data = self._render(formula_def['post_tgt_state']['data'], formula_def)
|
||||
tgt = formula_def['post_tgt_state']['tgt']
|
||||
ret = self.client.run_job(
|
||||
tgt=formula_def['post_tgt_state']['tgt'],
|
||||
fun='state.high',
|
||||
tgt_type=formula_def['post_tgt_state'].get('tgt_type', 'glob'),
|
||||
timout=self.opts['timeout'],
|
||||
data=high_data,
|
||||
)
|
||||
|
||||
formula_tar.close()
|
||||
|
||||
def _resolve_deps(self, formula_def):
|
||||
|
@ -998,6 +1037,27 @@ class SPMClient(object):
|
|||
return None
|
||||
return member
|
||||
|
||||
def _render(self, data, formula_def):
|
||||
'''
|
||||
Render a [pre|post]_local_state or [pre|post]_tgt_state script
|
||||
'''
|
||||
# FORMULA can contain a renderer option
|
||||
renderer = formula_def.get('renderer', self.opts.get('renderer', 'yaml_jinja'))
|
||||
rend = salt.loader.render(self.opts, {})
|
||||
blacklist = self.opts.get('renderer_blacklist')
|
||||
whitelist = self.opts.get('renderer_whitelist')
|
||||
template_vars = formula_def.copy()
|
||||
template_vars['opts'] = self.opts.copy()
|
||||
return compile_template(
|
||||
':string:',
|
||||
rend,
|
||||
renderer,
|
||||
blacklist,
|
||||
whitelist,
|
||||
input_data=data,
|
||||
**template_vars
|
||||
)
|
||||
|
||||
|
||||
class SPMUserInterface(object):
|
||||
'''
|
||||
|
|
|
@ -19,7 +19,9 @@ config = salt.config.minion_config(None)
|
|||
config['file_roots'] = {'base': [os.path.join(_TMP_SPM, 'salt')]}
|
||||
config['pillar_roots'] = {'base': [os.path.join(_TMP_SPM, 'pillar')]}
|
||||
|
||||
__opts__ = {
|
||||
__opts__ = salt.config.DEFAULT_MINION_OPTS
|
||||
|
||||
__opts__.update({
|
||||
'spm_logfile': os.path.join(_TMP_SPM, 'log'),
|
||||
'spm_repos_config': os.path.join(_TMP_SPM, 'etc', 'spm.repos'),
|
||||
'spm_cache_dir': os.path.join(_TMP_SPM, 'cache'),
|
||||
|
@ -37,9 +39,10 @@ __opts__ = {
|
|||
'force': False,
|
||||
'verbose': False,
|
||||
'cache': 'localfs',
|
||||
'cachedir': os.path.join(_TMP_SPM, 'cache'),
|
||||
'spm_repo_dups': 'ignore',
|
||||
'spm_share_dir': os.path.join(_TMP_SPM, 'share'),
|
||||
}
|
||||
})
|
||||
|
||||
_F1 = {
|
||||
'definition': {
|
||||
|
@ -90,6 +93,9 @@ class SPMTest(TestCase):
|
|||
os.mkdir(_TMP_SPM)
|
||||
self.ui = SPMTestUserInterface()
|
||||
self.client = salt.spm.SPMClient(self.ui, __opts__)
|
||||
master_cache = salt.config.DEFAULT_MASTER_OPTS['cachedir']
|
||||
if not os.path.exists(master_cache):
|
||||
os.makedirs(master_cache)
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(_TMP_SPM, ignore_errors=True)
|
||||
|
|
Loading…
Add table
Reference in a new issue