Completed
Pull Request — master (#3048)
by W
04:48
created

MistralRunnerTest.test_resume_unidentified_tasks()   B

Complexity

Conditions 1

Size

Total Lines 38

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 1
c 2
b 0
f 0
dl 0
loc 38
rs 8.8571
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 copy
17
import uuid
18
19
import mock
20
import yaml
21
22
from mistralclient.api.v2 import executions
23
from mistralclient.api.v2 import tasks
24
from mistralclient.api.v2 import workbooks
25
from mistralclient.api.v2 import workflows
26
from oslo_config import cfg
27
28
# XXX: actionsensor import depends on config being setup.
29
import st2tests.config as tests_config
30
tests_config.parse_args()
31
32
from mistral_v2 import MistralRunner
33
from st2common.bootstrap import actionsregistrar
34
import st2common.bootstrap.runnersregistrar as runners_registrar
35
from st2common.constants import action as action_constants
36
from st2common.models.db.liveaction import LiveActionDB
37
from st2common.persistence.liveaction import LiveAction
38
from st2common.runners import base as runners
39
from st2common.services import action as action_service
40
from st2common.transport.liveaction import LiveActionPublisher
41
from st2common.transport.publishers import CUDPublisher
42
from st2common.util import loader
43
from st2tests import DbTestCase
44
from st2tests import fixturesloader
45
from st2tests.mocks.liveaction import MockLiveActionPublisher
46
47
48
TEST_FIXTURES = {
49
    'workflows': [
50
        'workflow_v2.yaml',
51
        'workbook_v2_many_workflows.yaml'
52
    ],
53
    'actions': [
54
        'workflow_v2.yaml',
55
        'workbook_v2_many_workflows.yaml'
56
    ]
57
}
58
59
TEST_PACK = 'mistral_tests'
60
TEST_PACK_PATH = fixturesloader.get_fixtures_packs_base_path() + '/' + TEST_PACK
61
62
PACKS = [
63
    TEST_PACK_PATH,
64
    fixturesloader.get_fixtures_packs_base_path() + '/core'
65
]
66
67
# Action executions requirements
68
ACTION_PARAMS = {'friend': 'Rocky'}
69
NON_EMPTY_RESULT = 'non-empty'
70
71
# Workbook with multiple workflows
72
WB1_META_FILE_NAME = TEST_FIXTURES['workflows'][1]
73
WB1_META_FILE_PATH = TEST_PACK_PATH + '/actions/' + WB1_META_FILE_NAME
74
WB1_META_CONTENT = loader.load_meta_file(WB1_META_FILE_PATH)
75
WB1_NAME = WB1_META_CONTENT['pack'] + '.' + WB1_META_CONTENT['name']
76
WB1_ENTRY_POINT = TEST_PACK_PATH + '/actions/' + WB1_META_CONTENT['entry_point']
77
WB1_SPEC = yaml.safe_load(MistralRunner.get_workflow_definition(WB1_ENTRY_POINT))
78
WB1_YAML = yaml.safe_dump(WB1_SPEC, default_flow_style=False)
79
WB1 = workbooks.Workbook(None, {'name': WB1_NAME, 'definition': WB1_YAML})
80
WB1_MAIN_EXEC = {'id': str(uuid.uuid4()), 'state': 'RUNNING'}
81
WB1_MAIN_EXEC['workflow_name'] = WB1_NAME + '.main'
82
WB1_MAIN_EXEC_ERRORED = copy.deepcopy(WB1_MAIN_EXEC)
83
WB1_MAIN_EXEC_ERRORED['state'] = 'ERROR'
84
WB1_MAIN_TASK1 = {'id': str(uuid.uuid4()), 'name': 'greet', 'state': 'ERROR'}
85
WB1_MAIN_TASKS = [tasks.Task(None, WB1_MAIN_TASK1)]
86
WB1_MAIN_TASK_ID = WB1_MAIN_TASK1['id']
87
WB1_SUB1_EXEC = {'id': str(uuid.uuid4()), 'state': 'RUNNING', 'task_execution_id': WB1_MAIN_TASK_ID}
88
WB1_SUB1_EXEC['workflow_name'] = WB1_NAME + '.subflow1'
89
WB1_SUB1_EXEC_ERRORED = copy.deepcopy(WB1_SUB1_EXEC)
90
WB1_SUB1_EXEC_ERRORED['state'] = 'ERROR'
91
WB1_SUB1_TASK1 = {'id': str(uuid.uuid4()), 'name': 'say-greeting', 'state': 'SUCCESS'}
92
WB1_SUB1_TASK2 = {'id': str(uuid.uuid4()), 'name': 'say-friend', 'state': 'ERROR'}
93
WB1_SUB1_TASKS = [tasks.Task(None, WB1_SUB1_TASK1), tasks.Task(None, WB1_SUB1_TASK2)]
94
95
# Non-workbook with a single workflow
96
WF1_META_FILE_NAME = TEST_FIXTURES['workflows'][0]
97
WF1_META_FILE_PATH = TEST_PACK_PATH + '/actions/' + WF1_META_FILE_NAME
98
WF1_META_CONTENT = loader.load_meta_file(WF1_META_FILE_PATH)
99
WF1_NAME = WF1_META_CONTENT['pack'] + '.' + WF1_META_CONTENT['name']
100
WF1_ENTRY_POINT = TEST_PACK_PATH + '/actions/' + WF1_META_CONTENT['entry_point']
101
WF1_SPEC = yaml.safe_load(MistralRunner.get_workflow_definition(WF1_ENTRY_POINT))
102
WF1_YAML = yaml.safe_dump(WF1_SPEC, default_flow_style=False)
103
WF1 = workflows.Workflow(None, {'name': WF1_NAME, 'definition': WF1_YAML})
104
WF1_EXEC = {'id': str(uuid.uuid4()), 'state': 'ERROR', 'workflow_name': WF1_NAME}
105
WF1_EXEC_NOT_RERUNABLE = copy.deepcopy(WF1_EXEC)
106
WF1_EXEC_NOT_RERUNABLE['state'] = 'PAUSED'
107
WF1_TASK1 = {'id': str(uuid.uuid4()), 'name': 'say-greeting', 'state': 'SUCCESS'}
108
WF1_TASK2 = {'id': str(uuid.uuid4()), 'name': 'say-friend', 'state': 'SUCCESS'}
109
WF1_TASKS = [tasks.Task(None, WF1_TASK1), tasks.Task(None, WF1_TASK2)]
110
111
112
@mock.patch.object(
113
    CUDPublisher,
114
    'publish_update',
115
    mock.MagicMock(return_value=None))
116
@mock.patch.object(
117
    CUDPublisher,
118
    'publish_create',
119
    mock.MagicMock(side_effect=MockLiveActionPublisher.publish_create))
120
@mock.patch.object(
121
    LiveActionPublisher,
122
    'publish_state',
123
    mock.MagicMock(side_effect=MockLiveActionPublisher.publish_state))
124
class MistralRunnerTest(DbTestCase):
125
126
    @classmethod
127
    def setUpClass(cls):
128
        super(MistralRunnerTest, cls).setUpClass()
129
130
        # Override the retry configuration here otherwise st2tests.config.parse_args
131
        # in DbTestCase.setUpClass will reset these overrides.
132
        cfg.CONF.set_override('retry_exp_msec', 100, group='mistral')
133
        cfg.CONF.set_override('retry_exp_max_msec', 200, group='mistral')
134
        cfg.CONF.set_override('retry_stop_max_msec', 200, group='mistral')
135
136
        # Register runners.
137
        runners_registrar.register_runners()
138
139
        # Register test pack(s).
140
        registrar = actionsregistrar.ActionsRegistrar(
141
            use_pack_cache=False,
142
            fail_on_failure=True
143
        )
144
145
        for pack in PACKS:
146
            registrar.register_from_pack(pack)
147
148
    def setUp(self):
149
        super(MistralRunnerTest, self).setUp()
150
        cfg.CONF.set_override('api_url', 'http://0.0.0.0:9101', group='auth')
151
152
        # Mock the local runner run method.
153
        local_runner_cls = self.get_runner_class('local_runner')
154
        local_run_result = (action_constants.LIVEACTION_STATUS_SUCCEEDED, NON_EMPTY_RESULT, None)
155
        local_runner_cls.run = mock.Mock(return_value=local_run_result)
156
157
    @classmethod
158
    def get_runner_class(cls, runner_name):
159
        return runners.get_runner(runner_name).__class__
160
161
    @mock.patch.object(
162
        workflows.WorkflowManager, 'list',
163
        mock.MagicMock(return_value=[]))
164
    @mock.patch.object(
165
        workflows.WorkflowManager, 'get',
166
        mock.MagicMock(return_value=WF1))
167
    @mock.patch.object(
168
        workflows.WorkflowManager, 'create',
169
        mock.MagicMock(return_value=[WF1]))
170
    @mock.patch.object(
171
        executions.ExecutionManager, 'create',
172
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
173
    def test_resume_option(self):
174
        patched_mistral_runner = self.get_runner_class('mistral_v2')
175
176
        mock_resume_result = (
177
            action_constants.LIVEACTION_STATUS_RUNNING,
178
            {'tasks': []},
179
            {'execution_id': str(uuid.uuid4())}
180
        )
181
182
        with mock.patch.object(patched_mistral_runner, 'resume',
183
                               mock.MagicMock(return_value=mock_resume_result)):
184
185
            liveaction1 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
186
            liveaction1, execution1 = action_service.request(liveaction1)
187
            self.assertFalse(patched_mistral_runner.resume.called)
188
189
            # Rerun the execution.
190
            context = {
191
                're-run': {
192
                    'ref': execution1.id,
193
                    'tasks': ['x']
194
                }
195
            }
196
197
            liveaction2 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS, context=context)
198
            liveaction2, execution2 = action_service.request(liveaction2)
199
            liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
200
            self.assertEqual(liveaction2.status, action_constants.LIVEACTION_STATUS_RUNNING)
201
202
            task_specs = {
203
                'x': {
204
                    'reset': False
205
                }
206
            }
207
208
            patched_mistral_runner.resume.assert_called_with(
209
                ex_ref=execution1,
210
                task_specs=task_specs
211
            )
212
213
    @mock.patch.object(
214
        workflows.WorkflowManager, 'list',
215
        mock.MagicMock(return_value=[]))
216
    @mock.patch.object(
217
        workflows.WorkflowManager, 'get',
218
        mock.MagicMock(return_value=WF1))
219
    @mock.patch.object(
220
        workflows.WorkflowManager, 'create',
221
        mock.MagicMock(return_value=[WF1]))
222
    @mock.patch.object(
223
        executions.ExecutionManager, 'create',
224
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
225
    def test_resume_option_reset_tasks(self):
226
        patched_mistral_runner = self.get_runner_class('mistral_v2')
227
228
        mock_resume_result = (
229
            action_constants.LIVEACTION_STATUS_RUNNING,
230
            {'tasks': []},
231
            {'execution_id': str(uuid.uuid4())}
232
        )
233
234
        with mock.patch.object(patched_mistral_runner, 'resume',
235
                               mock.MagicMock(return_value=mock_resume_result)):
236
237
            liveaction1 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
238
            liveaction1, execution1 = action_service.request(liveaction1)
239
            self.assertFalse(patched_mistral_runner.resume.called)
240
241
            # Rerun the execution.
242
            context = {
243
                're-run': {
244
                    'ref': execution1.id,
245
                    'tasks': ['x', 'y'],
246
                    'reset': ['y']
247
                }
248
            }
249
250
            liveaction2 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS, context=context)
251
            liveaction2, execution2 = action_service.request(liveaction2)
252
            liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
253
            self.assertEqual(liveaction2.status, action_constants.LIVEACTION_STATUS_RUNNING)
254
255
            task_specs = {
256
                'x': {
257
                    'reset': False
258
                },
259
                'y': {
260
                    'reset': True
261
                }
262
            }
263
264
            patched_mistral_runner.resume.assert_called_with(
265
                ex_ref=execution1,
266
                task_specs=task_specs
267
            )
268
269
    @mock.patch.object(
270
        workflows.WorkflowManager, 'list',
271
        mock.MagicMock(return_value=[]))
272
    @mock.patch.object(
273
        workflows.WorkflowManager, 'get',
274
        mock.MagicMock(return_value=WF1))
275
    @mock.patch.object(
276
        workflows.WorkflowManager, 'create',
277
        mock.MagicMock(return_value=[WF1]))
278
    @mock.patch.object(
279
        executions.ExecutionManager, 'create',
280
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC_NOT_RERUNABLE)))
281
    @mock.patch.object(
282
        executions.ExecutionManager, 'get',
283
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC_NOT_RERUNABLE)))
284
    @mock.patch.object(
285
        tasks.TaskManager, 'list',
286
        mock.MagicMock(return_value=WF1_TASKS))
