Skip to content

Commit

Permalink
Merge pull request #53307 from max-arnold/slot-args
Browse files Browse the repository at this point in the history
Fix slot parsing in unless/onlyif function arguments
  • Loading branch information
dwoz committed Dec 28, 2019
2 parents 0b56d3a + 0c36b95 commit bd57aa6
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 8 deletions.
20 changes: 20 additions & 0 deletions doc/topics/releases/neon.rst
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,26 @@ The slot syntax has been updated to support parsing dictionary responses and to
Duration: 1.229 ms
Changes:
Also, slot parsing is now supported inside of nested state data structures (dicts, lists, unless/onlyif args):

.. code-block:: yaml
demo slot parsing for nested elements:
file.managed:
- name: /tmp/slot.txt
- source: salt://slot.j2
- template: jinja
- context:
# Slot inside of the nested context dictionary
variable: __slot__:salt:test.echo(a_value)
- unless:
- fun: file.search
args:
# Slot as unless argument
- __slot__:salt:test.echo(/tmp/slot.txt)
- "DO NOT OVERRIDE"
ignore_if_missing: True
State Changes
=============
Expand Down
21 changes: 13 additions & 8 deletions salt/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -870,6 +870,17 @@ def _run_check(self, low_data):

return ret

def _run_check_function(self, entry):
"""Format slot args and run unless/onlyif function."""
fun = entry.pop('fun')
args = entry.pop('args') if 'args' in entry else []
cdata = {
'args': args,
'kwargs': entry
}
self.format_slots(cdata)
return self.functions[fun](*cdata['args'], **cdata['kwargs'])

def _run_check_onlyif(self, low_data, cmd_opts):
'''
Check that unless doesn't return 0, and that onlyif returns a 0.
Expand Down Expand Up @@ -901,10 +912,7 @@ def _check_cmd(cmd):
log.warning(ret['comment'])
return ret

if 'args' in entry:
result = self.functions[entry.pop('fun')](*entry.pop('args'), **entry)
else:
result = self.functions[entry.pop('fun')](**entry)
result = self._run_check_function(entry)
if self.state_con.get('retcode', 0):
_check_cmd(self.state_con['retcode'])
elif not result:
Expand Down Expand Up @@ -949,10 +957,7 @@ def _check_cmd(cmd):
log.warning(ret['comment'])
return ret

if 'args' in entry:
result = self.functions[entry.pop('fun')](*entry.pop('args'), **entry)
else:
result = self.functions[entry.pop('fun')](**entry)
result = self._run_check_function(entry)
if self.state_con.get('retcode', 0):
_check_cmd(self.state_con['retcode'])
elif result:
Expand Down
86 changes: 86 additions & 0 deletions tests/unit/test_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@
patch)
from tests.support.mixins import AdaptedConfigurationTestCaseMixin
from tests.support.runtests import RUNTIME_VARS
from tests.support.helpers import with_tempfile

# Import Salt libs
import salt.exceptions
import salt.state
from salt.utils.odict import OrderedDict
from salt.utils.decorators import state as statedecorators
import salt.utils.files
import salt.utils.platform

try:
import pytest
Expand Down Expand Up @@ -135,6 +138,89 @@ def test_verify_unless_parse(self):
return_result = state_obj._run_check_unless(low_data, '')
self.assertEqual(expected_result, return_result)

def _expand_win_path(self, path):
"""
Expand C:/users/admini~1/appdata/local/temp/salt-tests-tmpdir/...
into C:/users/adminitrator/appdata/local/temp/salt-tests-tmpdir/...
to prevent file.search from expanding the "~" with os.path.expanduser
"""
if salt.utils.platform.is_windows():
import win32file
return win32file.GetLongPathName(path).replace('\\', '/')
else:
return path

@with_tempfile()
def test_verify_onlyif_parse_slots(self, name):
with salt.utils.files.fopen(name, 'w') as fp:
fp.write('file-contents')
low_data = {
"onlyif": [
{
"fun": "file.search",
"args": [
"__slot__:salt:test.echo({})".format(self._expand_win_path(name)),
],
"pattern": "__slot__:salt:test.echo(file-contents)",
}
],
"name": "mysql-server-5.7",
"state": "debconf",
"__id__": "set root password",
"fun": "set",
"__env__": "base",
"__sls__": "debconf",
"data": {
"mysql-server/root_password": {
"type": "password",
"value": "temp123"
}
},
"order": 10000
}
expected_result = {'comment': 'onlyif condition is true', 'result': False}
with patch('salt.state.State._gather_pillar') as state_patch:
minion_opts = self.get_temp_config('minion')
state_obj = salt.state.State(minion_opts)
return_result = state_obj._run_check_onlyif(low_data, '')
self.assertEqual(expected_result, return_result)

@with_tempfile()
def test_verify_unless_parse_slots(self, name):
with salt.utils.files.fopen(name, 'w') as fp:
fp.write('file-contents')
low_data = {
"unless": [
{
"fun": "file.search",
"args": [
"__slot__:salt:test.echo({})".format(self._expand_win_path(name)),
],
"pattern": "__slot__:salt:test.echo(file-contents)",
}
],
"name": "mysql-server-5.7",
"state": "debconf",
"__id__": "set root password",
"fun": "set",
"__env__": "base",
"__sls__": "debconf",
"data": {
"mysql-server/root_password": {
"type": "password",
"value": "temp123"
}
},
"order": 10000
}
expected_result = {'comment': 'unless condition is true', 'result': True, 'skip_watch': True}

with patch('salt.state.State._gather_pillar') as state_patch:
minion_opts = self.get_temp_config('minion')
state_obj = salt.state.State(minion_opts)
return_result = state_obj._run_check_unless(low_data, '')
self.assertEqual(expected_result, return_result)


class HighStateTestCase(TestCase, AdaptedConfigurationTestCaseMixin):
def setUp(self):
Expand Down

0 comments on commit bd57aa6

Please sign in to comment.