Fail/Success/Pause transition message
Now user can provide customize message for fail/success/pause transition. Change-Id: I84b1fbc63aaf8186c81eea9c852f5b49db93f0ff Implements: blueprint mistral-fail-transition-message
This commit is contained in:
parent
3641b46d15
commit
6b8f15385a
@ -35,7 +35,7 @@ def dispatch_workflow_commands(wf_ex, wf_cmds):
|
|||||||
if states.is_completed(cmd.new_state):
|
if states.is_completed(cmd.new_state):
|
||||||
wf_handler.stop_workflow(cmd.wf_ex, cmd.new_state, cmd.msg)
|
wf_handler.stop_workflow(cmd.wf_ex, cmd.new_state, cmd.msg)
|
||||||
else:
|
else:
|
||||||
wf_handler.set_workflow_state(wf_ex, cmd.new_state)
|
wf_handler.set_workflow_state(wf_ex, cmd.new_state, cmd.msg)
|
||||||
elif isinstance(cmd, commands.Noop):
|
elif isinstance(cmd, commands.Noop):
|
||||||
# Do nothing.
|
# Do nothing.
|
||||||
pass
|
pass
|
||||||
|
@ -313,3 +313,165 @@ class OrderEngineCommandsTest(base.EngineTestCase):
|
|||||||
|
|
||||||
self.await_task_error(task2_db.id)
|
self.await_task_error(task2_db.id)
|
||||||
self.await_execution_success(wf_ex.id)
|
self.await_execution_success(wf_ex.id)
|
||||||
|
|
||||||
|
WORKBOOK4 = """
|
||||||
|
---
|
||||||
|
version: '2.0'
|
||||||
|
|
||||||
|
name: my_wb
|
||||||
|
|
||||||
|
workflows:
|
||||||
|
wf:
|
||||||
|
type: direct
|
||||||
|
input:
|
||||||
|
- my_var
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
task1:
|
||||||
|
action: std.echo output='1'
|
||||||
|
on-complete:
|
||||||
|
- fail(msg='my_var value is 1'): <% $.my_var = 1 %>
|
||||||
|
- succeed(msg='my_var value is 2'): <% $.my_var = 2 %>
|
||||||
|
- pause(msg='my_var value is 3'): <% $.my_var = 3 %>
|
||||||
|
- task2
|
||||||
|
|
||||||
|
task2:
|
||||||
|
action: std.echo output='2'
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class SimpleEngineCmdsWithMsgTest(base.EngineTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
super(SimpleEngineCmdsWithMsgTest, self).setUp()
|
||||||
|
|
||||||
|
wb_service.create_workbook_v2(WORKBOOK4)
|
||||||
|
|
||||||
|
def test_fail(self):
|
||||||
|
wf_ex = self.engine.start_workflow('my_wb.wf', {'my_var': 1})
|
||||||
|
|
||||||
|
self.await_execution_error(wf_ex.id)
|
||||||
|
|
||||||
|
wf_ex = db_api.get_workflow_execution(wf_ex.id)
|
||||||
|
|
||||||
|
self.assertEqual(1, len(wf_ex.task_executions))
|
||||||
|
self._assert_single_item(
|
||||||
|
wf_ex.task_executions,
|
||||||
|
name='task1',
|
||||||
|
state=states.SUCCESS
|
||||||
|
)
|
||||||
|
self.assertEqual(states.ERROR, wf_ex.state)
|
||||||
|
self.assertEqual('my_var value is 1', wf_ex.state_info)
|
||||||
|
|
||||||
|
def test_succeed(self):
|
||||||
|
wf_ex = self.engine.start_workflow('my_wb.wf', {'my_var': 2})
|
||||||
|
|
||||||
|
self.await_execution_success(wf_ex.id)
|
||||||
|
|
||||||
|
wf_ex = db_api.get_workflow_execution(wf_ex.id)
|
||||||
|
|
||||||
|
self.assertEqual(1, len(wf_ex.task_executions))
|
||||||
|
self._assert_single_item(
|
||||||
|
wf_ex.task_executions,
|
||||||
|
name='task1',
|
||||||
|
state=states.SUCCESS
|
||||||
|
)
|
||||||
|
self.assertEqual(states.SUCCESS, wf_ex.state)
|
||||||
|
self.assertEqual("my_var value is 2", wf_ex.state_info)
|
||||||
|
|
||||||
|
def test_pause(self):
|
||||||
|
wf_ex = self.engine.start_workflow('my_wb.wf', {'my_var': 3})
|
||||||
|
|
||||||
|
self.await_execution_paused(wf_ex.id)
|
||||||
|
|
||||||
|
wf_ex = db_api.get_workflow_execution(wf_ex.id)
|
||||||
|
|
||||||
|
self.assertEqual(1, len(wf_ex.task_executions))
|
||||||
|
self._assert_single_item(
|
||||||
|
wf_ex.task_executions,
|
||||||
|
name='task1',
|
||||||
|
state=states.SUCCESS
|
||||||
|
)
|
||||||
|
self.assertEqual(states.PAUSED, wf_ex.state)
|
||||||
|
self.assertEqual("my_var value is 3", wf_ex.state_info)
|
||||||
|
|
||||||
|
WORKBOOK5 = """
|
||||||
|
---
|
||||||
|
version: '2.0'
|
||||||
|
|
||||||
|
name: my_wb
|
||||||
|
|
||||||
|
workflows:
|
||||||
|
wf:
|
||||||
|
type: direct
|
||||||
|
input:
|
||||||
|
- my_var
|
||||||
|
|
||||||
|
task-defaults:
|
||||||
|
on-complete:
|
||||||
|
- fail(msg='my_var value is 1'): <% $.my_var = 1 %>
|
||||||
|
- succeed(msg='my_var value is <% $.my_var %>'): <% $.my_var = 2 %>
|
||||||
|
- pause(msg='my_var value is 3'): <% $.my_var = 3 %>
|
||||||
|
- task2: <% $.my_var = 4 %> # (Never happens in this test)
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
task1:
|
||||||
|
action: std.echo output='1'
|
||||||
|
|
||||||
|
task2:
|
||||||
|
action: std.echo output='2'
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class SimpleEngineWorkflowLevelCmdsWithMsgTest(base.EngineTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
super(SimpleEngineWorkflowLevelCmdsWithMsgTest, self).setUp()
|
||||||
|
|
||||||
|
wb_service.create_workbook_v2(WORKBOOK5)
|
||||||
|
|
||||||
|
def test_fail(self):
|
||||||
|
wf_ex = self.engine.start_workflow('my_wb.wf', {'my_var': 1})
|
||||||
|
|
||||||
|
self.await_execution_error(wf_ex.id)
|
||||||
|
|
||||||
|
wf_ex = db_api.get_workflow_execution(wf_ex.id)
|
||||||
|
|
||||||
|
self.assertEqual(1, len(wf_ex.task_executions))
|
||||||
|
self._assert_single_item(
|
||||||
|
wf_ex.task_executions,
|
||||||
|
name='task1',
|
||||||
|
state=states.SUCCESS
|
||||||
|
)
|
||||||
|
self.assertEqual(states.ERROR, wf_ex.state)
|
||||||
|
self.assertEqual("my_var value is 1", wf_ex.state_info)
|
||||||
|
|
||||||
|
def test_succeed(self):
|
||||||
|
wf_ex = self.engine.start_workflow('my_wb.wf', {'my_var': 2})
|
||||||
|
|
||||||
|
self.await_execution_success(wf_ex.id)
|
||||||
|
|
||||||
|
wf_ex = db_api.get_workflow_execution(wf_ex.id)
|
||||||
|
|
||||||
|
self.assertEqual(1, len(wf_ex.task_executions))
|
||||||
|
self._assert_single_item(
|
||||||
|
wf_ex.task_executions,
|
||||||
|
name='task1',
|
||||||
|
state=states.SUCCESS
|
||||||
|
)
|
||||||
|
self.assertEqual(states.SUCCESS, wf_ex.state)
|
||||||
|
self.assertEqual("my_var value is 2", wf_ex.state_info)
|
||||||
|
|
||||||
|
def test_pause(self):
|
||||||
|
wf_ex = self.engine.start_workflow('my_wb.wf', {'my_var': 3})
|
||||||
|
|
||||||
|
self.await_execution_paused(wf_ex.id)
|
||||||
|
|
||||||
|
wf_ex = db_api.get_workflow_execution(wf_ex.id)
|
||||||
|
|
||||||
|
self.assertEqual(1, len(wf_ex.task_executions))
|
||||||
|
self._assert_single_item(
|
||||||
|
wf_ex.task_executions,
|
||||||
|
name='task1',
|
||||||
|
state=states.SUCCESS
|
||||||
|
)
|
||||||
|
self.assertEqual(states.PAUSED, wf_ex.state)
|
||||||
|
self.assertEqual("my_var value is 3", wf_ex.state_info)
|
||||||
|
@ -118,15 +118,15 @@ class WorkbookSpecValidation(base.WorkbookSpecValidationTestCase):
|
|||||||
task_defaults_spec = wf2_spec.get_task_defaults()
|
task_defaults_spec = wf2_spec.get_task_defaults()
|
||||||
|
|
||||||
self.assertListEqual(
|
self.assertListEqual(
|
||||||
[('fail', '<% $.my_val = 0 %>')],
|
[('fail', '<% $.my_val = 0 %>', {})],
|
||||||
task_defaults_spec.get_on_error()
|
task_defaults_spec.get_on_error()
|
||||||
)
|
)
|
||||||
self.assertListEqual(
|
self.assertListEqual(
|
||||||
[('pause', '')],
|
[('pause', '', {})],
|
||||||
task_defaults_spec.get_on_success()
|
task_defaults_spec.get_on_success()
|
||||||
)
|
)
|
||||||
self.assertListEqual(
|
self.assertListEqual(
|
||||||
[('succeed', '')],
|
[('succeed', '', {})],
|
||||||
task_defaults_spec.get_on_complete()
|
task_defaults_spec.get_on_complete()
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -147,15 +147,15 @@ class WorkbookSpecValidation(base.WorkbookSpecValidationTestCase):
|
|||||||
task3_spec.get_input()
|
task3_spec.get_input()
|
||||||
)
|
)
|
||||||
self.assertListEqual(
|
self.assertListEqual(
|
||||||
[('task4', '<% $.my_val = 1 %>')],
|
[('task4', '<% $.my_val = 1 %>', {})],
|
||||||
task3_spec.get_on_error()
|
task3_spec.get_on_error()
|
||||||
)
|
)
|
||||||
self.assertListEqual(
|
self.assertListEqual(
|
||||||
[('task5', '<% $.my_val = 2 %>')],
|
[('task5', '<% $.my_val = 2 %>', {})],
|
||||||
task3_spec.get_on_success()
|
task3_spec.get_on_success()
|
||||||
)
|
)
|
||||||
self.assertListEqual(
|
self.assertListEqual(
|
||||||
[('task6', '<% $.my_val = 3 %>')],
|
[('task6', '<% $.my_val = 3 %>', {})],
|
||||||
task3_spec.get_on_complete()
|
task3_spec.get_on_complete()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ from mistral import utils
|
|||||||
from mistral.workbook import types
|
from mistral.workbook import types
|
||||||
|
|
||||||
|
|
||||||
CMD_PTRN = re.compile("^[\w\.]+[^=\s\"]*")
|
CMD_PTRN = re.compile("^[\w\.]+[^=\(\s\"]*")
|
||||||
|
|
||||||
INLINE_YAQL = expr.INLINE_YAQL_REGEXP
|
INLINE_YAQL = expr.INLINE_YAQL_REGEXP
|
||||||
_ALL_IN_BRACKETS = "\[.*\]\s*"
|
_ALL_IN_BRACKETS = "\[.*\]\s*"
|
||||||
|
@ -18,6 +18,10 @@ import six
|
|||||||
from mistral.workbook import types
|
from mistral.workbook import types
|
||||||
from mistral.workbook.v2 import base
|
from mistral.workbook.v2 import base
|
||||||
from mistral.workbook.v2 import policies
|
from mistral.workbook.v2 import policies
|
||||||
|
from mistral.workbook.v2 import tasks
|
||||||
|
|
||||||
|
|
||||||
|
direct_wf_ts = tasks.DirectWorkflowTaskSpec
|
||||||
|
|
||||||
|
|
||||||
class TaskDefaultsSpec(base.BaseSpec):
|
class TaskDefaultsSpec(base.BaseSpec):
|
||||||
@ -67,9 +71,15 @@ class TaskDefaultsSpec(base.BaseSpec):
|
|||||||
'pause-before',
|
'pause-before',
|
||||||
'concurrency'
|
'concurrency'
|
||||||
)
|
)
|
||||||
self._on_complete = self._as_list_of_tuples("on-complete")
|
self._on_complete = direct_wf_ts.prepare_on_clause(
|
||||||
self._on_success = self._as_list_of_tuples("on-success")
|
self._as_list_of_tuples('on-complete')
|
||||||
self._on_error = self._as_list_of_tuples("on-error")
|
)
|
||||||
|
self._on_success = direct_wf_ts.prepare_on_clause(
|
||||||
|
self._as_list_of_tuples('on-success')
|
||||||
|
)
|
||||||
|
self._on_error = direct_wf_ts.prepare_on_clause(
|
||||||
|
self._as_list_of_tuples('on-error')
|
||||||
|
)
|
||||||
self._requires = data.get('requires', [])
|
self._requires = data.get('requires', [])
|
||||||
|
|
||||||
def validate_schema(self):
|
def validate_schema(self):
|
||||||
|
@ -244,9 +244,15 @@ class DirectWorkflowTaskSpec(TaskSpec):
|
|||||||
super(DirectWorkflowTaskSpec, self).__init__(data)
|
super(DirectWorkflowTaskSpec, self).__init__(data)
|
||||||
|
|
||||||
self._join = data.get('join')
|
self._join = data.get('join')
|
||||||
self._on_complete = self._as_list_of_tuples('on-complete')
|
self._on_complete = self.prepare_on_clause(
|
||||||
self._on_success = self._as_list_of_tuples('on-success')
|
self._as_list_of_tuples('on-complete')
|
||||||
self._on_error = self._as_list_of_tuples('on-error')
|
)
|
||||||
|
self._on_success = self.prepare_on_clause(
|
||||||
|
self._as_list_of_tuples('on-success')
|
||||||
|
)
|
||||||
|
self._on_error = self.prepare_on_clause(
|
||||||
|
self._as_list_of_tuples('on-error')
|
||||||
|
)
|
||||||
|
|
||||||
def validate_schema(self):
|
def validate_schema(self):
|
||||||
super(DirectWorkflowTaskSpec, self).validate_schema()
|
super(DirectWorkflowTaskSpec, self).validate_schema()
|
||||||
@ -262,6 +268,16 @@ class DirectWorkflowTaskSpec(TaskSpec):
|
|||||||
[self.validate_yaql_expr(t)
|
[self.validate_yaql_expr(t)
|
||||||
for t in ([val] if isinstance(val, six.string_types) else val)]
|
for t in ([val] if isinstance(val, six.string_types) else val)]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def prepare_on_clause(list_of_tuples):
|
||||||
|
for i, task in enumerate(list_of_tuples):
|
||||||
|
task_name, params = DirectWorkflowTaskSpec._parse_cmd_and_input(
|
||||||
|
task[0]
|
||||||
|
)
|
||||||
|
list_of_tuples[i] = (task_name, task[1], params)
|
||||||
|
|
||||||
|
return list_of_tuples
|
||||||
|
|
||||||
def get_join(self):
|
def get_join(self):
|
||||||
return self._join
|
return self._join
|
||||||
|
|
||||||
|
@ -146,7 +146,10 @@ def get_command_class(cmd_name):
|
|||||||
return RESERVED_CMDS[cmd_name] if cmd_name in RESERVED_CMDS else None
|
return RESERVED_CMDS[cmd_name] if cmd_name in RESERVED_CMDS else None
|
||||||
|
|
||||||
|
|
||||||
def create_command(cmd_name, wf_ex, task_spec, ctx):
|
def create_command(cmd_name, wf_ex, task_spec, ctx, explicit_params=None):
|
||||||
cmd_cls = get_command_class(cmd_name) or RunTask
|
cmd_cls = get_command_class(cmd_name) or RunTask
|
||||||
|
|
||||||
return cmd_cls(wf_ex, task_spec, ctx)
|
if issubclass(cmd_cls, SetWorkflowState):
|
||||||
|
return cmd_cls(wf_ex, task_spec, ctx, explicit_params.get('msg'))
|
||||||
|
else:
|
||||||
|
return cmd_cls(wf_ex, task_spec, ctx)
|
||||||
|
@ -103,7 +103,7 @@ class DirectWorkflowController(base.WorkflowController):
|
|||||||
|
|
||||||
cmds = []
|
cmds = []
|
||||||
|
|
||||||
for t_n in self._find_next_task_names(task_ex):
|
for t_n, params in self._find_next_tasks(task_ex):
|
||||||
t_s = self.wf_spec.get_tasks()[t_n]
|
t_s = self.wf_spec.get_tasks()[t_n]
|
||||||
|
|
||||||
if not (t_s or t_n in commands.RESERVED_CMDS):
|
if not (t_s or t_n in commands.RESERVED_CMDS):
|
||||||
@ -115,7 +115,8 @@ class DirectWorkflowController(base.WorkflowController):
|
|||||||
t_n,
|
t_n,
|
||||||
self.wf_ex,
|
self.wf_ex,
|
||||||
t_s,
|
t_s,
|
||||||
self._get_task_inbound_context(t_s)
|
self._get_task_inbound_context(t_s),
|
||||||
|
params
|
||||||
)
|
)
|
||||||
|
|
||||||
# NOTE(xylan): Decide whether or not a join task should run
|
# NOTE(xylan): Decide whether or not a join task should run
|
||||||
@ -153,7 +154,7 @@ class DirectWorkflowController(base.WorkflowController):
|
|||||||
def all_errors_handled(self):
|
def all_errors_handled(self):
|
||||||
for t_ex in wf_utils.find_error_task_executions(self.wf_ex):
|
for t_ex in wf_utils.find_error_task_executions(self.wf_ex):
|
||||||
|
|
||||||
tasks_on_error = self._find_next_task_names_for_clause(
|
tasks_on_error = self._find_next_tasks_for_clause(
|
||||||
self.wf_spec.get_on_error_clause(t_ex.name),
|
self.wf_spec.get_on_error_clause(t_ex.name),
|
||||||
data_flow.evaluate_task_outbound_context(t_ex)
|
data_flow.evaluate_task_outbound_context(t_ex)
|
||||||
)
|
)
|
||||||
@ -182,35 +183,44 @@ class DirectWorkflowController(base.WorkflowController):
|
|||||||
])
|
])
|
||||||
|
|
||||||
def _find_next_task_names(self, task_ex):
|
def _find_next_task_names(self, task_ex):
|
||||||
|
return [t[0] for t in self._find_next_tasks(task_ex)]
|
||||||
|
|
||||||
|
def _find_next_tasks(self, task_ex):
|
||||||
t_state = task_ex.state
|
t_state = task_ex.state
|
||||||
t_name = task_ex.name
|
t_name = task_ex.name
|
||||||
|
|
||||||
ctx = data_flow.evaluate_task_outbound_context(task_ex)
|
ctx = data_flow.evaluate_task_outbound_context(task_ex)
|
||||||
|
|
||||||
t_names = []
|
t_names_and_params = []
|
||||||
|
|
||||||
if states.is_completed(t_state):
|
if states.is_completed(t_state):
|
||||||
t_names += self._find_next_task_names_for_clause(
|
t_names_and_params += (
|
||||||
self.wf_spec.get_on_complete_clause(t_name),
|
self._find_next_tasks_for_clause(
|
||||||
ctx
|
self.wf_spec.get_on_complete_clause(t_name),
|
||||||
|
ctx
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if t_state == states.ERROR:
|
if t_state == states.ERROR:
|
||||||
t_names += self._find_next_task_names_for_clause(
|
t_names_and_params += (
|
||||||
self.wf_spec.get_on_error_clause(t_name),
|
self._find_next_tasks_for_clause(
|
||||||
ctx
|
self.wf_spec.get_on_error_clause(t_name),
|
||||||
|
ctx
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
elif t_state == states.SUCCESS:
|
elif t_state == states.SUCCESS:
|
||||||
t_names += self._find_next_task_names_for_clause(
|
t_names_and_params += (
|
||||||
self.wf_spec.get_on_success_clause(t_name),
|
self._find_next_tasks_for_clause(
|
||||||
ctx
|
self.wf_spec.get_on_success_clause(t_name),
|
||||||
|
ctx
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return t_names
|
return t_names_and_params
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _find_next_task_names_for_clause(clause, ctx):
|
def _find_next_tasks_for_clause(clause, ctx):
|
||||||
"""Finds next tasks names.
|
"""Finds next tasks names.
|
||||||
|
|
||||||
This method finds next task(command) base on given {name: condition}
|
This method finds next task(command) base on given {name: condition}
|
||||||
@ -226,8 +236,8 @@ class DirectWorkflowController(base.WorkflowController):
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
return [
|
return [
|
||||||
t_name
|
(t_name, expr.evaluate_recursively(params, ctx))
|
||||||
for t_name, condition in clause
|
for t_name, condition, params in clause
|
||||||
if not condition or expr.evaluate(condition, ctx)
|
if not condition or expr.evaluate(condition, ctx)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user