Passed
Push — master ( f1fe9e...5c5de8 )
by
unknown
03:44
created

LocalShellScriptRunner._get_runner()   A

Complexity

Conditions 1

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
dl 0
loc 14
rs 9.4285
c 0
b 0
f 0
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 os
17
import uuid
18
19
import mock
20
from oslo_config import cfg
21
22
import st2tests.config as tests_config
23
tests_config.parse_args()
24
25
from st2actions.container.service import RunnerContainerService
26
from st2common.constants import action as action_constants
27
from st2common.persistence.execution import ActionExecutionOutput
28
from st2tests.fixturesloader import FixturesLoader
29
from st2tests.fixturesloader import get_fixtures_base_path
30
from st2common.util.api import get_full_public_api_url
31
from st2common.util.green import shell
32
from st2common.constants.runners import LOCAL_RUNNER_DEFAULT_ACTION_TIMEOUT
33
from st2tests.base import RunnerTestCase
34
from st2tests.base import CleanDbTestCase
35
from st2tests.base import blocking_eventlet_spawn
36
from st2tests.base import make_mock_stream_readline
37
import local_runner
38
39
__all__ = [
40
    'LocalShellCommandRunnerTestCase',
41
    'LocalShellScriptRunnerTestCase'
42
]
43
44
MOCK_EXECUTION = mock.Mock()
45
MOCK_EXECUTION.id = '598dbf0c0640fd54bffc688b'
46
47
48
class LocalShellCommandRunnerTestCase(RunnerTestCase, CleanDbTestCase):
49
    fixtures_loader = FixturesLoader()
50
51
    def setUp(self):
52
        super(LocalShellCommandRunnerTestCase, self).setUp()
53
54
        # False is a default behavior so end result should be the same
55
        cfg.CONF.set_override(name='stream_output', group='actionrunner', override=False)
56
57
    def test_shell_command_action_basic(self):
58
        models = self.fixtures_loader.load_models(
59
            fixtures_pack='generic', fixtures_dict={'actions': ['local.yaml']})
60
        action_db = models['actions']['local.yaml']
61
62
        runner = self._get_runner(action_db, cmd='echo 10')
63
        runner.pre_run()
64
        status, result, _ = runner.run({})
65
        runner.post_run(status, result)
66
67
        self.assertEquals(status, action_constants.LIVEACTION_STATUS_SUCCEEDED)
68
        self.assertEquals(result['stdout'], 10)
69
70
        # End result should be the same when streaming is enabled
71
        cfg.CONF.set_override(name='stream_output', group='actionrunner', override=True)
72
73
        # Verify initial state
74
        output_dbs = ActionExecutionOutput.get_all()
75
        self.assertEqual(len(output_dbs), 0)
76
77
        runner = self._get_runner(action_db, cmd='echo 10')
78
        runner.pre_run()
79
        status, result, _ = runner.run({})
80
        runner.post_run(status, result)
81
82
        self.assertEquals(status, action_constants.LIVEACTION_STATUS_SUCCEEDED)
83
        self.assertEquals(result['stdout'], 10)
84
85
        output_dbs = ActionExecutionOutput.get_all()
86
        self.assertEqual(len(output_dbs), 1)
87
        self.assertEqual(output_dbs[0].output_type, 'stdout')
88
        self.assertEqual(output_dbs[0].data, '10\n')
89
90
    def test_shell_script_action(self):
91
        models = self.fixtures_loader.load_models(
92
            fixtures_pack='localrunner_pack', fixtures_dict={'actions': ['text_gen.yml']})
93
        action_db = models['actions']['text_gen.yml']
94
        entry_point = self.fixtures_loader.get_fixture_file_path_abs(
95
            'localrunner_pack', 'actions', 'text_gen.py')
96
        runner = self._get_runner(action_db, entry_point=entry_point)
97
        runner.pre_run()
98
        status, result, _ = runner.run({'chars': 1000})
99
        runner.post_run(status, result)
100
        self.assertEquals(status, action_constants.LIVEACTION_STATUS_SUCCEEDED)
101
        self.assertEquals(len(result['stdout']), 1000)
102
103
    def test_timeout(self):
104
        models = self.fixtures_loader.load_models(
105
            fixtures_pack='generic', fixtures_dict={'actions': ['local.yaml']})
