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') def tearDown(self): super(MistralRunnerTest, self).tearDown() cfg.CONF.set_default('max_attempts', 2, group='mistral') cfg.CONF.set_default('retry_wait', 1, 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))) 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), '__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), '__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), '__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())) 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('Failed to connect to mistral', 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']) @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://*****:*****@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://localhost: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) action_executions.ActionExecutionManager.update.assert_called_with( '12345', state='SUCCESS', output=NON_EMPTY_RESULT) @mock.patch.object( action_executions.ActionExecutionManager, 'update', mock.MagicMock( side_effect=[requests.exceptions.ConnectionError(), None])) def test_callback_retry(self): cfg.CONF.set_default('max_attempts', 3, group='mistral') cfg.CONF.set_default('retry_wait', 0, group='mistral') liveaction = LiveActionDB( action='core.local', parameters={'cmd': 'uname -a'}, callback={ 'source': 'mistral', 'url': 'http://*****:*****@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): cfg.CONF.set_default('max_attempts', 3, group='mistral') cfg.CONF.set_default('retry_wait', 0, group='mistral') liveaction = LiveActionDB( action='core.local', parameters={'cmd': 'uname -a'}, callback={ 'source': 'mistral', 'url': 'http://*****:*****@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) 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)
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)
class TestMistralRunner(DbTestCase): @classmethod def setUpClass(cls): super(TestMistralRunner, cls).setUpClass() runners_registrar.register_runner_types() metadata = fixture.ARTIFACTS['metadata'] action_local = ActionAPI(**copy.deepcopy(metadata['actions']['local'])) Action.add_or_update(ActionAPI.to_model(action_local)) action_wkflow = ActionAPI( **copy.deepcopy(metadata['actions']['workflow-v2'])) Action.add_or_update(ActionAPI.to_model(action_wkflow)) @mock.patch.object(workbooks.WorkbookManager, 'get', mock.MagicMock(return_value=WORKBOOK)) @mock.patch.object(executions.ExecutionManager, 'create', mock.MagicMock(return_value=EXECUTION)) def test_launch_workflow(self): MistralRunner.entry_point = mock.PropertyMock( return_value=WORKFLOW_YAML) execution = ActionExecutionDB(action='core.workflow-v2', parameters={'friend': 'Rocky'}) execution = action_service.schedule(execution) execution = ActionExecution.get_by_id(str(execution.id)) self.assertEqual(execution.status, ACTIONEXEC_STATUS_RUNNING) @mock.patch.object(workbooks.WorkbookManager, 'get', mock.MagicMock(return_value=WORKBOOK_OLD)) @mock.patch.object(workbooks.WorkbookManager, 'update', mock.MagicMock(return_value=WORKBOOK)) @mock.patch.object(executions.ExecutionManager, 'create', mock.MagicMock(return_value=EXECUTION)) def test_launch_workflow_when_definition_changed(self): MistralRunner.entry_point = mock.PropertyMock( return_value=WORKFLOW_YAML) execution = ActionExecutionDB(action='core.workflow-v2', parameters={'friend': 'Rocky'}) execution = action_service.schedule(execution) execution = ActionExecution.get_by_id(str(execution.id)) self.assertEqual(execution.status, ACTIONEXEC_STATUS_RUNNING) @mock.patch.object(workbooks.WorkbookManager, 'get', mock.MagicMock(return_value=Exception())) @mock.patch.object(workbooks.WorkbookManager, 'create', mock.MagicMock(return_value=None)) @mock.patch.object(executions.ExecutionManager, 'create', mock.MagicMock(return_value=EXECUTION)) def test_launch_workflow_when_workbook_not_exists(self): execution = ActionExecutionDB(action='core.workflow-v2', parameters={'friend': 'Rocky'}) execution = action_service.schedule(execution) execution = ActionExecution.get_by_id(str(execution.id)) self.assertEqual(execution.status, ACTIONEXEC_STATUS_RUNNING) @mock.patch.object( requests, 'request', mock.MagicMock(return_value=http.FakeResponse({}, 200, 'OK'))) def test_callback(self): execution = ActionExecutionDB( action='core.local', parameters={'cmd': 'uname -a'}, callback={ 'source': 'mistral', 'url': 'http://localhost:8989/v2/tasks/12345' }) execution = action_service.schedule(execution) execution = ActionExecution.get_by_id(str(execution.id)) self.assertEqual(execution.status, ACTIONEXEC_STATUS_SUCCEEDED) requests.request.assert_called_with( 'PUT', execution.callback['url'], data=json.dumps({ 'state': 'SUCCESS', 'result': '{}' }), headers={'content-type': 'application/json'})
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)