Example #1
0
    def test_list(self):
        self.requests_mock.get(self.TEST_URL + URL_TEMPLATE,
                               json={'executions': [EXEC, SUB_WF_EXEC]})

        execution_list = self.executions.list()

        self.assertEqual(2, len(execution_list))

        self.assertDictEqual(
            executions.Execution(self.executions, EXEC).to_dict(),
            execution_list[0].to_dict())

        self.assertDictEqual(
            executions.Execution(self.executions, SUB_WF_EXEC).to_dict(),
            execution_list[1].to_dict())
Example #2
0
    def test_update_state(self):
        states = ['RUNNING', 'SUCCESS', 'PAUSED', 'ERROR', 'CANCELLED']

        for state in states:
            self.client.executions.update.return_value = executions.Execution(
                mock, {
                    'id': '123',
                    'workflow_id': '123e4567-e89b-12d3-a456-426655440000',
                    'workflow_name': 'some',
                    'workflow_namespace': '',
                    'root_execution_id': '',
                    'description': '',
                    'state': state,
                    'state_info': None,
                    'created_at': '2020-02-07 08:10:32',
                    'updated_at': '2020-02-07 08:10:41',
                    'task_execution_id': None
                })

            ex_result = list(EX_RESULT)
            ex_result[7] = state

            # We'll ignore "duration" since for not terminal states
            # it is unpredictable.
            del ex_result[11]
            ex_result = tuple(ex_result)

            result = self.call(execution_cmd.Update,
                               app_args=['id', '-s', state])

            result_ex = list(result[1])
            del result_ex[11]
            result_ex = tuple(result_ex)

            self.assertEqual(ex_result, result_ex)
    def test_update_state(self):
        states = ['RUNNING', 'SUCCESS', 'PAUSED', 'ERROR', 'CANCELLED']

        for state in states:
            self.client.executions.update.return_value = executions.Execution(
                mock, {
                    'id': '123',
                    'workflow_id': '123e4567-e89b-12d3-a456-426655440000',
                    'workflow_name': 'some',
                    'description': '',
                    'state': state,
                    'state_info': None,
                    'created_at': '1',
                    'updated_at': '1',
                    'task_execution_id': None
                })

            ex_result = list(EX_RESULT)
            ex_result[5] = state
            ex_result = tuple(ex_result)

            result = self.call(execution_cmd.Update,
                               app_args=['id', '-s', state])

            self.assertEqual(ex_result, result[1])
Example #4
0
    def test_create(self):
        self.requests_mock.post(self.TEST_URL + URL_TEMPLATE,
                                json=EXEC,
                                status_code=201)

        body = {
            'workflow_name': EXEC['workflow_name'],
            'description': '',
            'input': jsonutils.dumps(EXEC['input'])
        }

        ex = self.executions.create(
            EXEC['workflow_name'],
            EXEC['workflow_namespace'],
            EXEC['input']
        )

        self.assertIsNotNone(ex)

        self.assertDictEqual(
            executions.Execution(self.executions, EXEC).to_dict(),
            ex.to_dict()
        )

        self.assertDictEqual(body, self.requests_mock.last_request.json())
    def test_get(self):
        mock = self.mock_http_get(content=EXEC)

        ex = self.executions.get(EXEC['id'])

        self.assertEqual(executions.Execution(self.executions, EXEC).to_dict(),
                         ex.to_dict())
        mock.assert_called_once_with(URL_TEMPLATE_ID % EXEC['id'])
Example #6
0
    def test_get_sub_executions(self):
        url = self.TEST_URL + URL_TEMPLATE_SUB_EXECUTIONS \
              % (EXEC['id'], '?max_depth=-1&errors_only=')

        self.requests_mock.get(url, json={'executions': [EXEC, SUB_WF_EXEC]})

        sub_execution_list = self.executions.get_ex_sub_executions(EXEC['id'])

        self.assertEqual(2, len(sub_execution_list))
        self.assertDictEqual(
            executions.Execution(self.executions, EXEC).to_dict(),
            sub_execution_list[0].to_dict()
        )
        self.assertDictEqual(
            executions.Execution(self.executions, SUB_WF_EXEC).to_dict(),
            sub_execution_list[1].to_dict()
        )
Example #7
0
    def test_get(self):
        url = self.TEST_URL + URL_TEMPLATE_ID % EXEC['id']
        self.requests_mock.get(url, json=EXEC)

        ex = self.executions.get(EXEC['id'])

        self.assertDictEqual(
            executions.Execution(self.executions, EXEC).to_dict(),
            ex.to_dict())
Example #8
0
    def test_get_sub_wf_ex(self):
        url = self.TEST_URL + URL_TEMPLATE_ID % SUB_WF_EXEC['id']
        self.requests_mock.get(url, json=SUB_WF_EXEC)

        ex = self.executions.get(SUB_WF_EXEC['id'])

        self.assertDictEqual(
            executions.Execution(self.executions, SUB_WF_EXEC).to_dict(),
            ex.to_dict())
    def test_list(self):
        mock = self.mock_http_get(content={'executions': [EXEC]})

        execution_list = self.executions.list()

        self.assertEqual(1, len(execution_list))
        ex = execution_list[0]

        self.assertEqual(executions.Execution(self.executions, EXEC).to_dict(),
                         ex.to_dict())
        mock.assert_called_once_with(URL_TEMPLATE)
    def test_update(self):
        mock = self.mock_http_put(content=EXEC)
        body = {
            'state': EXEC['state']
        }

        ex = self.executions.update(EXEC['id'], EXEC['state'])

        self.assertIsNotNone(ex)
        self.assertEqual(executions.Execution(self.executions, EXEC).to_dict(),
                         ex.to_dict())
        mock.assert_called_once_with(
            URL_TEMPLATE_ID % EXEC['id'], json.dumps(body))
Example #11
0
    def test_update(self):
        url = self.TEST_URL + URL_TEMPLATE_ID % EXEC['id']
        self.requests_mock.put(url, json=EXEC)
        body = {'state': EXEC['state']}

        ex = self.executions.update(EXEC['id'], EXEC['state'])

        self.assertIsNotNone(ex)

        self.assertDictEqual(
            executions.Execution(self.executions, EXEC).to_dict(),
            ex.to_dict())

        self.assertDictEqual(body, self.requests_mock.last_request.json())
    def test_create(self):
        mock = self.mock_http_post(content=EXEC)
        body = {
            'workflow_name': EXEC['workflow_name'],
            'description': '',
            'input': json.dumps(EXEC['input']),
        }

        ex = self.executions.create(EXEC['workflow_name'],
                                    EXEC['input'])

        self.assertIsNotNone(ex)
        self.assertEqual(executions.Execution(self.executions, EXEC).to_dict(),
                         ex.to_dict())
        mock.assert_called_once_with(URL_TEMPLATE, json.dumps(body))
    def test_create_with_source_execution_id(self):
        self.requests_mock.post(self.TEST_URL + URL_TEMPLATE,
                                json=SOURCE_EXEC,
                                status_code=201)

        body = {
            'description': '',
            'source_execution_id': SOURCE_EXEC['source_execution_id']
        }

        ex = self.executions.create(
            source_execution_id=SOURCE_EXEC['source_execution_id'])

        self.assertIsNotNone(ex)

        self.assertDictEqual(
            executions.Execution(self.executions, SOURCE_EXEC).to_dict(),
            ex.to_dict())

        self.assertDictEqual(body, self.requests_mock.last_request.json())
Example #14
0
class MistralRunnerTest(DbTestCase):
    @classmethod
    def setUpClass(cls):
        super(MistralRunnerTest, cls).setUpClass()
        runners_registrar.register_runner_types()

        for _, fixture in six.iteritems(FIXTURES['actions']):
            instance = ActionAPI(**fixture)
            Action.add_or_update(ActionAPI.to_model(instance))

    def setUp(self):
        super(MistralRunnerTest, self).setUp()
        cfg.CONF.set_override('api_url', 'http://0.0.0.0:9101', group='auth')

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    @mock.patch.object(
        MistralRunner, 'resume',
        mock.MagicMock(
            return_value=(action_constants.LIVEACTION_STATUS_RUNNING, {
                'tasks': []
            }, {
                'execution_id': str(uuid.uuid4())
            })))
    def test_resume_option(self):
        MistralRunner.entry_point = mock.PropertyMock(
            return_value=WF1_YAML_FILE_PATH)
        liveaction1 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction1, execution1 = action_service.request(liveaction1)
        self.assertFalse(MistralRunner.resume.called)

        # Rerun the execution.
        context = {'re-run': {'ref': execution1.id, 'tasks': ['x']}}

        liveaction2 = LiveActionDB(action=WF1_NAME,
                                   parameters=ACTION_PARAMS,
                                   context=context)
        liveaction2, execution2 = action_service.request(liveaction2)
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
        self.assertEqual(liveaction2.status,
                         action_constants.LIVEACTION_STATUS_RUNNING)

        task_specs = {'x': {'reset': False}}

        MistralRunner.resume.assert_called_with(ex_ref=execution1,
                                                task_specs=task_specs)

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    @mock.patch.object(
        MistralRunner, 'resume',
        mock.MagicMock(
            return_value=(action_constants.LIVEACTION_STATUS_RUNNING, {
                'tasks': []
            }, {
                'execution_id': str(uuid.uuid4())
            })))
    def test_resume_option_reset_tasks(self):
        MistralRunner.entry_point = mock.PropertyMock(
            return_value=WF1_YAML_FILE_PATH)
        liveaction1 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction1, execution1 = action_service.request(liveaction1)
        self.assertFalse(MistralRunner.resume.called)

        # Rerun the execution.
        context = {
            're-run': {
                'ref': execution1.id,
                'tasks': ['x', 'y'],
                'reset': ['y']
            }
        }

        liveaction2 = LiveActionDB(action=WF1_NAME,
                                   parameters=ACTION_PARAMS,
                                   context=context)
        liveaction2, execution2 = action_service.request(liveaction2)
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
        self.assertEqual(liveaction2.status,
                         action_constants.LIVEACTION_STATUS_RUNNING)

        task_specs = {'x': {'reset': False}, 'y': {'reset': True}}

        MistralRunner.resume.assert_called_with(ex_ref=execution1,
                                                task_specs=task_specs)

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(
            return_value=executions.Execution(None, WF1_EXEC_NOT_RERUNABLE)))
    @mock.patch.object(
        executions.ExecutionManager, 'get',
        mock.MagicMock(
            return_value=executions.Execution(None, WF1_EXEC_NOT_RERUNABLE)))
    @mock.patch.object(tasks.TaskManager, 'list',
                       mock.MagicMock(return_value=WF1_TASKS))
    def test_resume_workflow_not_in_rerunable_state(self):
        MistralRunner.entry_point = mock.PropertyMock(
            return_value=WF1_YAML_FILE_PATH)
        liveaction1 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction1, execution1 = action_service.request(liveaction1)

        # Rerun the execution.
        context = {'re-run': {'ref': execution1.id, 'tasks': ['say-friend']}}

        liveaction2 = LiveActionDB(action=WF1_NAME,
                                   parameters=ACTION_PARAMS,
                                   context=context)
        liveaction2, execution2 = action_service.request(liveaction2)
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
        self.assertEqual(liveaction2.status,
                         action_constants.LIVEACTION_STATUS_FAILED)
        self.assertIn('not in a rerunable state',
                      liveaction2.result.get('error'))

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'list',
        mock.MagicMock(return_value=[executions.Execution(None, WF1_EXEC)]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    @mock.patch.object(
        executions.ExecutionManager, 'get',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    @mock.patch.object(tasks.TaskManager, 'list',
                       mock.MagicMock(return_value=WF1_TASKS))
    def test_resume_tasks_not_in_rerunable_state(self):
        MistralRunner.entry_point = mock.PropertyMock(
            return_value=WF1_YAML_FILE_PATH)
        liveaction1 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction1, execution1 = action_service.request(liveaction1)

        # Rerun the execution.
        context = {'re-run': {'ref': execution1.id, 'tasks': ['say-friend']}}

        liveaction2 = LiveActionDB(action=WF1_NAME,
                                   parameters=ACTION_PARAMS,
                                   context=context)
        liveaction2, execution2 = action_service.request(liveaction2)
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
        self.assertEqual(liveaction2.status,
                         action_constants.LIVEACTION_STATUS_FAILED)
        self.assertIn('Unable to identify rerunable',
                      liveaction2.result.get('error'))

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'list',
        mock.MagicMock(return_value=[executions.Execution(None, WF1_EXEC)]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    @mock.patch.object(
        executions.ExecutionManager, 'get',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    @mock.patch.object(tasks.TaskManager, 'list',
                       mock.MagicMock(return_value=WF1_TASKS))
    def test_resume_unidentified_tasks(self):
        MistralRunner.entry_point = mock.PropertyMock(
            return_value=WF1_YAML_FILE_PATH)
        liveaction1 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction1, execution1 = action_service.request(liveaction1)

        # Rerun the execution.
        context = {'re-run': {'ref': execution1.id, 'tasks': ['x']}}

        liveaction2 = LiveActionDB(action=WF1_NAME,
                                   parameters=ACTION_PARAMS,
                                   context=context)
        liveaction2, execution2 = action_service.request(liveaction2)
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
        self.assertEqual(liveaction2.status,
                         action_constants.LIVEACTION_STATUS_FAILED)
        self.assertIn('Unable to identify', liveaction2.result.get('error'))

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workbooks.WorkbookManager, 'create',
                       mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB1_MAIN_EXEC)))
    @mock.patch.object(
        executions.ExecutionManager, 'get',
        mock.MagicMock(
            return_value=executions.Execution(None, WB1_MAIN_EXEC_ERRORED)))
    @mock.patch.object(executions.ExecutionManager, 'list',
                       mock.MagicMock(return_value=[
                           executions.Execution(None, WB1_MAIN_EXEC_ERRORED),
                           executions.Execution(None, WB1_SUB1_EXEC_ERRORED)
                       ]))
    @mock.patch.object(
        tasks.TaskManager, 'list',
        mock.MagicMock(side_effect=[WB1_MAIN_TASKS, WB1_SUB1_TASKS]))
    @mock.patch.object(tasks.TaskManager, 'rerun',
                       mock.MagicMock(return_value=None))
    def test_resume_subworkflow_task(self):
        MistralRunner.entry_point = mock.PropertyMock(
            return_value=WB1_YAML_FILE_PATH)
        liveaction1 = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS)
        liveaction1, execution1 = action_service.request(liveaction1)

        # Rerun the execution.
        context = {
            're-run': {
                'ref': execution1.id,
                'tasks': ['greet.say-friend']
            }
        }

        liveaction2 = LiveActionDB(action=WB1_NAME,
                                   parameters=ACTION_PARAMS,
                                   context=context)
        liveaction2, execution2 = action_service.request(liveaction2)
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))

        self.assertEqual(liveaction2.status,
                         action_constants.LIVEACTION_STATUS_RUNNING)

        expected_env = {
            'st2_liveaction_id': str(liveaction2.id),
            'st2_execution_id': str(execution2.id),
            '__actions': {
                'st2.action': {
                    'st2_context': {
                        'api_url': 'http://0.0.0.0:9101/v1',
                        'endpoint': 'http://0.0.0.0:9101/v1/actionexecutions',
                        'notify': {},
                        'parent': {
                            're-run': context['re-run'],
                            'execution_id': str(execution2.id)
                        },
                        'skip_notify_tasks': []
                    }
                }
            },
            'st2_action_api_url': 'http://0.0.0.0:9101/v1'
        }

        tasks.TaskManager.rerun.assert_called_with(WB1_SUB1_TASK2['id'],
                                                   reset=False,
                                                   env=expected_env)

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workbooks.WorkbookManager, 'create',
                       mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB1_MAIN_EXEC)))
    @mock.patch.object(
        executions.ExecutionManager, 'get',
        mock.MagicMock(
            return_value=executions.Execution(None, WB1_MAIN_EXEC_ERRORED)))
    @mock.patch.object(executions.ExecutionManager, 'list',
                       mock.MagicMock(return_value=[
                           executions.Execution(None, WB1_MAIN_EXEC_ERRORED),
                           executions.Execution(None, WB1_SUB1_EXEC_ERRORED)
                       ]))
    @mock.patch.object(
        tasks.TaskManager, 'list',
        mock.MagicMock(side_effect=[WB1_MAIN_TASKS, WB1_SUB1_TASKS]))
    def test_resume_unidentified_subworkflow_task(self):
        MistralRunner.entry_point = mock.PropertyMock(
            return_value=WB1_YAML_FILE_PATH)
        liveaction1 = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS)
        liveaction1, execution1 = action_service.request(liveaction1)

        # Rerun the execution.
        context = {'re-run': {'ref': execution1.id, 'tasks': ['greet.x']}}

        liveaction2 = LiveActionDB(action=WB1_NAME,
                                   parameters=ACTION_PARAMS,
                                   context=context)
        liveaction2, execution2 = action_service.request(liveaction2)
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
        self.assertEqual(liveaction2.status,
                         action_constants.LIVEACTION_STATUS_FAILED)
        self.assertIn('Unable to identify', liveaction2.result.get('error'))

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workbooks.WorkbookManager, 'create',
                       mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB1_MAIN_EXEC)))
    @mock.patch.object(
        executions.ExecutionManager, 'get',
        mock.MagicMock(
            return_value=executions.Execution(None, WB1_MAIN_EXEC_ERRORED)))
    @mock.patch.object(executions.ExecutionManager, 'list',
                       mock.MagicMock(return_value=[
                           executions.Execution(None, WB1_MAIN_EXEC_ERRORED),
                           executions.Execution(None, WB1_SUB1_EXEC_ERRORED)
                       ]))
    @mock.patch.object(
        tasks.TaskManager, 'list',
        mock.MagicMock(side_effect=[WB1_MAIN_TASKS, WB1_SUB1_TASKS]))
    @mock.patch.object(tasks.TaskManager, 'rerun',
                       mock.MagicMock(return_value=None))
    def test_resume_and_reset_subworkflow_task(self):
        MistralRunner.entry_point = mock.PropertyMock(
            return_value=WB1_YAML_FILE_PATH)
        liveaction1 = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS)
        liveaction1, execution1 = action_service.request(liveaction1)

        # Rerun the execution.
        context = {
            're-run': {
                'ref': execution1.id,
                'tasks': ['greet.say-friend'],
                'reset': ['greet.say-friend']
            }
        }

        liveaction2 = LiveActionDB(action=WB1_NAME,
                                   parameters=ACTION_PARAMS,
                                   context=context)
        liveaction2, execution2 = action_service.request(liveaction2)
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))

        self.assertEqual(liveaction2.status,
                         action_constants.LIVEACTION_STATUS_RUNNING)

        expected_env = {
            'st2_liveaction_id': str(liveaction2.id),
            'st2_execution_id': str(execution2.id),
            '__actions': {
                'st2.action': {
                    'st2_context': {
                        'api_url': 'http://0.0.0.0:9101/v1',
                        'endpoint': 'http://0.0.0.0:9101/v1/actionexecutions',
                        'notify': {},
                        'parent': {
                            're-run': context['re-run'],
                            'execution_id': str(execution2.id)
                        },
                        'skip_notify_tasks': []
                    }
                }
            },
            'st2_action_api_url': 'http://0.0.0.0:9101/v1'
        }

        tasks.TaskManager.rerun.assert_called_with(WB1_SUB1_TASK2['id'],
                                                   reset=True,
                                                   env=expected_env)