287
    def test_resume_workflow_not_in_rerunable_state(self):
288
        liveaction1 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
289
        liveaction1, execution1 = action_service.request(liveaction1)
290
291
        # Rerun the execution.
292
        context = {
293
            're-run': {
294
                'ref': execution1.id,
295
                'tasks': ['say-friend']
296
            }
297
        }
298
299
        liveaction2 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS, context=context)
300
        liveaction2, execution2 = action_service.request(liveaction2)
301
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
302
        self.assertEqual(liveaction2.status, action_constants.LIVEACTION_STATUS_FAILED)
303
        self.assertIn('not in a rerunable state', liveaction2.result.get('error'))
304
305
    @mock.patch.object(
306
        workflows.WorkflowManager, 'list',
307
        mock.MagicMock(return_value=[]))
308
    @mock.patch.object(
309
        workflows.WorkflowManager, 'get',
310
        mock.MagicMock(return_value=WF1))
311
    @mock.patch.object(
312
        workflows.WorkflowManager, 'create',
313
        mock.MagicMock(return_value=[WF1]))
314
    @mock.patch.object(
315
        executions.ExecutionManager, 'list',
316
        mock.MagicMock(return_value=[executions.Execution(None, WF1_EXEC)]))
317
    @mock.patch.object(
318
        executions.ExecutionManager, 'create',
319
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
320
    @mock.patch.object(
321
        executions.ExecutionManager, 'get',
322
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
323
    @mock.patch.object(
324
        tasks.TaskManager, 'list',
325
        mock.MagicMock(return_value=WF1_TASKS))
326
    def test_resume_tasks_not_in_rerunable_state(self):
327
        liveaction1 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
328
        liveaction1, execution1 = action_service.request(liveaction1)
329
330
        # Rerun the execution.
331
        context = {
332
            're-run': {
333
                'ref': execution1.id,
334
                'tasks': ['say-friend']
335
            }
336
        }
337
338
        liveaction2 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS, context=context)
339
        liveaction2, execution2 = action_service.request(liveaction2)
340
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
341
        self.assertEqual(liveaction2.status, action_constants.LIVEACTION_STATUS_FAILED)
342
        self.assertIn('Unable to identify rerunable', liveaction2.result.get('error'))
343
344
    @mock.patch.object(
345
        workflows.WorkflowManager, 'list',
346
        mock.MagicMock(return_value=[]))
347
    @mock.patch.object(
348
        workflows.WorkflowManager, 'get',
349
        mock.MagicMock(return_value=WF1))
350
    @mock.patch.object(
351
        workflows.WorkflowManager, 'create',
352
        mock.MagicMock(return_value=[WF1]))
353
    @mock.patch.object(
354
        executions.ExecutionManager, 'list',
355
        mock.MagicMock(return_value=[executions.Execution(None, WF1_EXEC)]))
356
    @mock.patch.object(
357
        executions.ExecutionManager, 'create',
358
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
359
    @mock.patch.object(
360
        executions.ExecutionManager, 'get',
361
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
362
    @mock.patch.object(
363
        tasks.TaskManager, 'list',
364
        mock.MagicMock(return_value=WF1_TASKS))
365
    def test_resume_unidentified_tasks(self):
366
        liveaction1 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
367
        liveaction1, execution1 = action_service.request(liveaction1)
368
369
        # Rerun the execution.
370
        context = {
371
            're-run': {
372
                'ref': execution1.id,
373
                'tasks': ['x']
374
            }
375
        }
376
377
        liveaction2 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS, context=context)
378
        liveaction2, execution2 = action_service.request(liveaction2)
379
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
380
        self.assertEqual(liveaction2.status, action_constants.LIVEACTION_STATUS_FAILED)
381
        self.assertIn('Unable to identify', liveaction2.result.get('error'))
382
383
    @mock.patch.object(
384
        workflows.WorkflowManager, 'list',
385
        mock.MagicMock(return_value=[]))
386
    @mock.patch.object(
387
        workflows.WorkflowManager, 'get',
388
        mock.MagicMock(return_value=WF1))
389
    @mock.patch.object(
390
        workbooks.WorkbookManager, 'create',
391
        mock.MagicMock(return_value=WB1))
392
    @mock.patch.object(
393
        executions.ExecutionManager, 'create',
394
        mock.MagicMock(return_value=executions.Execution(None, WB1_MAIN_EXEC)))
395
    @mock.patch.object(
396
        executions.ExecutionManager, 'get',
397
        mock.MagicMock(return_value=executions.Execution(None, WB1_MAIN_EXEC_ERRORED)))
398
    @mock.patch.object(
399
        executions.ExecutionManager, 'list',
400
        mock.MagicMock(
401
            return_value=[
402
                executions.Execution(None, WB1_MAIN_EXEC_ERRORED),
403
                executions.Execution(None, WB1_SUB1_EXEC_ERRORED)]))
404
    @mock.patch.object(
405
        tasks.TaskManager, 'list',
406
        mock.MagicMock(side_effect=[WB1_MAIN_TASKS, WB1_SUB1_TASKS]))
407
    @mock.patch.object(
408
        tasks.TaskManager, 'rerun',
409
        mock.MagicMock(return_value=None))
410
    def test_resume_subworkflow_task(self):
411
        liveaction1 = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS)
412
        liveaction1, execution1 = action_service.request(liveaction1)
413
414
        # Rerun the execution.
415
        context = {
416
            're-run': {
417
                'ref': execution1.id,
418
                'tasks': ['greet.say-friend']
419
            }
420
        }
421
422
        liveaction2 = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS, context=context)
