Moving to YAQL 1.0
Implements blueprint yaql-v1-0 * Now functions 'env()' and 'execution()' should be used instead of '$.__env' and '$.__execution' correspondingly. TODO: - Update examples in mistral-extra Change-Id: Ic96fe10966bf5d27898c08d70914eb294b7efe2f
This commit is contained in:
parent
0eb4fdad31
commit
6b89f6918e
@ -20,14 +20,15 @@ import re
|
|||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
import six
|
import six
|
||||||
import yaql
|
from yaql.language import exceptions as yaql_exc
|
||||||
from yaql import exceptions as yaql_exc
|
from yaql.language import factory
|
||||||
|
|
||||||
from mistral import exceptions as exc
|
from mistral import exceptions as exc
|
||||||
from mistral import yaql_utils
|
from mistral.utils import yaql_utils
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
YAQL_ENGINE = factory.YaqlFactory().create()
|
||||||
|
|
||||||
|
|
||||||
class Evaluator(object):
|
class Evaluator(object):
|
||||||
@ -75,7 +76,7 @@ class YAQLEvaluator(Evaluator):
|
|||||||
LOG.debug("Validating YAQL expression [expression='%s']", expression)
|
LOG.debug("Validating YAQL expression [expression='%s']", expression)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
yaql.parse(expression)
|
YAQL_ENGINE(expression)
|
||||||
except (yaql_exc.YaqlException, KeyError, ValueError, TypeError) as e:
|
except (yaql_exc.YaqlException, KeyError, ValueError, TypeError) as e:
|
||||||
raise exc.YaqlEvaluationException(e.message)
|
raise exc.YaqlEvaluationException(e.message)
|
||||||
|
|
||||||
@ -85,9 +86,8 @@ class YAQLEvaluator(Evaluator):
|
|||||||
% (expression, data_context))
|
% (expression, data_context))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = yaql.parse(expression).evaluate(
|
result = YAQL_ENGINE(expression).evaluate(
|
||||||
data=data_context,
|
context=yaql_utils.get_yaql_context(data_context)
|
||||||
context=yaql_utils.create_yaql_context()
|
|
||||||
)
|
)
|
||||||
except (yaql_exc.YaqlException, KeyError, ValueError, TypeError) as e:
|
except (yaql_exc.YaqlException, KeyError, ValueError, TypeError) as e:
|
||||||
raise exc.YaqlEvaluationException(
|
raise exc.YaqlEvaluationException(
|
||||||
|
@ -36,7 +36,7 @@ VARIABLES = {
|
|||||||
'verbose': True,
|
'verbose': True,
|
||||||
'__actions': {
|
'__actions': {
|
||||||
'std.sql': {
|
'std.sql': {
|
||||||
'conn': 'mysql://admin:secrete@{$.__env.host}/{$.__env.db}'
|
'conn': 'mysql://admin:secrete@<% env().host %>/<% env().db %>'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ class DataFlowEngineTest(engine_test_base.EngineTestCase):
|
|||||||
|
|
||||||
task3:
|
task3:
|
||||||
publish:
|
publish:
|
||||||
result: "<% $.hi %>, <% $.to %>! Your <% $.__env.from %>."
|
result: "<% $.hi %>, <% $.to %>! Your <% env().from %>."
|
||||||
"""
|
"""
|
||||||
|
|
||||||
wf_service.create_workflows(linear_wf)
|
wf_service.create_workflows(linear_wf)
|
||||||
@ -113,7 +113,7 @@ class DataFlowEngineTest(engine_test_base.EngineTestCase):
|
|||||||
|
|
||||||
task3:
|
task3:
|
||||||
publish:
|
publish:
|
||||||
result: "<% $.hi %>, <% $.to %>! Your <% $.__env.from %>."
|
result: "<% $.hi %>, <% $.to %>! Your <% env().from %>."
|
||||||
progress: "completed task3"
|
progress: "completed task3"
|
||||||
on-success:
|
on-success:
|
||||||
- notify
|
- notify
|
||||||
@ -338,7 +338,7 @@ class DataFlowEngineTest(engine_test_base.EngineTestCase):
|
|||||||
|
|
||||||
task4:
|
task4:
|
||||||
publish:
|
publish:
|
||||||
result: "<% $.greeting %>, <% $.to %>! <% $.__env.from %>."
|
result: "<% $.greeting %>, <% $.to %>! <% env().from %>."
|
||||||
"""
|
"""
|
||||||
|
|
||||||
wf_service.create_workflows(var_overwrite_wf)
|
wf_service.create_workflows(var_overwrite_wf)
|
||||||
|
@ -194,8 +194,8 @@ class DefaultEngineTest(base.DbTestCase):
|
|||||||
|
|
||||||
def test_start_workflow_with_adhoc_env(self):
|
def test_start_workflow_with_adhoc_env(self):
|
||||||
wf_input = {
|
wf_input = {
|
||||||
'param1': '<% $.__env.key1 %>',
|
'param1': '<% env().key1 %>',
|
||||||
'param2': '<% $.__env.key2 %>'
|
'param2': '<% env().key2 %>'
|
||||||
}
|
}
|
||||||
env = ENVIRONMENT['variables']
|
env = ENVIRONMENT['variables']
|
||||||
|
|
||||||
@ -215,8 +215,8 @@ class DefaultEngineTest(base.DbTestCase):
|
|||||||
@mock.patch.object(db_api, "get_environment", MOCK_ENVIRONMENT)
|
@mock.patch.object(db_api, "get_environment", MOCK_ENVIRONMENT)
|
||||||
def test_start_workflow_with_saved_env(self):
|
def test_start_workflow_with_saved_env(self):
|
||||||
wf_input = {
|
wf_input = {
|
||||||
'param1': '<% $.__env.key1 %>',
|
'param1': '<% env().key1 %>',
|
||||||
'param2': '<% $.__env.key2 %>'
|
'param2': '<% env().key2 %>'
|
||||||
}
|
}
|
||||||
env = ENVIRONMENT['variables']
|
env = ENVIRONMENT['variables']
|
||||||
|
|
||||||
@ -238,7 +238,7 @@ class DefaultEngineTest(base.DbTestCase):
|
|||||||
self.assertRaises(exc.NotFoundException,
|
self.assertRaises(exc.NotFoundException,
|
||||||
self.engine.start_workflow,
|
self.engine.start_workflow,
|
||||||
'wb.wf',
|
'wb.wf',
|
||||||
{'param1': '<% $.__env.key1 %>'},
|
{'param1': '<% env().key1 %>'},
|
||||||
env='foo',
|
env='foo',
|
||||||
task_name='task2')
|
task_name='task2')
|
||||||
|
|
||||||
@ -246,7 +246,7 @@ class DefaultEngineTest(base.DbTestCase):
|
|||||||
self.assertRaises(ValueError,
|
self.assertRaises(ValueError,
|
||||||
self.engine.start_workflow,
|
self.engine.start_workflow,
|
||||||
'wb.wf',
|
'wb.wf',
|
||||||
{'param1': '<% $.__env.key1 %>'},
|
{'param1': '<% env().key1 %>'},
|
||||||
env=True,
|
env=True,
|
||||||
task_name='task2')
|
task_name='task2')
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ workflows:
|
|||||||
type: direct
|
type: direct
|
||||||
tasks:
|
tasks:
|
||||||
t1:
|
t1:
|
||||||
with-items: i in <% range(0, 3).list() %>
|
with-items: i in <% list(range(0, 3)) %>
|
||||||
action: std.echo output="Task 1.<% $.i %>"
|
action: std.echo output="Task 1.<% $.i %>"
|
||||||
publish:
|
publish:
|
||||||
v1: <% $.t1 %>
|
v1: <% $.t1 %>
|
||||||
|
@ -51,14 +51,14 @@ workflows:
|
|||||||
tasks:
|
tasks:
|
||||||
task1:
|
task1:
|
||||||
action: std.echo output=<% $.param1 %>
|
action: std.echo output=<% $.param1 %>
|
||||||
target: <% $.__env.var1 %>
|
target: <% env().var1 %>
|
||||||
publish:
|
publish:
|
||||||
result1: <% $.task1 %>
|
result1: <% $.task1 %>
|
||||||
|
|
||||||
task2:
|
task2:
|
||||||
requires: [task1]
|
requires: [task1]
|
||||||
action: std.echo output="'<% $.result1 %> & <% $.param2 %>'"
|
action: std.echo output="'<% $.result1 %> & <% $.param2 %>'"
|
||||||
target: <% $.__env.var1 %>
|
target: <% env().var1 %>
|
||||||
publish:
|
publish:
|
||||||
final_result: <% $.task2 %>
|
final_result: <% $.task2 %>
|
||||||
|
|
||||||
@ -70,11 +70,11 @@ workflows:
|
|||||||
task1:
|
task1:
|
||||||
workflow: wf1
|
workflow: wf1
|
||||||
input:
|
input:
|
||||||
param1: <% $.__env.var2 %>
|
param1: <% env().var2 %>
|
||||||
param2: <% $.__env.var3 %>
|
param2: <% env().var3 %>
|
||||||
task_name: task2
|
task_name: task2
|
||||||
publish:
|
publish:
|
||||||
slogan: "<% $.task1.final_result %> is a cool <% $.__env.var4 %>!"
|
slogan: "<% $.task1.final_result %> is a cool <% env().var4 %>!"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
@ -191,7 +191,7 @@ class SubworkflowsTest(base.EngineTestCase):
|
|||||||
env = {
|
env = {
|
||||||
'var1': TARGET,
|
'var1': TARGET,
|
||||||
'var2': 'Bonnie',
|
'var2': 'Bonnie',
|
||||||
'var3': '<% $.__env.var5 %>',
|
'var3': '<% env().var5 %>',
|
||||||
'var4': 'movie',
|
'var4': 'movie',
|
||||||
'var5': 'Clyde'
|
'var5': 'Clyde'
|
||||||
}
|
}
|
||||||
|
@ -319,8 +319,8 @@ class JoinEngineTest(base.EngineTestCase):
|
|||||||
action: std.echo
|
action: std.echo
|
||||||
input:
|
input:
|
||||||
output: |
|
output: |
|
||||||
<% result1 in $ %>,<% result2 in $ %>,
|
<% result1 in $.keys() %>,<% result2 in $.keys() %>,
|
||||||
<% result3 in $ %>,<% result4 in $ %>
|
<% result3 in $.keys() %>,<% result4 in $.keys() %>
|
||||||
publish:
|
publish:
|
||||||
result5: <% $.task5 %>
|
result5: <% $.task5 %>
|
||||||
"""
|
"""
|
||||||
@ -390,8 +390,8 @@ class JoinEngineTest(base.EngineTestCase):
|
|||||||
action: std.echo
|
action: std.echo
|
||||||
input:
|
input:
|
||||||
output: |
|
output: |
|
||||||
<% result1 in $ %>,<% result2 in $ %>,
|
<% result1 in $.keys() %>,<% result2 in $.keys() %>,
|
||||||
<% result3 in $ %>
|
<% result3 in $.keys() %>
|
||||||
publish:
|
publish:
|
||||||
result4: <% $.task4 %>
|
result4: <% $.task4 %>
|
||||||
"""
|
"""
|
||||||
|
@ -59,7 +59,12 @@ class YaqlEvaluatorTest(base.BaseTest):
|
|||||||
res = self._evaluator.evaluate("$.status = 'Invalid value'", DATA)
|
res = self._evaluator.evaluate("$.status = 'Invalid value'", DATA)
|
||||||
self.assertFalse(res)
|
self.assertFalse(res)
|
||||||
|
|
||||||
self.assertIsNone(self._evaluator.evaluate('$.wrong_key', DATA))
|
self.assertRaises(
|
||||||
|
exc.YaqlEvaluationException,
|
||||||
|
self._evaluator.evaluate,
|
||||||
|
'$.wrong_key',
|
||||||
|
DATA
|
||||||
|
)
|
||||||
|
|
||||||
expression_str = 'invalid_expression_string'
|
expression_str = 'invalid_expression_string'
|
||||||
res = self._evaluator.evaluate(expression_str, DATA)
|
res = self._evaluator.evaluate(expression_str, DATA)
|
||||||
@ -78,12 +83,12 @@ class YaqlEvaluatorTest(base.BaseTest):
|
|||||||
self.assertEqual('3', self._evaluator.evaluate('str($)', 3))
|
self.assertEqual('3', self._evaluator.evaluate('str($)', 3))
|
||||||
|
|
||||||
def test_function_len(self):
|
def test_function_len(self):
|
||||||
self.assertEqual(3, self._evaluator.evaluate('$.len()', 'hey'))
|
self.assertEqual(3, self._evaluator.evaluate('len($)', 'hey'))
|
||||||
data = [{'some': 'thing'}]
|
data = [{'some': 'thing'}]
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
1,
|
1,
|
||||||
self._evaluator.evaluate('$[$.some = thing].len()', data)
|
self._evaluator.evaluate('$.where($.some = thing).len()', data)
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_validate(self):
|
def test_validate(self):
|
||||||
@ -289,8 +294,8 @@ class ExpressionsTest(base.BaseTest):
|
|||||||
'verbose': True,
|
'verbose': True,
|
||||||
'__actions': {
|
'__actions': {
|
||||||
'std.sql': {
|
'std.sql': {
|
||||||
'conn': 'mysql://admin:secrete@<% $.__env.host %>'
|
'conn': 'mysql://admin:secrete@<% env().host %>'
|
||||||
'/<% $.__env.db %>'
|
'/<% env().db %>'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
52
mistral/utils/yaql_utils.py
Normal file
52
mistral/utils/yaql_utils.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
# Copyright 2015 - Mirantis, Inc.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
import yaql
|
||||||
|
|
||||||
|
|
||||||
|
ROOT_CONTEXT = None
|
||||||
|
|
||||||
|
|
||||||
|
def get_yaql_context(data_context):
|
||||||
|
global ROOT_CONTEXT
|
||||||
|
|
||||||
|
if not ROOT_CONTEXT:
|
||||||
|
ROOT_CONTEXT = yaql.create_context()
|
||||||
|
_register_functions(ROOT_CONTEXT)
|
||||||
|
|
||||||
|
new_ctx = ROOT_CONTEXT.create_child_context()
|
||||||
|
new_ctx['$'] = data_context
|
||||||
|
|
||||||
|
if isinstance(data_context, dict):
|
||||||
|
new_ctx['__env'] = data_context.get('__env')
|
||||||
|
new_ctx['__execution'] = data_context.get('__execution')
|
||||||
|
|
||||||
|
return new_ctx
|
||||||
|
|
||||||
|
|
||||||
|
def _register_functions(yaql_ctx):
|
||||||
|
yaql_ctx.register_function(env_)
|
||||||
|
yaql_ctx.register_function(execution_)
|
||||||
|
|
||||||
|
|
||||||
|
# Additional convenience YAQL functions.
|
||||||
|
# If a function name ends with underscore then it doesn't need to pass
|
||||||
|
# the name of the function when context registers it.
|
||||||
|
|
||||||
|
def env_(context):
|
||||||
|
return context['__env']
|
||||||
|
|
||||||
|
|
||||||
|
def execution_(context):
|
||||||
|
return context['__execution']
|
@ -1,49 +0,0 @@
|
|||||||
# Copyright 2015 - Mirantis, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
import collections
|
|
||||||
|
|
||||||
import yaql
|
|
||||||
from yaql import context
|
|
||||||
|
|
||||||
|
|
||||||
def create_yaql_context():
|
|
||||||
ctx = yaql.create_context()
|
|
||||||
|
|
||||||
_register_functions(ctx)
|
|
||||||
|
|
||||||
return ctx
|
|
||||||
|
|
||||||
|
|
||||||
def _register_functions(yaql_ctx):
|
|
||||||
yaql_ctx.register_function(_sized_length, 'len')
|
|
||||||
yaql_ctx.register_function(_iterable_length, 'len')
|
|
||||||
yaql_ctx.register_function(to_str, 'str')
|
|
||||||
|
|
||||||
|
|
||||||
# Additional convenience YAQL functions.
|
|
||||||
|
|
||||||
|
|
||||||
@context.EvalArg('a', arg_type=collections.Sized)
|
|
||||||
def _sized_length(a):
|
|
||||||
return len(a)
|
|
||||||
|
|
||||||
|
|
||||||
@context.EvalArg('a', arg_type=collections.Iterable)
|
|
||||||
def _iterable_length(a):
|
|
||||||
return sum(1 for i in a)
|
|
||||||
|
|
||||||
|
|
||||||
def to_str(value):
|
|
||||||
return str(value())
|
|
@ -37,5 +37,5 @@ six>=1.9.0
|
|||||||
SQLAlchemy<1.1.0,>=0.9.7
|
SQLAlchemy<1.1.0,>=0.9.7
|
||||||
stevedore>=1.5.0 # Apache-2.0
|
stevedore>=1.5.0 # Apache-2.0
|
||||||
WSME>=0.7
|
WSME>=0.7
|
||||||
yaql>=0.2.7,!=0.3.0 # Apache 2.0 License
|
yaql>=1.0.0 # Apache 2.0 License
|
||||||
tooz>=0.16.0 # Apache-2.0
|
tooz>=0.16.0 # Apache-2.0
|
||||||
|
Loading…
Reference in New Issue
Block a user