Example #15
0
class MistralRunnerTest(ExecutionDbTestCase):
    @classmethod
    def setUpClass(cls):
        super(MistralRunnerTest, cls).setUpClass()

        cfg.CONF.set_override('api_url', 'http://0.0.0.0:9101', group='auth')

        # Register runners.
        runnersregistrar.register_runners()

        # Register test pack(s).
        actions_registrar = actionsregistrar.ActionsRegistrar(
            use_pack_cache=False, fail_on_failure=True)

        for pack in PACKS:
            actions_registrar.register_from_pack(pack)

    @classmethod
    def get_runner_class(cls, runner_name):
        return runners.get_runner(runner_name, runner_name).__class__

    def test_build_context(self):
        parent = {
            'mistral': {
                'workflow_name': 'foo',
                'workflow_execution_id':
                'b222b934-7473-4cd4-a2ec-e204a8c93848',
                'task_tags': None,
                'task_name': 'some_fancy_wf_task',
                'task_id': '6c7d4334-3e7d-49c6-918d-698e846affaf',
                'action_execution_id': '24da5c88-834c-4a65-8b56-4ddbd654eb68'
            }
        }

        current = {
            'workflow_name': 'foo.subwf',
            'workflow_execution_id': '135e3446-4c89-4afe-821f-6ec6a0849b27'
        }

        context = MistralRunner._build_mistral_context(parent, current)
        self.assertTrue(context is not None)
        self.assertTrue('parent' in list(context['mistral'].keys()))

        parent_dict = {
            'workflow_name': parent['mistral']['workflow_name'],
            'workflow_execution_id': parent['mistral']['workflow_execution_id']
        }

        self.assertDictEqual(context['mistral']['parent'], parent_dict)
        self.assertEqual(context['mistral']['workflow_execution_id'],
                         current['workflow_execution_id'])

        parent = None
        context = MistralRunner._build_mistral_context(parent, current)
        self.assertDictEqual(context['mistral'], current)

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow(self):
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'],
                         WF1_EXEC.get('workflow_name'))

        workflow_input = copy.deepcopy(ACTION_PARAMS)
        workflow_input.update({'count': '3'})

        env = {
            'st2_execution_id': str(execution.id),
            'st2_liveaction_id': str(liveaction.id),
            'st2_action_api_url': 'http://0.0.0.0:9101/v1',
            '__actions': {
                'st2.action': {
                    'st2_context': {
                        'api_url': 'http://0.0.0.0:9101/v1',
                        'endpoint': 'http://0.0.0.0:9101/v1/actionexecutions',
                        'parent': {
                            'pack': 'mistral_tests',
                            'execution_id': str(execution.id)
                        },
                        'notify': {},
                        'skip_notify_tasks': []
                    }
                }
            }
        }

        executions.ExecutionManager.create.assert_called_with(
            WF1_NAME, workflow_input=workflow_input, env=env, notify=NOTIFY)

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow_under_parent_chain_with_jinja_params(self):
        ac_ctx = {
            'chain': {
                'params': {
                    'var1': 'foobar',
                    'var2': '{{foobar}}',
                    'var3': ['{{foo}}', '{{bar}}'],
                    'var4': {
                        'foobar': '{{foobar}}'
                    },
                    'var5': {
                        'foobar': '{% for item in items %}foobar{% end for %}'
                    }
                }
            }
        }

        liveaction = LiveActionDB(action=WF1_NAME,
                                  parameters=ACTION_PARAMS,
                                  context=ac_ctx)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'],
                         WF1_EXEC.get('workflow_name'))

        workflow_input = copy.deepcopy(ACTION_PARAMS)
        workflow_input.update({'count': '3'})

        env = {
            'st2_execution_id': str(execution.id),
            'st2_liveaction_id': str(liveaction.id),
            'st2_action_api_url': 'http://0.0.0.0:9101/v1',
            '__actions': {
                'st2.action': {
                    'st2_context': {
                        'api_url': 'http://0.0.0.0:9101/v1',
                        'endpoint': 'http://0.0.0.0:9101/v1/actionexecutions',
                        'parent': {
                            'pack': 'mistral_tests',
                            'execution_id': str(execution.id),
                            'chain': {
                                'params': {
                                    'var1':
                                    'foobar',
                                    'var2':
                                    '{% raw %}{{foobar}}{% endraw %}',
                                    'var3': [
                                        '{% raw %}{{foo}}{% endraw %}',
                                        '{% raw %}{{bar}}{% endraw %}'
                                    ],
                                    'var4': {
                                        'foobar':
                                        '{% raw %}{{foobar}}{% endraw %}'
                                    },
                                    'var5': {
                                        'foobar':
                                        ('{% raw %}{% for item in items %}'
                                         'foobar{% end for %}{% endraw %}')
                                    }
                                }
                            }
                        },
                        'notify': {},
                        'skip_notify_tasks': []
                    }
                }
            }
        }

        executions.ExecutionManager.create.assert_called_with(
            WF1_NAME, workflow_input=workflow_input, env=env, notify=NOTIFY)

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow_under_parent_chain_with_jinja_parameters(self):
        ac_ctx = {
            'chain': {
                'parameters': {
                    'var1': 'foobar',
                    'var2': '{{foobar}}',
                    'var3': ['{{foo}}', '{{bar}}'],
                    'var4': {
                        'foobar': '{{foobar}}'
                    },
                }
            }
        }

        liveaction = LiveActionDB(action=WF1_NAME,
                                  parameters=ACTION_PARAMS,
                                  context=ac_ctx)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'],
                         WF1_EXEC.get('workflow_name'))

        workflow_input = copy.deepcopy(ACTION_PARAMS)
        workflow_input.update({'count': '3'})

        env = {
            'st2_execution_id': str(execution.id),
            'st2_liveaction_id': str(liveaction.id),
            'st2_action_api_url': 'http://0.0.0.0:9101/v1',
            '__actions': {
                'st2.action': {
                    'st2_context': {
                        'api_url': 'http://0.0.0.0:9101/v1',
                        'endpoint': 'http://0.0.0.0:9101/v1/actionexecutions',
                        'parent': {
                            'pack': 'mistral_tests',
                            'execution_id': str(execution.id),
                            'chain': {
                                'parameters': {
                                    'var1':
                                    'foobar',
                                    'var2':
                                    '{% raw %}{{foobar}}{% endraw %}',
                                    'var3': [
                                        '{% raw %}{{foo}}{% endraw %}',
                                        '{% raw %}{{bar}}{% endraw %}'
                                    ],
                                    'var4': {
                                        'foobar':
                                        '{% raw %}{{foobar}}{% endraw %}'
                                    }
                                }
                            }
                        },
                        'notify': {},
                        'skip_notify_tasks': []
                    }
                }
            }
        }

        executions.ExecutionManager.create.assert_called_with(
            WF1_NAME, workflow_input=workflow_input, env=env, notify=NOTIFY)

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow_under_parent_chain_with_nonetype_in_chain_context(
            self):
        ac_ctx = {'chain': None}

        liveaction = LiveActionDB(action=WF1_NAME,
                                  parameters=ACTION_PARAMS,
                                  context=ac_ctx)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'],
                         WF1_EXEC.get('workflow_name'))

        workflow_input = copy.deepcopy(ACTION_PARAMS)
        workflow_input.update({'count': '3'})

        env = {
            'st2_execution_id': str(execution.id),
            'st2_liveaction_id': str(liveaction.id),
            'st2_action_api_url': 'http://0.0.0.0:9101/v1',
            '__actions': {
                'st2.action': {
                    'st2_context': {
                        'api_url': 'http://0.0.0.0:9101/v1',
                        'endpoint': 'http://0.0.0.0:9101/v1/actionexecutions',
                        'parent': {
                            'pack': 'mistral_tests',
                            'execution_id': str(execution.id),
                            'chain': None
                        },
                        'notify': {},
                        'skip_notify_tasks': []
                    }
                }
            }
        }

        executions.ExecutionManager.create.assert_called_with(
            WF1_NAME, workflow_input=workflow_input, env=env, notify=NOTIFY)

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow_under_parent_chain_with_nonetype_in_params_context(
            self):
        ac_ctx = {'chain': {'params': None}}

        liveaction = LiveActionDB(action=WF1_NAME,
                                  parameters=ACTION_PARAMS,
                                  context=ac_ctx)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'],
                         WF1_EXEC.get('workflow_name'))

        workflow_input = copy.deepcopy(ACTION_PARAMS)
        workflow_input.update({'count': '3'})

        env = {
            'st2_execution_id': str(execution.id),
            'st2_liveaction_id': str(liveaction.id),
            'st2_action_api_url': 'http://0.0.0.0:9101/v1',
            '__actions': {
                'st2.action': {
                    'st2_context': {
                        'api_url': 'http://0.0.0.0:9101/v1',
                        'endpoint': 'http://0.0.0.0:9101/v1/actionexecutions',
                        'parent': {
                            'pack': 'mistral_tests',
                            'execution_id': str(execution.id),
                            'chain': {
                                'params': None
                            }
                        },
                        'notify': {},
                        'skip_notify_tasks': []
                    }
                }
            }
        }

        executions.ExecutionManager.create.assert_called_with(
            WF1_NAME, workflow_input=workflow_input, env=env, notify=NOTIFY)

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow_with_st2_https(self):
        cfg.CONF.set_override('api_url', 'https://0.0.0.0:9101', group='auth')

        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'],
                         WF1_EXEC.get('workflow_name'))

        workflow_input = copy.deepcopy(ACTION_PARAMS)
        workflow_input.update({'count': '3'})

        env = {
            'st2_execution_id': str(execution.id),
            'st2_liveaction_id': str(liveaction.id),
            'st2_action_api_url': 'https://0.0.0.0:9101/v1',
            '__actions': {
                'st2.action': {
                    'st2_context': {
                        'api_url': 'https://0.0.0.0:9101/v1',
                        'endpoint': 'https://0.0.0.0:9101/v1/actionexecutions',
                        'parent': {
                            'pack': 'mistral_tests',
                            'execution_id': str(execution.id)
                        },
                        'notify': {},
                        'skip_notify_tasks': []
                    }
                }
            }
        }

        executions.ExecutionManager.create.assert_called_with(
            WF1_NAME, workflow_input=workflow_input, env=env, notify=NOTIFY)

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow_with_notifications(self):
        notify_data = {
            'on_complete': {
                'routes': ['slack'],
                'message': '"@channel: Action succeeded."',
                'data': {}
            }
        }

        liveaction = LiveActionDB(action=WF1_NAME,
                                  parameters=ACTION_PARAMS,
                                  notify=notify_data)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'],
                         WF1_EXEC.get('workflow_name'))

        workflow_input = copy.deepcopy(ACTION_PARAMS)
        workflow_input.update({'count': '3'})

        env = {
            'st2_execution_id': str(execution.id),
            'st2_liveaction_id': str(liveaction.id),
            'st2_action_api_url': 'http://0.0.0.0:9101/v1',
            '__actions': {
                'st2.action': {
                    'st2_context': {
                        'api_url': 'http://0.0.0.0:9101/v1',
                        'endpoint': 'http://0.0.0.0:9101/v1/actionexecutions',
                        'parent': {
                            'pack': 'mistral_tests',
                            'execution_id': str(execution.id)
                        },
                        'notify':
                        NotificationsHelper.from_model(liveaction.notify),
                        'skip_notify_tasks': []
                    }
                }
            }
        }

        executions.ExecutionManager.create.assert_called_with(
            WF1_NAME, workflow_input=workflow_input, env=env, notify=NOTIFY)

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(side_effect=requests.exceptions.ConnectionError(
            'Connection refused')))
    def test_launch_workflow_mistral_offline(self):
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, action_constants.LIVEACTION_STATUS_FAILED)
        self.assertIn('Connection refused', liveaction.result['error'])

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(side_effect=[requests.exceptions.ConnectionError(), []])
    )
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow_mistral_retry(self):
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'],
                         WF1_EXEC.get('workflow_name'))

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(
            side_effect=[APIException(error_message='Duplicate entry.'), WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow_duplicate_error(self):
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'],
                         WF1_EXEC.get('workflow_name'))

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1_OLD))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(workflows.WorkflowManager, 'update',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_when_workflow_definition_changed(self):
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'],
                         WF1_EXEC.get('workflow_name'))

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(side_effect=Exception()))
    @mock.patch.object(workbooks.WorkbookManager, 'delete',
                       mock.MagicMock(side_effect=Exception()))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_when_workflow_not_exists(self):
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'],
                         WF1_EXEC.get('workflow_name'))

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF2))
    def test_launch_workflow_with_many_workflows(self):
        liveaction = LiveActionDB(action=WF2_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, action_constants.LIVEACTION_STATUS_FAILED)
        self.assertIn('Multiple workflows is not supported.',
                      liveaction.result['error'])

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(side_effect=Exception()))
    def test_launch_workflow_name_mistmatch(self):
        action_ref = TEST_PACK + '.workflow_v2_name_mismatch'
        liveaction = LiveActionDB(action=action_ref, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, action_constants.LIVEACTION_STATUS_FAILED)
        self.assertIn('Name of the workflow must be the same',
                      liveaction.result['error'])

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF3))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF3]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF3_EXEC)))
    def test_launch_workflow_reverse(self):
        # no differences for liveaction (direct == reverse)
        liveaction = LiveActionDB(action=WF3_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, action_constants.LIVEACTION_STATUS_RUNNING)

        # no differences for mistral_context (direct == reverse)
        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF3_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'],
                         WF3_EXEC.get('workflow_name'))

        # no differences for workflow_input (direct == reverse)
        workflow_input = copy.deepcopy(ACTION_PARAMS)
        workflow_input.update({'count': '3'})

        env = {
            'st2_execution_id': str(execution.id),
            'st2_liveaction_id': str(liveaction.id),
            'st2_action_api_url': 'http://0.0.0.0:9101/v1',
            '__actions': {
                'st2.action': {
                    'st2_context': {
                        'api_url': 'http://0.0.0.0:9101/v1',
                        'endpoint': 'http://0.0.0.0:9101/v1/actionexecutions',
                        'parent': {
                            'pack': 'mistral_tests',
                            'execution_id': str(execution.id)
                        },
                        'notify': {},
                        'skip_notify_tasks': []
                    }
                }
            }
        }

        # task_name must be passed to mistral.executions.create for reverse workflows
        task_name = WF3_REVERSE_TARGET_TASK_NAME

        executions.ExecutionManager.create.assert_called_with(
            WF3_NAME,
            workflow_input=workflow_input,
            env=env,
            notify=NOTIFY,
            task_name=task_name)

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workbooks.WorkbookManager, 'get',
                       mock.MagicMock(return_value=WB1))
    @mock.patch.object(workbooks.WorkbookManager, 'create',
                       mock.MagicMock(return_value=WB1))
    @mock.patch.object(workbooks.WorkbookManager, 'update',
                       mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB1_EXEC)))
    def test_launch_workbook(self):
        liveaction = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WB1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'],
                         WB1_EXEC.get('workflow_name'))

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workbooks.WorkbookManager, 'get',
                       mock.MagicMock(return_value=WB2))
    @mock.patch.object(workbooks.WorkbookManager, 'create',
                       mock.MagicMock(return_value=WB2))
    @mock.patch.object(workbooks.WorkbookManager, 'update',
                       mock.MagicMock(return_value=WB2))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB2_EXEC)))
    def test_launch_workbook_with_many_workflows(self):
        liveaction = LiveActionDB(action=WB2_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WB2_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'],
                         WB2_EXEC.get('workflow_name'))

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workbooks.WorkbookManager, 'get',
                       mock.MagicMock(return_value=WB3))
    @mock.patch.object(workbooks.WorkbookManager, 'create',
                       mock.MagicMock(return_value=WB3))
    @mock.patch.object(workbooks.WorkbookManager, 'update',
                       mock.MagicMock(return_value=WB3))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB3_EXEC)))
    def test_launch_workbook_with_many_workflows_no_default(self):
        liveaction = LiveActionDB(action=WB3_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, action_constants.LIVEACTION_STATUS_FAILED)
        self.assertIn('Default workflow cannot be determined.',
                      liveaction.result['error'])

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workbooks.WorkbookManager, 'get',
                       mock.MagicMock(return_value=WB1_OLD))
    @mock.patch.object(workbooks.WorkbookManager, 'create',
                       mock.MagicMock(return_value=WB1))
    @mock.patch.object(workbooks.WorkbookManager, 'update',
                       mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB1_EXEC)))
    def test_launch_when_workbook_definition_changed(self):
        liveaction = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WB1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'],
                         WB1_EXEC.get('workflow_name'))

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workbooks.WorkbookManager, 'get',
                       mock.MagicMock(side_effect=Exception()))
    @mock.patch.object(workflows.WorkflowManager, 'delete',
                       mock.MagicMock(side_effect=Exception()))
    @mock.patch.object(workbooks.WorkbookManager, 'create',
                       mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB1_EXEC)))
    def test_launch_when_workbook_not_exists(self):
        liveaction = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WB1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'],
                         WB1_EXEC.get('workflow_name'))

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workbooks.WorkbookManager, 'get',
                       mock.MagicMock(side_effect=Exception()))
    def test_launch_workbook_name_mismatch(self):
        action_ref = TEST_PACK + '.workbook_v2_name_mismatch'
        liveaction = LiveActionDB(action=action_ref, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = self._wait_on_status(
            liveaction, action_constants.LIVEACTION_STATUS_FAILED)
        self.assertIn('Name of the workbook must be the same',
                      liveaction.result['error'])
EXEC_DICT = {
    'id': '123',
    'workflow_id': '123e4567-e89b-12d3-a456-426655440000',
    'workflow_name': 'some',
    'workflow_namespace': '',
    'root_execution_id': '',
    'description': '',
    'state': 'RUNNING',
    'state_info': None,
    'created_at': '1',
    'updated_at': '1',
    'task_execution_id': None
}

EXEC = executions.Execution(mock, EXEC_DICT)

SUB_WF_EXEC = executions.Execution(
    mock, {
        'id': '456',
        'workflow_id': '123e4567-e89b-12d3-a456-426655440000',
        'workflow_name': 'some_sub_wf',
        'workflow_namespace': '',
        'root_execution_id': 'ROOT_EXECUTION_ID',
        'description': '',
        'state': 'RUNNING',
        'state_info': None,
        'created_at': '1',
        'updated_at': '1',
        'task_execution_id': 'abc'
    })
Example #17
0
class MistralRunnerCancelTest(DbTestCase):
    @classmethod
    def setUpClass(cls):
        super(MistralRunnerCancelTest, cls).setUpClass()

        # Override the retry configuration here otherwise st2tests.config.parse_args
        # in DbTestCase.setUpClass will reset these overrides.
        cfg.CONF.set_override('retry_exp_msec', 100, group='mistral')
        cfg.CONF.set_override('retry_exp_max_msec', 200, group='mistral')
        cfg.CONF.set_override('retry_stop_max_msec', 200, group='mistral')
        cfg.CONF.set_override('api_url', 'http://0.0.0.0:9101', group='auth')

        # Register runners.
        runnersregistrar.register_runners()

        # Register test pack(s).
        actions_registrar = actionsregistrar.ActionsRegistrar(
            use_pack_cache=False, fail_on_failure=True)

        for pack in PACKS:
            actions_registrar.register_from_pack(pack)

    @classmethod
    def get_runner_class(cls, runner_name):
        return runners.get_runner(runner_name).__class__

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    @mock.patch.object(
        executions.ExecutionManager, 'update',
        mock.MagicMock(
            return_value=executions.Execution(None, WF1_EXEC_CANCELLED)))
    def test_cancel(self):
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status,
                         action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'],
                         WF1_EXEC.get('workflow_name'))

        requester = cfg.CONF.system_user.user
        liveaction, execution = action_service.request_cancellation(
            liveaction, requester)
        executions.ExecutionManager.update.assert_called_with(
            WF1_EXEC.get('id'), 'CANCELLED')
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status,
                         action_constants.LIVEACTION_STATUS_CANCELING)

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(side_effect=[WF2, WF1]))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(side_effect=[[WF2], [WF1]]))
    @mock.patch.object(executions.ExecutionManager, 'create',
                       mock.MagicMock(side_effect=[
                           executions.Execution(None, WF2_EXEC),
                           executions.Execution(None, WF1_EXEC)
                       ]))
    @mock.patch.object(executions.ExecutionManager, 'update',
                       mock.MagicMock(side_effect=[
                           executions.Execution(None, WF2_EXEC_CANCELLED),
                           executions.Execution(None, WF1_EXEC_CANCELLED)
                       ]))
    def test_cancel_subworkflow_action(self):
        liveaction1 = LiveActionDB(action=WF2_NAME, parameters=ACTION_PARAMS)
        liveaction1, execution1 = action_service.request(liveaction1)
        liveaction1 = LiveAction.get_by_id(str(liveaction1.id))
        self.assertEqual(liveaction1.status,
                         action_constants.LIVEACTION_STATUS_RUNNING)

        liveaction2 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction2, execution2 = action_service.request(liveaction2)
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
        self.assertEqual(liveaction2.status,
                         action_constants.LIVEACTION_STATUS_RUNNING)

        # Mock the children of the parent execution to make this
        # test case has subworkflow execution.
        with mock.patch.object(
                ActionExecutionDB, 'children',
                new_callable=mock.PropertyMock) as action_ex_children_mock:
            action_ex_children_mock.return_value = [execution2.id]

            mistral_context = liveaction1.context.get('mistral', None)
            self.assertIsNotNone(mistral_context)
            self.assertEqual(mistral_context['execution_id'],
                             WF2_EXEC.get('id'))
            self.assertEqual(mistral_context['workflow_name'],
                             WF2_EXEC.get('workflow_name'))

            requester = cfg.CONF.system_user.user
            liveaction1, execution1 = action_service.request_cancellation(
                liveaction1, requester)

            self.assertTrue(executions.ExecutionManager.update.called)
            self.assertEqual(executions.ExecutionManager.update.call_count, 2)

            calls = [
                mock.call(WF2_EXEC.get('id'), 'CANCELLED'),
                mock.call(WF1_EXEC.get('id'), 'CANCELLED')
            ]

            executions.ExecutionManager.update.assert_has_calls(
                calls, any_order=False)

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    @mock.patch.object(executions.ExecutionManager, 'update',
                       mock.MagicMock(side_effect=[
                           requests.exceptions.ConnectionError(),
                           executions.Execution(None, WF1_EXEC_CANCELLED)
                       ]))
    def test_cancel_retry(self):
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status,
                         action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'],
                         WF1_EXEC.get('workflow_name'))

        requester = cfg.CONF.system_user.user
        liveaction, execution = action_service.request_cancellation(
            liveaction, requester)
        executions.ExecutionManager.update.assert_called_with(
            WF1_EXEC.get('id'), 'CANCELLED')
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status,
                         action_constants.LIVEACTION_STATUS_CANCELING)

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    @mock.patch.object(
        executions.ExecutionManager, 'update',
        mock.MagicMock(side_effect=requests.exceptions.ConnectionError(
            'Connection refused')))
    def test_cancel_retry_exhausted(self):
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status,
                         action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'],
                         WF1_EXEC.get('workflow_name'))

        requester = cfg.CONF.system_user.user
        liveaction, execution = action_service.request_cancellation(
            liveaction, requester)

        calls = [call(WF1_EXEC.get('id'), 'CANCELLED') for i in range(0, 2)]
        executions.ExecutionManager.update.assert_has_calls(calls)

        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status,
                         action_constants.LIVEACTION_STATUS_CANCELING)
