mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge pull request #46145 from terminalmage/issue46004
3 small fixes for runners/orchestration
This commit is contained in:
commit
d74cb14557
7 changed files with 79 additions and 36 deletions
|
@ -5,10 +5,10 @@ Orchestrate Runner
|
|||
==================
|
||||
|
||||
Executing states or highstate on a minion is perfect when you want to ensure that
|
||||
minion configured and running the way you want. Sometimes however you want to
|
||||
minion configured and running the way you want. Sometimes however you want to
|
||||
configure a set of minions all at once.
|
||||
|
||||
For example, if you want to set up a load balancer in front of a cluster of web
|
||||
For example, if you want to set up a load balancer in front of a cluster of web
|
||||
servers you can ensure the load balancer is set up first, and then the same
|
||||
matching configuration is applied consistently across the whole cluster.
|
||||
|
||||
|
@ -266,7 +266,35 @@ Given the above setup, the orchestration will be carried out as follows:
|
|||
3. Finally, the ``ceph`` SLS target will be executed on all minions which have
|
||||
a grain called ``role`` with a value of ``storage``.
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
Remember, salt-run is always executed on the master.
|
||||
Remember, salt-run is *always* executed on the master.
|
||||
|
||||
|
||||
Running States on the Master without a Minion
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The orchestrate runner can be used to execute states on the master without
|
||||
using a minion. For example, assume that ``salt://foo.sls`` contains the
|
||||
following SLS:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
/etc/foo.conf:
|
||||
file.managed:
|
||||
- source: salt://files/foo.conf
|
||||
- mode: 0600
|
||||
|
||||
In this case, running ``salt-run state.orchestrate foo`` would be the
|
||||
equivalent of running a ``state.sls foo``, but it would execute on the master
|
||||
only, and would not require a minion daemon to be running on the master.
|
||||
|
||||
This is not technically orchestration, but it can be useful in certain use
|
||||
cases.
|
||||
|
||||
Limitations
|
||||
^^^^^^^^^^^
|
||||
|
||||
Only one SLS target can be run at a time using this method, while using
|
||||
:py:func:`state.sls <salt.modules.state.sls>` allows for multiple SLS files to
|
||||
be passed in a comma-separated list.
|
||||
|
|
|
@ -229,7 +229,7 @@ class Runner(RunnerClient):
|
|||
async_pub = self._gen_async_pub()
|
||||
self.jid = async_pub['jid']
|
||||
|
||||
if low['fun'] == 'state.orchestrate':
|
||||
if low['fun'] in ('state.orchestrate', 'state.orch'):
|
||||
low['kwarg']['orchestration_jid'] = async_pub['jid']
|
||||
|
||||
# Run the runner!
|
||||
|
|
|
@ -82,19 +82,17 @@ def show_pillar(minion='*', **kwargs):
|
|||
pillar = runner.cmd('pillar.show_pillar', [])
|
||||
print(pillar)
|
||||
'''
|
||||
|
||||
pillarenv = None
|
||||
saltenv = 'base'
|
||||
pillarenv = __opts__['pillarenv'] if 'pillarenv' in __opts__ else None
|
||||
id_, grains, _ = salt.utils.minions.get_minion_data(minion, __opts__)
|
||||
if grains is None:
|
||||
grains = {'fqdn': minion}
|
||||
|
||||
for key in kwargs:
|
||||
if key == 'pillarenv':
|
||||
__opts__['pillarenv'] = kwargs[key]
|
||||
if key == 'saltenv':
|
||||
saltenv = kwargs[key]
|
||||
elif key == 'pillarenv':
|
||||
# pillarenv overridden on CLI
|
||||
pillarenv = kwargs[key]
|
||||
else:
|
||||
grains[key] = kwargs[key]
|
||||
|
|
|
@ -1262,7 +1262,7 @@ class State(object):
|
|||
'''
|
||||
err = []
|
||||
for chunk in chunks:
|
||||
err += self.verify_data(chunk)
|
||||
err.extend(self.verify_data(chunk))
|
||||
return err
|
||||
|
||||
def order_chunks(self, chunks):
|
||||
|
@ -2551,12 +2551,12 @@ class State(object):
|
|||
errors = []
|
||||
# If there is extension data reconcile it
|
||||
high, ext_errors = self.reconcile_extend(high)
|
||||
errors += ext_errors
|
||||
errors += self.verify_high(high)
|
||||
errors.extend(ext_errors)
|
||||
errors.extend(self.verify_high(high))
|
||||
if errors:
|
||||
return errors
|
||||
high, req_in_errors = self.requisite_in(high)
|
||||
errors += req_in_errors
|
||||
errors.extend(req_in_errors)
|
||||
high = self.apply_exclude(high)
|
||||
# Verify that the high data is structurally sound
|
||||
if errors:
|
||||
|
|
|
@ -63,9 +63,11 @@ class SaltCacheLoader(BaseLoader):
|
|||
else:
|
||||
self.searchpath = [path.join(opts['cachedir'], 'files', saltenv)]
|
||||
log.debug('Jinja search path: %s', self.searchpath)
|
||||
self._file_client = None
|
||||
self.cached = []
|
||||
self.pillar_rend = pillar_rend
|
||||
self._file_client = None
|
||||
# Instantiate the fileclient
|
||||
self.file_client()
|
||||
|
||||
def file_client(self):
|
||||
'''
|
||||
|
|
|
@ -11,7 +11,7 @@ import textwrap
|
|||
from tests.support.mixins import LoaderModuleMockMixin
|
||||
from tests.support.paths import TMP
|
||||
from tests.support.unit import TestCase, skipIf
|
||||
from tests.support.mock import MagicMock, patch
|
||||
from tests.support.mock import Mock, MagicMock, patch
|
||||
|
||||
# Import Salt libs
|
||||
import salt.utils
|
||||
|
@ -19,6 +19,7 @@ import salt.modules.file as filemod
|
|||
import salt.modules.config as configmod
|
||||
import salt.modules.cmdmod as cmdmod
|
||||
from salt.exceptions import CommandExecutionError
|
||||
from salt.utils.jinja import SaltCacheLoader
|
||||
|
||||
SED_CONTENT = '''test
|
||||
some
|
||||
|
@ -704,12 +705,13 @@ class FileModuleTestCase(TestCase, LoaderModuleMockMixin):
|
|||
'''
|
||||
contents = 'This is a {{ template }}.'
|
||||
defaults = {'template': 'templated file'}
|
||||
ret = filemod.apply_template_on_contents(
|
||||
contents,
|
||||
template='jinja',
|
||||
context={'opts': filemod.__opts__},
|
||||
defaults=defaults,
|
||||
saltenv='base')
|
||||
with patch.object(SaltCacheLoader, 'file_client', Mock()):
|
||||
ret = filemod.apply_template_on_contents(
|
||||
contents,
|
||||
template='jinja',
|
||||
context={'opts': filemod.__opts__},
|
||||
defaults=defaults,
|
||||
saltenv='base')
|
||||
self.assertEqual(ret, 'This is a templated file.')
|
||||
|
||||
def test_replace_line_in_empty_file(self):
|
||||
|
|
|
@ -14,7 +14,7 @@ import re
|
|||
# Import Salt Testing libs
|
||||
from tests.support.unit import skipIf, TestCase
|
||||
from tests.support.case import ModuleCase
|
||||
from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch, MagicMock
|
||||
from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch, MagicMock, Mock
|
||||
from tests.support.paths import TMP_CONF_DIR
|
||||
|
||||
# Import salt libs
|
||||
|
@ -65,7 +65,7 @@ class MockFileClient(object):
|
|||
|
||||
class TestSaltCacheLoader(TestCase):
|
||||
def __init__(self, *args, **kws):
|
||||
TestCase.__init__(self, *args, **kws)
|
||||
super(TestSaltCacheLoader, self).__init__(*args, **kws)
|
||||
self.opts = {
|
||||
'cachedir': TEMPLATES_DIR,
|
||||
'file_roots': {
|
||||
|
@ -83,7 +83,7 @@ class TestSaltCacheLoader(TestCase):
|
|||
tmp = tempfile.gettempdir()
|
||||
opts = copy.deepcopy(self.opts)
|
||||
opts.update({'cachedir': tmp})
|
||||
loader = SaltCacheLoader(opts, saltenv='test')
|
||||
loader = self.get_loader(opts=opts, saltenv='test')
|
||||
assert loader.searchpath == [os.path.join(tmp, 'files', 'test')]
|
||||
|
||||
def test_mockclient(self):
|
||||
|
@ -91,8 +91,7 @@ class TestSaltCacheLoader(TestCase):
|
|||
A MockFileClient is used that records all file requests normally sent
|
||||
to the master.
|
||||
'''
|
||||
loader = SaltCacheLoader(self.opts, 'test')
|
||||
fc = MockFileClient(loader)
|
||||
loader = self.get_loader(opts=self.opts, saltenv='test')
|
||||
res = loader.get_source(None, 'hello_simple')
|
||||
assert len(res) == 3
|
||||
# res[0] on Windows is unicode and use os.linesep so it works cross OS
|
||||
|
@ -100,17 +99,28 @@ class TestSaltCacheLoader(TestCase):
|
|||
tmpl_dir = os.path.join(TEMPLATES_DIR, 'files', 'test', 'hello_simple')
|
||||
self.assertEqual(res[1], tmpl_dir)
|
||||
assert res[2](), 'Template up to date?'
|
||||
assert len(fc.requests)
|
||||
self.assertEqual(fc.requests[0]['path'], 'salt://hello_simple')
|
||||
assert len(loader._file_client.requests)
|
||||
self.assertEqual(loader._file_client.requests[0]['path'], 'salt://hello_simple')
|
||||
|
||||
def get_loader(self, opts=None, saltenv='base'):
|
||||
'''
|
||||
Now that we instantiate the client in the __init__, we need to mock it
|
||||
'''
|
||||
if opts is None:
|
||||
opts = self.opts
|
||||
with patch.object(SaltCacheLoader, 'file_client', Mock()):
|
||||
loader = SaltCacheLoader(opts, saltenv)
|
||||
# Create a mock file client and attach it to the loader
|
||||
MockFileClient(loader)
|
||||
return loader
|
||||
|
||||
def get_test_saltenv(self):
|
||||
'''
|
||||
Setup a simple jinja test environment
|
||||
'''
|
||||
loader = SaltCacheLoader(self.opts, 'test')
|
||||
fc = MockFileClient(loader)
|
||||
loader = self.get_loader(saltenv='test')
|
||||
jinja = Environment(loader=loader)
|
||||
return fc, jinja
|
||||
return loader._file_client, jinja
|
||||
|
||||
def test_import(self):
|
||||
'''
|
||||
|
@ -146,7 +156,7 @@ class TestSaltCacheLoader(TestCase):
|
|||
|
||||
class TestGetTemplate(TestCase):
|
||||
def __init__(self, *args, **kws):
|
||||
TestCase.__init__(self, *args, **kws)
|
||||
super(TestGetTemplate, self).__init__(*args, **kws)
|
||||
self.local_opts = {
|
||||
'cachedir': TEMPLATES_DIR,
|
||||
'file_client': 'local',
|
||||
|
@ -446,7 +456,7 @@ class TestGetTemplate(TestCase):
|
|||
class TestCustomExtensions(TestCase):
|
||||
|
||||
def __init__(self, *args, **kws):
|
||||
TestCase.__init__(self, *args, **kws)
|
||||
super(TestCustomExtensions, self).__init__(*args, **kws)
|
||||
self.local_opts = {
|
||||
'cachedir': TEMPLATES_DIR,
|
||||
'file_client': 'local',
|
||||
|
@ -1066,7 +1076,8 @@ class TestDotNotationLookup(ModuleCase):
|
|||
'''
|
||||
tmpl_str = '''Hello, {{ salt['mocktest.ping']() }}.'''
|
||||
|
||||
ret = self.render(tmpl_str)
|
||||
with patch.object(SaltCacheLoader, 'file_client', Mock()):
|
||||
ret = self.render(tmpl_str)
|
||||
self.assertEqual(ret, 'Hello, True.')
|
||||
|
||||
def test_dotlookup(self):
|
||||
|
@ -1075,7 +1086,8 @@ class TestDotNotationLookup(ModuleCase):
|
|||
'''
|
||||
tmpl_str = '''Hello, {{ salt.mocktest.ping() }}.'''
|
||||
|
||||
ret = self.render(tmpl_str)
|
||||
with patch.object(SaltCacheLoader, 'file_client', Mock()):
|
||||
ret = self.render(tmpl_str)
|
||||
self.assertEqual(ret, 'Hello, True.')
|
||||
|
||||
def test_shadowed_dict_method(self):
|
||||
|
@ -1085,5 +1097,6 @@ class TestDotNotationLookup(ModuleCase):
|
|||
'''
|
||||
tmpl_str = '''Hello, {{ salt.mockgrains.get('id') }}.'''
|
||||
|
||||
ret = self.render(tmpl_str)
|
||||
with patch.object(SaltCacheLoader, 'file_client', Mock()):
|
||||
ret = self.render(tmpl_str)
|
||||
self.assertEqual(ret, 'Hello, jerry.')
|
||||
|
|
Loading…
Add table
Reference in a new issue