def test_created_temporary_auth_token_is_correctly_scoped_to_user_who_ran_the_action( self): params = { 'actionstr': 'bar', 'mock_status': action_constants.LIVEACTION_STATUS_SUCCEEDED } global global_runner global_runner = None def mock_get_runner(*args, **kwargs): global global_runner runner = original_get_runner(*args, **kwargs) global_runner = runner return runner # user joe_1 runner_container = get_runner_container() original_get_runner = runner_container._get_runner liveaction_db = self._get_failingaction_exec_db_model(params) liveaction_db = LiveAction.add_or_update(liveaction_db) liveaction_db.context = {'user': '******'} executions.create_execution_object(liveaction_db) runner_container._get_runner = mock_get_runner self.assertEqual(getattr(global_runner, 'auth_token', None), None) runner_container.dispatch(liveaction_db) self.assertEqual(global_runner.auth_token.user, 'user_joe_1') self.assertEqual(global_runner.auth_token.metadata['service'], 'actions_container') runner_container._get_runner = original_get_runner # user mark_1 global_runner = None runner_container = get_runner_container() original_get_runner = runner_container._get_runner liveaction_db = self._get_failingaction_exec_db_model(params) liveaction_db = LiveAction.add_or_update(liveaction_db) liveaction_db.context = {'user': '******'} executions.create_execution_object(liveaction_db) original_get_runner = runner_container._get_runner runner_container._get_runner = mock_get_runner self.assertEqual(getattr(global_runner, 'auth_token', None), None) runner_container.dispatch(liveaction_db) self.assertEqual(global_runner.auth_token.user, 'user_mark_2') self.assertEqual(global_runner.auth_token.metadata['service'], 'actions_container')
def test_created_temporary_auth_token_is_correctly_scoped_to_user_who_ran_the_action(self): params = { 'actionstr': 'bar', 'mock_status': action_constants.LIVEACTION_STATUS_SUCCEEDED } global global_runner global_runner = None def mock_get_runner(*args, **kwargs): global global_runner runner = original_get_runner(*args, **kwargs) global_runner = runner return runner # user joe_1 runner_container = get_runner_container() original_get_runner = runner_container._get_runner liveaction_db = self._get_failingaction_exec_db_model(params) liveaction_db = LiveAction.add_or_update(liveaction_db) liveaction_db.context = {'user': '******'} executions.create_execution_object(liveaction_db) runner_container._get_runner = mock_get_runner self.assertEqual(getattr(global_runner, 'auth_token', None), None) runner_container.dispatch(liveaction_db) self.assertEqual(global_runner.auth_token.user, 'user_joe_1') self.assertEqual(global_runner.auth_token.metadata['service'], 'actions_container') runner_container._get_runner = original_get_runner # user mark_1 global_runner = None runner_container = get_runner_container() original_get_runner = runner_container._get_runner liveaction_db = self._get_failingaction_exec_db_model(params) liveaction_db = LiveAction.add_or_update(liveaction_db) liveaction_db.context = {'user': '******'} executions.create_execution_object(liveaction_db) original_get_runner = runner_container._get_runner runner_container._get_runner = mock_get_runner self.assertEqual(getattr(global_runner, 'auth_token', None), None) runner_container.dispatch(liveaction_db) self.assertEqual(global_runner.auth_token.user, 'user_mark_2') self.assertEqual(global_runner.auth_token.metadata['service'], 'actions_container')
def _mark_inquiry_complete(self, inquiry_id, result): """Mark Inquiry as completed This function updates the local LiveAction and Execution with a successful status as well as call the "post_run" function for the Inquirer runner so that the appropriate callback function is executed :param inquiry: The Inquiry for which the response is given :param requester_user: The user providing the response :rtype: bool - True if requester_user is able to respond. False if not. """ # Update inquiry's execution result with a successful status and the validated response liveaction_db = action_utils.update_liveaction_status( status=action_constants.LIVEACTION_STATUS_SUCCEEDED, runner_info=system_info.get_process_info(), result=result, liveaction_id=inquiry_id) executions.update_execution(liveaction_db) # Call Inquiry runner's post_run to trigger callback to workflow runner_container = get_runner_container() action_db = get_action_by_ref(liveaction_db.action) runnertype_db = get_runnertype_by_name(action_db.runner_type['name']) runner = runner_container._get_runner(runnertype_db, action_db, liveaction_db) runner.post_run(status=action_constants.LIVEACTION_STATUS_SUCCEEDED, result=result) return liveaction_db
def test_state_db_created_for_polling_async_actions(self): runner_container = get_runner_container() params = {"actionstr": "foo", "actionint": 20, "async_test": True} liveaction_db = self._get_liveaction_model( RunnerContainerTest.polling_async_action_db, params) liveaction_db = LiveAction.add_or_update(liveaction_db) executions.create_execution_object(liveaction_db) # Assert that execution ran without exceptions. with mock.patch( "st2actions.container.base.get_runner", mock.Mock(return_value=polling_async_runner.get_runner()), ): runner_container.dispatch(liveaction_db) states = ActionExecutionState.get_all() found = [ state for state in states if state.execution_id == liveaction_db.id ] self.assertTrue(len(found) > 0, "There should be a state db object.") self.assertTrue( len(found) == 1, "There should only be one state db object.") self.assertIsNotNone(found[0].query_context) self.assertIsNotNone(found[0].query_module)
def respond(inquiry, response, requester=None): # Set requester to system user is not provided. if not requester: requester = cfg.CONF.system_user.user # Retrieve the liveaction from the database. liveaction_db = lv_db_access.LiveAction.get_by_id( inquiry.liveaction.get("id")) # Resume the parent workflow first. If the action execution for the inquiry is updated first, # it triggers handling of the action execution completion which will interact with the paused # parent workflow. The resuming logic that is executed here will then race with the completion # of the inquiry action execution, which will randomly result in the parent workflow stuck in # paused state. if liveaction_db.context.get("parent"): LOG.debug('Resuming workflow parent(s) for inquiry "%s".' % str(inquiry.id)) # For action execution under Action Chain workflows, request the entire # workflow to resume. Orquesta handles resume differently and so does not require root # to resume. Orquesta allows for specifc branches to resume while other is paused. When # there is no other paused branches, the conductor will resume the rest of the workflow. resume_target = ( action_service.get_parent_liveaction(liveaction_db) if workflow_service.is_action_execution_under_workflow_context( liveaction_db) else action_service.get_root_liveaction(liveaction_db)) if resume_target.status in action_constants.LIVEACTION_PAUSE_STATES: action_service.request_resume(resume_target, requester) # Succeed the liveaction and update result with the inquiry response. LOG.debug('Updating response for inquiry "%s".' % str(inquiry.id)) result = fast_deepcopy_dict(inquiry.result) result["response"] = response liveaction_db = action_utils.update_liveaction_status( status=action_constants.LIVEACTION_STATUS_SUCCEEDED, end_timestamp=date_utils.get_datetime_utc_now(), runner_info=sys_info_utils.get_process_info(), result=result, liveaction_id=str(liveaction_db.id), ) # Sync the liveaction with the corresponding action execution. execution_service.update_execution(liveaction_db) # Invoke inquiry post run to trigger a callback to parent workflow. LOG.debug('Invoking post run for inquiry "%s".' % str(inquiry.id)) runner_container = container.get_runner_container() action_db = action_utils.get_action_by_ref(liveaction_db.action) runnertype_db = action_utils.get_runnertype_by_name( action_db.runner_type["name"]) runner = runner_container._get_runner(runnertype_db, action_db, liveaction_db) runner.post_run(status=action_constants.LIVEACTION_STATUS_SUCCEEDED, result=result) return liveaction_db
def test_state_db_created_for_polling_async_actions(self): runner_container = get_runner_container() params = { 'actionstr': 'foo', 'actionint': 20, 'async_test': True } liveaction_db = self._get_liveaction_model( RunnerContainerTest.polling_async_action_db, params ) liveaction_db = LiveAction.add_or_update(liveaction_db) executions.create_execution_object(liveaction_db) # Assert that execution ran without exceptions. runner_container.dispatch(liveaction_db) states = ActionExecutionState.get_all() found = [state for state in states if state.execution_id == liveaction_db.id] self.assertTrue(len(found) > 0, 'There should be a state db object.') self.assertTrue(len(found) == 1, 'There should only be one state db object.') self.assertTrue(found[0].query_context is not None) self.assertTrue(found[0].query_module is not None)
def test_dispatch_runner_failure(self): runner_container = get_runner_container() params = {'actionstr': 'bar'} actionexec_db = self._get_failingaction_exec_db_model(params) actionexec_db = ActionExecution.add_or_update(actionexec_db) self.assertTrue(runner_container.dispatch(actionexec_db)) # pickup updated actionexec_db actionexec_db = ActionExecution.get_by_id(actionexec_db.id) self.assertTrue('message' in actionexec_db.result) self.assertTrue('traceback' in actionexec_db.result)
def respond(inquiry, response, requester=None): # Set requester to system user is not provided. if not requester: requester = cfg.CONF.system_user.user # Retrieve the liveaction from the database. liveaction_db = lv_db_access.LiveAction.get_by_id(inquiry.liveaction.get('id')) # Resume the parent workflow first. If the action execution for the inquiry is updated first, # it triggers handling of the action execution completion which will interact with the paused # parent workflow. The resuming logic that is executed here will then race with the completion # of the inquiry action execution, which will randomly result in the parent workflow stuck in # paused state. if liveaction_db.context.get('parent'): LOG.debug('Resuming workflow parent(s) for inquiry "%s".' % str(inquiry.id)) # For action execution under Action Chain and Mistral workflows, request the entire # workflow to resume. Orquesta handles resume differently and so does not require root # to resume. Orquesta allows for specifc branches to resume while other is paused. When # there is no other paused branches, the conductor will resume the rest of the workflow. resume_target = ( action_service.get_parent_liveaction(liveaction_db) if workflow_service.is_action_execution_under_workflow_context(liveaction_db) else action_service.get_root_liveaction(liveaction_db) ) if resume_target.status in action_constants.LIVEACTION_PAUSE_STATES: action_service.request_resume(resume_target, requester) # Succeed the liveaction and update result with the inquiry response. LOG.debug('Updating response for inquiry "%s".' % str(inquiry.id)) result = copy.deepcopy(inquiry.result) result['response'] = response liveaction_db = action_utils.update_liveaction_status( status=action_constants.LIVEACTION_STATUS_SUCCEEDED, end_timestamp=date_utils.get_datetime_utc_now(), runner_info=sys_info_utils.get_process_info(), result=result, liveaction_id=str(liveaction_db.id) ) # Sync the liveaction with the corresponding action execution. execution_service.update_execution(liveaction_db) # Invoke inquiry post run to trigger a callback to parent workflow. LOG.debug('Invoking post run for inquiry "%s".' % str(inquiry.id)) runner_container = container.get_runner_container() action_db = action_utils.get_action_by_ref(liveaction_db.action) runnertype_db = action_utils.get_runnertype_by_name(action_db.runner_type['name']) runner = runner_container._get_runner(runnertype_db, action_db, liveaction_db) runner.post_run(status=action_constants.LIVEACTION_STATUS_SUCCEEDED, result=result) return liveaction_db
def test_dispatch(self): runner_container = get_runner_container() params = {'actionstr': 'bar'} actionexec_db = self._get_action_exec_db_model(params) actionexec_db = ActionExecution.add_or_update(actionexec_db) # Assert that execution ran successfully. self.assertTrue(runner_container.dispatch(actionexec_db)) actionexec_db = ActionExecution.get_by_id(actionexec_db.id) result = actionexec_db.result self.assertTrue(result.get('action_params').get('actionint') == 10) self.assertTrue(result.get('action_params').get('actionstr') == 'bar')
def test_dispatch_runner_failure(self): runner_container = get_runner_container() params = {'actionstr': 'bar'} liveaction_db = self._get_failingaction_exec_db_model(params) liveaction_db = LiveAction.add_or_update(liveaction_db) executions.create_execution_object(liveaction_db) runner_container.dispatch(liveaction_db) # pickup updated liveaction_db liveaction_db = LiveAction.get_by_id(liveaction_db.id) self.assertTrue('error' in liveaction_db.result) self.assertTrue('traceback' in liveaction_db.result)
def test_dispatch_runner_failure(self): runner_container = get_runner_container() params = {"actionstr": "bar"} liveaction_db = self._get_failingaction_exec_db_model(params) liveaction_db = LiveAction.add_or_update(liveaction_db) executions.create_execution_object(liveaction_db) runner_container.dispatch(liveaction_db) # pickup updated liveaction_db liveaction_db = LiveAction.get_by_id(liveaction_db.id) self.assertTrue("message" in liveaction_db.result) self.assertTrue("traceback" in liveaction_db.result)
def test_dispatch_override_default_action_params(self): runner_container = get_runner_container() params = {"actionstr": "foo", "actionint": 20} liveaction_db = self._get_action_exec_db_model(RunnerContainerTest.action_db, params) liveaction_db = LiveAction.add_or_update(liveaction_db) executions.create_execution_object(liveaction_db) # Assert that execution ran successfully. runner_container.dispatch(liveaction_db) liveaction_db = LiveAction.get_by_id(liveaction_db.id) result = liveaction_db.result self.assertTrue(result.get("action_params").get("actionint") == 20) self.assertTrue(result.get("action_params").get("actionstr") == "foo")
def test_dispatch_runner_failure(self): runner_container = get_runner_container() params = { 'actionstr': 'bar' } actionexec_db = self._get_failingaction_exec_db_model(params) actionexec_db = ActionExecution.add_or_update(actionexec_db) self.assertTrue(runner_container.dispatch(actionexec_db)) # pickup updated actionexec_db actionexec_db = ActionExecution.get_by_id(actionexec_db.id) self.assertTrue('message' in actionexec_db.result) self.assertTrue('traceback' in actionexec_db.result)
def test_dispatch_non_utf8_result(self): runner_container = get_runner_container() params = {"cmd": "python -c 'print \"\\x82\"'"} liveaction_db = self._get_liveaction_model(RunnerContainerTest.local_action_db, params) liveaction_db = LiveAction.add_or_update(liveaction_db) executions.create_execution_object(liveaction_db) try: runner_container.dispatch(liveaction_db) self.fail("Mongo won't handle non UTF-8 strings. Should have failed.") except InvalidStringData: pass
def test_dispatch_override_default_action_params(self): runner_container = get_runner_container() params = {'actionstr': 'foo', 'actionint': 20} liveaction_db = self._get_liveaction_model( RunnerContainerTest.action_db, params) liveaction_db = LiveAction.add_or_update(liveaction_db) executions.create_execution_object(liveaction_db) # Assert that execution ran successfully. runner_container.dispatch(liveaction_db) liveaction_db = LiveAction.get_by_id(liveaction_db.id) result = liveaction_db.result self.assertTrue(result.get('action_params').get('actionint') == 20) self.assertTrue(result.get('action_params').get('actionstr') == 'foo')
def test_dispatch_override_default_action_params(self): runner_container = get_runner_container() params = {'actionstr': 'foo', 'actionint': 20} actionexec_db = self._get_action_exec_db_model(params) actionexec_db = ActionExecution.add_or_update(actionexec_db) # Assert that execution ran successfully. result = runner_container.dispatch(actionexec_db) self.assertTrue(result) actionexec_db = ActionExecution.get_by_id(actionexec_db.id) result = actionexec_db.result self.assertTrue(result.get('action_params').get('actionint') == 20) self.assertTrue(result.get('action_params').get('actionstr') == 'foo')
def test_dispatch(self): runner_container = get_runner_container() params = { 'actionstr': 'bar' } actionexec_db = self._get_action_exec_db_model(params) actionexec_db = ActionExecution.add_or_update(actionexec_db) # Assert that execution ran successfully. self.assertTrue(runner_container.dispatch(actionexec_db)) actionexec_db = ActionExecution.get_by_id(actionexec_db.id) result = actionexec_db.result self.assertTrue(result.get('action_params').get('actionint') == 10) self.assertTrue(result.get('action_params').get('actionstr') == 'bar')
def test_dispatch_non_utf8_result(self): runner_container = get_runner_container() params = {'cmd': "python -c 'print \"\\x82\"'"} liveaction_db = self._get_liveaction_model( RunnerContainerTest.local_action_db, params) liveaction_db = LiveAction.add_or_update(liveaction_db) executions.create_execution_object(liveaction_db) try: runner_container.dispatch(liveaction_db) self.fail( 'Mongo won\'t handle non UTF-8 strings. Should have failed.') except InvalidStringData: pass
def test_dispatch_unsupported_status(self): runner_container = get_runner_container() params = {'actionstr': 'bar'} liveaction_db = self._get_liveaction_model( RunnerContainerTest.action_db, params) liveaction_db = LiveAction.add_or_update(liveaction_db) executions.create_execution_object(liveaction_db) # Manually set the liveaction_db to some unsupported status. liveaction_db.status = action_constants.LIVEACTION_STATUS_CANCELED # Assert exception is raised on dispatch. self.assertRaises(ActionRunnerDispatchError, runner_container.dispatch, liveaction_db)
def test_dispatch_unsupported_status(self): runner_container = get_runner_container() params = {'actionstr': 'bar'} liveaction_db = self._get_liveaction_model(RunnerContainerTest.action_db, params) liveaction_db = LiveAction.add_or_update(liveaction_db) executions.create_execution_object(liveaction_db) # Manually set the liveaction_db to some unsupported status. liveaction_db.status = action_constants.LIVEACTION_STATUS_CANCELED # Assert exception is raised on dispatch. self.assertRaises( ActionRunnerDispatchError, runner_container.dispatch, liveaction_db )
def test_dispatch_override_default_action_params(self): runner_container = get_runner_container() params = { 'actionstr': 'foo', 'actionint': 20 } actionexec_db = self._get_action_exec_db_model(params) actionexec_db = ActionExecution.add_or_update(actionexec_db) # Assert that execution ran successfully. result = runner_container.dispatch(actionexec_db) self.assertTrue(result) actionexec_db = ActionExecution.get_by_id(actionexec_db.id) result = actionexec_db.result self.assertTrue(result.get('action_params').get('actionint') == 20) self.assertTrue(result.get('action_params').get('actionstr') == 'foo')
def test_dispatch(self): runner_container = get_runner_container() params = {"actionstr": "bar"} liveaction_db = self._get_action_exec_db_model(RunnerContainerTest.action_db, params) liveaction_db = LiveAction.add_or_update(liveaction_db) executions.create_execution_object(liveaction_db) # Assert that execution ran successfully. runner_container.dispatch(liveaction_db) liveaction_db = LiveAction.get_by_id(liveaction_db.id) result = liveaction_db.result self.assertTrue(result.get("action_params").get("actionint") == 10) self.assertTrue(result.get("action_params").get("actionstr") == "bar") # Assert that context is written correctly. context = {"user": "******", "third_party_system": {"ref_id": "1234"}} self.assertDictEqual(liveaction_db.context, context)
def test_state_db_creation_async_actions(self): runner_container = get_runner_container() params = {"actionstr": "foo", "actionint": 20, "async_test": True} liveaction_db = self._get_action_exec_db_model(RunnerContainerTest.async_action_db, params) liveaction_db = LiveAction.add_or_update(liveaction_db) executions.create_execution_object(liveaction_db) # Assert that execution ran without exceptions. runner_container.dispatch(liveaction_db) states = ActionExecutionState.get_all() found = None for state in states: if state.execution_id == liveaction_db.id: found = state self.assertTrue(found is not None, "There should be a state db object.") self.assertTrue(found.query_context is not None) self.assertTrue(found.query_module is not None)
def test_dispatch(self): runner_container = get_runner_container() params = {'actionstr': 'bar'} liveaction_db = self._get_liveaction_model( RunnerContainerTest.action_db, params) liveaction_db = LiveAction.add_or_update(liveaction_db) executions.create_execution_object(liveaction_db) # Assert that execution ran successfully. runner_container.dispatch(liveaction_db) liveaction_db = LiveAction.get_by_id(liveaction_db.id) result = liveaction_db.result self.assertTrue(result.get('action_params').get('actionint') == 10) self.assertTrue(result.get('action_params').get('actionstr') == 'bar') # Assert that context is written correctly. context = {'user': '******', 'third_party_system': {'ref_id': '1234'}} self.assertDictEqual(liveaction_db.context, context)
def test_dispatch(self): runner_container = get_runner_container() params = {"actionstr": "bar"} liveaction_db = self._get_liveaction_model( RunnerContainerTest.action_db, params) liveaction_db = LiveAction.add_or_update(liveaction_db) executions.create_execution_object(liveaction_db) # Assert that execution ran successfully. runner_container.dispatch(liveaction_db) liveaction_db = LiveAction.get_by_id(liveaction_db.id) result = liveaction_db.result self.assertTrue(result.get("action_params").get("actionint") == 10) self.assertTrue(result.get("action_params").get("actionstr") == "bar") # Assert that context is written correctly. context = {"user": "******", "third_party_system": {"ref_id": "1234"}} self.assertDictEqual(liveaction_db.context, context)
def test_state_db_not_created_for_async_actions(self): runner_container = get_runner_container() params = {"actionstr": "foo", "actionint": 20, "async_test": True} liveaction_db = self._get_liveaction_model( RunnerContainerTest.async_action_db, params) liveaction_db = LiveAction.add_or_update(liveaction_db) executions.create_execution_object(liveaction_db) # Assert that execution ran without exceptions. runner_container.dispatch(liveaction_db) states = ActionExecutionState.get_all() found = [ state for state in states if state.execution_id == liveaction_db.id ] self.assertTrue( len(found) == 0, "There should not be a state db object.")
def test_state_db_creation_async_actions(self): runner_container = get_runner_container() params = { 'actionstr': 'foo', 'actionint': 20, 'async_test': True } actionexec_db = self._get_action_exec_db_model(RunnerContainerTest.async_action_db, params) actionexec_db = ActionExecution.add_or_update(actionexec_db) # Assert that execution ran without exceptions. runner_container.dispatch(actionexec_db) states = ActionExecutionState.get_all() found = None for state in states: if state.execution_id == actionexec_db.id: found = state self.assertTrue(found is not None, 'There should be a state db object.') self.assertTrue(found.query_context is not None) self.assertTrue(found.query_module is not None)
def test_dispatch(self): runner_container = get_runner_container() params = { 'actionstr': 'bar' } liveaction_db = self._get_action_exec_db_model(RunnerContainerTest.action_db, params) liveaction_db = LiveAction.add_or_update(liveaction_db) executions.create_execution_object(liveaction_db) # Assert that execution ran successfully. runner_container.dispatch(liveaction_db) liveaction_db = LiveAction.get_by_id(liveaction_db.id) result = liveaction_db.result self.assertTrue(result.get('action_params').get('actionint') == 10) self.assertTrue(result.get('action_params').get('actionstr') == 'bar') # Assert that context is written correctly. context = { 'user': '******', 'third_party_system': { 'ref_id': '1234' } } self.assertDictEqual(liveaction_db.context, context)
def test_post_run_is_always_called_after_run(self): # 1. post_run should be called on success, failure, etc. runner_container = get_runner_container() params = { 'actionstr': 'bar', 'mock_status': action_constants.LIVEACTION_STATUS_SUCCEEDED } liveaction_db = self._get_failingaction_exec_db_model(params) liveaction_db = LiveAction.add_or_update(liveaction_db) executions.create_execution_object(liveaction_db) global global_runner global_runner = None original_get_runner = runner_container._get_runner def mock_get_runner(*args, **kwargs): global global_runner runner = original_get_runner(*args, **kwargs) global_runner = runner return runner runner_container._get_runner = mock_get_runner # Note: We can't assert here that post_run hasn't been called yet because runner instance # is only instantiated later inside dispatch method runner_container.dispatch(liveaction_db) self.assertTrue(global_runner.post_run_called) # 2. Verify post_run is called if run() throws runner_container = get_runner_container() params = {'actionstr': 'bar', 'raise': True} liveaction_db = self._get_failingaction_exec_db_model(params) liveaction_db = LiveAction.add_or_update(liveaction_db) executions.create_execution_object(liveaction_db) global_runner = None original_get_runner = runner_container._get_runner def mock_get_runner(*args, **kwargs): global global_runner runner = original_get_runner(*args, **kwargs) global_runner = runner return runner runner_container._get_runner = mock_get_runner # Note: We can't assert here that post_run hasn't been called yet because runner instance # is only instantiated later inside dispatch method runner_container.dispatch(liveaction_db) self.assertTrue(global_runner.post_run_called) # 2. Verify post_run is also called if _delete_auth_token throws runner_container = get_runner_container() runner_container._delete_auth_token = mock.Mock( side_effect=ValueError('throw')) params = { 'actionstr': 'bar', 'mock_status': action_constants.LIVEACTION_STATUS_SUCCEEDED } liveaction_db = self._get_failingaction_exec_db_model(params) liveaction_db = LiveAction.add_or_update(liveaction_db) executions.create_execution_object(liveaction_db) global_runner = None original_get_runner = runner_container._get_runner def mock_get_runner(*args, **kwargs): global global_runner runner = original_get_runner(*args, **kwargs) global_runner = runner return runner runner_container._get_runner = mock_get_runner # Note: We can't assert here that post_run hasn't been called yet because runner instance # is only instantiated later inside dispatch method runner_container.dispatch(liveaction_db) self.assertTrue(global_runner.post_run_called)
def purge_inquiries(logger): """Purge Inquiries that have exceeded their configured TTL At the moment, Inquiries do not have their own database model, so this function effectively is another, more specialized GC for executions. It will look for executions with a 'pending' status that use the 'inquirer' runner, which is the current definition for an Inquiry. Then it will mark those that have a nonzero TTL have existed longer than their TTL as "timed out". It will then request that the parent workflow(s) resume, where the failure can be handled as the user desires. """ # Get all existing Inquiries filters = {'runner__name': 'inquirer', 'status': action_constants.LIVEACTION_STATUS_PENDING} inquiries = list(ActionExecution.query(**filters)) gc_count = 0 # Inspect each Inquiry, and determine if TTL is expired for inquiry in inquiries: ttl = int(inquiry.result.get('ttl')) if ttl <= 0: logger.debug("Inquiry %s has a TTL of %s. Skipping." % (inquiry.id, ttl)) continue min_since_creation = int( (get_datetime_utc_now() - inquiry.start_timestamp).total_seconds() / 60 ) logger.debug("Inquiry %s has a TTL of %s and was started %s minute(s) ago" % ( inquiry.id, ttl, min_since_creation)) if min_since_creation > ttl: gc_count += 1 logger.info("TTL expired for Inquiry %s. Marking as timed out." % inquiry.id) liveaction_db = action_utils.update_liveaction_status( status=action_constants.LIVEACTION_STATUS_TIMED_OUT, result=inquiry.result, liveaction_id=inquiry.liveaction.get('id')) executions.update_execution(liveaction_db) # Call Inquiry runner's post_run to trigger callback to workflow runner_container = get_runner_container() action_db = get_action_by_ref(liveaction_db.action) runnertype_db = get_runnertype_by_name(action_db.runner_type['name']) runner = runner_container._get_runner(runnertype_db, action_db, liveaction_db) runner.post_run(status=action_constants.LIVEACTION_STATUS_TIMED_OUT, result=inquiry.result) if liveaction_db.context.get("parent"): # Request that root workflow resumes root_liveaction = action_service.get_root_liveaction(liveaction_db) action_service.request_resume( root_liveaction, UserDB(cfg.CONF.system_user.user) ) logger.info('Marked %s ttl-expired Inquiries as "timed out".' % (gc_count))
def test_post_run_is_always_called_after_run(self): # 1. post_run should be called on success, failure, etc. runner_container = get_runner_container() params = { 'actionstr': 'bar', 'mock_status': action_constants.LIVEACTION_STATUS_SUCCEEDED } liveaction_db = self._get_failingaction_exec_db_model(params) liveaction_db = LiveAction.add_or_update(liveaction_db) executions.create_execution_object(liveaction_db) global global_runner global_runner = None original_get_runner = runner_container._get_runner def mock_get_runner(*args, **kwargs): global global_runner runner = original_get_runner(*args, **kwargs) global_runner = runner return runner runner_container._get_runner = mock_get_runner # Note: We can't assert here that post_run hasn't been called yet because runner instance # is only instantiated later inside dispatch method runner_container.dispatch(liveaction_db) self.assertTrue(global_runner.post_run_called) # 2. Verify post_run is called if run() throws runner_container = get_runner_container() params = { 'actionstr': 'bar', 'raise': True } liveaction_db = self._get_failingaction_exec_db_model(params) liveaction_db = LiveAction.add_or_update(liveaction_db) executions.create_execution_object(liveaction_db) global_runner = None original_get_runner = runner_container._get_runner def mock_get_runner(*args, **kwargs): global global_runner runner = original_get_runner(*args, **kwargs) global_runner = runner return runner runner_container._get_runner = mock_get_runner # Note: We can't assert here that post_run hasn't been called yet because runner instance # is only instantiated later inside dispatch method runner_container.dispatch(liveaction_db) self.assertTrue(global_runner.post_run_called) # 2. Verify post_run is also called if _delete_auth_token throws runner_container = get_runner_container() runner_container._delete_auth_token = mock.Mock(side_effect=ValueError('throw')) params = { 'actionstr': 'bar', 'mock_status': action_constants.LIVEACTION_STATUS_SUCCEEDED } liveaction_db = self._get_failingaction_exec_db_model(params) liveaction_db = LiveAction.add_or_update(liveaction_db) executions.create_execution_object(liveaction_db) global_runner = None original_get_runner = runner_container._get_runner def mock_get_runner(*args, **kwargs): global global_runner runner = original_get_runner(*args, **kwargs) global_runner = runner return runner runner_container._get_runner = mock_get_runner # Note: We can't assert here that post_run hasn't been called yet because runner instance # is only instantiated later inside dispatch method runner_container.dispatch(liveaction_db) self.assertTrue(global_runner.post_run_called)