Passed
Push — develop ( b8d4ca...421369 )
by Plexxi
07:01 queued 03:57
created

RunnerContainerTest   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 168
Duplicated Lines 0 %

Importance

Changes 3
Bugs 1 Features 0
Metric Value
c 3
b 1
f 0
dl 0
loc 168
rs 10
wmc 16

12 Methods

Rating   Name   Duplication   Size   Complexity  
A setUpClass() 0 10 1
A _get_failingaction_exec_db_model() 0 12 1
A test_get_runner_module() 0 4 1
A test_get_runner_module_fail() 0 8 2
A test_dispatch_non_utf8_result() 0 18 2
B test_dispatch() 0 24 1
A _get_liveaction_model() 0 10 1
A tearDownClass() 0 5 1
A test_pre_run_runner_is_disabled() 0 9 1
A test_state_db_creation_async_actions() 0 21 3
A test_dispatch_runner_failure() 0 13 1
A test_dispatch_override_default_action_params() 0 15 1
1
# Licensed to the StackStorm, Inc ('StackStorm') under one or more
2
# contributor license agreements.  See the NOTICE file distributed with
3
# this work for additional information regarding copyright ownership.
4
# The ASF licenses this file to You under the Apache License, Version 2.0
5
# (the "License"); you may not use this file except in compliance with
6
# the License.  You may obtain a copy of the License at
7
#
8
#     http://www.apache.org/licenses/LICENSE-2.0
9
#
10
# Unless required by applicable law or agreed to in writing, software
11
# distributed under the License is distributed on an "AS IS" BASIS,
12
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
# See the License for the specific language governing permissions and
14
# limitations under the License.
15
16
import mock
17
18
from bson.errors import InvalidStringData
19
from oslo_config import cfg
20
21
from st2common.constants import action as action_constants
22
from st2common.runners import get_runner
23
import local_runner
24
from st2common.exceptions.actionrunner import ActionRunnerCreateError
25
from st2common.models.system.common import ResourceReference
26
from st2common.models.db.liveaction import LiveActionDB
27
from st2common.models.db.runner import RunnerTypeDB
28
from st2common.persistence.liveaction import LiveAction
29
from st2common.persistence.executionstate import ActionExecutionState
30
from st2common.services import executions
31
from st2common.util import date as date_utils
32
from st2common.transport.publishers import PoolPublisher
33
from st2tests.base import DbTestCase
34
import st2tests.config as tests_config
35
tests_config.parse_args()
36
from st2tests.fixturesloader import FixturesLoader
37
38
39
# XXX: There is dependency on config being setup before importing
40
# RunnerContainer. Do not move this until you fix config
41
# dependencies.
42
from st2actions.container.base import get_runner_container
43
44
TEST_FIXTURES = {
45
    'runners': ['run-local.yaml', 'testrunner1.yaml', 'testfailingrunner1.yaml',
46
                'testasyncrunner1.yaml'],
47
    'actions': ['local.yaml', 'action1.yaml', 'async_action1.yaml', 'action-invalid-runner.yaml']
48
}
49
50
FIXTURES_PACK = 'generic'
51
52
NON_UTF8_RESULT = {'stderr': '', 'stdout': '\x82\n', 'succeeded': True, 'failed': False,
53
                   'return_code': 0}
54
55
56
@mock.patch.object(PoolPublisher, 'publish', mock.MagicMock())
57
class RunnerContainerTest(DbTestCase):
58
    action_db = None
59
    async_action_db = None
60
    failingaction_db = None
61
    runnertype_db = None
62
    fixtures_loader = FixturesLoader()
63
64
    @classmethod
65
    def setUpClass(cls):
66
        super(RunnerContainerTest, cls).setUpClass()
67
        models = RunnerContainerTest.fixtures_loader.save_fixtures_to_db(
68
            fixtures_pack=FIXTURES_PACK, fixtures_dict=TEST_FIXTURES)
69
        RunnerContainerTest.runnertype_db = models['runners']['testrunner1.yaml']
70
        RunnerContainerTest.action_db = models['actions']['action1.yaml']
71
        RunnerContainerTest.local_action_db = models['actions']['local.yaml']
72
        RunnerContainerTest.async_action_db = models['actions']['async_action1.yaml']
73
        RunnerContainerTest.failingaction_db = models['actions']['action-invalid-runner.yaml']
74
75
    @classmethod
76
    def tearDownClass(cls):
77
        RunnerContainerTest.fixtures_loader.delete_fixtures_from_db(
78
            fixtures_pack=FIXTURES_PACK, fixtures_dict=TEST_FIXTURES)
79
        super(RunnerContainerTest, cls).tearDownClass()
80
81
    def test_get_runner_module(self):
82
        runnertype_db = RunnerContainerTest.runnertype_db
83
        runner = get_runner(runnertype_db.runner_module)
84
        self.assertTrue(runner is not None, 'TestRunner must be valid.')
85
86
    def test_pre_run_runner_is_disabled(self):
87
        runnertype_db = RunnerContainerTest.runnertype_db
88
        runner = get_runner(runnertype_db.runner_module)
89
90
        runner.runner_type_db = runnertype_db
91
        runner.runner_type_db.enabled = False
92
93
        expected_msg = 'Runner "test-runner-1" has been disabled by the administrator'
94
        self.assertRaisesRegexp(ValueError, expected_msg, runner.pre_run)
95
96
    def test_get_runner_module_fail(self):
97
        runnertype_db = RunnerTypeDB(name='dummy', runner_module='absent.module')
98
        runner = None
99
        try:
100
            runner = get_runner(runnertype_db.runner_module)
101
        except ActionRunnerCreateError:
