diff --git a/doc/source/draft/appdev-guide/murano_pl/actions.rst b/doc/source/draft/appdev-guide/murano_pl/actions.rst index 07ac0495..a133de77 100644 --- a/doc/source/draft/appdev-guide/murano_pl/actions.rst +++ b/doc/source/draft/appdev-guide/murano_pl/actions.rst @@ -32,9 +32,13 @@ can be tracked. .. note:: Now murano doesn't support big files download during action execution. This is - because action results are stored in murano database and are limited by approximately 10kb size. + because action results are stored in murano database and are limited by + approximately 10kb size. -To mark a method as an action, use ``Usage: Action``. +To mark a method as an action, use ``Scope: Public`` or ``Usage: Action``. +The latter option is deprecated for the package format versions > 1.3 and +occasionally will be no longer supported. Also, you cannot use both +``Usage: Action`` and ``Scope: Session`` in one method. The following example shows an action that returns an archive with a configuration file: @@ -42,7 +46,7 @@ configuration file: :: exportConfig: - Usage: Action + Scope: Public Body: - $._environment.reporter.report($this, 'Action exportConfig called') - $resources: new(sys:Resources) diff --git a/doc/source/draft/appdev-guide/murano_pl/class_templ.rst b/doc/source/draft/appdev-guide/murano_pl/class_templ.rst index 6f23d937..310d0ce3 100644 --- a/doc/source/draft/appdev-guide/murano_pl/class_templ.rst +++ b/doc/source/draft/appdev-guide/murano_pl/class_templ.rst @@ -282,7 +282,7 @@ Methods are defined in the Workflow section of the class using the following template:: methodName: - Usage: Action + Scope: Public Arguments: - list - of @@ -292,7 +292,7 @@ following template:: - of - instructions -Action is an optional parameter that specifies methods to be executed +Public is an optional parameter that specifies methods to be executed by direct triggering after deployment. Arguments are optional too, and are declared using the same syntax @@ -344,10 +344,28 @@ access it. The following usages are available: * - | Action - | Method can be invoked from outside (using Murano API). + This option is deprecated for the package format versions > 1.3 in + favor of ``Scope: Public`` and occasionally will be no longer + supported. See :ref:`actions` for details. -The usage attribute is optional and can be omitted (which implies ``Runtime``). +The ``Usage`` attribute is optional and can be omitted (which implies +``Runtime``). +Method scope +++++++++++++ + +The ``Scope`` attribute declares method visibility. It can have two possible +values: + +* `Session` - regular method that is accessible from anywhere in the current + execution session. This is the default if the attribute is omitted; + +* `Public` - accessible anywhere, both within the session and from + outside through the API call. + +The ``Scope`` attribute is optional and can be omitted (which implies +``Session``). Expressions +++++++++++ diff --git a/doc/source/draft/appdev-guide/murano_pl/metadata.rst b/doc/source/draft/appdev-guide/murano_pl/metadata.rst index b60f8e9c..cab86b6b 100644 --- a/doc/source/draft/appdev-guide/murano_pl/metadata.rst +++ b/doc/source/draft/appdev-guide/murano_pl/metadata.rst @@ -244,7 +244,7 @@ The following example shows how applications can access attached metadata: Methods: sampleAction: - Usage: Action + Scope: Public Body: - $._environment.reporter.report($this, typeinfo($).meta. where($ is MetaClassOne).single().description) @@ -295,7 +295,7 @@ meta-classes: Methods: importantAction: - Usage: Action + Scope: Public Meta: m:CallerMustBeAdmin diff --git a/doc/source/draft/appdev-guide/murano_pl/versioning.rst b/doc/source/draft/appdev-guide/murano_pl/versioning.rst index 28683f80..29c10dbe 100644 --- a/doc/source/draft/appdev-guide/murano_pl/versioning.rst +++ b/doc/source/draft/appdev-guide/murano_pl/versioning.rst @@ -165,6 +165,10 @@ The short description of versions: recommended specifying this format in new applications, where compatibility with older versions of murano is not required. + + **1.4** supported since Newton. Keyword ``Scope`` is introduced + for class methods to declare method's accessibility from + outside through the API call. ================== =========================================================== UI forms versioning diff --git a/murano/dsl/constants.py b/murano/dsl/constants.py index 079adfa2..7e543006 100644 --- a/murano/dsl/constants.py +++ b/murano/dsl/constants.py @@ -44,6 +44,7 @@ META_MURANO_METHOD = '?muranoMethod' META_NO_TRACE = '?noTrace' META_MPL_META = 'Meta' META_USAGE = 'Usage' +META_SCOPE = 'Scope' CORE_LIBRARY = 'io.murano' CORE_LIBRARY_OBJECT = 'io.murano.Object' @@ -56,3 +57,4 @@ RUNTIME_VERSION_1_0 = semantic_version.Version('1.0.0') RUNTIME_VERSION_1_1 = semantic_version.Version('1.1.0') RUNTIME_VERSION_1_2 = semantic_version.Version('1.2.0') RUNTIME_VERSION_1_3 = semantic_version.Version('1.3.0') +RUNTIME_VERSION_1_4 = semantic_version.Version('1.4.0') diff --git a/murano/dsl/dsl_types.py b/murano/dsl/dsl_types.py index 63b5b828..8182712c 100644 --- a/murano/dsl/dsl_types.py +++ b/murano/dsl/dsl_types.py @@ -57,6 +57,13 @@ class MethodUsages(object): StaticMethods = {Static, Extension} +class MethodScopes(object): + Session = 'Session' + Public = 'Public' + + All = {Session, Public} + + class MethodArgumentUsages(object): Standard = 'Standard' VarArgs = 'VarArgs' diff --git a/murano/dsl/executor.py b/murano/dsl/executor.py index 8d901e32..664e5b55 100644 --- a/murano/dsl/executor.py +++ b/murano/dsl/executor.py @@ -94,8 +94,7 @@ class MuranoDslExecutor(object): return stub(yaql_engine, method_context, this.real_this)( *args, **kwargs) - if (context[constants.CTX_ACTIONS_ONLY] and method.usage != - dsl_types.MethodUsages.Action): + if context[constants.CTX_ACTIONS_ONLY] and not method.is_action: raise Exception('{0} is not an action'.format(method.name)) if method.is_static: diff --git a/murano/dsl/murano_method.py b/murano/dsl/murano_method.py index 4f60152b..d3e34325 100644 --- a/murano/dsl/murano_method.py +++ b/murano/dsl/murano_method.py @@ -16,6 +16,7 @@ import collections import sys import weakref +from oslo_log import log as logging import six from yaql.language import specs @@ -31,6 +32,7 @@ from murano.dsl import virtual_exceptions from murano.dsl import yaql_integration +LOG = logging.getLogger(__name__) macros.register() virtual_exceptions.register() @@ -51,6 +53,7 @@ class MuranoMethod(dsl_types.MuranoMethod, meta.MetaProvider): self._body = yaql_integration.get_function_definition( payload, self_ref, original_name) self._arguments_scheme = None + self._scope = self._body.meta.get(constants.META_SCOPE) if declaring_type.extension_class and any(( helpers.inspect_is_static( declaring_type.extension_class, original_name), @@ -67,6 +70,9 @@ class MuranoMethod(dsl_types.MuranoMethod, meta.MetaProvider): if self._usage not in dsl_types.MethodUsages.InstanceMethods: raise ValueError( 'Invalid Usage for instance method ' + self.name) + self._resolve_usage_and_scope() + if self._scope is None: + self._scope = dsl_types.MethodScopes.Session if (self._body.name.startswith('#') or self._body.name.startswith('*')): raise ValueError( @@ -80,6 +86,10 @@ class MuranoMethod(dsl_types.MuranoMethod, meta.MetaProvider): self._body = macros.MethodBlock(payload.get('Body'), name) self._usage = payload.get( 'Usage') or dsl_types.MethodUsages.Runtime + self._scope = payload.get('Scope') + self._resolve_usage_and_scope() + if self._scope is None: + self._scope = dsl_types.MethodScopes.Session arguments_scheme = helpers.list_value(payload.get('Arguments')) if isinstance(arguments_scheme, dict): arguments_scheme = [{key: value} for key, value in @@ -123,6 +133,18 @@ class MuranoMethod(dsl_types.MuranoMethod, meta.MetaProvider): self._instance_stub, self._static_stub = \ yaql_integration.build_stub_function_definitions(self_ref) + def _resolve_usage_and_scope(self): + if self._usage == dsl_types.MethodUsages.Action: + runtime_version = self.declaring_type.package.runtime_version + if runtime_version > constants.RUNTIME_VERSION_1_3: + LOG.warning('"Usage: Action" is deprecated, ' + 'use "Scope: Public" instead') + if self._scope == dsl_types.MethodScopes.Session: + raise ValueError( + 'Both "Usage: Action" and "Scope: Session" are ' + 'provided for method ' + self.name) + self._scope = dsl_types.MethodScopes.Public + @property def name(self): return self._name @@ -151,6 +173,14 @@ class MuranoMethod(dsl_types.MuranoMethod, meta.MetaProvider): def usage(self, value): self._usage = value + @property + def scope(self): + return self._scope + + @scope.setter + def scope(self, value): + self._scope = value + @property def body(self): return self._body @@ -159,6 +189,11 @@ class MuranoMethod(dsl_types.MuranoMethod, meta.MetaProvider): def is_static(self): return self.usage in dsl_types.MethodUsages.StaticMethods + @property + def is_action(self): + return (self.scope == dsl_types.MethodScopes.Public or + self.usage == dsl_types.MethodUsages.Action) + def get_meta(self, context): def meta_producer(cls): method = cls.methods.get(self.name) diff --git a/murano/dsl/serializer.py b/murano/dsl/serializer.py index 516154a9..07c83a49 100644 --- a/murano/dsl/serializer.py +++ b/murano/dsl/serializer.py @@ -72,8 +72,7 @@ def serialize_model(root_object, executor, allow_refs=False): def _serialize_available_action(obj, current_actions): result = {} - actions = obj.type.find_methods( - lambda m: m.usage == dsl_types.MethodUsages.Action) + actions = obj.type.find_methods(lambda m: m.is_action) for action in actions: action_id = '{0}_{1}'.format(obj.object_id, action.name) entry = current_actions.get(action_id, {'enabled': True}) diff --git a/murano/packages/load_utils.py b/murano/packages/load_utils.py index 40ea757c..6b374123 100644 --- a/murano/packages/load_utils.py +++ b/murano/packages/load_utils.py @@ -36,7 +36,7 @@ def get_plugin_loader(): if PLUGIN_LOADER is None: PLUGIN_LOADER = package_types_loader.PluginLoader() - for runtime_version in ('1.0', '1.1', '1.2', '1.3'): + for runtime_version in ('1.0', '1.1', '1.2', '1.3', '1.4'): format_string = 'MuranoPL/' + runtime_version PLUGIN_LOADER.register_format( format_string, murano.packages.mpl_package.MuranoPlPackage) diff --git a/murano/tests/unit/test_actions.py b/murano/tests/unit/test_actions.py index ba61beaa..00b59c67 100644 --- a/murano/tests/unit/test_actions.py +++ b/murano/tests/unit/test_actions.py @@ -26,14 +26,22 @@ class TestActionsSerializer(base.MuranoTestCase): def _get_mocked_obj(self): method1 = mock.Mock() - method1.usage = dsl_types.MethodUsages.Action + method1.scope = dsl_types.MethodScopes.Public method1.name = 'method1' method2 = mock.Mock() method2.usage = dsl_types.MethodUsages.Runtime method2.name = 'method2' method3 = mock.Mock() - method3.usage = dsl_types.MethodUsages.Action + method3.scope = dsl_types.MethodScopes.Public method3.name = 'method3' + method4 = mock.Mock() + method4.usage = dsl_types.MethodUsages.Action + method4.name = 'method4' + + for method in [method1, method2, method3, method4]: + method.is_action = (method.scope == + dsl_types.MethodScopes.Public or + method.usage == dsl_types.MethodUsages.Action) obj2_type = mock.Mock() obj2_type.declared_parents = [] @@ -43,9 +51,10 @@ class TestActionsSerializer(base.MuranoTestCase): obj = mock.Mock() obj.object_id = 'id1' obj.type.declared_parents = [obj2_type] - obj.type.methods = {'method1': method1, 'method2': method2} + obj.type.methods = {'method1': method1, 'method2': method2, + 'method4': method4} obj.type.find_methods = lambda p: filter( - p, [method1, method2, method3]) + p, [method1, method2, method3, method4]) return obj @@ -54,9 +63,12 @@ class TestActionsSerializer(base.MuranoTestCase): obj_actions = serializer._serialize_available_action(obj, {}) - expected_result = {'name': 'method1', 'enabled': True} self.assertIn('id1_method1', obj_actions) + self.assertIn('id1_method4', obj_actions) + expected_result = {'name': 'method1', 'enabled': True} self.assertEqual(expected_result, obj_actions['id1_method1']) + expected_result = {'name': 'method4', 'enabled': True} + self.assertEqual(expected_result, obj_actions['id1_method4']) def test_that_only_actions_are_serialized(self): obj = self._get_mocked_obj() diff --git a/releasenotes/notes/action-syntax-3f2cbe843801f80d.yaml b/releasenotes/notes/action-syntax-3f2cbe843801f80d.yaml new file mode 100644 index 00000000..73ff662b --- /dev/null +++ b/releasenotes/notes/action-syntax-3f2cbe843801f80d.yaml @@ -0,0 +1,8 @@ +--- +upgrade: + - New manifest format 1.4.0 is available. Keyword 'Scope' is introduced + for class methods to declare method's accessibility from outside through + the API call. +deprecations: + - Keyword 'Usage Action' is deprecated to declare class method as an + action in favor of 'Scope Public' for the package format versions > 1.3.