Test Failed
Push — master ( 21460f...e380d0 )
by Tomaz
01:48
created

runners/mistral_v2/tests/unit/test_mistral_v2.py (2 issues)

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
from __future__ import absolute_import
17
import copy
18
import uuid
19
20
import mock
21
import requests
22
import yaml
23
24
from mistralclient.api.base import APIException
25
from mistralclient.api.v2 import executions
26
from mistralclient.api.v2 import workbooks
27
from mistralclient.api.v2 import workflows
28
from oslo_config import cfg
29
30
# XXX: actionsensor import depends on config being setup.
31
import st2tests.config as tests_config
32
tests_config.parse_args()
33
34
from mistral_v2.mistral_v2 import MistralRunner
35
from st2common.bootstrap import actionsregistrar
36
from st2common.bootstrap import runnersregistrar
37
from st2common.constants import action as action_constants
38
from st2common.models.api.notification import NotificationsHelper
39
from st2common.models.db.liveaction import LiveActionDB
40
from st2common.persistence.liveaction import LiveAction
41
from st2common.runners import base as runners
42
from st2common.services import action as action_service
43
from st2common.transport.liveaction import LiveActionPublisher
44
from st2common.transport.publishers import CUDPublisher
45
from st2common.util import loader
46
from st2tests import DbTestCase
47
from st2tests import fixturesloader
48
from st2tests.mocks.liveaction import MockLiveActionPublisher
49
50
51
TEST_FIXTURES = {
52
    'workflows': [
53
        'workbook_v2.yaml',
54
        'workbook_v2_many_workflows.yaml',
55
        'workbook_v2_many_workflows_no_default.yaml',
56
        'workflow_v2.yaml',
57
        'workflow_v2_many_workflows.yaml'
58
    ],
59
    'actions': [
60
        'workbook_v2.yaml',
61
        'workbook_v2_many_workflows.yaml',
62
        'workbook_v2_many_workflows_no_default.yaml',
63
        'workflow_v2.yaml',
64
        'workflow_v2_many_workflows.yaml',
65
        'workbook_v2_name_mismatch.yaml',
66
        'workflow_v2_name_mismatch.yaml'
67
    ]
68
}
69
70
TEST_PACK = 'mistral_tests'
71
TEST_PACK_PATH = fixturesloader.get_fixtures_packs_base_path() + '/' + TEST_PACK
72
73
PACKS = [
74
    TEST_PACK_PATH,
75
    fixturesloader.get_fixtures_packs_base_path() + '/core'
76
]
77
78
# Action executions requirements
79
MISTRAL_EXECUTION = {'id': str(uuid.uuid4()), 'state': 'RUNNING', 'workflow_name': None}
80
ACTION_PARAMS = {'friend': 'Rocky'}
81
NON_EMPTY_RESULT = 'non-empty'
82
83
# Workbook with a single workflow
84
WB1_META_FILE_NAME = TEST_FIXTURES['workflows'][0]
85
WB1_META_FILE_PATH = TEST_PACK_PATH + '/actions/' + WB1_META_FILE_NAME
86
WB1_META_CONTENT = loader.load_meta_file(WB1_META_FILE_PATH)
87
WB1_NAME = WB1_META_CONTENT['pack'] + '.' + WB1_META_CONTENT['name']
88
WB1_ENTRY_POINT = TEST_PACK_PATH + '/actions/' + WB1_META_CONTENT['entry_point']
89
WB1_ENTRY_POINT_X = WB1_ENTRY_POINT.replace(WB1_META_FILE_NAME, 'xformed_' + WB1_META_FILE_NAME)
90
WB1_SPEC = yaml.safe_load(MistralRunner.get_workflow_definition(WB1_ENTRY_POINT_X))
91
WB1_YAML = yaml.safe_dump(WB1_SPEC, default_flow_style=False)
92
WB1 = workbooks.Workbook(None, {'name': WB1_NAME, 'definition': WB1_YAML})
93
WB1_OLD = workbooks.Workbook(None, {'name': WB1_NAME, 'definition': ''})
94
WB1_EXEC = copy.deepcopy(MISTRAL_EXECUTION)
95
WB1_EXEC['workflow_name'] = WB1_NAME
96
97
# Workbook with many workflows
98
WB2_META_FILE_NAME = TEST_FIXTURES['workflows'][1]
99
WB2_META_FILE_PATH = TEST_PACK_PATH + '/actions/' + WB2_META_FILE_NAME
100
WB2_META_CONTENT = loader.load_meta_file(WB2_META_FILE_PATH)
101
WB2_NAME = WB2_META_CONTENT['pack'] + '.' + WB2_META_CONTENT['name']
102
WB2_ENTRY_POINT = TEST_PACK_PATH + '/actions/' + WB2_META_CONTENT['entry_point']
103
WB2_ENTRY_POINT_X = WB2_ENTRY_POINT.replace(WB2_META_FILE_NAME, 'xformed_' + WB2_META_FILE_NAME)
104
WB2_SPEC = yaml.safe_load(MistralRunner.get_workflow_definition(WB2_ENTRY_POINT_X))
105
WB2_YAML = yaml.safe_dump(WB2_SPEC, default_flow_style=False)
106
WB2 = workbooks.Workbook(None, {'name': WB2_NAME, 'definition': WB2_YAML})
107
WB2_EXEC = copy.deepcopy(MISTRAL_EXECUTION)
108
WB2_EXEC['workflow_name'] = WB2_NAME
109
110
# Workbook with many workflows but no default workflow is defined
111
WB3_META_FILE_NAME = TEST_FIXTURES['workflows'][2]
112
WB3_META_FILE_PATH = TEST_PACK_PATH + '/actions/' + WB3_META_FILE_NAME
113
WB3_META_CONTENT = loader.load_meta_file(WB3_META_FILE_PATH)
114
WB3_NAME = WB3_META_CONTENT['pack'] + '.' + WB3_META_CONTENT['name']
115
WB3_ENTRY_POINT = TEST_PACK_PATH + '/actions/' + WB3_META_CONTENT['entry_point']
116
WB3_ENTRY_POINT_X = WB3_ENTRY_POINT.replace(WB3_META_FILE_NAME, 'xformed_' + WB3_META_FILE_NAME)
117
WB3_SPEC = yaml.safe_load(MistralRunner.get_workflow_definition(WB3_ENTRY_POINT_X))
118
WB3_YAML = yaml.safe_dump(WB3_SPEC, default_flow_style=False)
119
WB3 = workbooks.Workbook(None, {'name': WB3_NAME, 'definition': WB3_YAML})
120
WB3_EXEC = copy.deepcopy(MISTRAL_EXECUTION)
121
WB3_EXEC['workflow_name'] = WB3_NAME
122
123
# Non-workbook with a single workflow
124
WF1_META_FILE_NAME = TEST_FIXTURES['workflows'][3]
125
WF1_META_FILE_PATH = TEST_PACK_PATH + '/actions/' + WF1_META_FILE_NAME
126
WF1_META_CONTENT = loader.load_meta_file(WF1_META_FILE_PATH)
127
WF1_NAME = WF1_META_CONTENT['pack'] + '.' + WF1_META_CONTENT['name']
128
WF1_ENTRY_POINT = TEST_PACK_PATH + '/actions/' + WF1_META_CONTENT['entry_point']
129
WF1_ENTRY_POINT_X = WF1_ENTRY_POINT.replace(WF1_META_FILE_NAME, 'xformed_' + WF1_META_FILE_NAME)
130
WF1_SPEC = yaml.safe_load(MistralRunner.get_workflow_definition(WF1_ENTRY_POINT_X))
131
WF1_YAML = yaml.safe_dump(WF1_SPEC, default_flow_style=False)
132
WF1 = workflows.Workflow(None, {'name': WF1_NAME, 'definition': WF1_YAML})
133
WF1_OLD = workflows.Workflow(None, {'name': WF1_NAME, 'definition': ''})
134
WF1_EXEC = copy.deepcopy(MISTRAL_EXECUTION)
135
WF1_EXEC['workflow_name'] = WF1_NAME
136
137
# Non-workbook with a many workflows
138
WF2_META_FILE_NAME = TEST_FIXTURES['workflows'][4]
139
WF2_META_FILE_PATH = TEST_PACK_PATH + '/actions/' + WF2_META_FILE_NAME
140
WF2_META_CONTENT = loader.load_meta_file(WF2_META_FILE_PATH)
141
WF2_NAME = WF2_META_CONTENT['pack'] + '.' + WF2_META_CONTENT['name']
142
WF2_ENTRY_POINT = TEST_PACK_PATH + '/actions/' + WF2_META_CONTENT['entry_point']
143
WF2_ENTRY_POINT_X = WF2_ENTRY_POINT.replace(WF2_META_FILE_NAME, 'xformed_' + WF2_META_FILE_NAME)
144
WF2_SPEC = yaml.safe_load(MistralRunner.get_workflow_definition(WF2_ENTRY_POINT_X))
145
WF2_YAML = yaml.safe_dump(WF2_SPEC, default_flow_style=False)
146
WF2 = workflows.Workflow(None, {'name': WF2_NAME, 'definition': WF2_YAML})
147
WF2_EXEC = copy.deepcopy(MISTRAL_EXECUTION)
148
WF2_EXEC['workflow_name'] = WF2_NAME
149
150
# Data for the notify param
151
NOTIFY = [{'type': 'st2'}]
152
153
154
@mock.patch.object(
155
    CUDPublisher,
156
    'publish_update',
157
    mock.MagicMock(return_value=None))
