diff --git a/contrib/linux/sensors/file_watch_sensor.py b/contrib/linux/sensors/file_watch_sensor.py index d0a74c71a9..3768e7f4a6 100644 --- a/contrib/linux/sensors/file_watch_sensor.py +++ b/contrib/linux/sensors/file_watch_sensor.py @@ -1,5 +1,7 @@ import os +import eventlet + from logshipper.tail import Tail from st2reactor.sensor.base import Sensor @@ -42,6 +44,9 @@ def add_trigger(self, trigger): if not self._trigger: raise Exception('Trigger %s did not contain a ref.' % trigger) + # Wait a bit to avoid initialization race in logshipper library + eventlet.sleep(1.0) + self._tail.add_file(filename=file_path) self._logger.info('Added file "%s"' % (file_path)) diff --git a/st2common/st2common/services/trigger_dispatcher.py b/st2common/st2common/services/trigger_dispatcher.py index 14850a595d..92a46825ae 100644 --- a/st2common/st2common/services/trigger_dispatcher.py +++ b/st2common/st2common/services/trigger_dispatcher.py @@ -40,7 +40,7 @@ def dispatch(self, trigger, payload=None, trace_tag=None, throw_on_validation_er """ Method which dispatches the trigger. - :param trigger: Reference to the TriggerType (.). + :param trigger: Reference to the TriggerTypeDB (.) or TriggerDB object. :type trigger: ``str`` :param payload: Trigger payload. @@ -64,7 +64,7 @@ def dispatch_with_context(self, trigger, payload=None, trace_context=None, """ Method which dispatches the trigger. - :param trigger: Reference to the TriggerType (.). + :param trigger: Reference to the TriggerTypeDB (.) or TriggerDB object. :type trigger: ``str`` :param payload: Trigger payload. diff --git a/st2common/st2common/validators/api/reactor.py b/st2common/st2common/validators/api/reactor.py index 0e0f4397bc..35cbcb9d69 100644 --- a/st2common/st2common/validators/api/reactor.py +++ b/st2common/st2common/validators/api/reactor.py @@ -14,8 +14,9 @@ # limitations under the License. from __future__ import absolute_import -import six +import six +import uuid from oslo_config import cfg from apscheduler.triggers.cron import CronTrigger @@ -113,7 +114,7 @@ def validate_trigger_payload(trigger_type_ref, payload, throw_on_inexistent_trig """ This function validates trigger payload parameters for system and user-defined triggers. - :param trigger_type_ref: Reference of a trigger type or a trigger dictionary object. + :param trigger_type_ref: Reference of a trigger type / trigger / trigger dictionary object. :type trigger_type_ref: ``str`` :param payload: Trigger payload. @@ -144,7 +145,23 @@ def validate_trigger_payload(trigger_type_ref, payload, throw_on_inexistent_trig # System trigger payload_schema = SYSTEM_TRIGGER_TYPES[trigger_type_ref]['payload_schema'] else: + # We assume Trigger ref and not TriggerType ref is passed in if second + # part (trigger name) is a valid UUID version 4 + try: + trigger_uuid = uuid.UUID(trigger_type_ref.split('.')[-1]) + except ValueError: + is_trigger_db = False + else: + is_trigger_db = (trigger_uuid.version == 4) + + if is_trigger_db: + trigger_db = triggers.get_trigger_db_by_ref(trigger_type_ref) + + if trigger_db: + trigger_type_ref = trigger_db.type + trigger_type_db = triggers.get_trigger_type_db(trigger_type_ref) + if not trigger_type_db: # Trigger doesn't exist in the database if throw_on_inexistent_trigger: diff --git a/st2reactor/tests/unit/test_sensor_service.py b/st2reactor/tests/unit/test_sensor_service.py index d3058b77f0..214b28dc69 100644 --- a/st2reactor/tests/unit/test_sensor_service.py +++ b/st2reactor/tests/unit/test_sensor_service.py @@ -28,9 +28,14 @@ } -class TriggerTypeMock(object): - def __init__(self, schema={}): - self.payload_schema = schema +class TriggerTypeDBMock(object): + def __init__(self, schema=None): + self.payload_schema = schema or {} + + +class TriggerDBMock(object): + def __init__(self, type=None): + self.type = type class SensorServiceTestCase(unittest2.TestCase): @@ -54,7 +59,7 @@ def tearDown(self): cfg.CONF.system.validate_trigger_payload = self.validate_trigger_payload @mock.patch('st2common.services.triggers.get_trigger_type_db', - mock.MagicMock(return_value=TriggerTypeMock(TEST_SCHEMA))) + mock.MagicMock(return_value=TriggerTypeDBMock(TEST_SCHEMA))) def test_dispatch_success_valid_payload_validation_enabled(self): cfg.CONF.system.validate_trigger_payload = True @@ -75,7 +80,33 @@ def test_dispatch_success_valid_payload_validation_enabled(self): self.assertEqual(self._dispatched_count, 1) @mock.patch('st2common.services.triggers.get_trigger_type_db', - mock.MagicMock(return_value=TriggerTypeMock(TEST_SCHEMA))) + mock.MagicMock(return_value=TriggerTypeDBMock(TEST_SCHEMA))) + @mock.patch('st2common.services.triggers.get_trigger_db_by_ref', + mock.MagicMock(return_value=TriggerDBMock(type='trigger-type-ref'))) + def test_dispatch_success_with_validation_enabled_trigger_reference(self): + # Test a scenario where a Trigger ref and not TriggerType ref is provided + cfg.CONF.system.validate_trigger_payload = True + + # define a valid payload + payload = { + 'name': 'John Doe', + 'age': 25, + 'career': ['foo, Inc.', 'bar, Inc.'], + 'married': True, + 'awards': {'2016': ['hoge prize', 'fuga prize']}, + 'income': 50000 + } + + self.assertEqual(self._dispatched_count, 0) + + # dispatching a trigger + self.sensor_service.dispatch('pack.86582f21-1fbc-44ea-88cb-0cd2b610e93b', payload) + + # This assumed that the target tirgger dispatched + self.assertEqual(self._dispatched_count, 1) + + @mock.patch('st2common.services.triggers.get_trigger_type_db', + mock.MagicMock(return_value=TriggerTypeDBMock(TEST_SCHEMA))) def test_dispatch_success_with_validation_disabled_and_invalid_payload(self): """ Tests that an invalid payload still results in dispatch success with default config @@ -108,7 +139,7 @@ def test_dispatch_success_with_validation_disabled_and_invalid_payload(self): self.assertEqual(self._dispatched_count, 1) @mock.patch('st2common.services.triggers.get_trigger_type_db', - mock.MagicMock(return_value=TriggerTypeMock(TEST_SCHEMA))) + mock.MagicMock(return_value=TriggerTypeDBMock(TEST_SCHEMA))) def test_dispatch_failure_caused_by_incorrect_type(self): # define a invalid payload (the type of 'age' is incorrect) payload = { @@ -131,7 +162,7 @@ def test_dispatch_failure_caused_by_incorrect_type(self): self.assertEqual(self._dispatched_count, 1) @mock.patch('st2common.services.triggers.get_trigger_type_db', - mock.MagicMock(return_value=TriggerTypeMock(TEST_SCHEMA))) + mock.MagicMock(return_value=TriggerTypeDBMock(TEST_SCHEMA))) def test_dispatch_failure_caused_by_lack_of_required_parameter(self): # define a invalid payload (lack of required property) payload = { @@ -149,7 +180,7 @@ def test_dispatch_failure_caused_by_lack_of_required_parameter(self): self.assertEqual(self._dispatched_count, 1) @mock.patch('st2common.services.triggers.get_trigger_type_db', - mock.MagicMock(return_value=TriggerTypeMock(TEST_SCHEMA))) + mock.MagicMock(return_value=TriggerTypeDBMock(TEST_SCHEMA))) def test_dispatch_failure_caused_by_extra_parameter(self): # define a invalid payload ('hobby' is extra) payload = { @@ -162,7 +193,7 @@ def test_dispatch_failure_caused_by_extra_parameter(self): self.assertEqual(self._dispatched_count, 0) @mock.patch('st2common.services.triggers.get_trigger_type_db', - mock.MagicMock(return_value=TriggerTypeMock(TEST_SCHEMA))) + mock.MagicMock(return_value=TriggerTypeDBMock(TEST_SCHEMA))) def test_dispatch_success_with_multiple_type_value(self): payload = { 'name': 'John Doe', @@ -180,7 +211,7 @@ def test_dispatch_success_with_multiple_type_value(self): self.assertEqual(self._dispatched_count, 2) @mock.patch('st2common.services.triggers.get_trigger_type_db', - mock.MagicMock(return_value=TriggerTypeMock(TEST_SCHEMA))) + mock.MagicMock(return_value=TriggerTypeDBMock(TEST_SCHEMA))) def test_dispatch_success_with_null(self): payload = { 'name': 'John Doe', @@ -193,7 +224,7 @@ def test_dispatch_success_with_null(self): self.assertEqual(self._dispatched_count, 1) @mock.patch('st2common.services.triggers.get_trigger_type_db', - mock.MagicMock(return_value=TriggerTypeMock())) + mock.MagicMock(return_value=TriggerTypeDBMock())) def test_dispatch_success_without_payload_schema(self): # the case trigger has no property self.sensor_service.dispatch('trigger-name', {})