Repository: StackStorm/st2

Issue:
Unable to use encrypted values with sensors (sensor_wrapper)
We would like to be able to use encrypted datastore values in our aws sensor but there is no `decrypt=True` option like there is in the python action runner.

https://github.com/StackStorm/st2/blob/373ab7027304aa9f8f1a0a553185c9adc04330ea/st2reactor/st2reactor/container/sensor_wrapper.py#L149




Discussion:
Will track this at https://github.com/StackStorm/st2/issues/4057

Resolving patch:
diff --git a/contrib/runners/python_runner/python_runner/python_action_wrapper.py b/contrib/runners/python_runner/python_runner/python_action_wrapper.py
--- a/contrib/runners/python_runner/python_runner/python_action_wrapper.py
+++ b/contrib/runners/python_runner/python_runner/python_action_wrapper.py
@@ -113,17 +113,18 @@ def get_user_info(self):
     ##################################
 
     def list_values(self, local=True, prefix=None):
-        return self.datastore_service.list_values(local, prefix)
+        return self.datastore_service.list_values(local=local, prefix=prefix)
 
     def get_value(self, name, local=True, scope=SYSTEM_SCOPE, decrypt=False):
-        return self.datastore_service.get_value(name, local, scope=scope, decrypt=decrypt)
+        return self.datastore_service.get_value(name=name, local=local, scope=scope,
+                                                decrypt=decrypt)
 
     def set_value(self, name, value, ttl=None, local=True, scope=SYSTEM_SCOPE, encrypt=False):
-        return self.datastore_service.set_value(name, value, ttl, local, scope=scope,
-                                                encrypt=encrypt)
+        return self.datastore_service.set_value(name=name, value=value, ttl=ttl, local=local,
+                                                scope=scope, encrypt=encrypt)
 
     def delete_value(self, name, local=True, scope=SYSTEM_SCOPE):
-        return self.datastore_service.delete_value(name, local)
+        return self.datastore_service.delete_value(name=name, local=local, scope=scope)
 
 
 class PythonActionWrapper(object):
diff --git a/st2reactor/st2reactor/container/sensor_wrapper.py b/st2reactor/st2reactor/container/sensor_wrapper.py
--- a/st2reactor/st2reactor/container/sensor_wrapper.py
+++ b/st2reactor/st2reactor/container/sensor_wrapper.py
@@ -24,6 +24,7 @@
 from jsonschema import ValidationError
 
 from st2common import log as logging
+from st2common.constants.keyvalue import SYSTEM_SCOPE
 from st2common.logging.misc import set_log_level_for_all_loggers
 from st2common.models.api.trace import TraceContext
 from st2common.models.api.trigger import TriggerAPI
@@ -84,7 +85,7 @@ def get_logger(self, name):
     ##################################
 
     def get_user_info(self):
-        return self.datastore_service.get_user_info()
+        return self._datastore_service.get_user_info()
 
     ##################################
     # Sensor related methods
@@ -145,16 +146,18 @@ def dispatch_with_context(self, trigger, payload=None, trace_context=None):
     ##################################
 
     def list_values(self, local=True, prefix=None):
-        return self._datastore_service.list_values(local, prefix)
+        return self.datastore_service.list_values(local=local, prefix=prefix)
 
-    def get_value(self, name, local=True):
-        return self._datastore_service.get_value(name, local)
+    def get_value(self, name, local=True, scope=SYSTEM_SCOPE, decrypt=False):
+        return self.datastore_service.get_value(name=name, local=local, scope=scope,
+                                                decrypt=decrypt)
 
-    def set_value(self, name, value, ttl=None, local=True):
-        return self._datastore_service.set_value(name, value, ttl, local)
+    def set_value(self, name, value, ttl=None, local=True, scope=SYSTEM_SCOPE, encrypt=False):
+        return self.datastore_service.set_value(name=name, value=value, ttl=ttl, local=local,
+                                                scope=scope, encrypt=encrypt)
 
-    def delete_value(self, name, local=True):
-        return self._datastore_service.delete_value(name, local)
+    def delete_value(self, name, local=True, scope=SYSTEM_SCOPE):
+        return self.datastore_service.delete_value(name=name, local=local, scope=scope)
 
 
 class SensorWrapper(object):


Test patch:
diff --git a/st2reactor/tests/unit/test_sensor_service.py b/st2reactor/tests/unit/test_sensor_service.py
--- a/st2reactor/tests/unit/test_sensor_service.py
+++ b/st2reactor/tests/unit/test_sensor_service.py
@@ -5,6 +5,12 @@
 from oslo_config import cfg
 
 from st2reactor.container.sensor_wrapper import SensorService
+from st2common.constants.keyvalue import SYSTEM_SCOPE
+from st2common.constants.keyvalue import USER_SCOPE
+
+__all__ = [
+    'SensorServiceTestCase'
+]
 
 # This trigger has schema that uses all property types
 TEST_SCHEMA = {
@@ -202,3 +208,41 @@ def test_dispatch_trigger__type_not_in_db_should_still_dispatch(self):
 
         self.sensor_service.dispatch('not-in-database-ref', {})
         self.assertEqual(self._dispatched_count, 1)
+
+    def test_datastore_methods(self):
+        self.sensor_service._datastore_service = mock.Mock()
+
+        # Verify methods take encrypt, decrypt and scope arguments
+        self.sensor_service.get_value(name='foo1', scope=SYSTEM_SCOPE, decrypt=True)
+
+        call_kwargs = self.sensor_service.datastore_service.get_value.call_args[1]
+        expected_kwargs = {
+            'name': 'foo1',
+            'local': True,
+            'scope': SYSTEM_SCOPE,
+            'decrypt': True
+        }
+        self.assertEqual(call_kwargs, expected_kwargs)
+
+        self.sensor_service.set_value(name='foo2', value='bar', scope=USER_SCOPE, encrypt=True)
+
+        call_kwargs = self.sensor_service.datastore_service.set_value.call_args[1]
+        expected_kwargs = {
+            'name': 'foo2',
+            'value': 'bar',
+            'ttl': None,
+            'local': True,
+            'scope': USER_SCOPE,
+            'encrypt': True
+        }
+        self.assertEqual(call_kwargs, expected_kwargs)
+
+        self.sensor_service.delete_value(name='foo3', scope=USER_SCOPE)
+
+        call_kwargs = self.sensor_service.datastore_service.delete_value.call_args[1]
+        expected_kwargs = {
+            'name': 'foo3',
+            'local': True,
+            'scope': USER_SCOPE
+        }
+        self.assertEqual(call_kwargs, expected_kwargs)