Example #18
0
class MistralRunnerPolicyTest(DbTestCase):
    @classmethod
    def setUpClass(cls):
        super(MistralRunnerPolicyTest, cls).setUpClass()

        # Override the retry configuration here otherwise st2tests.config.parse_args
        # in DbTestCase.setUpClass will reset these overrides.
        cfg.CONF.set_override('retry_exp_msec', 100, group='mistral')
        cfg.CONF.set_override('retry_exp_max_msec', 200, group='mistral')
        cfg.CONF.set_override('retry_stop_max_msec', 200, group='mistral')
        cfg.CONF.set_override('api_url', 'http://0.0.0.0:9101', group='auth')

    def setUp(self):
        super(MistralRunnerPolicyTest, self).setUp()

        # Start with a clean database for each test.
        self._establish_connection_and_re_create_db()

        # Register runners.
        runnersregistrar.register_runners()

        actions_registrar = actionsregistrar.ActionsRegistrar(
            use_pack_cache=False, fail_on_failure=True)

        for pack in PACKS:
            actions_registrar.register_from_pack(pack)

        # Register policies required for the tests.
        policiesregistrar.register_policy_types(st2common)

        policies_registrar = policiesregistrar.PolicyRegistrar(
            use_pack_cache=False, fail_on_failure=True)

        for pack in PACKS:
            policies_registrar.register_from_pack(pack)

    @classmethod
    def get_runner_class(cls, runner_name):
        return runners.get_runner(runner_name).__class__

    def _drop_all_other_policies(self, test_policy):
        policy_dbs = [
            policy_db for policy_db in Policy.get_all()
            if policy_db.ref != test_policy
        ]

        for policy_db in policy_dbs:
            Policy.delete(policy_db, publish=False)

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    @mock.patch.object(action_executions.ActionExecutionManager, 'update',
                       mock.MagicMock(return_value=None))
    def test_cancel_on_task_action_concurrency(self):
        # Delete other policies in the test pack to avoid conflicts.
        required_policy = 'mistral_tests.cancel_on_concurrency'
        self._drop_all_other_policies(required_policy)

        # Get threshold from the policy.
        policy = Policy.get_by_ref(required_policy)
        threshold = policy.parameters.get('threshold', 0)
        self.assertGreater(threshold, 0)

        # Launch instances of the workflow up to threshold.
        for i in range(0, threshold):
            liveaction = LiveActionDB(action=WF1_NAME,
                                      parameters={'friend': 'friend' + str(i)})
            liveaction, execution1 = action_service.request(liveaction)
            liveaction = LiveAction.get_by_id(str(liveaction.id))
            self.assertEqual(liveaction.status,
                             action_constants.LIVEACTION_STATUS_RUNNING)

        # Check number of running instances
        running = LiveAction.count(
            action=WF1_NAME, status=action_constants.LIVEACTION_STATUS_RUNNING)

        self.assertEqual(running, threshold)

        # Mock the mistral runner cancel method to assert cancel is called.
        mistral_runner_cls = self.get_runner_class('mistral_v2')

        with mock.patch.object(mistral_runner_cls, 'cancel',
                               mock.MagicMock(return_value=None)):
            # Launch another instance of the workflow with mistral callback defined
            # to indicate that this is executed under a workflow.
            callback = {
                'source': MISTRAL_RUNNER_NAME,
                'url': 'http://127.0.0.1:8989/v2/action_executions/12345'
            }

            params = {'friend': 'grande animalerie'}

            liveaction2 = LiveActionDB(action=WF1_NAME,
                                       parameters=params,
                                       callback=callback)
            liveaction2, execution2 = action_service.request(liveaction2)

            action_executions.ActionExecutionManager.update.assert_called_once_with(
                '12345',
                output='{"error": "Execution canceled by user."}',
                state='CANCELLED')

            liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
            self.assertEqual(liveaction2.status,
                             action_constants.LIVEACTION_STATUS_CANCELED)

            # Assert cancel has been called.
            mistral_runner_cls.cancel.assert_called_once_with()

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    @mock.patch.object(action_executions.ActionExecutionManager, 'update',
                       mock.MagicMock(return_value=None))
    def test_cancel_on_task_action_concurrency_by_attr(self):
        # Delete other policies in the test pack to avoid conflicts.
        required_policy = 'mistral_tests.cancel_on_concurrency_by_attr'
        self._drop_all_other_policies(required_policy)

        # Get threshold from the policy.
        policy = Policy.get_by_ref(required_policy)
        threshold = policy.parameters.get('threshold', 0)
        self.assertGreater(threshold, 0)

        params = {'friend': 'grande animalerie'}

        # Launch instances of the workflow up to threshold.
        for i in range(0, threshold):
            liveaction = LiveActionDB(action=WF1_NAME, parameters=params)
            liveaction, execution1 = action_service.request(liveaction)
            liveaction = LiveAction.get_by_id(str(liveaction.id))
            self.assertEqual(liveaction.status,
                             action_constants.LIVEACTION_STATUS_RUNNING)

        # Check number of running instances
        running = LiveAction.count(
            action=WF1_NAME,
            status=action_constants.LIVEACTION_STATUS_RUNNING,
            parameters__friend=params['friend'])

        self.assertEqual(running, threshold)

        # Mock the mistral runner cancel method to assert cancel is called.
        mistral_runner_cls = self.get_runner_class('mistral_v2')

        with mock.patch.object(mistral_runner_cls, 'cancel',
                               mock.MagicMock(return_value=None)):
            # Launch another instance of the workflow with mistral callback defined
            # to indicate that this is executed under a workflow.
            callback = {
                'source': MISTRAL_RUNNER_NAME,
                'url': 'http://127.0.0.1:8989/v2/action_executions/12345'
            }

            liveaction2 = LiveActionDB(action=WF1_NAME,
                                       parameters=params,
                                       callback=callback)
            liveaction2, execution2 = action_service.request(liveaction2)

            action_executions.ActionExecutionManager.update.assert_called_once_with(
                '12345',
                output='{"error": "Execution canceled by user."}',
                state='CANCELLED')

            liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
            self.assertEqual(liveaction2.status,
                             action_constants.LIVEACTION_STATUS_CANCELED)

            # Assert cancel has been called.
            mistral_runner_cls.cancel.assert_called_once_with()
import mock
import pkg_resources as pkg
import six
import sys

from mistralclient.api.v2 import executions
from mistralclient.commands.v2 import executions as execution_cmd
from mistralclient.tests.unit import base

EXEC = executions.Execution(
    mock, {
        'id': '123',
        'workflow_id': '123e4567-e89b-12d3-a456-426655440000',
        'workflow_name': 'some',
        'description': '',
        'state': 'RUNNING',
        'state_info': None,
        'created_at': '1',
        'updated_at': '1',
        'task_execution_id': None
    })