106
        action_db = models['actions']['local.yaml']
107
        # smaller timeout == faster tests.
108
        runner = self._get_runner(action_db, cmd='sleep 10', timeout=0.01)
109
        runner.pre_run()
110
        status, result, _ = runner.run({})
111
        runner.post_run(status, result)
112
        self.assertEquals(status, action_constants.LIVEACTION_STATUS_TIMED_OUT)
113
114
    @mock.patch.object(
115
        shell, 'run_command',
116
        mock.MagicMock(return_value=(-15, '', '', False)))
117
    def test_shutdown(self):
118
        models = self.fixtures_loader.load_models(
119
            fixtures_pack='generic', fixtures_dict={'actions': ['local.yaml']})
120
        action_db = models['actions']['local.yaml']
121
        runner = self._get_runner(action_db, cmd='sleep 0.1')
122
        runner.pre_run()
123
        status, result, _ = runner.run({})
124
        self.assertEquals(status, action_constants.LIVEACTION_STATUS_ABANDONED)
125
126
    def test_large_stdout(self):
127
        models = self.fixtures_loader.load_models(
128
            fixtures_pack='localrunner_pack', fixtures_dict={'actions': ['text_gen.yml']})
129
        action_db = models['actions']['text_gen.yml']
130
        entry_point = self.fixtures_loader.get_fixture_file_path_abs(
131
            'localrunner_pack', 'actions', 'text_gen.py')
132
        runner = self._get_runner(action_db, entry_point=entry_point)
133
        runner.pre_run()
134
        char_count = 10 ** 6  # Note 10^7 succeeds but ends up being slow.
135
        status, result, _ = runner.run({'chars': char_count})
136
        runner.post_run(status, result)
137
        self.assertEquals(status, action_constants.LIVEACTION_STATUS_SUCCEEDED)
138
        self.assertEquals(len(result['stdout']), char_count)
139
140
    def test_common_st2_env_vars_are_available_to_the_action(self):
141
        models = self.fixtures_loader.load_models(
142
            fixtures_pack='generic', fixtures_dict={'actions': ['local.yaml']})
143
        action_db = models['actions']['local.yaml']
144
145
        runner = self._get_runner(action_db, cmd='echo $ST2_ACTION_API_URL')
146
        runner.pre_run()
147
        status, result, _ = runner.run({})
148
        runner.post_run(status, result)
149
150
        self.assertEquals(status, action_constants.LIVEACTION_STATUS_SUCCEEDED)
151
        self.assertEqual(result['stdout'].strip(), get_full_public_api_url())
152
153
        runner = self._get_runner(action_db, cmd='echo $ST2_ACTION_AUTH_TOKEN')
154
        runner.pre_run()
155
        status, result, _ = runner.run({})
156
        runner.post_run(status, result)
157
158
        self.assertEquals(status, action_constants.LIVEACTION_STATUS_SUCCEEDED)
159
        self.assertEqual(result['stdout'].strip(), 'mock-token')
160
161
    def test_sudo_and_env_variable_preservation(self):
162
        # Verify that the environment environment are correctly preserved when running as a
163
        # root / non-system user
164
        # Note: This test will fail if SETENV option is not present in the sudoers file
165
        models = self.fixtures_loader.load_models(
166
            fixtures_pack='generic', fixtures_dict={'actions': ['local.yaml']})
167
        action_db = models['actions']['local.yaml']
168
169
        cmd = 'echo `whoami` ; echo ${VAR1}'
170
        env = {'VAR1': 'poniesponies'}
171
        runner = self._get_runner(action_db, cmd=cmd, sudo=True, env=env)
172
        runner.pre_run()
173
        status, result, _ = runner.run({})
174
        runner.post_run(status, result)
175
176
        self.assertEquals(status, action_constants.LIVEACTION_STATUS_SUCCEEDED)
177
        self.assertEqual(result['stdout'].strip(), 'root\nponiesponies')
178
179
    @mock.patch('st2common.util.green.shell.subprocess.Popen')
180
    @mock.patch('st2common.util.green.shell.eventlet.spawn')
181
    def test_action_stdout_and_stderr_is_stored_in_the_db(self, mock_spawn, mock_popen):
182
        # Feature is enabled
183
        cfg.CONF.set_override(name='stream_output', group='actionrunner', override=True)