102
            pass
103
        self.assertFalse(runner, 'TestRunner must be valid.')
104
105
    def test_dispatch(self):
106
        runner_container = get_runner_container()
107
        params = {
108
            'actionstr': 'bar'
109
        }
110
        liveaction_db = self._get_liveaction_model(RunnerContainerTest.action_db, params)
111
        liveaction_db = LiveAction.add_or_update(liveaction_db)
112
        executions.create_execution_object(liveaction_db)
113
        # Assert that execution ran successfully.
114
        runner_container.dispatch(liveaction_db)
115
        liveaction_db = LiveAction.get_by_id(liveaction_db.id)
116
        result = liveaction_db.result
117
        self.assertTrue(result.get('action_params').get('actionint') == 10)
118
        self.assertTrue(result.get('action_params').get('actionstr') == 'bar')
119
120
        # Assert that context is written correctly.
121
        context = {
122
            'user': 'stanley',
123
            'third_party_system': {
124
                'ref_id': '1234'
125
            }
126
        }
127
128
        self.assertDictEqual(liveaction_db.context, context)
129
130
    @mock.patch.object(local_runner.LocalShellRunner, 'run', mock.MagicMock(
131
        return_value=(action_constants.LIVEACTION_STATUS_SUCCEEDED, NON_UTF8_RESULT, None)))
132
    @mock.patch('st2common.runners.register_runner',
133
                mock.MagicMock(return_value=local_runner))
134
    def test_dispatch_non_utf8_result(self):
135
        runner_container = get_runner_container()
136
        params = {
137
            'cmd': "python -c 'print \"\\x82\"'"
138
        }
139
        liveaction_db = self._get_liveaction_model(RunnerContainerTest.local_action_db, params)
140
        liveaction_db = LiveAction.add_or_update(liveaction_db)
141
        executions.create_execution_object(liveaction_db)
142
143
        try:
144
            runner_container.dispatch(liveaction_db)
145
            self.fail('Mongo won\'t handle non UTF-8 strings. Should have failed.')
146
        except InvalidStringData:
147
            pass
148
149
    def test_dispatch_runner_failure(self):
150
        runner_container = get_runner_container()
151
        params = {
152
            'actionstr': 'bar'
153
        }
154
        liveaction_db = self._get_failingaction_exec_db_model(params)
155
        liveaction_db = LiveAction.add_or_update(liveaction_db)
156
        executions.create_execution_object(liveaction_db)
157
        runner_container.dispatch(liveaction_db)
158
        # pickup updated liveaction_db
159
        liveaction_db = LiveAction.get_by_id(liveaction_db.id)
160
        self.assertTrue('error' in liveaction_db.result)
161
        self.assertTrue('traceback' in liveaction_db.result)
162
163
    def test_dispatch_override_default_action_params(self):
164
        runner_container = get_runner_container()
165
        params = {
166
            'actionstr': 'foo',
167
            'actionint': 20
168
        }
169
        liveaction_db = self._get_liveaction_model(RunnerContainerTest.action_db, params)
170
        liveaction_db = LiveAction.add_or_update(liveaction_db)
171
        executions.create_execution_object(liveaction_db)
172
        # Assert that execution ran successfully.
173
        runner_container.dispatch(liveaction_db)
174
        liveaction_db = LiveAction.get_by_id(liveaction_db.id)
175
        result = liveaction_db.result
176
        self.assertTrue(result.get('action_params').get('actionint') == 20)
177
        self.assertTrue(result.get('action_params').get('actionstr') == 'foo')
178
179
    def test_state_db_creation_async_actions(self):
180
        runner_container = get_runner_container()
181
        params = {
182
            'actionstr': 'foo',
183
            'actionint': 20,
184
            'async_test': True
185
        }
186
        liveaction_db = self._get_liveaction_model(RunnerContainerTest.async_action_db, params)
187
        liveaction_db = LiveAction.add_or_update(liveaction_db)
188
        executions.create_execution_object(liveaction_db)
189
        # Assert that execution ran without exceptions.
190
        runner_container.dispatch(liveaction_db)
191
        states = ActionExecutionState.get_all()
192
193
        found = None
194
        for state in states:
195
            if state.execution_id == liveaction_db.id:
196
                found = state
197
        self.assertTrue(found is not None, 'There should be a state db object.')
198
        self.assertTrue(found.query_context is not None)
199
        self.assertTrue(found.query_module is not None)
200
201
    def _get_liveaction_model(self, action_db, params):
202
        status = action_constants.LIVEACTION_STATUS_REQUESTED
203
        start_timestamp = date_utils.get_datetime_utc_now()
204
        action_ref = ResourceReference(name=action_db.name, pack=action_db.pack).ref
205
        parameters = params
206
        context = {'user': cfg.CONF.system_user.user}
207
        liveaction_db = LiveActionDB(status=status, start_timestamp=start_timestamp,
208
                                     action=action_ref, parameters=parameters,
209
                                     context=context)
210
        return liveaction_db
211
212
    def _get_failingaction_exec_db_model(self, params):
213
        status = action_constants.LIVEACTION_STATUS_REQUESTED
214
        start_timestamp = date_utils.get_datetime_utc_now()
215
        action_ref = ResourceReference(
216
            name=RunnerContainerTest.failingaction_db.name,
217
            pack=RunnerContainerTest.failingaction_db.pack).ref
218
        parameters = params
219
        context = {'user': cfg.CONF.system_user.user}
220
        liveaction_db = LiveActionDB(status=status, start_timestamp=start_timestamp,
221
                                     action=action_ref, parameters=parameters,
222
                                     context=context)
223
        return liveaction_db
224