SUB_WF_EXEC = executions.Execution(
    mock, {
        'id': '456',
        'workflow_id': '123e4567-e89b-12d3-a456-426655440000',
        'workflow_name': 'some_sub_wf',
        'description': '',
        'state': 'RUNNING',
        'state_info': None,
        'created_at': '1',
Example #20
0
class MistralAuthTest(DbTestCase):

    @classmethod
    def setUpClass(cls):
        super(MistralAuthTest, cls).setUpClass()

        # Override the retry configuration here otherwise st2tests.config.parse_args
        # in DbTestCase.setUpClass will reset these overrides.
        cfg.CONF.set_override('retry_exp_msec', 100, group='mistral')
        cfg.CONF.set_override('retry_exp_max_msec', 200, group='mistral')
        cfg.CONF.set_override('retry_stop_max_msec', 200, group='mistral')

        # Register runners.
        runners_registrar.register_runners()

        # Register test pack(s).
        registrar = actionsregistrar.ActionsRegistrar(
            use_pack_cache=False,
            fail_on_failure=True
        )

        for pack in PACKS:
            registrar.register_from_pack(pack)

    def setUp(self):
        super(MistralAuthTest, self).setUp()
        cfg.CONF.set_override('api_url', 'http://0.0.0.0:9101', group='auth')

        # Mock the local runner run method.
        local_runner_cls = self.get_runner_class('local_runner')
        local_run_result = (action_constants.LIVEACTION_STATUS_SUCCEEDED, NON_EMPTY_RESULT, None)
        local_runner_cls.run = mock.Mock(return_value=local_run_result)

    @classmethod
    def get_runner_class(cls, runner_name):
        return runners.get_runner(runner_name).__class__

    def tearDown(self):
        super(MistralAuthTest, self).tearDown()
        cfg.CONF.set_default('keystone_username', None, group='mistral')
        cfg.CONF.set_default('keystone_password', None, group='mistral')
        cfg.CONF.set_default('keystone_project_name', None, group='mistral')
        cfg.CONF.set_default('keystone_auth_url', None, group='mistral')

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(return_value=WF1))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    @mock.patch.object(
        access_service, 'create_token',
        mock.MagicMock(return_value=TOKEN_DB))
    def test_launch_workflow_with_st2_auth(self):
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS, context=ACTION_CONTEXT)
        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'], WF1_EXEC.get('workflow_name'))

        workflow_input = copy.deepcopy(ACTION_PARAMS)
        workflow_input.update({'count': '3'})

        env = {
            'st2_execution_id': str(execution.id),
            'st2_liveaction_id': str(liveaction.id),
            'st2_action_api_url': 'http://0.0.0.0:9101/v1',
            '__actions': {
                'st2.action': {
                    'st2_context': {
                        'auth_token': TOKEN_DB.token,
                        'api_url': 'http://0.0.0.0:9101/v1',
                        'endpoint': 'http://0.0.0.0:9101/v1/actionexecutions',
                        'parent': {
                            'user': liveaction.context['user'],
                            'execution_id': str(execution.id)
                        },
                        'notify': {},
                        'skip_notify_tasks': []
                    }
                }
            }
        }

        executions.ExecutionManager.create.assert_called_with(
            WF1_NAME, workflow_input=workflow_input, env=env)

    @mock.patch.object(
        client.Client, 'authenticate',
        mock.MagicMock(return_value=(cfg.CONF.mistral.v2_base_url, '123', 'abc', 'xyz')))
    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(return_value=WF1))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow_with_mistral_auth(self):
        cfg.CONF.set_default('keystone_username', 'foo', group='mistral')
        cfg.CONF.set_default('keystone_password', 'bar', group='mistral')
        cfg.CONF.set_default('keystone_project_name', 'admin', group='mistral')
        cfg.CONF.set_default('keystone_auth_url', 'http://127.0.0.1:5000/v3', group='mistral')

        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'], WF1_EXEC.get('workflow_name'))

        workflow_input = copy.deepcopy(ACTION_PARAMS)
        workflow_input.update({'count': '3'})

        env = {
            'st2_execution_id': str(execution.id),
            'st2_liveaction_id': str(liveaction.id),
            'st2_action_api_url': 'http://0.0.0.0:9101/v1',
            '__actions': {
                'st2.action': {
                    'st2_context': {
                        'api_url': 'http://0.0.0.0:9101/v1',
                        'endpoint': 'http://0.0.0.0:9101/v1/actionexecutions',
                        'parent': {
                            'execution_id': str(execution.id)
                        },
                        'notify': {},
                        'skip_notify_tasks': []
                    }
                }
            }
        }

        client.Client.authenticate.assert_called_with(
            cfg.CONF.mistral.v2_base_url,
            cfg.CONF.mistral.keystone_username,
            cfg.CONF.mistral.keystone_password,
            cfg.CONF.mistral.keystone_project_name,
            cfg.CONF.mistral.keystone_auth_url,
            None, 'publicURL', 'workflow', None, None, None, False)

        executions.ExecutionManager.create.assert_called_with(
            WF1_NAME, workflow_input=workflow_input, env=env)
