Implement new syntax for action declaration
`Scope` keyword is introduced to declare method visibility. For that package format version is incremented to 1.4. `Scope` attribute 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 (i.e. it is an action in current terminology). It allows to declare static actions with the following syntax: methodName: Usage: Static Scope: Public For backward compatibility `Usage: Action` remains valid and acceptable but it is deprecated for format versions > 1.3. An exception is raised if both `Usage: Action` and `Scope: Session` are specified. Change-Id: I5a6d664d017d5ea99a5159d7eb2e111143c7b0a3 Partially-implements: blueprint static-actions
This commit is contained in:
parent
d8ccb215be
commit
91fd92c136
@ -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)
|
||||
|
@ -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
|
||||
+++++++++++
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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')
|
||||
|
@ -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'
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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})
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
8
releasenotes/notes/action-syntax-3f2cbe843801f80d.yaml
Normal file
8
releasenotes/notes/action-syntax-3f2cbe843801f80d.yaml
Normal file
@ -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.
|
Loading…
Reference in New Issue
Block a user