184
185
        # Note: We need to mock spawn function so we can test everything in single event loop
186
        # iteration
187
        mock_spawn.side_effect = blocking_eventlet_spawn
188
189
        # No output to stdout and no result (implicit None)
190
        mock_stdout = [
191
            'stdout line 1\n',
192
            'stdout line 2\n',
193
        ]
194
        mock_stderr = [
195
            'stderr line 1\n',
196
            'stderr line 2\n',
197
            'stderr line 3\n'
198
        ]
199
200
        mock_process = mock.Mock()
201
        mock_process.returncode = 0
202
        mock_popen.return_value = mock_process
203
        mock_process.stdout.closed = False
204
        mock_process.stderr.closed = False
205
        mock_process.stdout.readline = make_mock_stream_readline(mock_process.stdout, mock_stdout,
206
                                                                 stop_counter=2)
207
        mock_process.stderr.readline = make_mock_stream_readline(mock_process.stderr, mock_stderr,
208
                                                                 stop_counter=3)
209
210
        models = self.fixtures_loader.load_models(
211
            fixtures_pack='generic', fixtures_dict={'actions': ['local.yaml']})
212
        action_db = models['actions']['local.yaml']
213
214
        runner = self._get_runner(action_db, cmd='echo $ST2_ACTION_API_URL')
215
        runner.pre_run()
216
        status, result, _ = runner.run({})
217
        runner.post_run(status, result)
218
219
        self.assertEquals(status, action_constants.LIVEACTION_STATUS_SUCCEEDED)
220
221
        self.assertEqual(result['stdout'], 'stdout line 1\nstdout line 2')
222
        self.assertEqual(result['stderr'], 'stderr line 1\nstderr line 2\nstderr line 3')
223
        self.assertEqual(result['return_code'], 0)
224
225
        # Verify stdout and stderr lines have been correctly stored in the db
226
        output_dbs = ActionExecutionOutput.query(output_type='stdout')
227
        self.assertEqual(len(output_dbs), 2)
228
        self.assertEqual(output_dbs[0].data, mock_stdout[0])
229
        self.assertEqual(output_dbs[1].data, mock_stdout[1])
230
231
        output_dbs = ActionExecutionOutput.query(output_type='stderr')
232
        self.assertEqual(len(output_dbs), 3)
233
        self.assertEqual(output_dbs[0].data, mock_stderr[0])
234
        self.assertEqual(output_dbs[1].data, mock_stderr[1])
235
        self.assertEqual(output_dbs[2].data, mock_stderr[2])
236
237
    @mock.patch('st2common.util.green.shell.subprocess.Popen')
238
    @mock.patch('st2common.util.green.shell.eventlet.spawn')
239
    def test_action_stdout_and_stderr_is_stored_in_the_db_short_running_action(self, mock_spawn,
240
                                                                               mock_popen):
241
        # Verify that we correctly retrieve all the output and wait for stdout and stderr reading
242
        # threads for short running actions.
243
        models = self.fixtures_loader.load_models(
244
            fixtures_pack='generic', fixtures_dict={'actions': ['local.yaml']})
245
        action_db = models['actions']['local.yaml']
246
247
        # Feature is enabled
248
        cfg.CONF.set_override(name='stream_output', group='actionrunner', override=True)
249
250
        # Note: We need to mock spawn function so we can test everything in single event loop
251
        # iteration
252
        mock_spawn.side_effect = blocking_eventlet_spawn
253
254
        # No output to stdout and no result (implicit None)
255
        mock_stdout = [
256
            'stdout line 1\n',
257
            'stdout line 2\n'
258
        ]
259
        mock_stderr = [
260
            'stderr line 1\n',
261
            'stderr line 2\n'
262
        ]
263
264
        # We add a sleep to simulate action process exiting before we finish reading data from
265
        mock_process = mock.Mock()
266
        mock_process.returncode = 0
267
        mock_popen.return_value = mock_process
268
        mock_process.stdout.closed = False
269
        mock_process.stderr.closed = False
270
        mock_process.stdout.readline = make_mock_stream_readline(mock_process.stdout, mock_stdout,
271
                                                                 stop_counter=2,
272
                                                                 sleep_delay=1)
273
        mock_process.stderr.readline = make_mock_stream_readline(mock_process.stderr, mock_stderr,
274
                                                                 stop_counter=2)