Example #21
0
class TestMistralRunner(DbTestCase):

    @classmethod
    def setUpClass(cls):
        super(TestMistralRunner, cls).setUpClass()
        runners_registrar.register_runner_types()

        for _, fixture in six.iteritems(FIXTURES['actions']):
            instance = ActionAPI(**fixture)
            Action.add_or_update(ActionAPI.to_model(instance))

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(return_value=WF1))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow(self):
        MistralRunner.entry_point = mock.PropertyMock(return_value=WF1_YAML_FILE_PATH)
        execution = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.request(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'], WF1_EXEC.get('workflow_name'))

        workflow_input = copy.deepcopy(ACTION_PARAMS)
        workflow_input.update({'count': '3'})

        env = {
            '__actions': {
                'st2.action': {
                    'st2_context': {
                        'endpoint': 'http://0.0.0.0:9101/v1/actionexecutions',
                        'parent': str(liveaction.id),
                        'notify': {},
                        'skip_notify_tasks': []
                    }
                }
            }
        }

        executions.ExecutionManager.create.assert_called_with(
            WF1_NAME, workflow_input=workflow_input, env=env)

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(return_value=WF1))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    @mock.patch.object(
        access_service, 'create_token',
        mock.MagicMock(return_value=TOKEN_DB))
    def test_launch_workflow_with_auth(self):
        MistralRunner.entry_point = mock.PropertyMock(return_value=WF1_YAML_FILE_PATH)
        execution = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS, context=ACTION_CONTEXT)
        liveaction, _ = action_service.request(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'], WF1_EXEC.get('workflow_name'))

        workflow_input = copy.deepcopy(ACTION_PARAMS)
        workflow_input.update({'count': '3'})

        env = {
            '__actions': {
                'st2.action': {
                    'st2_context': {
                        'auth_token': TOKEN_DB.token,
                        'endpoint': 'http://0.0.0.0:9101/v1/actionexecutions',
                        'parent': str(liveaction.id),
                        'notify': {},
                        'skip_notify_tasks': []
                    }
                }
            }
        }

        executions.ExecutionManager.create.assert_called_with(
            WF1_NAME, workflow_input=workflow_input, env=env)

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(return_value=WF1))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow_with_notifications(self):
        notify_data = {'on_complete': {'channels': ['slack'],
                       'message': '"@channel: Action succeeded."', 'data': {}}}

        MistralRunner.entry_point = mock.PropertyMock(return_value=WF1_YAML_FILE_PATH)
        execution = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS, notify=notify_data)
        liveaction, _ = action_service.request(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'], WF1_EXEC.get('workflow_name'))

        workflow_input = copy.deepcopy(ACTION_PARAMS)
        workflow_input.update({'count': '3'})

        env = {
            '__actions': {
                'st2.action': {
                    'st2_context': {
                        'endpoint': 'http://0.0.0.0:9101/v1/actionexecutions',
                        'parent': str(liveaction.id),
                        'notify': NotificationsHelper.from_model(liveaction.notify),
                        'skip_notify_tasks': []
                    }
                }
            }
        }

        executions.ExecutionManager.create.assert_called_with(
            WF1_NAME, workflow_input=workflow_input, env=env)

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(side_effect=requests.exceptions.ConnectionError()))
    def test_launch_workflow_mistral_offline(self):
        MistralRunner.entry_point = mock.PropertyMock(return_value=WF1_YAML_FILE_PATH)
        execution = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.request(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_FAILED)
        self.assertIn('Failed to connect to mistral', liveaction.result['message'])

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(side_effect=[requests.exceptions.ConnectionError(), []]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(return_value=WF1))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow_mistral_retry(self):
        MistralRunner.entry_point = mock.PropertyMock(return_value=WF1_YAML_FILE_PATH)
        execution = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.request(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'], WF1_EXEC.get('workflow_name'))

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(return_value=WF1))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(side_effect=[APIException(error_message='Duplicate entry.'), WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow_duplicate_error(self):
        MistralRunner.entry_point = mock.PropertyMock(return_value=WF1_YAML_FILE_PATH)
        execution = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.request(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'], WF1_EXEC.get('workflow_name'))

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(return_value=WF1_OLD))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        workflows.WorkflowManager, 'update',
        mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_when_workflow_definition_changed(self):
        MistralRunner.entry_point = mock.PropertyMock(return_value=WF1_YAML_FILE_PATH)
        execution = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.request(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'], WF1_EXEC.get('workflow_name'))

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(side_effect=Exception()))
    @mock.patch.object(
        workbooks.WorkbookManager, 'delete',
        mock.MagicMock(side_effect=Exception()))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_when_workflow_not_exists(self):
        execution = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.request(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'], WF1_EXEC.get('workflow_name'))

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(return_value=WF2))
    def test_launch_workflow_with_many_workflows(self):
        MistralRunner.entry_point = mock.PropertyMock(return_value=WF2_YAML_FILE_PATH)
        execution = LiveActionDB(action=WF2_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.request(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_FAILED)
        self.assertIn('Multiple workflows is not supported.', liveaction.result['message'])

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(side_effect=Exception()))
    def test_launch_workflow_name_mistmatch(self):
        action_ref = 'generic.workflow_v2_name_mismatch'
        MistralRunner.entry_point = mock.PropertyMock(return_value=WF1_YAML_FILE_PATH)
        execution = LiveActionDB(action=action_ref, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.request(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_FAILED)
        self.assertIn('Name of the workflow must be the same', liveaction.result['message'])

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workbooks.WorkbookManager, 'get',
        mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        workbooks.WorkbookManager, 'create',
        mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        workbooks.WorkbookManager, 'update',
        mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB1_EXEC)))
    def test_launch_workbook(self):
        MistralRunner.entry_point = mock.PropertyMock(return_value=WB1_YAML_FILE_PATH)
        execution = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.request(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WB1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'], WB1_EXEC.get('workflow_name'))

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workbooks.WorkbookManager, 'get',
        mock.MagicMock(return_value=WB2))
    @mock.patch.object(
        workbooks.WorkbookManager, 'create',
        mock.MagicMock(return_value=WB2))
    @mock.patch.object(
        workbooks.WorkbookManager, 'update',
        mock.MagicMock(return_value=WB2))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB2_EXEC)))
    def test_launch_workbook_with_many_workflows(self):
        MistralRunner.entry_point = mock.PropertyMock(return_value=WB2_YAML_FILE_PATH)
        execution = LiveActionDB(action=WB2_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.request(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WB2_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'], WB2_EXEC.get('workflow_name'))

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workbooks.WorkbookManager, 'get',
        mock.MagicMock(return_value=WB3))
    @mock.patch.object(
        workbooks.WorkbookManager, 'create',
        mock.MagicMock(return_value=WB3))
    @mock.patch.object(
        workbooks.WorkbookManager, 'update',
        mock.MagicMock(return_value=WB3))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB3_EXEC)))
    def test_launch_workbook_with_many_workflows_no_default(self):
        MistralRunner.entry_point = mock.PropertyMock(return_value=WB3_YAML_FILE_PATH)
        execution = LiveActionDB(action=WB3_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.request(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_FAILED)
        self.assertIn('Default workflow cannot be determined.', liveaction.result['message'])

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workbooks.WorkbookManager, 'get',
        mock.MagicMock(return_value=WB1_OLD))
    @mock.patch.object(
        workbooks.WorkbookManager, 'create',
        mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        workbooks.WorkbookManager, 'update',
        mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB1_EXEC)))
    def test_launch_when_workbook_definition_changed(self):
        MistralRunner.entry_point = mock.PropertyMock(return_value=WB1_YAML_FILE_PATH)
        execution = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.request(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WB1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'], WB1_EXEC.get('workflow_name'))

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workbooks.WorkbookManager, 'get',
        mock.MagicMock(side_effect=Exception()))
    @mock.patch.object(
        workflows.WorkflowManager, 'delete',
        mock.MagicMock(side_effect=Exception()))
    @mock.patch.object(
        workbooks.WorkbookManager, 'create',
        mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB1_EXEC)))
    def test_launch_when_workbook_not_exists(self):
        execution = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.request(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WB1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'], WB1_EXEC.get('workflow_name'))

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workbooks.WorkbookManager, 'get',
        mock.MagicMock(side_effect=Exception()))
    def test_launch_workbook_name_mismatch(self):
        action_ref = 'generic.workbook_v2_name_mismatch'
        MistralRunner.entry_point = mock.PropertyMock(return_value=WB1_YAML_FILE_PATH)
        execution = LiveActionDB(action=action_ref, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.request(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_FAILED)
        self.assertIn('Name of the workbook must be the same', liveaction.result['message'])

    @mock.patch.object(
        requests, 'request',
        mock.MagicMock(return_value=http.FakeResponse({}, 200, 'OK')))
    def test_callback_handler_with_result_as_text(self):
        MistralCallbackHandler.callback('http://localhost:8989/v2/action_executions/12345', {},
                                        action_constants.LIVEACTION_STATUS_SUCCEEDED,
                                        '<html></html>')

    @mock.patch.object(
        requests, 'request',
        mock.MagicMock(return_value=http.FakeResponse({}, 200, 'OK')))
    def test_callback_handler_with_result_as_dict(self):
        MistralCallbackHandler.callback('http://localhost:8989/v2/action_executions/12345', {},
                                        action_constants.LIVEACTION_STATUS_SUCCEEDED, {'a': 1})

    @mock.patch.object(
        requests, 'request',
        mock.MagicMock(return_value=http.FakeResponse({}, 200, 'OK')))
    def test_callback_handler_with_result_as_json_str(self):
        MistralCallbackHandler.callback('http://localhost:8989/v2/action_executions/12345', {},
                                        action_constants.LIVEACTION_STATUS_SUCCEEDED, '{"a": 1}')
        MistralCallbackHandler.callback('http://localhost:8989/v2/action_executions/12345', {},
                                        action_constants.LIVEACTION_STATUS_SUCCEEDED, "{'a': 1}")

    @mock.patch.object(
        requests, 'request',
        mock.MagicMock(return_value=http.FakeResponse({}, 200, 'OK')))
    def test_callback_handler_with_result_as_list(self):
        MistralCallbackHandler.callback('http://localhost:8989/v2/action_executions/12345', {},
                                        action_constants.LIVEACTION_STATUS_SUCCEEDED,
                                        ["a", "b", "c"])

    @mock.patch.object(
        requests, 'request',
        mock.MagicMock(return_value=http.FakeResponse({}, 200, 'OK')))
    def test_callback_handler_with_result_as_list_str(self):
        MistralCallbackHandler.callback('http://localhost:8989/v2/action_executions/12345', {},
                                        action_constants.LIVEACTION_STATUS_SUCCEEDED,
                                        '["a", "b", "c"]')

    @mock.patch.object(
        requests, 'request',
        mock.MagicMock(return_value=http.FakeResponse({}, 200, 'OK')))
    def test_callback(self):
        execution = LiveActionDB(
            action='core.local', parameters={'cmd': 'uname -a'},
            callback={
                'source': 'mistral',
                'url': 'http://localhost:8989/v2/action_executions/12345'
            }
        )

        liveaction, _ = action_service.request(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_SUCCEEDED)
        requests.request.assert_called_with('PUT', liveaction.callback['url'],
                                            data=json.dumps({'state': 'SUCCESS',
                                                             'output': NON_EMPTY_RESULT}),
                                            headers={'content-type': 'application/json'})

    def test_build_context(self):
        parent = {
            'mistral': {
                'workflow_name': 'foo',
                'workflow_execution_id': 'b222b934-7473-4cd4-a2ec-e204a8c93848',
                'task_tags': None,
                'task_name': 'some_fancy_wf_task',
                'task_id': '6c7d4334-3e7d-49c6-918d-698e846affaf',
                'action_execution_id': '24da5c88-834c-4a65-8b56-4ddbd654eb68'
            }
        }

        current = {
            'workflow_name': 'foo.subwf',
            'workflow_execution_id': '135e3446-4c89-4afe-821f-6ec6a0849b27'
        }

        context = MistralRunner._build_mistral_context(parent, current)
        self.assertTrue(context is not None)
        self.assertTrue('parent' in context['mistral'].keys())

        parent_dict = {
            'workflow_name': parent['mistral']['workflow_name'],
            'workflow_execution_id': parent['mistral']['workflow_execution_id']
        }

        self.assertDictEqual(context['mistral']['parent'], parent_dict)
        self.assertEqual(context['mistral']['workflow_execution_id'],
                         current['workflow_execution_id'])

        parent = None
        context = MistralRunner._build_mistral_context(parent, current)
        self.assertDictEqual(context['mistral'], current)
Example #22
0
class MistralRunnerTest(DbTestCase):
    @classmethod
    def setUpClass(cls):
        super(MistralRunnerTest, cls).setUpClass()

        # Override the retry configuration here otherwise st2tests.config.parse_args
        # in DbTestCase.setUpClass will reset these overrides.
        cfg.CONF.set_override('retry_exp_msec', 100, group='mistral')
        cfg.CONF.set_override('retry_exp_max_msec', 200, group='mistral')
        cfg.CONF.set_override('retry_stop_max_msec', 200, group='mistral')
        cfg.CONF.set_override('api_url', 'http://0.0.0.0:9101', group='auth')

        # Register runners.
        runnersregistrar.register_runners()

        # Register test pack(s).
        actions_registrar = actionsregistrar.ActionsRegistrar(
            use_pack_cache=False, fail_on_failure=True)

        for pack in PACKS:
            actions_registrar.register_from_pack(pack)

    def setUp(self):
        super(MistralRunnerTest, self).setUp()

        # Mock the local runner run method.
        local_runner_cls = self.get_runner_class('local_runner')
        local_run_result = (action_constants.LIVEACTION_STATUS_SUCCEEDED,
                            NON_EMPTY_RESULT, None)
        local_runner_cls.run = mock.Mock(return_value=local_run_result)

    @classmethod
    def get_runner_class(cls, runner_name):
        return runners.get_runner(runner_name).__class__

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_resume_option(self):
        patched_mistral_runner = self.get_runner_class('mistral_v2')

        mock_resume_result = (action_constants.LIVEACTION_STATUS_RUNNING, {
            'tasks': []
        }, {
            'execution_id': str(uuid.uuid4())
        })

        with mock.patch.object(
                patched_mistral_runner, 'resume',
                mock.MagicMock(return_value=mock_resume_result)):

            liveaction1 = LiveActionDB(action=WF1_NAME,
                                       parameters=ACTION_PARAMS)
            liveaction1, execution1 = action_service.request(liveaction1)
            self.assertFalse(patched_mistral_runner.resume.called)

            # Rerun the execution.
            context = {'re-run': {'ref': execution1.id, 'tasks': ['x']}}

            liveaction2 = LiveActionDB(action=WF1_NAME,
                                       parameters=ACTION_PARAMS,
                                       context=context)
            liveaction2, execution2 = action_service.request(liveaction2)
            liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
            self.assertEqual(liveaction2.status,
                             action_constants.LIVEACTION_STATUS_RUNNING)

            task_specs = {'x': {'reset': False}}

            patched_mistral_runner.resume.assert_called_with(
                ex_ref=execution1, task_specs=task_specs)

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_resume_option_reset_tasks(self):
        patched_mistral_runner = self.get_runner_class('mistral_v2')

        mock_resume_result = (action_constants.LIVEACTION_STATUS_RUNNING, {
            'tasks': []
        }, {
            'execution_id': str(uuid.uuid4())
        })

        with mock.patch.object(
                patched_mistral_runner, 'resume',
                mock.MagicMock(return_value=mock_resume_result)):

            liveaction1 = LiveActionDB(action=WF1_NAME,
                                       parameters=ACTION_PARAMS)
            liveaction1, execution1 = action_service.request(liveaction1)
            self.assertFalse(patched_mistral_runner.resume.called)

            # Rerun the execution.
            context = {
                're-run': {
                    'ref': execution1.id,
                    'tasks': ['x', 'y'],
                    'reset': ['y']
                }
            }

            liveaction2 = LiveActionDB(action=WF1_NAME,
                                       parameters=ACTION_PARAMS,
                                       context=context)
            liveaction2, execution2 = action_service.request(liveaction2)
            liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
            self.assertEqual(liveaction2.status,
                             action_constants.LIVEACTION_STATUS_RUNNING)

            task_specs = {'x': {'reset': False}, 'y': {'reset': True}}

            patched_mistral_runner.resume.assert_called_with(
                ex_ref=execution1, task_specs=task_specs)

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(
            return_value=executions.Execution(None, WF1_EXEC_NOT_RERUNABLE)))
    @mock.patch.object(
        executions.ExecutionManager, 'get',
        mock.MagicMock(
            return_value=executions.Execution(None, WF1_EXEC_NOT_RERUNABLE)))
    @mock.patch.object(tasks.TaskManager, 'list',
                       mock.MagicMock(return_value=WF1_TASKS))
    def test_resume_workflow_not_in_rerunable_state(self):
        liveaction1 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction1, execution1 = action_service.request(liveaction1)

        # Rerun the execution.
        context = {'re-run': {'ref': execution1.id, 'tasks': ['say-friend']}}

        liveaction2 = LiveActionDB(action=WF1_NAME,
                                   parameters=ACTION_PARAMS,
                                   context=context)
        liveaction2, execution2 = action_service.request(liveaction2)
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
        self.assertEqual(liveaction2.status,
                         action_constants.LIVEACTION_STATUS_FAILED)
        self.assertIn('not in a rerunable state',
                      liveaction2.result.get('error'))

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'list',
        mock.MagicMock(return_value=[executions.Execution(None, WF1_EXEC)]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    @mock.patch.object(
        executions.ExecutionManager, 'get',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    @mock.patch.object(tasks.TaskManager, 'list',
                       mock.MagicMock(return_value=WF1_TASKS))
    def test_resume_tasks_not_in_rerunable_state(self):
        liveaction1 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction1, execution1 = action_service.request(liveaction1)

        # Rerun the execution.
        context = {'re-run': {'ref': execution1.id, 'tasks': ['say-friend']}}

        liveaction2 = LiveActionDB(action=WF1_NAME,
                                   parameters=ACTION_PARAMS,
                                   context=context)
        liveaction2, execution2 = action_service.request(liveaction2)
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
        self.assertEqual(liveaction2.status,
                         action_constants.LIVEACTION_STATUS_FAILED)
        self.assertIn('Unable to identify rerunable',
                      liveaction2.result.get('error'))

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'list',
        mock.MagicMock(return_value=[executions.Execution(None, WF1_EXEC)]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    @mock.patch.object(
        executions.ExecutionManager, 'get',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    @mock.patch.object(tasks.TaskManager, 'list',
                       mock.MagicMock(return_value=WF1_TASKS))
    def test_resume_unidentified_tasks(self):
        liveaction1 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction1, execution1 = action_service.request(liveaction1)

        # Rerun the execution.
        context = {'re-run': {'ref': execution1.id, 'tasks': ['x']}}

        liveaction2 = LiveActionDB(action=WF1_NAME,
                                   parameters=ACTION_PARAMS,
                                   context=context)
        liveaction2, execution2 = action_service.request(liveaction2)
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
        self.assertEqual(liveaction2.status,
                         action_constants.LIVEACTION_STATUS_FAILED)
        self.assertIn('Unable to identify', liveaction2.result.get('error'))

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workbooks.WorkbookManager, 'create',
                       mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB1_MAIN_EXEC)))
    @mock.patch.object(
        executions.ExecutionManager, 'get',
        mock.MagicMock(
            return_value=executions.Execution(None, WB1_MAIN_EXEC_ERRORED)))
    @mock.patch.object(executions.ExecutionManager, 'list',
                       mock.MagicMock(return_value=[
                           executions.Execution(None, WB1_MAIN_EXEC_ERRORED),
                           executions.Execution(None, WB1_SUB1_EXEC_ERRORED)
                       ]))
    @mock.patch.object(
        tasks.TaskManager, 'list',
        mock.MagicMock(side_effect=[WB1_MAIN_TASKS, WB1_SUB1_TASKS]))
    @mock.patch.object(tasks.TaskManager, 'rerun',
                       mock.MagicMock(return_value=None))
    def test_resume_subworkflow_task(self):
        liveaction1 = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS)
        liveaction1, execution1 = action_service.request(liveaction1)

        # Rerun the execution.
        context = {
            're-run': {
                'ref': execution1.id,
                'tasks': ['greet.say-friend']
            }
        }

        liveaction2 = LiveActionDB(action=WB1_NAME,
                                   parameters=ACTION_PARAMS,
                                   context=context)
        liveaction2, execution2 = action_service.request(liveaction2)
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))

        self.assertEqual(liveaction2.status,
                         action_constants.LIVEACTION_STATUS_RUNNING)

        expected_env = {
            'st2_liveaction_id': str(liveaction2.id),
            'st2_execution_id': str(execution2.id),
            '__actions': {
                'st2.action': {
                    'st2_context': {
                        'api_url': 'http://0.0.0.0:9101/v1',
                        'endpoint': 'http://0.0.0.0:9101/v1/actionexecutions',
                        'notify': {},
                        'parent': {
                            'pack': 'mistral_tests',
                            're-run': context['re-run'],
                            'execution_id': str(execution2.id)
                        },
                        'skip_notify_tasks': []
                    }
                }
            },
            'st2_action_api_url': 'http://0.0.0.0:9101/v1'
        }

        tasks.TaskManager.rerun.assert_called_with(WB1_SUB1_TASK2['id'],
                                                   reset=False,
                                                   env=expected_env)

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workbooks.WorkbookManager, 'create',
                       mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB1_MAIN_EXEC)))
    @mock.patch.object(
        executions.ExecutionManager, 'get',
        mock.MagicMock(
            return_value=executions.Execution(None, WB1_MAIN_EXEC_ERRORED)))
    @mock.patch.object(executions.ExecutionManager, 'list',
                       mock.MagicMock(return_value=[
                           executions.Execution(None, WB1_MAIN_EXEC_ERRORED),
                           executions.Execution(None, WB1_SUB1_EXEC_ERRORED)
                       ]))
    @mock.patch.object(
        tasks.TaskManager, 'list',
        mock.MagicMock(side_effect=[WB1_MAIN_TASKS, WB1_SUB1_TASKS]))
    def test_resume_unidentified_subworkflow_task(self):
        liveaction1 = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS)
        liveaction1, execution1 = action_service.request(liveaction1)

        # Rerun the execution.
        context = {'re-run': {'ref': execution1.id, 'tasks': ['greet.x']}}

        liveaction2 = LiveActionDB(action=WB1_NAME,
                                   parameters=ACTION_PARAMS,
                                   context=context)
        liveaction2, execution2 = action_service.request(liveaction2)
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
        self.assertEqual(liveaction2.status,
                         action_constants.LIVEACTION_STATUS_FAILED)
        self.assertIn('Unable to identify', liveaction2.result.get('error'))

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workbooks.WorkbookManager, 'create',
                       mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB1_MAIN_EXEC)))
    @mock.patch.object(
        executions.ExecutionManager, 'get',
        mock.MagicMock(
            return_value=executions.Execution(None, WB1_MAIN_EXEC_ERRORED)))
    @mock.patch.object(executions.ExecutionManager, 'list',
                       mock.MagicMock(return_value=[
                           executions.Execution(None, WB1_MAIN_EXEC_ERRORED),
                           executions.Execution(None, WB1_SUB1_EXEC_ERRORED)
                       ]))
    @mock.patch.object(
        tasks.TaskManager, 'list',
        mock.MagicMock(side_effect=[WB1_MAIN_TASKS, WB1_SUB1_TASKS]))
    @mock.patch.object(tasks.TaskManager, 'rerun',
                       mock.MagicMock(return_value=None))
    def test_resume_and_reset_subworkflow_task(self):
        liveaction1 = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS)
        liveaction1, execution1 = action_service.request(liveaction1)

        # Rerun the execution.
        context = {
            're-run': {
                'ref': execution1.id,
                'tasks': ['greet.say-friend'],
                'reset': ['greet.say-friend']
            }
        }

        liveaction2 = LiveActionDB(action=WB1_NAME,
                                   parameters=ACTION_PARAMS,
                                   context=context)
        liveaction2, execution2 = action_service.request(liveaction2)
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))

        self.assertEqual(liveaction2.status,
                         action_constants.LIVEACTION_STATUS_RUNNING)

        expected_env = {
            'st2_liveaction_id': str(liveaction2.id),
            'st2_execution_id': str(execution2.id),
            '__actions': {
                'st2.action': {
                    'st2_context': {
                        'api_url': 'http://0.0.0.0:9101/v1',
                        'endpoint': 'http://0.0.0.0:9101/v1/actionexecutions',
                        'notify': {},
                        'parent': {
                            'pack': 'mistral_tests',
                            're-run': context['re-run'],
                            'execution_id': str(execution2.id)
                        },
                        'skip_notify_tasks': []
                    }
                }
            },
            'st2_action_api_url': 'http://0.0.0.0:9101/v1'
        }

        tasks.TaskManager.rerun.assert_called_with(WB1_SUB1_TASK2['id'],
                                                   reset=True,
                                                   env=expected_env)
Example #23
0
]

MOCK_WF_TASKS_RUNNING = [
    {'name': 'task1', 'state': 'SUCCESS'},
    {'name': 'task2', 'state': 'RUNNING'}
]

MOCK_WF_EX_DATA = {
    'id': uuid.uuid4().hex,
    'name': 'main',
    'output': '{"k1": "v1"}',
    'state': 'SUCCESS',
    'state_info': None
}

MOCK_WF_EX = executions.Execution(None, MOCK_WF_EX_DATA)