158
@mock.patch.object(
159
    CUDPublisher,
160
    'publish_create',
161
    mock.MagicMock(side_effect=MockLiveActionPublisher.publish_create))
162
@mock.patch.object(
163
    LiveActionPublisher,
164
    'publish_state',
165
    mock.MagicMock(side_effect=MockLiveActionPublisher.publish_state))
166
class MistralRunnerTest(DbTestCase):
167
168
    @classmethod
169
    def setUpClass(cls):
170
        super(MistralRunnerTest, cls).setUpClass()
171
172
        # Override the retry configuration here otherwise st2tests.config.parse_args
173
        # in DbTestCase.setUpClass will reset these overrides.
174
        cfg.CONF.set_override('retry_exp_msec', 100, group='mistral')
175
        cfg.CONF.set_override('retry_exp_max_msec', 200, group='mistral')
176
        cfg.CONF.set_override('retry_stop_max_msec', 200, group='mistral')
177
        cfg.CONF.set_override('api_url', 'http://0.0.0.0:9101', group='auth')
178
179
        # Register runners.
180
        runnersregistrar.register_runners()
181
182
        # Register test pack(s).
183
        actions_registrar = actionsregistrar.ActionsRegistrar(
184
            use_pack_cache=False,
185
            fail_on_failure=True
186
        )
