Merge "JavaScript action: part 2"

This commit is contained in:
Jenkins 2015-02-06 04:01:25 +00:00 committed by Gerrit Code Review
commit e4dd81a2c2
5 changed files with 135 additions and 10 deletions

View File

@ -354,12 +354,18 @@ class JavaScriptAction(base.Action):
"""Evaluates given JavaScript.
"""
def __init__(self, script):
def __init__(self, script, context=None):
self.script = script
self.context = context
def run(self):
try:
return javascript.evaluate(self.script)
script = """function f() {
%s
}
f()
""" % self.script
return javascript.evaluate(script, self.context)
except Exception as e:
raise exc.ActionException("JavaScriptAction failed: %s" % str(e))

View File

@ -159,11 +159,15 @@ def get_action_context(task_db):
}
def has_action_context(action, attributes):
def _has_argument(action, attributes, argument_name):
action_cls = action_factory.construct_action_class(action, attributes)
arg_spec = inspect.getargspec(action_cls.__init__)
return _ACTION_CTX_PARAM in arg_spec.args
return argument_name in arg_spec.args
def has_action_context(action, attributes):
return _has_argument(action, attributes, _ACTION_CTX_PARAM)
def resolve_adhoc_action_name(workbook, action_name):

View File

@ -20,9 +20,11 @@ from mistral.utils import javascript
class JavascriptActionTest(base.BaseTest):
@mock.patch.object(javascript, 'evaluate', mock.Mock(return_value="3"))
@mock.patch.object(
javascript, 'evaluate', mock.Mock(return_value="3")
)
def test_js_action(self):
script = "1 + 2"
script = "return 1 + 2"
action = std.JavaScriptAction(script)
self.assertEqual("3", action.run())

View File

@ -0,0 +1,107 @@
# 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 mock
from oslo.config import cfg
import testtools
from mistral.db.v2 import api as db_api
from mistral.engine import states
from mistral.openstack.common import log as logging
from mistral.services import workbooks as wb_service
from mistral.tests.unit.engine1 import base
from mistral.utils import javascript
LOG = logging.getLogger(__name__)
# Use the set_default method to set value otherwise in certain test cases
# the change in value is not permanent.
cfg.CONF.set_default('auth_enable', False, group='pecan')
WORKBOOK = """
---
version: "2.0"
name: test_js
workflows:
js_test:
type: direct
input:
- num
tasks:
task1:
description: |
This task reads variable from context,
increasing its value 10 times, writes result to context and
returns 100 (expected result)
action: std.javascript
input:
script: f = 50 * 10; return f
# Skip this '$' sign until bug
# https://bugs.launchpad.net/mistral/+bug/1415886 is resolved.
# return $['num'] * 10
context: $
publish:
result: $.task1
"""
def fake_evaluate(_, context):
return context['num'] * 10
class JavaScriptEngineTest(base.EngineTestCase):
def setUp(self):
super(JavaScriptEngineTest, self).setUp()
@testtools.skip('It requires installed JS engine.')
def test_javascript_action(self):
wb_service.create_workbook_v2(WORKBOOK)
# Start workflow.
exec_db = self.engine.start_workflow('test_js.js_test', {'num': 50})
self._await(lambda: self.is_execution_success(exec_db.id))
# Note: We need to reread execution to access related tasks.
exec_db = db_api.get_execution(exec_db.id)
task_db = exec_db.tasks[0]
self.assertEqual(states.SUCCESS, task_db.state)
self.assertDictEqual({}, task_db.runtime_context)
self.assertEqual(500, task_db.output['num_10_times'])
self.assertEqual(100, task_db.output['result'])
@mock.patch.object(javascript, 'evaluate', fake_evaluate)
def test_fake_javascript_action_data_context(self):
wb_service.create_workbook_v2(WORKBOOK)
# Start workflow.
exec_db = self.engine.start_workflow('test_js.js_test', {'num': 50})
self._await(lambda: self.is_execution_success(exec_db.id))
# Note: We need to reread execution to access related tasks.
exec_db = db_api.get_execution(exec_db.id)
task_db = exec_db.tasks[0]
self.assertEqual(states.SUCCESS, task_db.state)
self.assertDictEqual({}, task_db.runtime_context)
self.assertEqual(500, task_db.output['result'])

View File

@ -13,6 +13,7 @@
# limitations under the License.
import abc
import json
from mistral import exceptions as exc
from mistral.openstack.common import importutils
@ -24,24 +25,29 @@ _PYV8 = importutils.try_import('PyV8')
class JSEvaluator(object):
@classmethod
@abc.abstractmethod
def evaluate(cls, script):
def evaluate(cls, script, context):
"""Executes given JavaScript.
"""
pass
class V8Evaluator(JSEvaluator):
@classmethod
def evaluate(cls, script):
def evaluate(cls, script, context):
if not _PYV8:
raise exc.MistralException(
"PyV8 module is not available. Please install PyV8."
)
with _PYV8.JSContext() as ctx:
# Prepare data context and way for interaction with it.
ctx.eval('$ = %s' % json.dumps(context))
return ctx.eval(script)
# TODO(nmakhotkin) Make it configurable.
EVALUATOR = V8Evaluator
def evaluate(script):
return EVALUATOR.evaluate(script)
def evaluate(script, context):
return EVALUATOR.evaluate(script, context)