423
        liveaction2, execution2 = action_service.request(liveaction2)
424
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
425
426
        self.assertEqual(liveaction2.status, action_constants.LIVEACTION_STATUS_RUNNING)
427
428
        expected_env = {
429
            'st2_liveaction_id': str(liveaction2.id),
430
            'st2_execution_id': str(execution2.id),
431
            '__actions': {
432
                'st2.action': {
433
                    'st2_context': {
434
                        'api_url': 'http://0.0.0.0:9101/v1',
435
                        'endpoint': 'http://0.0.0.0:9101/v1/actionexecutions',
436
                        'notify': {},
437
                        'parent': {
438
                            're-run': context['re-run'],
439
                            'execution_id': str(execution2.id)
440
                        },
441
                        'skip_notify_tasks': []
442
                    }
443
                }
444
            },
445
            'st2_action_api_url': 'http://0.0.0.0:9101/v1'
446
        }
447
448
        tasks.TaskManager.rerun.assert_called_with(
449
            WB1_SUB1_TASK2['id'],
450
            reset=False,
451
            env=expected_env
452
        )
453
454
    @mock.patch.object(
455
        workflows.WorkflowManager, 'list',
456
        mock.MagicMock(return_value=[]))
457
    @mock.patch.object(
458
        workflows.WorkflowManager, 'get',
459
        mock.MagicMock(return_value=WF1))