187
188
        for pack in PACKS:
189
            actions_registrar.register_from_pack(pack)
190
191
    @classmethod
192
    def get_runner_class(cls, runner_name):
193
        return runners.get_runner(runner_name, runner_name).__class__
194
195
    def test_build_context(self):
196
        parent = {
197
            'mistral': {
198
                'workflow_name': 'foo',
199
                'workflow_execution_id': 'b222b934-7473-4cd4-a2ec-e204a8c93848',
200
                'task_tags': None,
201
                'task_name': 'some_fancy_wf_task',
202
                'task_id': '6c7d4334-3e7d-49c6-918d-698e846affaf',
203
                'action_execution_id': '24da5c88-834c-4a65-8b56-4ddbd654eb68'
204
            }
205
        }
206
207
        current = {
208
            'workflow_name': 'foo.subwf',
209
            'workflow_execution_id': '135e3446-4c89-4afe-821f-6ec6a0849b27'
210
        }
211
212
        context = MistralRunner._build_mistral_context(parent, current)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _build_mistral_context was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
213
        self.assertTrue(context is not None)
214
        self.assertTrue('parent' in list(context['mistral'].keys()))
215
216
        parent_dict = {
217
            'workflow_name': parent['mistral']['workflow_name'],
218
            'workflow_execution_id': parent['mistral']['workflow_execution_id']
219
        }
220
221
        self.assertDictEqual(context['mistral']['parent'], parent_dict)
222
        self.assertEqual(context['mistral']['workflow_execution_id'],
223
                         current['workflow_execution_id'])
224
225
        parent = None
226
        context = MistralRunner._build_mistral_context(parent, current)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _build_mistral_context was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
227
        self.assertDictEqual(context['mistral'], current)
228
229
    @mock.patch.object(
230
        workflows.WorkflowManager, 'list',
231
        mock.MagicMock(return_value=[]))
232
    @mock.patch.object(
233
        workflows.WorkflowManager, 'get',
234
        mock.MagicMock(return_value=WF1))
235
    @mock.patch.object(
236
        workflows.WorkflowManager, 'create',
237
        mock.MagicMock(return_value=[WF1]))
238
    @mock.patch.object(
239
        executions.ExecutionManager, 'create',
240
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
241
    def test_launch_workflow(self):
242
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
243
        liveaction, execution = action_service.request(liveaction)
244
        liveaction = LiveAction.get_by_id(str(liveaction.id))
245
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)
246
247
        mistral_context = liveaction.context.get('mistral', None)
248
        self.assertIsNotNone(mistral_context)
249
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
250
        self.assertEqual(mistral_context['workflow_name'], WF1_EXEC.get('workflow_name'))
251
252
        workflow_input = copy.deepcopy(ACTION_PARAMS)
253
        workflow_input.update({'count': '3'})
254
255
        env = {
256
            'st2_execution_id': str(execution.id),
257
            'st2_liveaction_id': str(liveaction.id),
258
            'st2_action_api_url': 'http://0.0.0.0:9101/v1',
259
            '__actions': {
260
                'st2.action': {
261
                    'st2_context': {
262
                        'api_url': 'http://0.0.0.0:9101/v1',
263
                        'endpoint': 'http://0.0.0.0:9101/v1/actionexecutions',
264
                        'parent': {
265
                            'pack': 'mistral_tests',
266
                            'execution_id': str(execution.id)
267
                        },
268
                        'notify': {},
269
                        'skip_notify_tasks': []
270
                    }
271
                }
272
            }
273
        }
274
275
        executions.ExecutionManager.create.assert_called_with(
276
            WF1_NAME, workflow_input=workflow_input, env=env, notify=NOTIFY)
277
278
    @mock.patch.object(
279
        workflows.WorkflowManager, 'list',
280
        mock.MagicMock(return_value=[]))
281
    @mock.patch.object(
282
        workflows.WorkflowManager, 'get',
283
        mock.MagicMock(return_value=WF1))
284
    @mock.patch.object(
285
        workflows.WorkflowManager, 'create',
286
        mock.MagicMock(return_value=[WF1]))
287
    @mock.patch.object(
288
        executions.ExecutionManager, 'create',
289
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
290
    def test_launch_workflow_under_parent_chain_with_jinja_params(self):
291
        ac_ctx = {
292
            'chain': {
293
                'params': {
294
                    'var1': 'foobar',
295
                    'var2': '{{foobar}}',
296
                    'var3': ['{{foo}}', '{{bar}}'],
297
                    'var4': {
298
                        'foobar': '{{foobar}}'
299
                    },
300
                    'var5': {
301
                        'foobar': '{% for item in items %}foobar{% end for %}'
302
                    }
303
                }
304
            }
305
        }
306
307
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS, context=ac_ctx)
308
        liveaction, execution = action_service.request(liveaction)
309
        liveaction = LiveAction.get_by_id(str(liveaction.id))
310
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)
311
312
        mistral_context = liveaction.context.get('mistral', None)