275
276
        for index in range(1, 4):
277
            mock_process.stdout.closed = False
278
            mock_process.stderr.closed = False
279
280
            mock_process.stdout.counter = 0
281
            mock_process.stderr.counter = 0
282
283
            runner = self._get_runner(action_db, cmd='echo "foobar"')
284
            runner.pre_run()
285
            status, result, _ = runner.run({})
286
287
            self.assertEquals(status, action_constants.LIVEACTION_STATUS_SUCCEEDED)
288
289
            self.assertEqual(result['stdout'], 'stdout line 1\nstdout line 2')
290
            self.assertEqual(result['stderr'], 'stderr line 1\nstderr line 2')
291
            self.assertEqual(result['return_code'], 0)
292
293
            # Verify stdout and stderr lines have been correctly stored in the db
294
            output_dbs = ActionExecutionOutput.query(output_type='stdout')
295
296
            if index == 1:
297
                db_index_1 = 0
298
                db_index_2 = 1
299
            elif index == 2:
300
                db_index_1 = 2
301
                db_index_2 = 3
302
            elif index == 3:
303
                db_index_1 = 4
304
                db_index_2 = 5
305
            elif index == 4:
306
                db_index_1 = 6
307
                db_index_2 = 7
308
309
            self.assertEqual(len(output_dbs), (index * 2))
310
            self.assertEqual(output_dbs[db_index_1].data, mock_stdout[0])
311
            self.assertEqual(output_dbs[db_index_2].data, mock_stdout[1])
312
313
            output_dbs = ActionExecutionOutput.query(output_type='stderr')
314
            self.assertEqual(len(output_dbs), (index * 2))
315
            self.assertEqual(output_dbs[db_index_1].data, mock_stderr[0])
316
            self.assertEqual(output_dbs[db_index_2].data, mock_stderr[1])
317
318
    @staticmethod
319
    def _get_runner(action_db,
320
                    entry_point=None,
321
                    cmd=None,
322
                    on_behalf_user=None,
323
                    user=None,
324
                    kwarg_op=local_runner.DEFAULT_KWARG_OP,
325
                    timeout=LOCAL_RUNNER_DEFAULT_ACTION_TIMEOUT,
326
                    sudo=False,
327
                    env=None):
328
        runner = local_runner.LocalShellRunner(uuid.uuid4().hex)
329
        runner.container_service = RunnerContainerService()
330
        runner.execution = MOCK_EXECUTION
331
        runner.action = action_db
332
        runner.action_name = action_db.name
333
        runner.liveaction_id = uuid.uuid4().hex
334
        runner.entry_point = entry_point
335
        runner.runner_parameters = {local_runner.RUNNER_COMMAND: cmd,
336
                                    local_runner.RUNNER_SUDO: sudo,
337
                                    local_runner.RUNNER_ENV: env,
338
                                    local_runner.RUNNER_ON_BEHALF_USER: user,
339
                                    local_runner.RUNNER_KWARG_OP: kwarg_op,
340
                                    local_runner.RUNNER_TIMEOUT: timeout}
341
        runner.context = dict()
342
        runner.callback = dict()
343
        runner.libs_dir_path = None
344
        runner.auth_token = mock.Mock()
345
        runner.auth_token.token = 'mock-token'
346
        return runner
347
348
349
class LocalShellScriptRunnerTestCase(RunnerTestCase, CleanDbTestCase):
350
    fixtures_loader = FixturesLoader()
351
352
    def setUp(self):
353
        super(LocalShellScriptRunnerTestCase, self).setUp()
354
355
        # False is a default behavior so end result should be the same
356
        cfg.CONF.set_override(name='stream_output', group='actionrunner', override=False)
357
358
    def test_script_with_paramters_parameter_serialization(self):
359
        models = self.fixtures_loader.load_models(
360
            fixtures_pack='generic', fixtures_dict={'actions': ['local_script_with_params.yaml']})
361
        action_db = models['actions']['local_script_with_params.yaml']
362
        entry_point = os.path.join(get_fixtures_base_path(),
363
                                   'generic/actions/local_script_with_params.sh')
364
365
        action_parameters = {
366
            'param_string': 'test string',
367
            'param_integer': 1,
368
            'param_float': 2.55,
369
            'param_boolean': True,
370
            'param_list': ['a', 'b', 'c'],
371
            'param_object': {'foo': 'bar'}
372
        }