460
    @mock.patch.object(
461
        workbooks.WorkbookManager, 'create',
462
        mock.MagicMock(return_value=WB1))
463
    @mock.patch.object(
464
        executions.ExecutionManager, 'create',
465
        mock.MagicMock(return_value=executions.Execution(None, WB1_MAIN_EXEC)))
466
    @mock.patch.object(
467
        executions.ExecutionManager, 'get',
468
        mock.MagicMock(return_value=executions.Execution(None, WB1_MAIN_EXEC_ERRORED)))
469
    @mock.patch.object(
470
        executions.ExecutionManager, 'list',
471
        mock.MagicMock(
472
            return_value=[
473
                executions.Execution(None, WB1_MAIN_EXEC_ERRORED),
474
                executions.Execution(None, WB1_SUB1_EXEC_ERRORED)]))
475
    @mock.patch.object(
476
        tasks.TaskManager, 'list',
477
        mock.MagicMock(side_effect=[WB1_MAIN_TASKS, WB1_SUB1_TASKS]))
478
    def test_resume_unidentified_subworkflow_task(self):
479
        liveaction1 = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS)
480
        liveaction1, execution1 = action_service.request(liveaction1)
481
482
        # Rerun the execution.
483
        context = {
484
            're-run': {
485
                'ref': execution1.id,
486
                'tasks': ['greet.x']
487
            }
488
        }