313
        self.assertIsNotNone(mistral_context)
314
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
315
        self.assertEqual(mistral_context['workflow_name'], WF1_EXEC.get('workflow_name'))
316
317
        workflow_input = copy.deepcopy(ACTION_PARAMS)
318
        workflow_input.update({'count': '3'})
319
320
        env = {
321
            'st2_execution_id': str(execution.id),
322
            'st2_liveaction_id': str(liveaction.id),
323
            'st2_action_api_url': 'http://0.0.0.0:9101/v1',
324
            '__actions': {
325
                'st2.action': {
326
                    'st2_context': {
327
                        'api_url': 'http://0.0.0.0:9101/v1',
328
                        'endpoint': 'http://0.0.0.0:9101/v1/actionexecutions',
329
                        'parent': {
330
                            'pack': 'mistral_tests',
331
                            'execution_id': str(execution.id),
332
                            'chain': {
333
                                'params': {
334
                                    'var1': 'foobar',
335
                                    'var2': '{% raw %}{{foobar}}{% endraw %}',
336
                                    'var3': [
337
                                        '{% raw %}{{foo}}{% endraw %}',
338
                                        '{% raw %}{{bar}}{% endraw %}'
339
                                    ],
340
                                    'var4': {
341
                                        'foobar': '{% raw %}{{foobar}}{% endraw %}'
342
                                    },
343
                                    'var5': {
344
                                        'foobar': (
345
                                            '{% raw %}{% for item in items %}'
346
                                            'foobar{% end for %}{% endraw %}'
347
                                        )
348
                                    }
349
                                }
350
                            }
351
                        },
352
                        'notify': {},
353
                        'skip_notify_tasks': []
354
                    }
355
                }
356
            }
357
        }
358
359
        executions.ExecutionManager.create.assert_called_with(
360
            WF1_NAME, workflow_input=workflow_input, env=env, notify=NOTIFY)
361
362
    @mock.patch.object(
363
        workflows.WorkflowManager, 'list',
364
        mock.MagicMock(return_value=[]))
365
    @mock.patch.object(
366
        workflows.WorkflowManager, 'get',
367
        mock.MagicMock(return_value=WF1))
368
    @mock.patch.object(
369
        workflows.WorkflowManager, 'create',
370
        mock.MagicMock(return_value=[WF1]))
371
    @mock.patch.object(
372
        executions.ExecutionManager, 'create',
373
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
374
    def test_launch_workflow_under_parent_chain_with_jinja_parameters(self):
375
        ac_ctx = {
376
            'chain': {
377
                'parameters': {
378
                    'var1': 'foobar',
379
                    'var2': '{{foobar}}',
380
                    'var3': ['{{foo}}', '{{bar}}'],
381
                    'var4': {
382
                        'foobar': '{{foobar}}'
383
                    },
384
                }
385
            }
386
        }
387
388
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS, context=ac_ctx)
389
        liveaction, execution = action_service.request(liveaction)
390
        liveaction = LiveAction.get_by_id(str(liveaction.id))
391
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)
392
393
        mistral_context = liveaction.context.get('mistral', None)
394
        self.assertIsNotNone(mistral_context)
395
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
396
        self.assertEqual(mistral_context['workflow_name'], WF1_EXEC.get('workflow_name'))
397
398
        workflow_input = copy.deepcopy(ACTION_PARAMS)
399
        workflow_input.update({'count': '3'})
400
401
        env = {
402
            'st2_execution_id': str(execution.id),
403
            'st2_liveaction_id': str(liveaction.id),
404
            'st2_action_api_url': 'http://0.0.0.0:9101/v1',
405
            '__actions': {
406
                'st2.action': {
407
                    'st2_context': {
408
                        'api_url': 'http://0.0.0.0:9101/v1',
409
                        'endpoint': 'http://0.0.0.0:9101/v1/actionexecutions',
410
                        'parent': {
411
                            'pack': 'mistral_tests',
412
                            'execution_id': str(execution.id),
413
                            'chain': {
414
                                'parameters': {
415
                                    'var1': 'foobar',
416
                                    'var2': '{% raw %}{{foobar}}{% endraw %}',
417
                                    'var3': [
418
                                        '{% raw %}{{foo}}{% endraw %}',
419
                                        '{% raw %}{{bar}}{% endraw %}'
420
                                    ],
421
                                    'var4': {
422
                                        'foobar': '{% raw %}{{foobar}}{% endraw %}'
423
                                    }
424
                                }
425
                            }
426
                        },
427
                        'notify': {},
428
                        'skip_notify_tasks': []
429
                    }
430
                }
431
            }
432
        }
433
434
        executions.ExecutionManager.create.assert_called_with(
435
            WF1_NAME, workflow_input=workflow_input, env=env, notify=NOTIFY)
436
437
    @mock.patch.object(
438
        workflows.WorkflowManager, 'list',
439
        mock.MagicMock(return_value=[]))
440
    @mock.patch.object(
441
        workflows.WorkflowManager, 'get',
442
        mock.MagicMock(return_value=WF1))
443
    @mock.patch.object(
444
        workflows.WorkflowManager, 'create',
445
        mock.MagicMock(return_value=[WF1]))