373
374
        runner = self._get_runner(action_db=action_db, entry_point=entry_point)
375
        runner.pre_run()
376
        status, result, _ = runner.run(action_parameters=action_parameters)
377
        runner.post_run(status, result)
378
379
        self.assertEqual(status, action_constants.LIVEACTION_STATUS_SUCCEEDED)
380
        self.assertTrue('PARAM_STRING=test string' in result['stdout'])
381
        self.assertTrue('PARAM_INTEGER=1' in result['stdout'])
382
        self.assertTrue('PARAM_FLOAT=2.55' in result['stdout'])
383
        self.assertTrue('PARAM_BOOLEAN=1' in result['stdout'])
384
        self.assertTrue('PARAM_LIST=a,b,c' in result['stdout'])
385
        self.assertTrue('PARAM_OBJECT={"foo": "bar"}' in result['stdout'])
386
387
        action_parameters = {
388
            'param_string': 'test string',
389
            'param_integer': 1,
390
            'param_float': 2.55,
391
            'param_boolean': False,
392
            'param_list': ['a', 'b', 'c'],
393
            'param_object': {'foo': 'bar'}
394
        }
395
396
        runner = self._get_runner(action_db=action_db, entry_point=entry_point)
397
        runner.pre_run()
398
        status, result, _ = runner.run(action_parameters=action_parameters)
399
        runner.post_run(status, result)
400
401
        self.assertEqual(status, action_constants.LIVEACTION_STATUS_SUCCEEDED)
402
        self.assertTrue('PARAM_BOOLEAN=0' in result['stdout'])
403
404
        action_parameters = {
405
            'param_string': '',
406
            'param_integer': None,
407
            'param_float': None,
408
        }
409
410
        runner = self._get_runner(action_db=action_db, entry_point=entry_point)
411
        runner.pre_run()
412
        status, result, _ = runner.run(action_parameters=action_parameters)
413
        runner.post_run(status, result)
414
415
        self.assertEqual(status, action_constants.LIVEACTION_STATUS_SUCCEEDED)
416
        self.assertTrue('PARAM_STRING=\n' in result['stdout'])
417
        self.assertTrue('PARAM_INTEGER=\n' in result['stdout'])
418
        self.assertTrue('PARAM_FLOAT=\n' in result['stdout'])
419
420
        # End result should be the same when streaming is enabled
421
        cfg.CONF.set_override(name='stream_output', group='actionrunner', override=True)
422
423
        # Verify initial state
424
        output_dbs = ActionExecutionOutput.get_all()
425
        self.assertEqual(len(output_dbs), 0)
426
427
        action_parameters = {
428
            'param_string': 'test string',
429
            'param_integer': 1,
430
            'param_float': 2.55,
431
            'param_boolean': True,
432
            'param_list': ['a', 'b', 'c'],
433
            'param_object': {'foo': 'bar'}
434
        }
435
436
        runner = self._get_runner(action_db=action_db, entry_point=entry_point)
437
        runner.pre_run()
438
        status, result, _ = runner.run(action_parameters=action_parameters)
439
        runner.post_run(status, result)
440
441
        self.assertEqual(status, action_constants.LIVEACTION_STATUS_SUCCEEDED)
442
        self.assertTrue('PARAM_STRING=test string' in result['stdout'])
443
        self.assertTrue('PARAM_INTEGER=1' in result['stdout'])
444
        self.assertTrue('PARAM_FLOAT=2.55' in result['stdout'])
445
        self.assertTrue('PARAM_BOOLEAN=1' in result['stdout'])
446
        self.assertTrue('PARAM_LIST=a,b,c' in result['stdout'])
447
        self.assertTrue('PARAM_OBJECT={"foo": "bar"}' in result['stdout'])
448
449
        output_dbs = ActionExecutionOutput.query(output_type='stdout')
450
        self.assertEqual(len(output_dbs), 6)
451
        self.assertEqual(output_dbs[0].data, 'PARAM_STRING=test string\n')
452
        self.assertEqual(output_dbs[5].data, 'PARAM_OBJECT={"foo": "bar"}\n')
453
454
        output_dbs = ActionExecutionOutput.query(output_type='stderr')