MOCK_WF_EX_TASKS_DATA = [
    {
        'id': uuid.uuid4().hex,
        'name': 'task1',
        'workflow_execution_id': MOCK_WF_EX_DATA['id'],
        'workflow_name': MOCK_WF_EX_DATA['name'],
        'created_at': str(datetime.datetime.utcnow()),
        'updated_at': str(datetime.datetime.utcnow()),
        'state': 'SUCCESS',
        'state_info': None,
        'input': '{"a": "b"}',
        'result': '{"c": "d"}',
        'published': '{"c": "d"}'
    },
Example #24
0
class MistralRunnerTest(DbTestCase):

    @classmethod
    def setUpClass(cls):
        super(MistralRunnerTest, cls).setUpClass()
        runners_registrar.register_runner_types()

        for _, fixture in six.iteritems(FIXTURES['actions']):
            instance = ActionAPI(**fixture)
            Action.add_or_update(ActionAPI.to_model(instance))

    def setUp(self):
        super(MistralRunnerTest, self).setUp()
        cfg.CONF.set_override('api_url', 'http://0.0.0.0:9101', group='auth')

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(return_value=WF1))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow(self):
        MistralRunner.entry_point = mock.PropertyMock(return_value=WF1_YAML_FILE_PATH)
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'], WF1_EXEC.get('workflow_name'))

        workflow_input = copy.deepcopy(ACTION_PARAMS)
        workflow_input.update({'count': '3'})

        env = {
            'st2_execution_id': str(execution.id),
            'st2_liveaction_id': str(liveaction.id),
            'st2_action_api_url': 'http://0.0.0.0:9101/v1',
            '__actions': {
                'st2.action': {
                    'st2_context': {
                        'endpoint': 'http://0.0.0.0:9101/v1/actionexecutions',
                        'parent': {
                            'execution_id': str(execution.id)
                        },
                        'notify': {},
                        'skip_notify_tasks': []
                    }
                }
            }
        }

        executions.ExecutionManager.create.assert_called_with(
            WF1_NAME, workflow_input=workflow_input, env=env)

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(return_value=WF1))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow_with_st2_https(self):
        cfg.CONF.set_override('api_url', 'https://0.0.0.0:9101', group='auth')

        MistralRunner.entry_point = mock.PropertyMock(return_value=WF1_YAML_FILE_PATH)
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'], WF1_EXEC.get('workflow_name'))

        workflow_input = copy.deepcopy(ACTION_PARAMS)
        workflow_input.update({'count': '3'})

        env = {
            'st2_execution_id': str(execution.id),
            'st2_liveaction_id': str(liveaction.id),
            'st2_action_api_url': 'https://0.0.0.0:9101/v1',
            '__actions': {
                'st2.action': {
                    'st2_context': {
                        'endpoint': 'https://0.0.0.0:9101/v1/actionexecutions',
                        'parent': {
                            'execution_id': str(execution.id)
                        },
                        'notify': {},
                        'skip_notify_tasks': []
                    }
                }
            }
        }

        executions.ExecutionManager.create.assert_called_with(
            WF1_NAME, workflow_input=workflow_input, env=env)

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(return_value=WF1))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow_with_notifications(self):
        notify_data = {'on_complete': {'routes': ['slack'],
                       'message': '"@channel: Action succeeded."', 'data': {}}}

        MistralRunner.entry_point = mock.PropertyMock(return_value=WF1_YAML_FILE_PATH)
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS, notify=notify_data)
        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'], WF1_EXEC.get('workflow_name'))

        workflow_input = copy.deepcopy(ACTION_PARAMS)
        workflow_input.update({'count': '3'})

        env = {
            'st2_execution_id': str(execution.id),
            'st2_liveaction_id': str(liveaction.id),
            'st2_action_api_url': 'http://0.0.0.0:9101/v1',
            '__actions': {
                'st2.action': {
                    'st2_context': {
                        'endpoint': 'http://0.0.0.0:9101/v1/actionexecutions',
                        'parent': {
                            'execution_id': str(execution.id)
                        },
                        'notify': NotificationsHelper.from_model(liveaction.notify),
                        'skip_notify_tasks': []
                    }
                }
            }
        }

        executions.ExecutionManager.create.assert_called_with(
            WF1_NAME, workflow_input=workflow_input, env=env)

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(side_effect=requests.exceptions.ConnectionError('Connection refused')))
    def test_launch_workflow_mistral_offline(self):
        MistralRunner.entry_point = mock.PropertyMock(return_value=WF1_YAML_FILE_PATH)
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_FAILED)
        self.assertIn('Connection refused', liveaction.result['error'])

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(side_effect=[requests.exceptions.ConnectionError(), []]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(return_value=WF1))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow_mistral_retry(self):
        MistralRunner.entry_point = mock.PropertyMock(return_value=WF1_YAML_FILE_PATH)
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'], WF1_EXEC.get('workflow_name'))

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(return_value=WF1))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(side_effect=[APIException(error_message='Duplicate entry.'), WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow_duplicate_error(self):
        MistralRunner.entry_point = mock.PropertyMock(return_value=WF1_YAML_FILE_PATH)
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'], WF1_EXEC.get('workflow_name'))

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(return_value=WF1_OLD))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        workflows.WorkflowManager, 'update',
        mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_when_workflow_definition_changed(self):
        MistralRunner.entry_point = mock.PropertyMock(return_value=WF1_YAML_FILE_PATH)
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'], WF1_EXEC.get('workflow_name'))

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(side_effect=Exception()))
    @mock.patch.object(
        workbooks.WorkbookManager, 'delete',
        mock.MagicMock(side_effect=Exception()))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_when_workflow_not_exists(self):
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'], WF1_EXEC.get('workflow_name'))

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(return_value=WF2))
    def test_launch_workflow_with_many_workflows(self):
        MistralRunner.entry_point = mock.PropertyMock(return_value=WF2_YAML_FILE_PATH)
        liveaction = LiveActionDB(action=WF2_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_FAILED)
        self.assertIn('Multiple workflows is not supported.', liveaction.result['error'])

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(side_effect=Exception()))
    def test_launch_workflow_name_mistmatch(self):
        action_ref = 'generic.workflow_v2_name_mismatch'
        MistralRunner.entry_point = mock.PropertyMock(return_value=WF1_YAML_FILE_PATH)
        liveaction = LiveActionDB(action=action_ref, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_FAILED)
        self.assertIn('Name of the workflow must be the same', liveaction.result['error'])

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workbooks.WorkbookManager, 'get',
        mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        workbooks.WorkbookManager, 'create',
        mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        workbooks.WorkbookManager, 'update',
        mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB1_EXEC)))
    def test_launch_workbook(self):
        MistralRunner.entry_point = mock.PropertyMock(return_value=WB1_YAML_FILE_PATH)
        liveaction = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WB1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'], WB1_EXEC.get('workflow_name'))

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workbooks.WorkbookManager, 'get',
        mock.MagicMock(return_value=WB2))
    @mock.patch.object(
        workbooks.WorkbookManager, 'create',
        mock.MagicMock(return_value=WB2))
    @mock.patch.object(
        workbooks.WorkbookManager, 'update',
        mock.MagicMock(return_value=WB2))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB2_EXEC)))
    def test_launch_workbook_with_many_workflows(self):
        MistralRunner.entry_point = mock.PropertyMock(return_value=WB2_YAML_FILE_PATH)
        liveaction = LiveActionDB(action=WB2_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WB2_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'], WB2_EXEC.get('workflow_name'))

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workbooks.WorkbookManager, 'get',
        mock.MagicMock(return_value=WB3))
    @mock.patch.object(
        workbooks.WorkbookManager, 'create',
        mock.MagicMock(return_value=WB3))
    @mock.patch.object(
        workbooks.WorkbookManager, 'update',
        mock.MagicMock(return_value=WB3))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB3_EXEC)))
    def test_launch_workbook_with_many_workflows_no_default(self):
        MistralRunner.entry_point = mock.PropertyMock(return_value=WB3_YAML_FILE_PATH)
        liveaction = LiveActionDB(action=WB3_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_FAILED)
        self.assertIn('Default workflow cannot be determined.', liveaction.result['error'])

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workbooks.WorkbookManager, 'get',
        mock.MagicMock(return_value=WB1_OLD))
    @mock.patch.object(
        workbooks.WorkbookManager, 'create',
        mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        workbooks.WorkbookManager, 'update',
        mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB1_EXEC)))
    def test_launch_when_workbook_definition_changed(self):
        MistralRunner.entry_point = mock.PropertyMock(return_value=WB1_YAML_FILE_PATH)
        liveaction = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WB1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'], WB1_EXEC.get('workflow_name'))

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workbooks.WorkbookManager, 'get',
        mock.MagicMock(side_effect=Exception()))
    @mock.patch.object(
        workflows.WorkflowManager, 'delete',
        mock.MagicMock(side_effect=Exception()))
    @mock.patch.object(
        workbooks.WorkbookManager, 'create',
        mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB1_EXEC)))
    def test_launch_when_workbook_not_exists(self):
        liveaction = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WB1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'], WB1_EXEC.get('workflow_name'))

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workbooks.WorkbookManager, 'get',
        mock.MagicMock(side_effect=Exception()))
    def test_launch_workbook_name_mismatch(self):
        action_ref = 'generic.workbook_v2_name_mismatch'
        MistralRunner.entry_point = mock.PropertyMock(return_value=WB1_YAML_FILE_PATH)
        liveaction = LiveActionDB(action=action_ref, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_FAILED)
        self.assertIn('Name of the workbook must be the same', liveaction.result['error'])

    def test_callback_handler_status_map(self):
        # Ensure all StackStorm status are mapped otherwise leads to zombie workflow.
        self.assertListEqual(sorted(mistral_status_map.keys()),
                             sorted(action_constants.LIVEACTION_STATUSES))

    @mock.patch.object(
        action_executions.ActionExecutionManager, 'update',
        mock.MagicMock(return_value=None))
    def test_callback_handler_with_result_as_text(self):
        MistralCallbackHandler.callback('http://127.0.0.1:8989/v2/action_executions/12345', {},
                                        action_constants.LIVEACTION_STATUS_SUCCEEDED,
                                        '<html></html>')

    @mock.patch.object(
        action_executions.ActionExecutionManager, 'update',
        mock.MagicMock(return_value=None))
    def test_callback_handler_with_result_as_dict(self):
        MistralCallbackHandler.callback('http://127.0.0.1:8989/v2/action_executions/12345', {},
                                        action_constants.LIVEACTION_STATUS_SUCCEEDED, {'a': 1})

    @mock.patch.object(
        action_executions.ActionExecutionManager, 'update',
        mock.MagicMock(return_value=None))
    def test_callback_handler_with_result_as_json_str(self):
        MistralCallbackHandler.callback('http://127.0.0.1:8989/v2/action_executions/12345', {},
                                        action_constants.LIVEACTION_STATUS_SUCCEEDED, '{"a": 1}')
        MistralCallbackHandler.callback('http://127.0.0.1:8989/v2/action_executions/12345', {},
                                        action_constants.LIVEACTION_STATUS_SUCCEEDED, "{'a': 1}")

    @mock.patch.object(
        action_executions.ActionExecutionManager, 'update',
        mock.MagicMock(return_value=None))
    def test_callback_handler_with_result_as_list(self):
        MistralCallbackHandler.callback('http://127.0.0.1:8989/v2/action_executions/12345', {},
                                        action_constants.LIVEACTION_STATUS_SUCCEEDED,
                                        ["a", "b", "c"])

    @mock.patch.object(
        action_executions.ActionExecutionManager, 'update',
        mock.MagicMock(return_value=None))
    def test_callback_handler_with_result_as_list_str(self):
        MistralCallbackHandler.callback('http://127.0.0.1:8989/v2/action_executions/12345', {},
                                        action_constants.LIVEACTION_STATUS_SUCCEEDED,
                                        '["a", "b", "c"]')

    @mock.patch.object(
        action_executions.ActionExecutionManager, 'update',
        mock.MagicMock(return_value=None))
    def test_callback(self):
        liveaction = LiveActionDB(
            action='core.local', parameters={'cmd': 'uname -a'},
            callback={
                'source': 'mistral',
                'url': 'http://127.0.0.1:8989/v2/action_executions/12345'
            }
        )

        for status in action_constants.LIVEACTION_COMPLETED_STATES:
            expected_mistral_status = mistral_status_map[status]
            LocalShellRunner.run = mock.Mock(return_value=(status, NON_EMPTY_RESULT, None))
            liveaction, execution = action_service.request(liveaction)
            liveaction = LiveAction.get_by_id(str(liveaction.id))
            self.assertEqual(liveaction.status, status)
            action_executions.ActionExecutionManager.update.assert_called_with(
                '12345', state=expected_mistral_status, output=NON_EMPTY_RESULT)

    @mock.patch.object(
        LocalShellRunner, 'run',
        mock.MagicMock(return_value=(action_constants.LIVEACTION_STATUS_RUNNING,
                                     NON_EMPTY_RESULT, None)))
    @mock.patch.object(
        action_executions.ActionExecutionManager, 'update',
        mock.MagicMock(return_value=None))
    def test_callback_incomplete_state(self):
        liveaction = LiveActionDB(
            action='core.local', parameters={'cmd': 'uname -a'},
            callback={
                'source': 'mistral',
                'url': 'http://127.0.0.1:8989/v2/action_executions/12345'
            }
        )

        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)
        self.assertFalse(action_executions.ActionExecutionManager.update.called)

    @mock.patch.object(
        LocalShellRunner, 'run',
        mock.MagicMock(return_value=(action_constants.LIVEACTION_STATUS_SUCCEEDED,
                                     NON_EMPTY_RESULT, None)))
    @mock.patch.object(
        action_executions.ActionExecutionManager, 'update',
        mock.MagicMock(side_effect=[
            requests.exceptions.ConnectionError(),
            None]))
    def test_callback_retry(self):
        liveaction = LiveActionDB(
            action='core.local', parameters={'cmd': 'uname -a'},
            callback={
                'source': 'mistral',
                'url': 'http://127.0.0.1:8989/v2/action_executions/12345'
            }
        )

        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_SUCCEEDED)

        calls = [call('12345', state='SUCCESS', output=NON_EMPTY_RESULT) for i in range(0, 2)]
        action_executions.ActionExecutionManager.update.assert_has_calls(calls)

    @mock.patch.object(
        LocalShellRunner, 'run',
        mock.MagicMock(return_value=(action_constants.LIVEACTION_STATUS_SUCCEEDED,
                                     NON_EMPTY_RESULT, None)))
    @mock.patch.object(
        action_executions.ActionExecutionManager, 'update',
        mock.MagicMock(side_effect=[
            requests.exceptions.ConnectionError(),
            requests.exceptions.ConnectionError(),
            requests.exceptions.ConnectionError(),
            requests.exceptions.ConnectionError(),
            None]))
    def test_callback_retry_exhausted(self):
        liveaction = LiveActionDB(
            action='core.local', parameters={'cmd': 'uname -a'},
            callback={
                'source': 'mistral',
                'url': 'http://127.0.0.1:8989/v2/action_executions/12345'
            }
        )

        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_SUCCEEDED)

        # This test initially setup mock for action_executions.ActionExecutionManager.update
        # to fail the first 4 times and return success on the 5th times. The max attempts
        # is set to 3. We expect only 3 calls to pass thru the update method.
        calls = [call('12345', state='SUCCESS', output=NON_EMPTY_RESULT) for i in range(0, 2)]
        action_executions.ActionExecutionManager.update.assert_has_calls(calls)

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(return_value=WF1))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    @mock.patch.object(
        executions.ExecutionManager, 'update',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC_PAUSED)))
    def test_cancel(self):
        MistralRunner.entry_point = mock.PropertyMock(return_value=WF1_YAML_FILE_PATH)
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'], WF1_EXEC.get('workflow_name'))

        requester = cfg.CONF.system_user.user
        liveaction, execution = action_service.request_cancellation(liveaction, requester)
        executions.ExecutionManager.update.assert_called_with(WF1_EXEC.get('id'), 'PAUSED')
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_CANCELED)

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(return_value=WF1))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    @mock.patch.object(
        executions.ExecutionManager, 'update',
        mock.MagicMock(side_effect=[requests.exceptions.ConnectionError(),
                                    executions.Execution(None, WF1_EXEC_PAUSED)]))
    def test_cancel_retry(self):
        MistralRunner.entry_point = mock.PropertyMock(return_value=WF1_YAML_FILE_PATH)
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'], WF1_EXEC.get('workflow_name'))

        requester = cfg.CONF.system_user.user
        liveaction, execution = action_service.request_cancellation(liveaction, requester)
        executions.ExecutionManager.update.assert_called_with(WF1_EXEC.get('id'), 'PAUSED')
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_CANCELED)

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(return_value=WF1))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    @mock.patch.object(
        executions.ExecutionManager, 'update',
        mock.MagicMock(side_effect=requests.exceptions.ConnectionError('Connection refused')))
    def test_cancel_retry_exhausted(self):
        MistralRunner.entry_point = mock.PropertyMock(return_value=WF1_YAML_FILE_PATH)
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'], WF1_EXEC.get('workflow_name'))

        requester = cfg.CONF.system_user.user
        liveaction, execution = action_service.request_cancellation(liveaction, requester)

        calls = [call(WF1_EXEC.get('id'), 'PAUSED') for i in range(0, 2)]
        executions.ExecutionManager.update.assert_has_calls(calls)

        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_CANCELING)

    def test_build_context(self):
        parent = {
            'mistral': {
                'workflow_name': 'foo',
                'workflow_execution_id': 'b222b934-7473-4cd4-a2ec-e204a8c93848',
                'task_tags': None,
                'task_name': 'some_fancy_wf_task',
                'task_id': '6c7d4334-3e7d-49c6-918d-698e846affaf',
                'action_execution_id': '24da5c88-834c-4a65-8b56-4ddbd654eb68'
            }
        }

        current = {
            'workflow_name': 'foo.subwf',
            'workflow_execution_id': '135e3446-4c89-4afe-821f-6ec6a0849b27'
        }

        context = MistralRunner._build_mistral_context(parent, current)
        self.assertTrue(context is not None)
        self.assertTrue('parent' in context['mistral'].keys())

        parent_dict = {
            'workflow_name': parent['mistral']['workflow_name'],
            'workflow_execution_id': parent['mistral']['workflow_execution_id']
        }

        self.assertDictEqual(context['mistral']['parent'], parent_dict)
        self.assertEqual(context['mistral']['workflow_execution_id'],
                         current['workflow_execution_id'])

        parent = None
        context = MistralRunner._build_mistral_context(parent, current)
        self.assertDictEqual(context['mistral'], current)
Example #25
0
from st2common.persistence.action import Action, ActionExecution

CHAMPION = worker.Worker(None)
WORKFLOW_YAML = [f for f in fixture.WORKFLOW_YAMLS
                 if 'workflow-v2.yaml' in f][0]
WORKBOOK_SPEC = fixture.ARTIFACTS['workflows']['workflow-v2']
WORKBOOK = workbooks.Workbook(None, {
    'name': 'workflow-v2',
    'definition': WORKBOOK_SPEC
})
WORKBOOK_OLD = workbooks.Workbook(None, {
    'name': 'workflow-v2',
    'definition': ''
})
EXECUTION = executions.Execution(None, {
    'id': str(uuid.uuid4()),
    'state': 'RUNNING'
})


def process_create(payload):
    if isinstance(payload, ActionExecutionDB):
        CHAMPION.execute_action(payload)


@mock.patch.object(FabricRunner, '_run', mock.MagicMock(return_value={}))
@mock.patch.object(CUDPublisher, 'publish_create',
                   mock.MagicMock(side_effect=process_create))
@mock.patch.object(CUDPublisher, 'publish_update',
                   mock.MagicMock(return_value=None))
class TestMistralRunner(DbTestCase):
    @classmethod
