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 slot parsing inside dict and lists of dict or strings #52351

Merged
merged 4 commits into from Apr 9, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
41 changes: 38 additions & 3 deletions salt/state.py
Expand Up @@ -2120,18 +2120,53 @@ def format_slots(self, cdata):
'''
Read in the arguments from the low level slot syntax to make a last
minute runtime call to gather relevant data for the specific routine

Will parse strings, first level of dictionary values, and strings and
first level dict values inside of lists
'''
# __slot__:salt.cmd.run(foo, bar, baz=qux)
SLOT_TEXT = '__slot__:'
ctx = (('args', enumerate(cdata['args'])),
('kwargs', cdata['kwargs'].items()))
for atype, avalues in ctx:
for ind, arg in avalues:
arg = salt.utils.data.decode(arg, keep=True)
if not isinstance(arg, six.text_type) \
or not arg.startswith('__slot__:'):
if isinstance(arg, dict):
# Search dictionary values for __slot__:
for key, value in arg.items():
try:
if value.startswith(SLOT_TEXT):
log.trace("Slot processsing dict value %s", value)
cdata[atype][ind][key] = self.__eval_slot(value)
except AttributeError:
# Not a string/slot
continue
elif isinstance(arg, list):
for idx, listvalue in enumerate(arg):
log.trace("Slot processing list value: %s", listvalue)
if isinstance(listvalue, dict):
# Search dict values in list for __slot__:
for key, value in listvalue.items():
try:
if value.startswith(SLOT_TEXT):
log.trace("Slot processsing nested dict value %s", value)
cdata[atype][ind][idx][key] = self.__eval_slot(value)
except AttributeError:
# Not a string/slot
continue
if isinstance(listvalue, six.text_type):
# Search strings in a list for __slot__:
if listvalue.startswith(SLOT_TEXT):
log.trace("Slot processsing nested string %s", listvalue)
cdata[atype][ind][idx] = self.__eval_slot(listvalue)
elif isinstance(arg, six.text_type) \
and arg.startswith(SLOT_TEXT):
# Search strings for __slot__:
log.trace("Slot processsing %s", arg)
cdata[atype][ind] = self.__eval_slot(arg)
else:
# Not a slot, skip it
continue
cdata[atype][ind] = self.__eval_slot(arg)

def verify_retry_data(self, retry_data):
'''
Expand Down
54 changes: 54 additions & 0 deletions tests/unit/test_state.py
Expand Up @@ -337,6 +337,60 @@ def test_format_slots_arg(self):
mock.assert_called_once_with('fun_arg', fun_key='fun_val')
self.assertEqual(cdata, {'args': ['fun_return'], 'kwargs': {'key': 'val'}})

def test_format_slots_dict_arg(self):
'''
Test the format slots is calling a slot specified in dict arg.
'''
cdata = {
'args': [
{'subarg': '__slot__:salt:mod.fun(fun_arg, fun_key=fun_val)'},
],
'kwargs': {
'key': 'val',
}
}
mock = MagicMock(return_value='fun_return')
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': [{'subarg': 'fun_return'}], 'kwargs': {'key': 'val'}})

def test_format_slots_listdict_arg(self):
'''
Test the format slots is calling a slot specified in list containing a dict.
'''
cdata = {
'args': [[
{'subarg': '__slot__:salt:mod.fun(fun_arg, fun_key=fun_val)'},
]],
'kwargs': {
'key': 'val',
}
}
mock = MagicMock(return_value='fun_return')
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': [[{'subarg': 'fun_return'}]], 'kwargs': {'key': 'val'}})

def test_format_slots_liststr_arg(self):
'''
Test the format slots is calling a slot specified in list containing a dict.
'''
cdata = {
'args': [[
'__slot__:salt:mod.fun(fun_arg, fun_key=fun_val)',
]],
'kwargs': {
'key': 'val',
}
}
mock = MagicMock(return_value='fun_return')
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': [['fun_return']], 'kwargs': {'key': 'val'}})

def test_format_slots_kwarg(self):
'''
Test the format slots is calling a slot specified in kwargs with corresponding arguments.
Expand Down