446
    @mock.patch.object(
447
        executions.ExecutionManager, 'create',
448
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
449
    def test_launch_workflow_under_parent_chain_with_nonetype_in_chain_context(self):
450
        ac_ctx = {'chain': None}
451
452
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS, context=ac_ctx)
453
        liveaction, execution = action_service.request(liveaction)
454
        liveaction = LiveAction.get_by_id(str(liveaction.id))
455
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)
456
457
        mistral_context = liveaction.context.get('mistral', None)
458
        self.assertIsNotNone(mistral_context)
459
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
460
        self.assertEqual(mistral_context['workflow_name'], WF1_EXEC.get('workflow_name'))
461
462
        workflow_input = copy.deepcopy(ACTION_PARAMS)
463
        workflow_input.update({'count': '3'})
464
465
        env = {
466
            'st2_execution_id': str(execution.id),
467
            'st2_liveaction_id': str(liveaction.id),
468
            'st2_action_api_url': 'http://0.0.0.0:9101/v1',
469
            '__actions': {
470
                'st2.action': {
471
                    'st2_context': {
472
                        'api_url': 'http://0.0.0.0:9101/v1',
473
                        'endpoint': 'http://0.0.0.0:9101/v1/actionexecutions',
474
                        'parent': {
475
                            'pack': 'mistral_tests',
476
                            'execution_id': str(execution.id),
477
                            'chain': None
478
                        },
479
                        'notify': {},
480
                        'skip_notify_tasks': []
481
                    }
482
                }
483
            }
484
        }
485
486
        executions.ExecutionManager.create.assert_called_with(
487
            WF1_NAME, workflow_input=workflow_input, env=env, notify=NOTIFY)
488
489
    @mock.patch.object(
490
        workflows.WorkflowManager, 'list',
491
        mock.MagicMock(return_value=[]))
492
    @mock.patch.object(
493
        workflows.WorkflowManager, 'get',
494
        mock.MagicMock(return_value=WF1))
495
    @mock.patch.object(
496
        workflows.WorkflowManager, 'create',
497
        mock.MagicMock(return_value=[WF1]))
498
    @mock.patch.object(
499
        executions.ExecutionManager, 'create',
500
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
501
    def test_launch_workflow_under_parent_chain_with_nonetype_in_params_context(self):
502
        ac_ctx = {'chain': {'params': None}}
503
504
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS, context=ac_ctx)
505
        liveaction, execution = action_service.request(liveaction)
506
        liveaction = LiveAction.get_by_id(str(liveaction.id))
507
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)
508
509
        mistral_context = liveaction.context.get('mistral', None)
510
        self.assertIsNotNone(mistral_context)
511
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
512
        self.assertEqual(mistral_context['workflow_name'], WF1_EXEC.get('workflow_name'))
513
514
        workflow_input = copy.deepcopy(ACTION_PARAMS)
515
        workflow_input.update({'count': '3'})
516
517
        env = {
518
            'st2_execution_id': str(execution.id),
519
            'st2_liveaction_id': str(liveaction.id),
520
            'st2_action_api_url': 'http://0.0.0.0:9101/v1',
521
            '__actions': {
522
                'st2.action': {
523
                    'st2_context': {
524
                        'api_url': 'http://0.0.0.0:9101/v1',
525
                        'endpoint': 'http://0.0.0.0:9101/v1/actionexecutions',
526
                        'parent': {
527
                            'pack': 'mistral_tests',
528
                            'execution_id': str(execution.id),
529
                            'chain': {
530
                                'params': None
531
                            }
532
                        },
533
                        'notify': {},
534
                        'skip_notify_tasks': []
535
                    }
536
                }
537
            }
538
        }
539
540
        executions.ExecutionManager.create.assert_called_with(
541
            WF1_NAME, workflow_input=workflow_input, env=env, notify=NOTIFY)
542
543
    @mock.patch.object(
544
        workflows.WorkflowManager, 'list',
545
        mock.MagicMock(return_value=[]))
546
    @mock.patch.object(
547
        workflows.WorkflowManager, 'get',
548
        mock.MagicMock(return_value=WF1))
549
    @mock.patch.object(
550
        workflows.WorkflowManager, 'create',
551
        mock.MagicMock(return_value=[WF1]))
552
    @mock.patch.object(
553
        executions.ExecutionManager, 'create',
554
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
555
    def test_launch_workflow_with_st2_https(self):
556
        cfg.CONF.set_override('api_url', 'https://0.0.0.0:9101', group='auth')
557
558
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
559
        liveaction, execution = action_service.request(liveaction)
560
        liveaction = LiveAction.get_by_id(str(liveaction.id))
561
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)
562
563
        mistral_context = liveaction.context.get('mistral', None)
564
        self.assertIsNotNone(mistral_context)
565
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
566
        self.assertEqual(mistral_context['workflow_name'], WF1_EXEC.get('workflow_name'))
567
568
        workflow_input = copy.deepcopy(ACTION_PARAMS)
569
        workflow_input.update({'count': '3'})
570
571
        env = {
572
            'st2_execution_id': str(execution.id),
573
            'st2_liveaction_id': str(liveaction.id),
574
            'st2_action_api_url': 'https://0.0.0.0:9101/v1',
575
            '__actions': {
576
                'st2.action': {
577
                    'st2_context': {
578
                        'api_url': 'https://0.0.0.0:9101/v1',
579
                        'endpoint': 'https://0.0.0.0:9101/v1/actionexecutions',
580
                        'parent': {
581
                            'pack': 'mistral_tests',
582
                            'execution_id': str(execution.id)
583
                        },
584
                        'notify': {},
585
                        'skip_notify_tasks': []
586
                    }
587
                }
588
            }
589
        }
