Merge pull request #46145 from terminalmage/issue46004

3 small fixes for runners/orchestration
This commit is contained in:
Nicole Thomas 2018-02-22 17:11:10 -05:00 committed by GitHub
commit d74cb14557
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 79 additions and 36 deletions

View file

@ -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.

View file

@ -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!

View file

@ -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]

View file

@ -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:

View file

@ -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):
'''

View file

@ -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):

View file

@ -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.')