Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

draft support for parsing slot results and appending text #52187

Merged
merged 13 commits into from Apr 9, 2019
25 changes: 25 additions & 0 deletions doc/topics/releases/neon.rst
Expand Up @@ -261,6 +261,31 @@ editing XML IDs.
- xpath: .//actor[@id='1']
- value: William Shatner

Slot Syntax Updates
===================

The slot syntax has been updated to support parsing dictionary responses and to append text.

.. code-block:: yaml

demo dict parsing and append:
test.configurable_test_state:
- name: slot example
- changes: False
- comment: __slot__:salt:test.arg(shell="/bin/bash").kwargs.shell ~ /appended

.. code-block:: none

local:
----------
ID: demo dict parsing and append
Function: test.configurable_test_state
Name: slot example
Result: True
Comment: /bin/bash/appended
Started: 09:59:58.623575
Duration: 1.229 ms
Changes:

State Changes
=============
Expand Down
39 changes: 38 additions & 1 deletion salt/state.py
Expand Up @@ -2114,7 +2114,44 @@ def __eval_slot(self, slot):
'test.arg(\'arg\', kw=\'kwarg\')')
return slot
log.debug('Calling slot: %s(%s, %s)', fun, args, kwargs)
return self.functions[fun](*args, **kwargs)
slot_return = self.functions[fun](*args, **kwargs)

# Given input __slot__:salt:test.arg(somekey="value").not.exist ~ /appended
# slot_text should be __slot...).not.exist
# append_data should be ~ /appended
slot_text = fmt[2].split('~')[0]
append_data = fmt[2].split('~', 1)[1:]
log.debug('slot_text: %s', slot_text)
log.debug('append_data: %s', append_data)

# Support parsing slot dict response
# return_get should result in a kwargs.nested.dict path by getting
# everything after first closing paren: )
return_get = None
try:
return_get = slot_text[slot_text.rindex(')')+1:]
except ValueError:
pass
if return_get:
#remove first period
return_get = return_get.split('.', 1)[1].strip()
log.debug('Searching slot result %s for %s', slot_return, return_get)
slot_return = salt.utils.data.traverse_dict(slot_return,
return_get,
default=None,
delimiter='.'
)

if append_data:
if isinstance(slot_return, six.string_types):
# Append text to slot string result
append_data = ' '.join(append_data).strip()
log.debug('appending to slot result: %s', append_data)
slot_return += append_data
else:
log.error('Ignoring slot append, slot result is not a string')

return slot_return

def format_slots(self, cdata):
'''
Expand Down
38 changes: 38 additions & 0 deletions tests/unit/test_state.py
Expand Up @@ -416,3 +416,41 @@ def test_format_slots_malformed(self):
self.state_obj.format_slots(cdata)
mock.assert_not_called()
self.assertEqual(cdata, sls_data)

def test_slot_traverse_dict(self):
'''
Test the slot parsing of dict response.
'''
cdata = {
'args': [
'arg',
],
'kwargs': {
'key': '__slot__:salt:mod.fun(fun_arg, fun_key=fun_val).key1',
}
}
return_data = {'key1': 'value1'}
mock = MagicMock(return_value=return_data)
with patch.dict(self.state_obj.functions, {'mod.fun': mock}):
self.state_obj.format_slots(cdata)
mock.assert_called_once_with('fun_arg', fun_key='fun_val')
self.assertEqual(cdata, {'args': ['arg'], 'kwargs': {'key': 'value1'}})

def test_slot_append(self):
'''
Test the slot parsing of dict response.
'''
cdata = {
'args': [
'arg',
],
'kwargs': {
'key': '__slot__:salt:mod.fun(fun_arg, fun_key=fun_val).key1 ~ thing~',
}
}
return_data = {'key1': 'value1'}
mock = MagicMock(return_value=return_data)
with patch.dict(self.state_obj.functions, {'mod.fun': mock}):
self.state_obj.format_slots(cdata)
mock.assert_called_once_with('fun_arg', fun_key='fun_val')
self.assertEqual(cdata, {'args': ['arg'], 'kwargs': {'key': 'value1thing~'}})