489
490
        liveaction2 = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS, context=context)
491
        liveaction2, execution2 = action_service.request(liveaction2)
492
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
493
        self.assertEqual(liveaction2.status, action_constants.LIVEACTION_STATUS_FAILED)
494
        self.assertIn('Unable to identify', liveaction2.result.get('error'))
495
496
    @mock.patch.object(
497
        workflows.WorkflowManager, 'list',
498
        mock.MagicMock(return_value=[]))
499
    @mock.patch.object(
500
        workflows.WorkflowManager, 'get',
501
        mock.MagicMock(return_value=WF1))
502
    @mock.patch.object(
503
        workbooks.WorkbookManager, 'create',
504
        mock.MagicMock(return_value=WB1))
505
    @mock.patch.object(
506
        executions.ExecutionManager, 'create',
507
        mock.MagicMock(return_value=executions.Execution(None, WB1_MAIN_EXEC)))
508
    @mock.patch.object(
509
        executions.ExecutionManager, 'get',
510
        mock.MagicMock(return_value=executions.Execution(None, WB1_MAIN_EXEC_ERRORED)))
511
    @mock.patch.object(
512
        executions.ExecutionManager, 'list',
513
        mock.MagicMock(
514
            return_value=[
515
                executions.Execution(None, WB1_MAIN_EXEC_ERRORED),
516
                executions.Execution(None, WB1_SUB1_EXEC_ERRORED)]))