Example #26
0
class MistralAuthTest(DbTestCase):
    @classmethod
    def setUpClass(cls):
        super(MistralAuthTest, cls).setUpClass()
        runners_registrar.register_runner_types()

        for _, fixture in six.iteritems(FIXTURES['actions']):
            instance = ActionAPI(**fixture)
            Action.add_or_update(ActionAPI.to_model(instance))

    def setUp(self):
        super(MistralAuthTest, self).setUp()
        cfg.CONF.set_override('api_url', 'http://0.0.0.0:9101', group='auth')

    def tearDown(self):
        super(MistralAuthTest, self).tearDown()
        cfg.CONF.set_default('keystone_username', None, group='mistral')
        cfg.CONF.set_default('keystone_password', None, group='mistral')
        cfg.CONF.set_default('keystone_project_name', None, group='mistral')
        cfg.CONF.set_default('keystone_auth_url', None, group='mistral')

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    @mock.patch.object(access_service, 'create_token',
                       mock.MagicMock(return_value=TOKEN_DB))
    def test_launch_workflow_with_st2_auth(self):
        MistralRunner.entry_point = mock.PropertyMock(
            return_value=WF1_YAML_FILE_PATH)
        liveaction = LiveActionDB(action=WF1_NAME,
                                  parameters=ACTION_PARAMS,
                                  context=ACTION_CONTEXT)
        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status,
                         action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'],
                         WF1_EXEC.get('workflow_name'))

        workflow_input = copy.deepcopy(ACTION_PARAMS)
        workflow_input.update({'count': '3'})

        env = {
            'st2_execution_id': str(execution.id),
            'st2_liveaction_id': str(liveaction.id),
            'st2_action_api_url': 'http://0.0.0.0:9101/v1',
            '__actions': {
                'st2.action': {
                    'st2_context': {
                        'auth_token': TOKEN_DB.token,
                        'endpoint': 'http://0.0.0.0:9101/v1/actionexecutions',
                        'parent': {
                            'user': liveaction.context['user'],
                            'execution_id': str(execution.id)
                        },
                        'notify': {},
                        'skip_notify_tasks': []
                    }
                }
            }
        }

        executions.ExecutionManager.create.assert_called_with(
            WF1_NAME, workflow_input=workflow_input, env=env)

    @mock.patch.object(
        client.Client, 'authenticate',
        mock.MagicMock(return_value=(cfg.CONF.mistral.v2_base_url, '123',
                                     'abc', 'xyz')))
    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow_with_mistral_auth(self):
        cfg.CONF.set_default('keystone_username', 'foo', group='mistral')
        cfg.CONF.set_default('keystone_password', 'bar', group='mistral')
        cfg.CONF.set_default('keystone_project_name', 'admin', group='mistral')
        cfg.CONF.set_default('keystone_auth_url',
                             'http://127.0.0.1:5000/v3',
                             group='mistral')

        MistralRunner.entry_point = mock.PropertyMock(
            return_value=WF1_YAML_FILE_PATH)
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status,
                         action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'],
                         WF1_EXEC.get('workflow_name'))

        workflow_input = copy.deepcopy(ACTION_PARAMS)
        workflow_input.update({'count': '3'})

        env = {
            'st2_execution_id': str(execution.id),
            'st2_liveaction_id': str(liveaction.id),
            'st2_action_api_url': 'http://0.0.0.0:9101/v1',
            '__actions': {
                'st2.action': {
                    'st2_context': {
                        'endpoint': 'http://0.0.0.0:9101/v1/actionexecutions',
                        'parent': {
                            'execution_id': str(execution.id)
                        },
                        'notify': {},
                        'skip_notify_tasks': []
                    }
                }
            }
        }

        client.Client.authenticate.assert_called_with(
            cfg.CONF.mistral.v2_base_url, cfg.CONF.mistral.keystone_username,
            cfg.CONF.mistral.keystone_password,
            cfg.CONF.mistral.keystone_project_name,
            cfg.CONF.mistral.keystone_auth_url, None, 'publicURL', 'workflow',
            None, None, None, False)

        executions.ExecutionManager.create.assert_called_with(
            WF1_NAME, workflow_input=workflow_input, env=env)
#    License for the specific language governing permissions and limitations
#    under the License.
#

import mock
import pkg_resources as pkg

from mistralclient.api.v2 import executions
from mistralclient.commands.v2 import executions as execution_cmd
from mistralclient.tests.unit import base

EXECUTION = executions.Execution(mock, {
    'id': '123',
    'workflow_name': 'some',
    'description': '',
    'state': 'RUNNING',
    'state_info': None,
    'created_at': '1',
    'updated_at': '1'
})


class TestCLIExecutionsV2(base.BaseCommandTest):
    def test_create_wf_input_string(self):
        self.client.executions.create.return_value = EXECUTION

        result = self.call(execution_cmd.Create,
                           app_args=['id', '{ "context": true }'])

        self.assertEqual(('123', 'some', '', 'RUNNING', None,
                          '1', '1'), result[1])
Example #28
0
def data(TEST):
    # MistralActions
    TEST.mistralclient_actions = test_data_utils.TestDataContainer()
    action_1 = actions.Action(
        actions.ActionManager(None),
        {'name': 'a',
         'is_system': True,
         'input': 'param1',
         'description': 'my cool action',
         'tags': ['test'],
         'created_at': '1',
         'updated_at': '1'
         }
    )
    TEST.mistralclient_actions.add(action_1)

    # MistralExecutions
    TEST.mistralclient_executions = test_data_utils.TestDataContainer()
    execution_1 = executions.Execution(
        executions.ExecutionManager(None),
        {'id': '123',
         'workflow_name': 'my_wf',
         'description': '',
         'state': 'RUNNING',
         'input': {
             'person': {
                 'first_name': 'John',
                 'last_name': 'Doe'
             }
         }}
    )
    TEST.mistralclient_executions.add(execution_1)

    # Tasks
    TEST.mistralclient_tasks = test_data_utils.TestDataContainer()
    task_1 = tasks.Task(
        tasks.TaskManager(None),
        {'id': '1',
         'workflow_execution_id': '123',
         'name': 'my_task',
         'workflow_name': 'my_wf',
         'state': 'RUNNING',
         'type': 'ACTION',
         'tags': ['deployment', 'demo'],
         'result': {'some': 'result'}})
    TEST.mistralclient_tasks.add(task_1)

    # Workbooks
    TEST.mistralclient_workbooks = test_data_utils.TestDataContainer()
    workbook_1 = workbooks.Workbook(
        workbooks.WorkbookManager(None),
        {'name': 'a',
         'tags': ['a', 'b'],
         'created_at': '1',
         'updated_at': '1',
         'definition': WB_DEF}
    )
    TEST.mistralclient_workbooks.add(workbook_1)

    # Workflows
    TEST.mistralclient_workflows = test_data_utils.TestDataContainer()
    workflow_1 = workflows.Workflow(
        workflows.WorkflowManager(None),
        {'name': 'a',
         'tags': ['a', 'b'],
         'input': 'param',
         'created_at': '1',
         'updated_at': '1',
         'definition': WF_DEF}
    )
    TEST.mistralclient_workflows.add(workflow_1)

    # MistralActionsExecutions
    TEST.mistralclient_action_executions = test_data_utils.TestDataContainer()
    action_executions_1 = action_executions.ActionExecution(
        action_executions.ActionExecutionManager(None),
        {'id': '1',
         'name': 'a',
         'tags': ['a', 'b'],
         'workflow_name': 'my work flow',
         'task_execution_id': '1',
         'task_name': 'b',
         'description': '',
         'created_at': '1',
         'updated_at': '1',
         'accepted': True,
         'state': 'RUNNING'
         }
    )
    TEST.mistralclient_action_executions.add(action_executions_1)

    # MistralCronTriggers
    TEST.mistralclient_cron_triggers = test_data_utils.TestDataContainer()
    cron_triggers_1 = cron_triggers.CronTrigger(
        cron_triggers.CronTriggerManager(None),
        {'id': '1',
         'name': 'a',
         'workflow_name': 'my work flow',
         'pattern': '',
         'next_execution_time': '',
         'remaining_executions': '',
         'first_execution_time': '',
         'created_at': '1',
         'updated_at': '1'
         })
    TEST.mistralclient_cron_triggers.add(cron_triggers_1)
Example #29
0
class MistralRunnerPauseResumeTest(DbTestCase):

    @classmethod
    def setUpClass(cls):
        super(MistralRunnerPauseResumeTest, cls).setUpClass()

        # Override the retry configuration here otherwise st2tests.config.parse_args
        # in DbTestCase.setUpClass will reset these overrides.
        cfg.CONF.set_override('retry_exp_msec', 100, group='mistral')
        cfg.CONF.set_override('retry_exp_max_msec', 200, group='mistral')
        cfg.CONF.set_override('retry_stop_max_msec', 200, group='mistral')
        cfg.CONF.set_override('api_url', 'http://0.0.0.0:9101', group='auth')

        # Register runners.
        runnersregistrar.register_runners()

        # Register test pack(s).
        actions_registrar = actionsregistrar.ActionsRegistrar(
            use_pack_cache=False,
            fail_on_failure=True
        )

        for pack in PACKS:
            actions_registrar.register_from_pack(pack)

    @classmethod
    def get_runner_class(cls, runner_name):
        return runners.get_runner(runner_name).__class__

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(return_value=WF1))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    @mock.patch.object(
        executions.ExecutionManager, 'update',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC_PAUSED)))
    def test_pause(self):
        # Launch the workflow execution.
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'], WF1_EXEC.get('workflow_name'))

        # Pause the workflow execution.
        requester = cfg.CONF.system_user.user
        liveaction, execution = action_service.request_pause(liveaction, requester)
        executions.ExecutionManager.update.assert_called_with(WF1_EXEC.get('id'), 'PAUSED')
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_PAUSING)

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(return_value=WF1))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    @mock.patch.object(
        executions.ExecutionManager, 'update',
        mock.MagicMock(side_effect=[
            executions.Execution(None, WF1_EXEC_PAUSED),
            executions.Execution(None, WF1_EXEC)]))
    def test_resume(self):
        # Launch the workflow execution.
        liveaction = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, execution = action_service.request(liveaction)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'], WF1_EXEC.get('workflow_name'))

        # Pause the workflow execution.
        requester = cfg.CONF.system_user.user
        liveaction, execution = action_service.request_pause(liveaction, requester)
        executions.ExecutionManager.update.assert_called_with(WF1_EXEC.get('id'), 'PAUSED')
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_PAUSING)

        # Manually update the liveaction from pausing to paused. The paused state
        # is usually updated by the mistral querier.
        action_service.update_status(liveaction, action_constants.LIVEACTION_STATUS_PAUSED)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_PAUSED)

        # Resume the workflow execution.
        liveaction, execution = action_service.request_resume(liveaction, requester)
        executions.ExecutionManager.update.assert_called_with(WF1_EXEC.get('id'), 'RUNNING')
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, action_constants.LIVEACTION_STATUS_RUNNING)

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(side_effect=[WF2, WF1]))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(side_effect=[[WF2], [WF1]]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(side_effect=[
            executions.Execution(None, WF2_EXEC),
            executions.Execution(None, WF1_EXEC)]))
    @mock.patch.object(
        executions.ExecutionManager, 'update',
        mock.MagicMock(side_effect=[
            executions.Execution(None, WF2_EXEC_PAUSED),
            executions.Execution(None, WF1_EXEC_PAUSED),
            executions.Execution(None, WF2_EXEC),
            executions.Execution(None, WF1_EXEC)]))
    def test_resume_subworkflow_action(self):
        requester = cfg.CONF.system_user.user

        liveaction1 = LiveActionDB(action=WF2_NAME, parameters=ACTION_PARAMS)
        liveaction1, execution1 = action_service.request(liveaction1)
        liveaction1 = LiveAction.get_by_id(str(liveaction1.id))
        self.assertEqual(liveaction1.status, action_constants.LIVEACTION_STATUS_RUNNING)

        liveaction2 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction2, execution2 = action_service.request(liveaction2)
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
        self.assertEqual(liveaction2.status, action_constants.LIVEACTION_STATUS_RUNNING)

        # Mock the children of the parent execution to make this
        # test case has subworkflow execution.
        with mock.patch.object(
                ActionExecutionDB, 'children',
                new_callable=mock.PropertyMock) as action_ex_children_mock:
            action_ex_children_mock.return_value = [execution2.id]

            mistral_context = liveaction1.context.get('mistral', None)
            self.assertIsNotNone(mistral_context)
            self.assertEqual(mistral_context['execution_id'], WF2_EXEC.get('id'))
            self.assertEqual(mistral_context['workflow_name'], WF2_EXEC.get('workflow_name'))

            # Pause the parent liveaction and check that the request is cascaded down.
            liveaction1, execution1 = action_service.request_pause(liveaction1, requester)

            self.assertTrue(executions.ExecutionManager.update.called)
            self.assertEqual(executions.ExecutionManager.update.call_count, 2)

            calls = [
                mock.call(WF2_EXEC.get('id'), 'PAUSED'),
                mock.call(WF1_EXEC.get('id'), 'PAUSED')
            ]

            executions.ExecutionManager.update.assert_has_calls(calls, any_order=False)

            liveaction1 = LiveAction.get_by_id(str(liveaction1.id))
            self.assertEqual(liveaction1.status, action_constants.LIVEACTION_STATUS_PAUSING)

            liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
            self.assertEqual(liveaction2.status, action_constants.LIVEACTION_STATUS_PAUSING)

            # Manually set the liveaction status to PAUSED.
            action_service.update_status(liveaction2, action_constants.LIVEACTION_STATUS_PAUSED)
            action_service.update_status(liveaction1, action_constants.LIVEACTION_STATUS_PAUSED)

            liveaction1 = LiveAction.get_by_id(str(liveaction1.id))
            self.assertEqual(liveaction1.status, action_constants.LIVEACTION_STATUS_PAUSED)

            liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
            self.assertEqual(liveaction2.status, action_constants.LIVEACTION_STATUS_PAUSED)

            # Resume the parent liveaction and check that the request is cascaded down.
            liveaction1, execution1 = action_service.request_resume(liveaction1, requester)

            # Includes the previous calls.
            self.assertTrue(executions.ExecutionManager.update.called)
            self.assertEqual(executions.ExecutionManager.update.call_count, 4)

            calls = [
                mock.call(WF2_EXEC.get('id'), 'PAUSED'),
                mock.call(WF1_EXEC.get('id'), 'PAUSED'),
                mock.call(WF2_EXEC.get('id'), 'RUNNING'),
                mock.call(WF1_EXEC.get('id'), 'RUNNING')
            ]

            executions.ExecutionManager.update.assert_has_calls(calls, any_order=False)

            liveaction1 = LiveAction.get_by_id(str(liveaction1.id))
            self.assertEqual(liveaction1.status, action_constants.LIVEACTION_STATUS_RUNNING)

            liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
            self.assertEqual(liveaction2.status, action_constants.LIVEACTION_STATUS_RUNNING)

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(side_effect=[WF2, WF1]))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(side_effect=[[WF2], [WF1]]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(side_effect=[
            executions.Execution(None, WF2_EXEC),
            executions.Execution(None, WF1_EXEC)]))
    @mock.patch.object(
        executions.ExecutionManager, 'update',
        mock.MagicMock(side_effect=[
            executions.Execution(None, WF2_EXEC_PAUSED),
            executions.Execution(None, WF1_EXEC_PAUSED),
            executions.Execution(None, WF2_EXEC),
            executions.Execution(None, WF1_EXEC)]))
    def test_pause_missing_subworkflow_action(self):
        requester = cfg.CONF.system_user.user

        liveaction1 = LiveActionDB(action=WF2_NAME, parameters=ACTION_PARAMS)
        liveaction1, execution1 = action_service.request(liveaction1)
        liveaction1 = LiveAction.get_by_id(str(liveaction1.id))
        self.assertEqual(liveaction1.status, action_constants.LIVEACTION_STATUS_RUNNING)

        # Mock the children of the parent execution to make this
        # test case has subworkflow execution.
        with mock.patch.object(
                ActionExecutionDB, 'children',
                new_callable=mock.PropertyMock) as action_ex_children_mock:
            action_ex_children_mock.return_value = [uuid.uuid4().hex]

            mistral_context = liveaction1.context.get('mistral', None)
            self.assertIsNotNone(mistral_context)
            self.assertEqual(mistral_context['execution_id'], WF2_EXEC.get('id'))
            self.assertEqual(mistral_context['workflow_name'], WF2_EXEC.get('workflow_name'))

            # Pause the parent liveaction and check that the request is cascaded down.
            liveaction1, execution1 = action_service.request_pause(liveaction1, requester)

            self.assertTrue(executions.ExecutionManager.update.called)
            self.assertEqual(executions.ExecutionManager.update.call_count, 1)

            calls = [
                mock.call(WF2_EXEC.get('id'), 'PAUSED'),
            ]

            executions.ExecutionManager.update.assert_has_calls(calls, any_order=False)

            # The workflow execution will fail because the liveaction for the subworkflow
            # should not be missing and we do not know what state it is in.
            liveaction1 = LiveAction.get_by_id(str(liveaction1.id))
            self.assertEqual(liveaction1.status, action_constants.LIVEACTION_STATUS_FAILED)
            self.assertIn('not a valid ObjectId', liveaction1.result.get('error', ''))

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(return_value=[]))
    @mock.patch.object(
        workflows.WorkflowManager, 'get',
        mock.MagicMock(side_effect=[WF2, WF1]))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(side_effect=[[WF2], [WF1]]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(side_effect=[
            executions.Execution(None, WF2_EXEC),
            executions.Execution(None, WF1_EXEC)]))
    @mock.patch.object(
        executions.ExecutionManager, 'update',
        mock.MagicMock(side_effect=[
            executions.Execution(None, WF2_EXEC_PAUSED),
            executions.Execution(None, WF1_EXEC_PAUSED),
            executions.Execution(None, WF2_EXEC),
            executions.Execution(None, WF1_EXEC)]))
    def test_resume_missing_subworkflow_action(self):
        requester = cfg.CONF.system_user.user

        liveaction1 = LiveActionDB(action=WF2_NAME, parameters=ACTION_PARAMS)
        liveaction1, execution1 = action_service.request(liveaction1)
        liveaction1 = LiveAction.get_by_id(str(liveaction1.id))
        self.assertEqual(liveaction1.status, action_constants.LIVEACTION_STATUS_RUNNING)

        liveaction2 = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction2, execution2 = action_service.request(liveaction2)
        liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
        self.assertEqual(liveaction2.status, action_constants.LIVEACTION_STATUS_RUNNING)

        # Mock the children of the parent execution to make this
        # test case has subworkflow execution.
        with mock.patch.object(
                ActionExecutionDB, 'children',
                new_callable=mock.PropertyMock) as action_ex_children_mock:
            action_ex_children_mock.return_value = [execution2.id]

            mistral_context = liveaction1.context.get('mistral', None)
            self.assertIsNotNone(mistral_context)
            self.assertEqual(mistral_context['execution_id'], WF2_EXEC.get('id'))
            self.assertEqual(mistral_context['workflow_name'], WF2_EXEC.get('workflow_name'))

            # Pause the parent liveaction and check that the request is cascaded down.
            liveaction1, execution1 = action_service.request_pause(liveaction1, requester)

            self.assertTrue(executions.ExecutionManager.update.called)
            self.assertEqual(executions.ExecutionManager.update.call_count, 2)

            calls = [
                mock.call(WF2_EXEC.get('id'), 'PAUSED'),
                mock.call(WF1_EXEC.get('id'), 'PAUSED')
            ]

            executions.ExecutionManager.update.assert_has_calls(calls, any_order=False)

            liveaction1 = LiveAction.get_by_id(str(liveaction1.id))
            self.assertEqual(liveaction1.status, action_constants.LIVEACTION_STATUS_PAUSING)

            liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
            self.assertEqual(liveaction2.status, action_constants.LIVEACTION_STATUS_PAUSING)

            # Manually set the liveaction status to PAUSED.
            action_service.update_status(liveaction2, action_constants.LIVEACTION_STATUS_PAUSED)
            action_service.update_status(liveaction1, action_constants.LIVEACTION_STATUS_PAUSED)

            liveaction1 = LiveAction.get_by_id(str(liveaction1.id))
            self.assertEqual(liveaction1.status, action_constants.LIVEACTION_STATUS_PAUSED)

            liveaction2 = LiveAction.get_by_id(str(liveaction2.id))
            self.assertEqual(liveaction2.status, action_constants.LIVEACTION_STATUS_PAUSED)

        # Mock the children of the parent execution to make this
        # test case has subworkflow execution.
        with mock.patch.object(
                ActionExecutionDB, 'children',
                new_callable=mock.PropertyMock) as action_ex_children_mock:
            action_ex_children_mock.return_value = [uuid.uuid4().hex]

            # Resume the parent liveaction and check that the request is cascaded down.
            liveaction1, execution1 = action_service.request_resume(liveaction1, requester)

            # Includes the previous calls.
            self.assertTrue(executions.ExecutionManager.update.called)
            self.assertEqual(executions.ExecutionManager.update.call_count, 3)

            calls = [
                mock.call(WF2_EXEC.get('id'), 'PAUSED'),
                mock.call(WF1_EXEC.get('id'), 'PAUSED'),
                mock.call(WF2_EXEC.get('id'), 'RUNNING'),
            ]

            executions.ExecutionManager.update.assert_has_calls(calls, any_order=False)

            # The workflow execution will fail because the liveaction for the subworkflow
            # should not be missing and we do not know what state it is in.
            liveaction1 = LiveAction.get_by_id(str(liveaction1.id))
            self.assertEqual(liveaction1.status, action_constants.LIVEACTION_STATUS_FAILED)
            self.assertIn('not a valid ObjectId', liveaction1.result.get('error', ''))