455
        self.assertEqual(len(output_dbs), 0)
456
457
    @mock.patch('st2common.util.green.shell.subprocess.Popen')
458
    @mock.patch('st2common.util.green.shell.eventlet.spawn')
459
    def test_action_stdout_and_stderr_is_stored_in_the_db(self, mock_spawn, mock_popen):
460
        # Feature is enabled
461
        cfg.CONF.set_override(name='stream_output', group='actionrunner', override=True)
462
463
        # Note: We need to mock spawn function so we can test everything in single event loop
464
        # iteration
465
        mock_spawn.side_effect = blocking_eventlet_spawn
466
467
        # No output to stdout and no result (implicit None)
468
        mock_stdout = [
469
            'stdout line 1\n',
470
            'stdout line 2\n',
471
            'stdout line 3\n',
472
            'stdout line 4\n'
473
        ]
474
        mock_stderr = [
475
            'stderr line 1\n',
476
            'stderr line 2\n',
477
            'stderr line 3\n'
478
        ]
479
480
        mock_process = mock.Mock()
481
        mock_process.returncode = 0
482
        mock_popen.return_value = mock_process
483
        mock_process.stdout.closed = False
484
        mock_process.stderr.closed = False
485
        mock_process.stdout.readline = make_mock_stream_readline(mock_process.stdout, mock_stdout,
486
                                                                 stop_counter=4)
487
        mock_process.stderr.readline = make_mock_stream_readline(mock_process.stderr, mock_stderr,
488
                                                                 stop_counter=3)
489
490
        models = self.fixtures_loader.load_models(
491
            fixtures_pack='generic', fixtures_dict={'actions': ['local_script_with_params.yaml']})
492
        action_db = models['actions']['local_script_with_params.yaml']
493
        entry_point = os.path.join(get_fixtures_base_path(),
494
                                   'generic/actions/local_script_with_params.sh')
495
496
        action_parameters = {
497
            'param_string': 'test string',
498
            'param_integer': 1,
499
            'param_float': 2.55,
500
            'param_boolean': True,
501
            'param_list': ['a', 'b', 'c'],
502
            'param_object': {'foo': 'bar'}
503
        }
504
505
        runner = self._get_runner(action_db=action_db, entry_point=entry_point)
506
        runner.pre_run()
507
        status, result, _ = runner.run(action_parameters=action_parameters)
508
        runner.post_run(status, result)
509
510
        self.assertEqual(result['stdout'],
511
                         'stdout line 1\nstdout line 2\nstdout line 3\nstdout line 4')
512
        self.assertEqual(result['stderr'], 'stderr line 1\nstderr line 2\nstderr line 3')
513
        self.assertEqual(result['return_code'], 0)
514
515
        # Verify stdout and stderr lines have been correctly stored in the db
516
        output_dbs = ActionExecutionOutput.query(output_type='stdout')
517
        self.assertEqual(len(output_dbs), 4)
518
        self.assertEqual(output_dbs[0].data, mock_stdout[0])
519
        self.assertEqual(output_dbs[1].data, mock_stdout[1])
520
        self.assertEqual(output_dbs[2].data, mock_stdout[2])
521
        self.assertEqual(output_dbs[3].data, mock_stdout[3])
522
523
        output_dbs = ActionExecutionOutput.query(output_type='stderr')
524
        self.assertEqual(len(output_dbs), 3)
525
        self.assertEqual(output_dbs[0].data, mock_stderr[0])
526
        self.assertEqual(output_dbs[1].data, mock_stderr[1])
527
        self.assertEqual(output_dbs[2].data, mock_stderr[2])
528
529
    def _get_runner(self, action_db, entry_point):
530
        runner = local_runner.LocalShellRunner(uuid.uuid4().hex)
531
        runner.container_service = RunnerContainerService()
532
        runner.execution = MOCK_EXECUTION
533
        runner.action = action_db
534
        runner.action_name = action_db.name
535
        runner.liveaction_id = uuid.uuid4().hex
536
        runner.entry_point = entry_point
537
        runner.runner_parameters = {}
538
        runner.context = dict()
539
        runner.callback = dict()
540
        runner.libs_dir_path = None
541
        runner.auth_token = mock.Mock()
542
        runner.auth_token.token = 'mock-token'
543
        return runner
544