diff --git a/st2actions/st2actions/bootstrap/actionsregistrar.py b/st2actions/st2actions/bootstrap/actionsregistrar.py index 4d7c744e32..254b938251 100644 --- a/st2actions/st2actions/bootstrap/actionsregistrar.py +++ b/st2actions/st2actions/bootstrap/actionsregistrar.py @@ -8,7 +8,6 @@ from st2common import log as logging from st2common.content.loader import ContentPackLoader from st2common.content.requirementsvalidator import RequirementsValidator -from st2common.exceptions.db import StackStormDBObjectNotFoundError from st2common.persistence.action import Action from st2common.models.db.action import ActionDB from st2common.util.action_db import get_runnertype_by_name @@ -16,19 +15,18 @@ LOG = logging.getLogger(__name__) -def _get_actions_from_pack(pack): - return glob.glob(pack + '/*.json') +class ActionsRegistrar(object): + def _get_actions_from_pack(self, pack): + return glob.glob(pack + '/*.json') - -def _register_actions_from_pack(pack, actions): - for action in actions: - LOG.debug('Loading action from %s.', action) + def _register_action(self, pack, action): with open(action, 'r') as fd: try: content = json.load(fd) except ValueError: - LOG.exception('Unable to load action from %s.', action) - continue + LOG.exception('Failed loading action json from %s.', action) + raise + try: model = Action.get_by_name(str(content['name'])) except ValueError: @@ -36,7 +34,7 @@ def _register_actions_from_pack(pack, actions): model.name = content['name'] model.description = content['description'] model.enabled = content['enabled'] - pack_name = content.get('pack', None) + pack_name = content.get('content_pack', None) if not pack_name or pack_name == '': pack_name = pack LOG.warning('Pack name missing. Using: %s', pack_name) @@ -44,46 +42,63 @@ def _register_actions_from_pack(pack, actions): model.entry_point = content['entry_point'] model.parameters = content.get('parameters', {}) model.required_parameters = content.get('required_parameters', []) - try: - runner_type = get_runnertype_by_name(str(content['runner_type'])) - model.runner_type = {'name': runner_type.name} - except StackStormDBObjectNotFoundError: - LOG.exception('Failed to register action %s as runner %s was not found', - model.name, str(content['runner_type'])) - continue + runner_type = str(content['runner_type']) + valid_runner_type, runner_type_db = self._has_valid_runner_type(runner_type) + if valid_runner_type: + model.runner_type = {'name': runner_type_db.name} + else: + LOG.exception('Runner type %s doesn\'t exist.') + raise + try: model = Action.add_or_update(model) LOG.audit('Action created. Action %s from %s.', model, action) except Exception: - LOG.exception('Failed to create action %s.', model.name) + LOG.exception('Failed to write action to db %s.', model.name) + raise + def _has_valid_runner_type(self, runner_type): + try: + return True, get_runnertype_by_name(runner_type) + except: + return False, None -# XXX: Requirements for actions is tricky because actions can execute remotely. -# Currently, this method is unused. -def _is_requirements_ok(actions_dir): - rqmnts_file = os.path.join(actions_dir, 'requirements.txt') + def _register_actions_from_pack(self, pack, actions): + for action in actions: + try: + LOG.debug('Loading action from %s.', action) + self._register_action(pack, action) + except Exception: + LOG.exception('Unable to register action: %s', action) + continue - if not os.path.exists(rqmnts_file): - return True + # XXX: Requirements for actions is tricky because actions can execute remotely. + # Currently, this method is unused. + def _is_requirements_ok(self, actions_dir): + rqmnts_file = os.path.join(actions_dir, 'requirements.txt') - missing = RequirementsValidator.validate(rqmnts_file) - if missing: - LOG.warning('Actions in %s missing dependencies: %s', actions_dir, ','.join(missing)) - return False - return True + if not os.path.exists(rqmnts_file): + return True + missing = RequirementsValidator.validate(rqmnts_file) + if missing: + LOG.warning('Actions in %s missing dependencies: %s', actions_dir, ','.join(missing)) + return False + return True -def _register_actions_from_packs(base_dir): - pack_loader = ContentPackLoader() - dirs = pack_loader.get_content(base_dir=base_dir, - content_type='actions') - for pack, actions_dir in six.iteritems(dirs): - try: - actions = _get_actions_from_pack(actions_dir) - _register_actions_from_pack(pack, actions) - except: - LOG.exception('Failed registering all actions from pack: %s', actions_dir) + def register_actions_from_packs(self, base_dir): + pack_loader = ContentPackLoader() + dirs = pack_loader.get_content(base_dir=base_dir, + content_type='actions') + for pack, actions_dir in six.iteritems(dirs): + try: + actions = self._get_actions_from_pack(actions_dir) + self._register_actions_from_pack(pack, actions) + except: + LOG.exception('Failed registering all actions from pack: %s', actions_dir) -def register_actions(): - return _register_actions_from_packs(cfg.CONF.content.content_packs_base_path) +def register_actions(content_packs_base_path=None): + if not content_packs_base_path: + content_packs_base_path = cfg.CONF.content.content_packs_base_path + return ActionsRegistrar().register_actions_from_packs(content_packs_base_path) diff --git a/st2actions/st2actions/container/__init__.py b/st2actions/st2actions/container/__init__.py index 15caccf896..e69de29bb2 100644 --- a/st2actions/st2actions/container/__init__.py +++ b/st2actions/st2actions/container/__init__.py @@ -1,159 +0,0 @@ -import importlib - -from st2common import log as logging -from st2common.exceptions.actionrunner import ActionRunnerCreateError -from st2common.models.api.action import (ACTIONEXEC_STATUS_COMPLETE, - ACTIONEXEC_STATUS_ERROR) - -from st2common.util.action_db import (update_actionexecution_status, get_actionexec_by_id) - -from st2actions.container import actionsensor -from st2actions.container.service import (RunnerContainerService) -import six - - -LOG = logging.getLogger(__name__) - - -class RunnerContainer(): - - def __init__(self): - LOG.info('Action RunnerContainer instantiated.') - self._pending = [] - - def _get_runner(self, runnertype_db): - """ - Load the module specified by the runnertype_db.runner_module field and - return an instance of the runner. - """ - - module_name = runnertype_db.runner_module - LOG.debug('Runner loading python module: %s', module_name) - try: - module = importlib.import_module(module_name, package=None) - except Exception as e: - LOG.exception('Failed to import module %s.', module_name) - raise ActionRunnerCreateError(e) - - LOG.debug('Instance of runner module: %s', module) - - runner = module.get_runner() - LOG.debug('Instance of runner: %s', runner) - return runner - - def dispatch(self, liveaction_db, runnertype_db, action_db, actionexec_db): - - runner_type = runnertype_db.name - - LOG.info('Dispatching runner for Live Action "%s"', liveaction_db) - LOG.debug(' liverunner_type: %s', runner_type) - LOG.debug(' RunnerType: %s', runnertype_db) - LOG.debug(' ActionExecution: %s', actionexec_db) - - # Get runner instance. - runner = self._get_runner(runnertype_db) - LOG.debug('Runner instance for RunnerType "%s" is: %s', runnertype_db.name, runner) - - # Invoke pre_run, run, post_run cycle. - result = self._do_run(liveaction_db.id, runner, runnertype_db, action_db, actionexec_db) - LOG.debug('runner do_run result: %s', result) - - actionsensor.post_trigger(actionexec_db) - LOG.audit('ActionExecution complete. liveaction_id="%s" resulted in ' - 'actionexecution_db="%s"', liveaction_db.id, actionexec_db) - - return result - - def _do_run(self, liveaction_id, runner, runnertype_db, action_db, actionexec_db): - # Runner parameters should use the defaults from the RunnerType object. - # The runner parameter defaults may be overridden by values provided in - # the Action and ActionExecution. - actionexec_runner_parameters, actionexec_action_parameters = RunnerContainer._split_params( - runnertype_db, action_db, actionexec_db) - action_action_parameters = dict(action_db.parameters) - - # Create runner parameter by merging default values with dynamic values - runner_parameters = {k: v['default'] if 'default' in v else None - for k, v in six.iteritems(runnertype_db.runner_parameters)} - - # pick overrides from action_action_parameters & actionexec_runner_parameters - for param in runner_parameters: - # values from actionexec_runner_parameters override action_action_parameters. - if param in actionexec_runner_parameters: - runner_parameters[param] = actionexec_runner_parameters[param] - continue - if param in action_action_parameters and 'default' in action_action_parameters[param]: - runner_parameters[param] = action_action_parameters[param]['default'] - - # Create action parameters by merging default values with dynamic values - action_parameters = {k: v['default'] if 'default' in v else None - for k, v in six.iteritems(action_db.parameters)} - - # pick overrides from actionexec_action_parameters - for param in action_parameters: - if param in actionexec_action_parameters: - action_parameters[param] = actionexec_action_parameters[param] - - runner.liveaction_id = liveaction_id - runner.container_service = RunnerContainerService(self) - runner.action = action_db - runner.action_name = action_db.name - runner.action_execution_id = str(actionexec_db.id) - runner.entry_point = action_db.entry_point - runner.runner_parameters = runner_parameters - runner.context = getattr(actionexec_db, 'context', dict()) - runner.callback = getattr(actionexec_db, 'callback', dict()) - - LOG.debug('Performing pre-run for runner: %s', runner) - runner.pre_run() - - LOG.debug('Performing run for runner: %s', runner) - run_result = runner.run(action_parameters) - LOG.debug('Result of run: %s', run_result) - - # Re-load Action Execution from DB: - actionexec_db = get_actionexec_by_id(actionexec_db.id) - - # TODO: Store payload when DB model can hold payload data - action_result = runner.container_service.get_result() - actionexec_status = runner.container_service.get_status() - LOG.debug('Result as reporter to container service: %s', action_result) - - if action_result is None: - # If the runner didn't set an exit code then the liveaction didn't complete. - # Therefore, the liveaction produced an error. - result = False - if not actionexec_status: - actionexec_status = ACTIONEXEC_STATUS_ERROR - runner.container_service.report_status(actionexec_status) - else: - # So long as the runner produced an exit code, we can assume that the - # Live Action ran to completion. - result = True - actionexec_db.result = action_result - if not actionexec_status: - actionexec_status = ACTIONEXEC_STATUS_COMPLETE - runner.container_service.report_status(actionexec_status) - - # Push result data and updated status to ActionExecution DB - update_actionexecution_status(actionexec_status, actionexec_db=actionexec_db) - - LOG.debug('Performing post_run for runner: %s', runner) - runner.post_run() - runner.container_service = None - - return result - - @staticmethod - def _split_params(runnertype_db, action_db, actionexec_db): - return ( - {param: actionexec_db.parameters[param] - for param in runnertype_db.runner_parameters if param in actionexec_db.parameters}, - - {param: actionexec_db.parameters[param] - for param in action_db.parameters if param in actionexec_db.parameters} - ) - - -def get_runner_container(): - return RunnerContainer() diff --git a/st2actions/st2actions/container/base.py b/st2actions/st2actions/container/base.py new file mode 100644 index 0000000000..15caccf896 --- /dev/null +++ b/st2actions/st2actions/container/base.py @@ -0,0 +1,159 @@ +import importlib + +from st2common import log as logging +from st2common.exceptions.actionrunner import ActionRunnerCreateError +from st2common.models.api.action import (ACTIONEXEC_STATUS_COMPLETE, + ACTIONEXEC_STATUS_ERROR) + +from st2common.util.action_db import (update_actionexecution_status, get_actionexec_by_id) + +from st2actions.container import actionsensor +from st2actions.container.service import (RunnerContainerService) +import six + + +LOG = logging.getLogger(__name__) + + +class RunnerContainer(): + + def __init__(self): + LOG.info('Action RunnerContainer instantiated.') + self._pending = [] + + def _get_runner(self, runnertype_db): + """ + Load the module specified by the runnertype_db.runner_module field and + return an instance of the runner. + """ + + module_name = runnertype_db.runner_module + LOG.debug('Runner loading python module: %s', module_name) + try: + module = importlib.import_module(module_name, package=None) + except Exception as e: + LOG.exception('Failed to import module %s.', module_name) + raise ActionRunnerCreateError(e) + + LOG.debug('Instance of runner module: %s', module) + + runner = module.get_runner() + LOG.debug('Instance of runner: %s', runner) + return runner + + def dispatch(self, liveaction_db, runnertype_db, action_db, actionexec_db): + + runner_type = runnertype_db.name + + LOG.info('Dispatching runner for Live Action "%s"', liveaction_db) + LOG.debug(' liverunner_type: %s', runner_type) + LOG.debug(' RunnerType: %s', runnertype_db) + LOG.debug(' ActionExecution: %s', actionexec_db) + + # Get runner instance. + runner = self._get_runner(runnertype_db) + LOG.debug('Runner instance for RunnerType "%s" is: %s', runnertype_db.name, runner) + + # Invoke pre_run, run, post_run cycle. + result = self._do_run(liveaction_db.id, runner, runnertype_db, action_db, actionexec_db) + LOG.debug('runner do_run result: %s', result) + + actionsensor.post_trigger(actionexec_db) + LOG.audit('ActionExecution complete. liveaction_id="%s" resulted in ' + 'actionexecution_db="%s"', liveaction_db.id, actionexec_db) + + return result + + def _do_run(self, liveaction_id, runner, runnertype_db, action_db, actionexec_db): + # Runner parameters should use the defaults from the RunnerType object. + # The runner parameter defaults may be overridden by values provided in + # the Action and ActionExecution. + actionexec_runner_parameters, actionexec_action_parameters = RunnerContainer._split_params( + runnertype_db, action_db, actionexec_db) + action_action_parameters = dict(action_db.parameters) + + # Create runner parameter by merging default values with dynamic values + runner_parameters = {k: v['default'] if 'default' in v else None + for k, v in six.iteritems(runnertype_db.runner_parameters)} + + # pick overrides from action_action_parameters & actionexec_runner_parameters + for param in runner_parameters: + # values from actionexec_runner_parameters override action_action_parameters. + if param in actionexec_runner_parameters: + runner_parameters[param] = actionexec_runner_parameters[param] + continue + if param in action_action_parameters and 'default' in action_action_parameters[param]: + runner_parameters[param] = action_action_parameters[param]['default'] + + # Create action parameters by merging default values with dynamic values + action_parameters = {k: v['default'] if 'default' in v else None + for k, v in six.iteritems(action_db.parameters)} + + # pick overrides from actionexec_action_parameters + for param in action_parameters: + if param in actionexec_action_parameters: + action_parameters[param] = actionexec_action_parameters[param] + + runner.liveaction_id = liveaction_id + runner.container_service = RunnerContainerService(self) + runner.action = action_db + runner.action_name = action_db.name + runner.action_execution_id = str(actionexec_db.id) + runner.entry_point = action_db.entry_point + runner.runner_parameters = runner_parameters + runner.context = getattr(actionexec_db, 'context', dict()) + runner.callback = getattr(actionexec_db, 'callback', dict()) + + LOG.debug('Performing pre-run for runner: %s', runner) + runner.pre_run() + + LOG.debug('Performing run for runner: %s', runner) + run_result = runner.run(action_parameters) + LOG.debug('Result of run: %s', run_result) + + # Re-load Action Execution from DB: + actionexec_db = get_actionexec_by_id(actionexec_db.id) + + # TODO: Store payload when DB model can hold payload data + action_result = runner.container_service.get_result() + actionexec_status = runner.container_service.get_status() + LOG.debug('Result as reporter to container service: %s', action_result) + + if action_result is None: + # If the runner didn't set an exit code then the liveaction didn't complete. + # Therefore, the liveaction produced an error. + result = False + if not actionexec_status: + actionexec_status = ACTIONEXEC_STATUS_ERROR + runner.container_service.report_status(actionexec_status) + else: + # So long as the runner produced an exit code, we can assume that the + # Live Action ran to completion. + result = True + actionexec_db.result = action_result + if not actionexec_status: + actionexec_status = ACTIONEXEC_STATUS_COMPLETE + runner.container_service.report_status(actionexec_status) + + # Push result data and updated status to ActionExecution DB + update_actionexecution_status(actionexec_status, actionexec_db=actionexec_db) + + LOG.debug('Performing post_run for runner: %s', runner) + runner.post_run() + runner.container_service = None + + return result + + @staticmethod + def _split_params(runnertype_db, action_db, actionexec_db): + return ( + {param: actionexec_db.parameters[param] + for param in runnertype_db.runner_parameters if param in actionexec_db.parameters}, + + {param: actionexec_db.parameters[param] + for param in action_db.parameters if param in actionexec_db.parameters} + ) + + +def get_runner_container(): + return RunnerContainer() diff --git a/st2actions/st2actions/controllers/liveactions.py b/st2actions/st2actions/controllers/liveactions.py index 820bd76835..fb1d004fc4 100644 --- a/st2actions/st2actions/controllers/liveactions.py +++ b/st2actions/st2actions/controllers/liveactions.py @@ -1,5 +1,5 @@ +import st2actions.container.base as runner_container from st2common import log as logging - from st2common.exceptions.db import StackStormDBObjectNotFoundError from st2common.exceptions.actionrunner import ActionRunnerPreRunError, ActionRunnerException from st2common.models.api.action import ACTIONEXEC_STATUS_RUNNING, ACTIONEXEC_STATUS_ERROR @@ -8,8 +8,6 @@ from st2common.util.action_db import (get_actionexec_by_id, get_action_by_dict, update_actionexecution_status, get_runnertype_by_name) -from st2actions import container - LOG = logging.getLogger(__name__) @@ -17,7 +15,7 @@ class LiveActionsController(): def __init__(self): - self.container = container.get_runner_container() + self.container = runner_container.get_runner_container() def execute_action(self, actionexecution): """ diff --git a/st2actions/tests/config.py b/st2actions/tests/config.py index d191a478f5..05f0bc6478 100644 --- a/st2actions/tests/config.py +++ b/st2actions/tests/config.py @@ -13,25 +13,34 @@ def __register_opts(opts, group=None): def __setup_config_opts(): - api_opts = [ - cfg.StrOpt('host', default='0.0.0.0', help='action API server host'), - cfg.IntOpt('port', default=9501, help='action API server port') + action_sensor_opts = [ + cfg.BoolOpt('enable', default=True, + help='Whether to enable or disable the ability to post a trigger on action.'), + cfg.StrOpt('triggers_base_url', default='http://localhost:9101/triggertypes/', + help='URL for action sensor to post TriggerType.'), + cfg.StrOpt('webhook_sensor_base_url', default='http://localhost:6000/webhooks/st2/', + help='URL for action sensor to post TriggerInstances.'), + cfg.IntOpt('request_timeout', default=1, + help='Timeout value of all httprequests made by action sensor.'), + cfg.IntOpt('max_attempts', default=10, + help='No. of times to retry registration.'), + cfg.IntOpt('retry_wait', default=1, + help='Amount of time to wait prior to retrying a request.') ] - __register_opts(api_opts, group='action_controller_api') - - # note : template_path value only works if started from the top-level of the codebase. Brittle! - pecan_opts = [ - cfg.StrOpt('root', - default='st2actionrunnercontroller.controllers.root.RootController', - help='Pecan root controller'), - cfg.StrOpt('template_path', default=('%(confdir)s/st2actionrunnercontroller/' - 'st2actionrunnercontroller/templates')), - cfg.ListOpt('modules', default=['st2actionrunnercontroller']), - cfg.BoolOpt('debug', default=True), - cfg.BoolOpt('auth_enable', default=True), - cfg.DictOpt('errors', default={404: '/error/404', '__force_dict__': True}) + CONF.register_opts(action_sensor_opts, group='action_sensor') + + ssh_runner_opts = [ + cfg.StrOpt('user', + default='stanley', + help='User for running remote tasks via the FabricRunner.'), + cfg.StrOpt('ssh_key_file', + default='/home/vagrant/.ssh/stanley_rsa', + help='SSH private key for running remote tasks via the FabricRunner.'), + cfg.StrOpt('remote_dir', + default='/tmp', + help='Location of the script on the remote filesystem.'), ] - __register_opts(pecan_opts, group='action_runner_controller_pecan') + CONF.register_opts(ssh_runner_opts, group='ssh_runner') def parse_args(): diff --git a/st2actions/tests/fixtures/packs/wolfpack/actions/action_1.json b/st2actions/tests/fixtures/packs/wolfpack/actions/action_1.json new file mode 100644 index 0000000000..653a2d10fe --- /dev/null +++ b/st2actions/tests/fixtures/packs/wolfpack/actions/action_1.json @@ -0,0 +1,12 @@ +{ + "name": "st2.dummy.action1", + "description": "test description", + "enabled": true, + "content_pack": "wolfpack", + "entry_point": "/tmp/test/action1.sh", + "runner_type": "run-local", + "parameters": { + "a": {"type": "string", "default": "A1"}, + "b": {"type": "string", "default": "B1"} + } +} diff --git a/st2actions/tests/fixtures/packs/wolfpack/actions/action_2_bad_json.json b/st2actions/tests/fixtures/packs/wolfpack/actions/action_2_bad_json.json new file mode 100644 index 0000000000..d620a2caa4 --- /dev/null +++ b/st2actions/tests/fixtures/packs/wolfpack/actions/action_2_bad_json.json @@ -0,0 +1,12 @@ +{ + "name": "st2.dummy.action2, + "description": "test description", + "enabled": true, + "content_pack": "wolfpack", + "entry_point": "/tmp/test/action1.sh", + "runner_type": "run-local", + "parameters": { + "a": {"type": "string", "default": "A1"}, + "b": {"type": "string", "default": "B1"} + } +} diff --git a/st2actions/tests/fixtures/packs/wolfpack/actions/action_3_content_pack_missing.json b/st2actions/tests/fixtures/packs/wolfpack/actions/action_3_content_pack_missing.json new file mode 100644 index 0000000000..52399026d5 --- /dev/null +++ b/st2actions/tests/fixtures/packs/wolfpack/actions/action_3_content_pack_missing.json @@ -0,0 +1,11 @@ +{ + "name": "st2.dummy.action3", + "description": "test description", + "enabled": true, + "entry_point": "/tmp/test/action1.sh", + "runner_type": "run-local", + "parameters": { + "a": {"type": "string", "default": "A1"}, + "b": {"type": "string", "default": "B1"} + } +} diff --git a/st2actions/tests/test_actions_registrar.py b/st2actions/tests/test_actions_registrar.py new file mode 100644 index 0000000000..48bc84c715 --- /dev/null +++ b/st2actions/tests/test_actions_registrar.py @@ -0,0 +1,58 @@ +try: + import simplejson as json +except ImportError: + import json +import os + +import mock + +import st2actions.bootstrap.actionsregistrar as actions_registrar +from st2common.persistence.action import Action +from st2common.models.db.action import RunnerTypeDB +from st2tests.base import DbTestCase + +MOCK_RUNNER_TYPE_DB = RunnerTypeDB() +MOCK_RUNNER_TYPE_DB.name = 'run-local' +MOCK_RUNNER_TYPE_DB.runner_module = 'st2.runners.local' + + +class ActionsRegistrarTest(DbTestCase): + @mock.patch.object(actions_registrar.ActionsRegistrar, '_has_valid_runner_type', + mock.MagicMock(return_value=(True, MOCK_RUNNER_TYPE_DB))) + def test_register_all_actions(self): + try: + content_packs_base_path = os.path.join( + os.path.dirname(os.path.realpath(__file__)), 'fixtures/packs/') + all_actions_in_db = Action.get_all() + actions_registrar.register_actions(content_packs_base_path=content_packs_base_path) + all_actions_in_db = Action.get_all() + self.assertTrue(len(all_actions_in_db) > 0) + except Exception as e: + print(str(e)) + self.fail('All actions must be registered without exceptions.') + + def test_register_actions_from_bad_pack(self): + content_packs_base_path = os.path.join( + os.path.dirname(os.path.realpath(__file__)), 'fixtures/badpacks/') + try: + actions_registrar.register_actions(content_packs_base_path=content_packs_base_path) + self.fail('Should have thrown.') + except: + pass + + @mock.patch.object(actions_registrar.ActionsRegistrar, '_has_valid_runner_type', + mock.MagicMock(return_value=(True, MOCK_RUNNER_TYPE_DB))) + def test_content_pack_name_missing(self): + registrar = actions_registrar.ActionsRegistrar() + action_file = os.path.join(os.path.dirname( + os.path.realpath(__file__)), + 'fixtures/packs/wolfpack/actions/action_3_content_pack_missing.json') + registrar._register_action('dummy', action_file) + action_name = None + with open(action_file, 'r') as fd: + content = json.load(fd) + action_name = str(content['name']) + action_db = Action.get_by_name(action_name) + self.assertEquals(action_db.content_pack, 'dummy', 'Content pack must be ' + + 'set to dummy') + Action.delete(action_db) diff --git a/st2actions/tests/test_runner_container.py b/st2actions/tests/test_runner_container.py new file mode 100644 index 0000000000..588639a523 --- /dev/null +++ b/st2actions/tests/test_runner_container.py @@ -0,0 +1,20 @@ +from st2common.models.db.action import RunnerTypeDB +from st2tests.base import DbTestCase +import tests.config as tests_config +tests_config.parse_args() + +# XXX: There is dependency on config being setup before importing +# RunnerContainer. Do not move this until you fix config +# dependencies. +from st2actions.container.base import RunnerContainer + + +class RunnerContainerTest(DbTestCase): + + def test_get_runner_module(self): + runner_type_db = RunnerTypeDB() + runner_type_db.name = 'run-local' + runner_type_db.runner_module = 'st2actions.runners.fabricrunner' + runner_container = RunnerContainer() + runner = runner_container._get_runner(runner_type_db) + self.assertTrue(runner is not None, 'Runner must be valid.') diff --git a/st2actions/tests/test_runner_container_service.py b/st2actions/tests/test_runner_container_service.py new file mode 100644 index 0000000000..a771a3d042 --- /dev/null +++ b/st2actions/tests/test_runner_container_service.py @@ -0,0 +1,39 @@ +try: + import simplejson as json +except ImportError: + import json + +import os + +from oslo.config import cfg +import unittest2 + +from st2actions.container.service import RunnerContainerService + + +class RunnerContainerServiceTest(unittest2.TestCase): + + def test_get_entry_point_absolute_path(self): + service = RunnerContainerService(None) + orig_path = cfg.CONF.content.content_packs_base_path + cfg.CONF.content.content_packs_base_path = '/tests/packs' + acutal_path = service.get_entry_point_abs_path(pack='foo', entry_point='/foo/bar.py') + self.assertEquals(acutal_path, '/foo/bar.py', 'Entry point path doesn\'t match.') + cfg.CONF.content.content_packs_base_path = orig_path + + def test_get_entry_point_relative_path(self): + service = RunnerContainerService(None) + orig_path = cfg.CONF.content.content_packs_base_path + cfg.CONF.content.content_packs_base_path = '/tests/packs' + acutal_path = service.get_entry_point_abs_path(pack='foo', entry_point='foo/bar.py') + expected_path = os.path.join(cfg.CONF.content.content_packs_base_path, 'foo', 'actions', + 'foo/bar.py') + self.assertEquals(acutal_path, expected_path, 'Entry point path doesn\'t match.') + cfg.CONF.content.content_packs_base_path = orig_path + + def test_report_result_json(self): + service = RunnerContainerService(None) + result = '["foo", {"bar": ["baz", null, 1.0, 2]}]' + service.report_result(result) + self.assertEquals(json.dumps(service.get_result()), result, + 'JON results aren\'t handled right') diff --git a/st2common/st2common/content/bootstrap.py b/st2common/st2common/content/bootstrap.py index a6cc9234e9..ed65e076c8 100644 --- a/st2common/st2common/content/bootstrap.py +++ b/st2common/st2common/content/bootstrap.py @@ -8,7 +8,7 @@ import st2common.config as config from st2common.models.db import db_setup from st2common.models.db import db_teardown -import st2reactor.bootstrap.registrar as rules_registrar +import st2reactor.bootstrap.rulesregistrar as rules_registrar LOG = logging.getLogger('st2common.content.bootstrap') diff --git a/st2reactor/st2reactor/bootstrap/registrar.py b/st2reactor/st2reactor/bootstrap/rulesregistrar.py similarity index 90% rename from st2reactor/st2reactor/bootstrap/registrar.py rename to st2reactor/st2reactor/bootstrap/rulesregistrar.py index 959409e83d..3081ffbab2 100644 --- a/st2reactor/st2reactor/bootstrap/registrar.py +++ b/st2reactor/st2reactor/bootstrap/rulesregistrar.py @@ -63,5 +63,7 @@ def _register_rules_from_packs(base_dir): LOG.exception('Failed registering all rules from pack: %s', rules_dir) -def register_rules(): - return _register_rules_from_packs(cfg.CONF.content.content_packs_base_path) +def register_rules(content_packs_base_path=None): + if not content_packs_base_path: + content_packs_base_path = cfg.CONF.content.content_packs_base_path + return _register_rules_from_packs(content_packs_base_path) diff --git a/st2tests/st2tests/base.py b/st2tests/st2tests/base.py index d56e6ad965..9aa5ef5154 100644 --- a/st2tests/st2tests/base.py +++ b/st2tests/st2tests/base.py @@ -49,6 +49,8 @@ def setUpClass(cls): st2tests.config.parse_args() DbTestCase.db_connection = db_setup(cfg.CONF.database.db_name, cfg.CONF.database.host, cfg.CONF.database.port) + cls.drop_collections() + DbTestCase.db_connection.drop_database(cfg.CONF.database.db_name) @classmethod def tearDownClass(cls):