Example #30
0
class TestMistralRunner(DbTestCase):
    @classmethod
    def setUpClass(cls):
        super(TestMistralRunner, cls).setUpClass()
        runners_registrar.register_runner_types()

        for _, fixture in six.iteritems(FIXTURES['actions']):
            instance = ActionAPI(**fixture)
            Action.add_or_update(ActionAPI.to_model(instance))

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow(self):
        MistralRunner.entry_point = mock.PropertyMock(
            return_value=WF1_YAML_FILE_PATH)
        execution = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.schedule(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'],
                         WF1_EXEC.get('workflow_name'))

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(side_effect=requests.exceptions.ConnectionError()))
    def test_launch_workflow_mistral_offline(self):
        MistralRunner.entry_point = mock.PropertyMock(
            return_value=WF1_YAML_FILE_PATH)
        execution = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.schedule(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, LIVEACTION_STATUS_FAILED)
        self.assertIn('Failed to connect to mistral',
                      liveaction.result['message'])

    @mock.patch.object(
        workflows.WorkflowManager, 'list',
        mock.MagicMock(side_effect=[requests.exceptions.ConnectionError(), []])
    )
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow_mistral_retry(self):
        MistralRunner.entry_point = mock.PropertyMock(
            return_value=WF1_YAML_FILE_PATH)
        execution = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.schedule(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'],
                         WF1_EXEC.get('workflow_name'))

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1))
    @mock.patch.object(
        workflows.WorkflowManager, 'create',
        mock.MagicMock(
            side_effect=[APIException(error_message='Duplicate entry.'), WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_workflow_duplicate_error(self):
        MistralRunner.entry_point = mock.PropertyMock(
            return_value=WF1_YAML_FILE_PATH)
        execution = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.schedule(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'],
                         WF1_EXEC.get('workflow_name'))

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF1_OLD))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(workflows.WorkflowManager, 'update',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_when_workflow_definition_changed(self):
        MistralRunner.entry_point = mock.PropertyMock(
            return_value=WF1_YAML_FILE_PATH)
        execution = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.schedule(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'],
                         WF1_EXEC.get('workflow_name'))

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(side_effect=Exception()))
    @mock.patch.object(workbooks.WorkbookManager, 'delete',
                       mock.MagicMock(side_effect=Exception()))
    @mock.patch.object(workflows.WorkflowManager, 'create',
                       mock.MagicMock(return_value=[WF1]))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WF1_EXEC)))
    def test_launch_when_workflow_not_exists(self):
        execution = LiveActionDB(action=WF1_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.schedule(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WF1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'],
                         WF1_EXEC.get('workflow_name'))

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(return_value=WF2))
    def test_launch_workflow_with_many_workflows(self):
        MistralRunner.entry_point = mock.PropertyMock(
            return_value=WF2_YAML_FILE_PATH)
        execution = LiveActionDB(action=WF2_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.schedule(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, LIVEACTION_STATUS_FAILED)
        self.assertIn('Multiple workflows is not supported.',
                      liveaction.result['message'])

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workflows.WorkflowManager, 'get',
                       mock.MagicMock(side_effect=Exception()))
    def test_launch_workflow_name_mistmatch(self):
        action_ref = 'generic.workflow_v2_name_mismatch'
        MistralRunner.entry_point = mock.PropertyMock(
            return_value=WF1_YAML_FILE_PATH)
        execution = LiveActionDB(action=action_ref, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.schedule(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, LIVEACTION_STATUS_FAILED)
        self.assertIn('Name of the workflow must be the same',
                      liveaction.result['message'])

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workbooks.WorkbookManager, 'get',
                       mock.MagicMock(return_value=WB1))
    @mock.patch.object(workbooks.WorkbookManager, 'create',
                       mock.MagicMock(return_value=WB1))
    @mock.patch.object(workbooks.WorkbookManager, 'update',
                       mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB1_EXEC)))
    def test_launch_workbook(self):
        MistralRunner.entry_point = mock.PropertyMock(
            return_value=WB1_YAML_FILE_PATH)
        execution = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.schedule(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WB1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'],
                         WB1_EXEC.get('workflow_name'))

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workbooks.WorkbookManager, 'get',
                       mock.MagicMock(return_value=WB2))
    @mock.patch.object(workbooks.WorkbookManager, 'create',
                       mock.MagicMock(return_value=WB2))
    @mock.patch.object(workbooks.WorkbookManager, 'update',
                       mock.MagicMock(return_value=WB2))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB2_EXEC)))
    def test_launch_workbook_with_many_workflows(self):
        MistralRunner.entry_point = mock.PropertyMock(
            return_value=WB2_YAML_FILE_PATH)
        execution = LiveActionDB(action=WB2_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.schedule(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WB2_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'],
                         WB2_EXEC.get('workflow_name'))

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workbooks.WorkbookManager, 'get',
                       mock.MagicMock(return_value=WB3))
    @mock.patch.object(workbooks.WorkbookManager, 'create',
                       mock.MagicMock(return_value=WB3))
    @mock.patch.object(workbooks.WorkbookManager, 'update',
                       mock.MagicMock(return_value=WB3))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB3_EXEC)))
    def test_launch_workbook_with_many_workflows_no_default(self):
        MistralRunner.entry_point = mock.PropertyMock(
            return_value=WB3_YAML_FILE_PATH)
        execution = LiveActionDB(action=WB3_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.schedule(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, LIVEACTION_STATUS_FAILED)
        self.assertIn('Default workflow cannot be determined.',
                      liveaction.result['message'])

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workbooks.WorkbookManager, 'get',
                       mock.MagicMock(return_value=WB1_OLD))
    @mock.patch.object(workbooks.WorkbookManager, 'create',
                       mock.MagicMock(return_value=WB1))
    @mock.patch.object(workbooks.WorkbookManager, 'update',
                       mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB1_EXEC)))
    def test_launch_when_workbook_definition_changed(self):
        MistralRunner.entry_point = mock.PropertyMock(
            return_value=WB1_YAML_FILE_PATH)
        execution = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.schedule(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WB1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'],
                         WB1_EXEC.get('workflow_name'))

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workbooks.WorkbookManager, 'get',
                       mock.MagicMock(side_effect=Exception()))
    @mock.patch.object(workflows.WorkflowManager, 'delete',
                       mock.MagicMock(side_effect=Exception()))
    @mock.patch.object(workbooks.WorkbookManager, 'create',
                       mock.MagicMock(return_value=WB1))
    @mock.patch.object(
        executions.ExecutionManager, 'create',
        mock.MagicMock(return_value=executions.Execution(None, WB1_EXEC)))
    def test_launch_when_workbook_not_exists(self):
        execution = LiveActionDB(action=WB1_NAME, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.schedule(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, LIVEACTION_STATUS_RUNNING)

        mistral_context = liveaction.context.get('mistral', None)
        self.assertIsNotNone(mistral_context)
        self.assertEqual(mistral_context['execution_id'], WB1_EXEC.get('id'))
        self.assertEqual(mistral_context['workflow_name'],
                         WB1_EXEC.get('workflow_name'))

    @mock.patch.object(workflows.WorkflowManager, 'list',
                       mock.MagicMock(return_value=[]))
    @mock.patch.object(workbooks.WorkbookManager, 'get',
                       mock.MagicMock(side_effect=Exception()))
    def test_launch_workbook_name_mismatch(self):
        action_ref = 'generic.workbook_v2_name_mismatch'
        MistralRunner.entry_point = mock.PropertyMock(
            return_value=WB1_YAML_FILE_PATH)
        execution = LiveActionDB(action=action_ref, parameters=ACTION_PARAMS)
        liveaction, _ = action_service.schedule(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, LIVEACTION_STATUS_FAILED)
        self.assertIn('Name of the workbook must be the same',
                      liveaction.result['message'])

    @mock.patch.object(
        requests, 'request',
        mock.MagicMock(return_value=http.FakeResponse({}, 200, 'OK')))
    def test_callback_handler_with_result_as_text(self):
        MistralCallbackHandler.callback('http://localhost:8989/v2/tasks/12345',
                                        {}, LIVEACTION_STATUS_SUCCEEDED,
                                        '<html></html>')

    @mock.patch.object(
        requests, 'request',
        mock.MagicMock(return_value=http.FakeResponse({}, 200, 'OK')))
    def test_callback_handler_with_result_as_dict(self):
        MistralCallbackHandler.callback('http://localhost:8989/v2/tasks/12345',
                                        {}, LIVEACTION_STATUS_SUCCEEDED,
                                        {'a': 1})

    @mock.patch.object(
        requests, 'request',
        mock.MagicMock(return_value=http.FakeResponse({}, 200, 'OK')))
    def test_callback_handler_with_result_as_json_str(self):
        MistralCallbackHandler.callback('http://localhost:8989/v2/tasks/12345',
                                        {}, LIVEACTION_STATUS_SUCCEEDED,
                                        '{"a": 1}')
        MistralCallbackHandler.callback('http://localhost:8989/v2/tasks/12345',
                                        {}, LIVEACTION_STATUS_SUCCEEDED,
                                        "{'a': 1}")

    @mock.patch.object(
        requests, 'request',
        mock.MagicMock(return_value=http.FakeResponse({}, 200, 'OK')))
    def test_callback_handler_with_result_as_list(self):
        MistralCallbackHandler.callback('http://localhost:8989/v2/tasks/12345',
                                        {}, LIVEACTION_STATUS_SUCCEEDED,
                                        ["a", "b", "c"])

    @mock.patch.object(
        requests, 'request',
        mock.MagicMock(return_value=http.FakeResponse({}, 200, 'OK')))
    def test_callback_handler_with_result_as_list_str(self):
        MistralCallbackHandler.callback('http://localhost:8989/v2/tasks/12345',
                                        {}, LIVEACTION_STATUS_SUCCEEDED,
                                        '["a", "b", "c"]')

    @mock.patch.object(
        requests, 'request',
        mock.MagicMock(return_value=http.FakeResponse({}, 200, 'OK')))
    def test_callback(self):
        execution = LiveActionDB(action='core.local',
                                 parameters={'cmd': 'uname -a'},
                                 callback={
                                     'source': 'mistral',
                                     'url':
                                     'http://localhost:8989/v2/tasks/12345'
                                 })
        liveaction, _ = action_service.schedule(execution)
        liveaction = LiveAction.get_by_id(str(liveaction.id))
        self.assertEqual(liveaction.status, LIVEACTION_STATUS_SUCCEEDED)
        requests.request.assert_called_with(
            'PUT',
            liveaction.callback['url'],
            data=json.dumps({
                'state': 'SUCCESS',
                'result': NON_EMPTY_RESULT
            }),
            headers={'content-type': 'application/json'})

    def test_build_context(self):
        parent = {
            'mistral': {
                'workflow_name': 'foo',
                'execution_id': 'zbbjb-r87t84-bbjd',
                'task_tags': None,
                'task_name': 'some_fancy_wf_task',
                'task_id': '6c7d4334-3e7d-49c6-918d-698e846affaf'
            }
        }
        current = {
            'workflow_name': 'foo.subwf',
            'execution_id': 'cbjhv-csvhjvsh-vvshvc'
        }
        context = MistralRunner._build_mistral_context(parent, current)
        self.assertTrue(context is not None)
        self.assertTrue('parent' in context['mistral'].keys())
        parent_dict = {
            'workflow_name': parent['mistral']['workflow_name'],
            'execution_id': parent['mistral']['execution_id']
        }
        self.assertDictEqual(context['mistral']['parent'], parent_dict)
        self.assertEqual(context['mistral']['execution_id'],
                         current['execution_id'])

        parent = None
        context = MistralRunner._build_mistral_context(parent, current)
        self.assertDictEqual(context['mistral'], current)