517
    @mock.patch.object(
518
        tasks.TaskManager, 'list',
519
        mock.MagicMock(side_effect=[WB1_MAIN_TASKS, WB1_SUB1_TASKS]))
520
    @mock.patch.object(
521
        tasks.TaskManager, 'rerun',
522
        mock.MagicMock(return_value=None))
523
    def test_resume_and_reset_subworkflow_task(self):
524
        liveaction1 = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS)
525
        liveaction1, execution1 = action_service.request(liveaction1)
526
527
        # Rerun the execution.
528
        context = {
529
            're-run': {
530
                'ref': execution1.id,
531
                'tasks': ['greet.say-friend'],
532
                'reset': ['greet.say-friend']
533
            }
534
        }
535
536
        liveaction2 = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS, context=context)
537
        liveaction2, execution2 = action_service.request(liveaction2)
538
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
539
540
        self.assertEqual(liveaction2.status, action_constants.LIVEACTION_STATUS_RUNNING)
541
542
        expected_env = {
543
            'st2_liveaction_id': str(liveaction2.id),
544
            'st2_execution_id': str(execution2.id),
545
            '__actions': {
546
                'st2.action': {
547
                    'st2_context': {
548
                        'api_url': 'http://0.0.0.0:9101/v1',
549
                        'endpoint': 'http://0.0.0.0:9101/v1/actionexecutions',
550
                        'notify': {},
551
                        'parent': {
552
                            're-run': context['re-run'],
553
                            'execution_id': str(execution2.id)
554
                        },
555
                        'skip_notify_tasks': []
556
                    }
557
                }
558
            },
559
            'st2_action_api_url': 'http://0.0.0.0:9101/v1'
560
        }
561
562
        tasks.TaskManager.rerun.assert_called_with(
563
            WB1_SUB1_TASK2['id'],
564
            reset=True,
565
            env=expected_env
566
        )
567