File explore-module.run-response-to-catch-the-result-in-d.patch of Package salt

From 9fec0d667a73df85537cfe9ac0f97e914c2ba725 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?=
 <psuarezhernandez@suse.com>
Date: Wed, 7 Mar 2018 09:42:46 +0000
Subject: [PATCH] Explore 'module.run' response to catch the 'result' in
 depth

Fix Python3 and pylint issue

Rename and fix recursive method

Add new unit test to check state.apply within module.run
---
 salt/states/module.py            | 19 +++++++++++++
 tests/unit/states/module_test.py | 61 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 80 insertions(+)

diff --git a/salt/states/module.py b/salt/states/module.py
index adc6e12c9d..90c3760805 100644
--- a/salt/states/module.py
+++ b/salt/states/module.py
@@ -99,6 +99,7 @@ import salt.loader
 import salt.utils
 import salt.utils.jid
 from salt.ext.six.moves import range
+from salt.ext import six
 
 
 def wait(name, **kwargs):
@@ -291,6 +292,24 @@ def run(name, **kwargs):
                 ret['result'] = changes_ret.get('result', {})
             elif changes_ret.get('retcode', 0) != 0:
                 ret['result'] = False
+            # Explore dict in depth to determine if there is a
+            # 'result' key set to False which sets the global
+            # state result.
+            else:
+                ret['result'] = _get_dict_result(changes_ret)
+    return ret
+
+
+def _get_dict_result(node):
+    ret = True
+    for key, val in six.iteritems(node):
+        if key == 'result' and val is False:
+            ret = False
+            break
+        elif isinstance(val, dict):
+            ret = _get_dict_result(val)
+            if ret is False:
+                break
     return ret
 
 mod_watch = salt.utils.alias_function(run, 'mod_watch')
diff --git a/tests/unit/states/module_test.py b/tests/unit/states/module_test.py
index 20dda73938..86a37ac0dc 100644
--- a/tests/unit/states/module_test.py
+++ b/tests/unit/states/module_test.py
@@ -27,6 +27,57 @@ MOCK = MagicMock()
 module.__salt__ = {CMD: MOCK}
 module.__opts__ = {'test': False}
 
+STATE_APPLY_RET = {
+    'module_|-test2_|-state.apply_|-run': {
+        'comment': 'Module function state.apply executed',
+        'name': 'state.apply',
+        'start_time': '16:11:48.818932',
+        'result': False,
+        'duration': 179.439,
+        '__run_num__': 0,
+        'changes': {
+            'ret': {
+                'module_|-test3_|-state.apply_|-run': {
+                    'comment': 'Module function state.apply executed',
+                    'name': 'state.apply',
+                    'start_time': '16:11:48.904796',
+                    'result': True,
+                    'duration': 89.522,
+                    '__run_num__': 0,
+                    'changes': {
+                        'ret': {
+                            'module_|-test4_|-cmd.run_|-run': {
+                                'comment': 'Module function cmd.run executed',
+                                'name': 'cmd.run',
+                                'start_time': '16:11:48.988574',
+                                'result': True,
+                                'duration': 4.543,
+                                '__run_num__': 0,
+                                'changes': {
+                                    'ret': 'Wed Mar  7 16:11:48 CET 2018'
+                                },
+                                '__id__': 'test4'
+                            }
+                        }
+                    },
+                    '__id__': 'test3'
+                },
+                'module_|-test3_fail_|-test3_fail_|-run': {
+                    'comment': 'Module function test3_fail is not available',
+                    'name': 'test3_fail',
+                    'start_time': '16:11:48.994607',
+                    'result': False,
+                    'duration': 0.466,
+                    '__run_num__': 1,
+                    'changes': {},
+                    '__id__': 'test3_fail'
+                }
+            }
+        },
+        '__id__': 'test2'
+    }
+}
+
 
 @skipIf(NO_MOCK, NO_MOCK_REASON)
 class ModuleStateTest(TestCase):
@@ -64,6 +115,16 @@ class ModuleStateTest(TestCase):
             comment = 'Module function {0} is set to execute'.format(CMD)
             self.assertEqual(ret['comment'], comment)
 
+    def test_run_state_apply_result_false(self):
+        '''
+        Tests the 'result' of module.run that calls state.apply execution module
+        :return:
+        '''
+        with patch.dict(module.__salt__, {"state.apply": MagicMock(return_value=STATE_APPLY_RET)}):
+            ret = module.run(**{"name": "state.apply", 'mods': 'test2'})
+            if ret['result']:
+                self.fail('module.run did not report false result: {0}'.format(ret))
+
     @patch('salt.utils.args.get_function_argspec', MagicMock(return_value=aspec))
     def test_module_run_missing_arg(self):
         '''
-- 
2.15.1


openSUSE Build Service is sponsored by