590
591
        executions.ExecutionManager.create.assert_called_with(
592
            WF1_NAME, workflow_input=workflow_input, env=env, notify=NOTIFY)
593
594
    @mock.patch.object(
595
        workflows.WorkflowManager, 'list',
596
        mock.MagicMock(return_value=[]))
597
    @mock.patch.object(
598
        workflows.WorkflowManager, 'get',
599
        mock.MagicMock(return_value=WF1))
600
    @mock.patch.object(
601
        workflows.WorkflowManager, 'create',
602
        mock.MagicMock(return_value=[WF1]))
603
    @mock.patch.object(
604
        executions.ExecutionManager, 'create',
605
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
606
    def test_launch_workflow_with_notifications(self):
607
        notify_data = {'on_complete': {'routes': ['slack'],
608
                       'message': '"@channel: Action succeeded."', 'data': {}}}
609
610
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS, notify=notify_data)
611
        liveaction, execution = action_service.request(liveaction)
612
        liveaction = LiveAction.get_by_id(str(liveaction.id))
613
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)
614
615
        mistral_context = liveaction.context.get('mistral', None)
616
        self.assertIsNotNone(mistral_context)
617
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
618
        self.assertEqual(mistral_context['workflow_name'], WF1_EXEC.get('workflow_name'))
619
620
        workflow_input = copy.deepcopy(ACTION_PARAMS)
621
        workflow_input.update({'count': '3'})
622
623
        env = {
624
            'st2_execution_id': str(execution.id),
625
            'st2_liveaction_id': str(liveaction.id),
626
            'st2_action_api_url': 'http://0.0.0.0:9101/v1',
627
            '__actions': {
628
                'st2.action': {
629
                    'st2_context': {
630
                        'api_url': 'http://0.0.0.0:9101/v1',
631
                        'endpoint': 'http://0.0.0.0:9101/v1/actionexecutions',
632
                        'parent': {
633
                            'pack': 'mistral_tests',
634
                            'execution_id': str(execution.id)
635
                        },
636
                        'notify': NotificationsHelper.from_model(liveaction.notify),
637
                        'skip_notify_tasks': []
638
                    }
639
                }
640
            }
641
        }
642
643
        executions.ExecutionManager.create.assert_called_with(
644
            WF1_NAME, workflow_input=workflow_input, env=env, notify=NOTIFY)
645
646
    @mock.patch.object(
647
        workflows.WorkflowManager, 'list',
648
        mock.MagicMock(side_effect=requests.exceptions.ConnectionError('Connection refused')))
649
    def test_launch_workflow_mistral_offline(self):
650
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
651
        liveaction, execution = action_service.request(liveaction)
652
        liveaction = LiveAction.get_by_id(str(liveaction.id))
653
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_FAILED)
654
        self.assertIn('Connection refused', liveaction.result['error'])
655
656
    @mock.patch.object(
657
        workflows.WorkflowManager, 'list',
658
        mock.MagicMock(side_effect=[requests.exceptions.ConnectionError(), []]))
659
    @mock.patch.object(
660
        workflows.WorkflowManager, 'get',
661
        mock.MagicMock(return_value=WF1))
662
    @mock.patch.object(
663
        workflows.WorkflowManager, 'create',
664
        mock.MagicMock(return_value=[WF1]))
665
    @mock.patch.object(
666
        executions.ExecutionManager, 'create',
667
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
668
    def test_launch_workflow_mistral_retry(self):
669
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
670
        liveaction, execution = action_service.request(liveaction)
671
        liveaction = LiveAction.get_by_id(str(liveaction.id))
672
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)
673
674
        mistral_context = liveaction.context.get('mistral', None)
675
        self.assertIsNotNone(mistral_context)
676
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
677
        self.assertEqual(mistral_context['workflow_name'], WF1_EXEC.get('workflow_name'))
678
679
    @mock.patch.object(
680
        workflows.WorkflowManager, 'list',
681
        mock.MagicMock(return_value=[]))
682
    @mock.patch.object(
683
        workflows.WorkflowManager, 'get',
684
        mock.MagicMock(return_value=WF1))
685
    @mock.patch.object(
686
        workflows.WorkflowManager, 'create',
687
        mock.MagicMock(side_effect=[APIException(error_message='Duplicate entry.'), WF1]))
688
    @mock.patch.object(
689
        executions.ExecutionManager, 'create',
690
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
691
    def test_launch_workflow_duplicate_error(self):
692
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
693
        liveaction, execution = action_service.request(liveaction)
694
        liveaction = LiveAction.get_by_id(str(liveaction.id))
695
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)
696
697
        mistral_context = liveaction.context.get('mistral', None)
698
        self.assertIsNotNone(mistral_context)
699
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
700
        self.assertEqual(mistral_context['workflow_name'], WF1_EXEC.get('workflow_name'))
701
702
    @mock.patch.object(
703
        workflows.WorkflowManager, 'list',
704
        mock.MagicMock(return_value=[]))
