Merge pull request #46149 from rallytime/bp-46145

Back-port #46145 to oxygen.rc1
This commit is contained in:
Nicole Thomas 2018-02-22 16:57:51 -05:00 committed by GitHub
commit e6374bf68d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 78 additions and 35 deletions

View file

@ -332,10 +332,9 @@ 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.
.. _orchestrate-runner-parsing-results-programatically:
@ -603,3 +602,32 @@ succeeded. In addition, ``salt.function`` jobs which failed because the
:ref:`fail function <orchestrate-runner-fail-functions>` returned ``False``
used to handle their failures in the same way ``salt.state`` jobs did, and this
has likewise been corrected.
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

@ -232,7 +232,7 @@ class Runner(RunnerClient):
else:
user = salt.utils.user.get_specific_user()
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

@ -1245,7 +1245,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):
@ -2684,12 +2684,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

@ -67,9 +67,11 @@ class SaltCacheLoader(BaseLoader):
else:
self.searchpath = [os.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, mock_open
from tests.support.mock import MagicMock, Mock, patch, mock_open
try:
import pytest
@ -19,6 +19,7 @@ except ImportError:
pytest = None
# Import Salt libs
from salt.ext import six
import salt.config
import salt.loader
import salt.utils.files
@ -28,7 +29,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.ext import six
from salt.utils.jinja import SaltCacheLoader
SED_CONTENT = '''test
some
@ -754,12 +755,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.')

View file

@ -14,7 +14,7 @@ import tempfile
# 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
@ -71,7 +71,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': {
@ -89,7 +89,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):
@ -97,8 +97,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
@ -106,17 +105,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):
'''
@ -152,7 +162,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',
@ -514,7 +524,7 @@ class TestJinjaDefaultOptions(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',
@ -1202,7 +1212,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):
@ -1211,7 +1222,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):
@ -1221,5 +1233,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.')