705
    @mock.patch.object(
706
        workflows.WorkflowManager, 'get',
707
        mock.MagicMock(return_value=WF1_OLD))
708
    @mock.patch.object(
709
        workflows.WorkflowManager, 'create',
710
        mock.MagicMock(return_value=[WF1]))
711
    @mock.patch.object(
712
        workflows.WorkflowManager, 'update',
713
        mock.MagicMock(return_value=[WF1]))
714
    @mock.patch.object(
715
        executions.ExecutionManager, 'create',
716
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
717
    def test_launch_when_workflow_definition_changed(self):
718
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
719
        liveaction, execution = action_service.request(liveaction)
720
        liveaction = LiveAction.get_by_id(str(liveaction.id))
721
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)
722
723
        mistral_context = liveaction.context.get('mistral', None)
724
        self.assertIsNotNone(mistral_context)
725
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
726
        self.assertEqual(mistral_context['workflow_name'], WF1_EXEC.get('workflow_name'))
727
728
    @mock.patch.object(
729
        workflows.WorkflowManager, 'list',
730
        mock.MagicMock(return_value=[]))
731
    @mock.patch.object(
732
        workflows.WorkflowManager, 'get',
733
        mock.MagicMock(side_effect=Exception()))
734
    @mock.patch.object(
735
        workbooks.WorkbookManager, 'delete',
736
        mock.MagicMock(side_effect=Exception()))
737
    @mock.patch.object(
738
        workflows.WorkflowManager, 'create',
739
        mock.MagicMock(return_value=[WF1]))
740
    @mock.patch.object(
741
        executions.ExecutionManager, 'create',
742
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
743
    def test_launch_when_workflow_not_exists(self):
744
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
745
        liveaction, execution = action_service.request(liveaction)
746
        liveaction = LiveAction.get_by_id(str(liveaction.id))
747
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)
748
749
        mistral_context = liveaction.context.get('mistral', None)
750
        self.assertIsNotNone(mistral_context)
751
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
752
        self.assertEqual(mistral_context['workflow_name'], WF1_EXEC.get('workflow_name'))
753
754
    @mock.patch.object(
755
        workflows.WorkflowManager, 'list',
756
        mock.MagicMock(return_value=[]))
757
    @mock.patch.object(
758
        workflows.WorkflowManager, 'get',
759
        mock.MagicMock(return_value=WF2))
760
    def test_launch_workflow_with_many_workflows(self):
761
        liveaction = LiveActionDB(action=WF2_NAME, parameters=ACTION_PARAMS)
762
        liveaction, execution = action_service.request(liveaction)
763
        liveaction = LiveAction.get_by_id(str(liveaction.id))
764
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_FAILED)
765
        self.assertIn('Multiple workflows is not supported.', liveaction.result['error'])
766
767
    @mock.patch.object(
768
        workflows.WorkflowManager, 'list',
769
        mock.MagicMock(return_value=[]))
770
    @mock.patch.object(
771
        workflows.WorkflowManager, 'get',
772
        mock.MagicMock(side_effect=Exception()))
773
    def test_launch_workflow_name_mistmatch(self):
774
        action_ref = TEST_PACK + '.workflow_v2_name_mismatch'
775
        liveaction = LiveActionDB(action=action_ref, parameters=ACTION_PARAMS)
776
        liveaction, execution = action_service.request(liveaction)
777
        liveaction = LiveAction.get_by_id(str(liveaction.id))
778
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_FAILED)
779
        self.assertIn('Name of the workflow must be the same', liveaction.result['error'])
780
781
    @mock.patch.object(
782
        workflows.WorkflowManager, 'list',
783
        mock.MagicMock(return_value=[]))
784
    @mock.patch.object(
785
        workbooks.WorkbookManager, 'get',
786
        mock.MagicMock(return_value=WB1))
787
    @mock.patch.object(
788
        workbooks.WorkbookManager, 'create',
789
        mock.MagicMock(return_value=WB1))
790
    @mock.patch.object(
791
        workbooks.WorkbookManager, 'update',
792
        mock.MagicMock(return_value=WB1))
793
    @mock.patch.object(
794
        executions.ExecutionManager, 'create',
795
        mock.MagicMock(return_value=executions.Execution(None, WB1_EXEC)))
796
    def test_launch_workbook(self):
797
        liveaction = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS)
798
        liveaction, execution = action_service.request(liveaction)
799
        liveaction = LiveAction.get_by_id(str(liveaction.id))
800
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)
801
802
        mistral_context = liveaction.context.get('mistral', None)
803
        self.assertIsNotNone(mistral_context)
804
        self.assertEqual(mistral_context['execution_id'], WB1_EXEC.get('id'))
805
        self.assertEqual(mistral_context['workflow_name'], WB1_EXEC.get('workflow_name'))
806
807
    @mock.patch.object(
808
        workflows.WorkflowManager, 'list',
809
        mock.MagicMock(return_value=[]))
810
    @mock.patch.object(
811
        workbooks.WorkbookManager, 'get',
812
        mock.MagicMock(return_value=WB2))
813
    @mock.patch.object(
814
        workbooks.WorkbookManager, 'create',
815
        mock.MagicMock(return_value=WB2))
816
    @mock.patch.object(
817
        workbooks.WorkbookManager, 'update',
818
        mock.MagicMock(return_value=WB2))
819
    @mock.patch.object(
820
        executions.ExecutionManager, 'create',
821
        mock.MagicMock(return_value=executions.Execution(None, WB2_EXEC)))
822
    def test_launch_workbook_with_many_workflows(self):
823
        liveaction = LiveActionDB(action=WB2_NAME, parameters=ACTION_PARAMS)
824
        liveaction, execution = action_service.request(liveaction)
825
        liveaction = LiveAction.get_by_id(str(liveaction.id))
826
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)
827
828
        mistral_context = liveaction.context.get('mistral', None)
829
        self.assertIsNotNone(mistral_context)
830
        self.assertEqual(mistral_context['execution_id'], WB2_EXEC.get('id'))
831
        self.assertEqual(mistral_context['workflow_name'], WB2_EXEC.get('workflow_name'))
832
833
    @mock.patch.object(
834
        workflows.WorkflowManager, 'list',
835
        mock.MagicMock(return_value=[]))
836
    @mock.patch.object(
837
        workbooks.WorkbookManager, 'get',
838
        mock.MagicMock(return_value=WB3))
839
    @mock.patch.object(
840
        workbooks.WorkbookManager, 'create',
841
        mock.MagicMock(return_value=WB3))
842
    @mock.patch.object(
843
        workbooks.WorkbookManager, 'update',
844
        mock.MagicMock(return_value=WB3))
845
    @mock.patch.object(
846
        executions.ExecutionManager, 'create',
847
        mock.MagicMock(return_value=executions.Execution(None, WB3_EXEC)))
848
    def test_launch_workbook_with_many_workflows_no_default(self):
849
        liveaction = LiveActionDB(action=WB3_NAME, parameters=ACTION_PARAMS)
850
        liveaction, execution = action_service.request(liveaction)
851
        liveaction = LiveAction.get_by_id(str(liveaction.id))
852
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_FAILED)
853
        self.assertIn('Default workflow cannot be determined.', liveaction.result['error'])
854
855
    @mock.patch.object(
856
        workflows.WorkflowManager, 'list',
857
        mock.MagicMock(return_value=[]))
858
    @mock.patch.object(
859
        workbooks.WorkbookManager, 'get',
860
        mock.MagicMock(return_value=WB1_OLD))
861
    @mock.patch.object(
862
        workbooks.WorkbookManager, 'create',
863
        mock.MagicMock(return_value=WB1))
864
    @mock.patch.object(
865
        workbooks.WorkbookManager, 'update',
866
        mock.MagicMock(return_value=WB1))
867
    @mock.patch.object(
868
        executions.ExecutionManager, 'create',
869
        mock.MagicMock(return_value=executions.Execution(None, WB1_EXEC)))
870
    def test_launch_when_workbook_definition_changed(self):
871
        liveaction = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS)
872
        liveaction, execution = action_service.request(liveaction)
873
        liveaction = LiveAction.get_by_id(str(liveaction.id))
874
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)
875
876
        mistral_context = liveaction.context.get('mistral', None)
877
        self.assertIsNotNone(mistral_context)
878
        self.assertEqual(mistral_context['execution_id'], WB1_EXEC.get('id'))
879
        self.assertEqual(mistral_context['workflow_name'], WB1_EXEC.get('workflow_name'))
880
881
    @mock.patch.object(
882
        workflows.WorkflowManager, 'list',
883
        mock.MagicMock(return_value=[]))
884
    @mock.patch.object(
885
        workbooks.WorkbookManager, 'get',
886
        mock.MagicMock(side_effect=Exception()))
887
    @mock.patch.object(
888
        workflows.WorkflowManager, 'delete',
889
        mock.MagicMock(side_effect=Exception()))
890
    @mock.patch.object(
891
        workbooks.WorkbookManager, 'create',
892
        mock.MagicMock(return_value=WB1))
893
    @mock.patch.object(
894
        executions.ExecutionManager, 'create',
895
        mock.MagicMock(return_value=executions.Execution(None, WB1_EXEC)))
896
    def test_launch_when_workbook_not_exists(self):
897
        liveaction = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS)
898
        liveaction, execution = action_service.request(liveaction)
899
        liveaction = LiveAction.get_by_id(str(liveaction.id))
900
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)
901
902
        mistral_context = liveaction.context.get('mistral', None)
903
        self.assertIsNotNone(mistral_context)
904
        self.assertEqual(mistral_context['execution_id'], WB1_EXEC.get('id'))
905
        self.assertEqual(mistral_context['workflow_name'], WB1_EXEC.get('workflow_name'))
906
907
    @mock.patch.object(
908
        workflows.WorkflowManager, 'list',
909
        mock.MagicMock(return_value=[]))
910
    @mock.patch.object(
911
        workbooks.WorkbookManager, 'get',
912
        mock.MagicMock(side_effect=Exception()))
913
    def test_launch_workbook_name_mismatch(self):
914
        action_ref = TEST_PACK + '.workbook_v2_name_mismatch'
915
        liveaction = LiveActionDB(action=action_ref, parameters=ACTION_PARAMS)
916
        liveaction, execution = action_service.request(liveaction)
917
        liveaction = LiveAction.get_by_id(str(liveaction.id))
918
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_FAILED)
919
        self.assertIn('Name of the workbook must be the same', liveaction.result['error'])
920