Trigger remaining-executions and first-exec-date

Adds the support of two optionals parameters for the cron-trigger resource:
  -first-execution-time : --first-time <"YYYY-MM-DD HH:MM">
  -remaining_occurrences : --count <integer>

The positional parameter "pattern" is now: --pattern <"* * * * *">

Implements: blueprint mistral-cron-triggers-start-and-repeat
Depends-On: I55bc28e98f89ffdfdce9cb3daa3848a17d85fd20
Change-Id: I2059db83e217ff415fb3f43dca27904b8f9acdce
This commit is contained in:
Pierre-Arthur MATHIEU
2015-03-12 16:50:11 +00:00
parent 6066a75dbe
commit 103e5ecb9a
6 changed files with 111 additions and 36 deletions

View File

@@ -24,17 +24,19 @@ class CronTrigger(base.Resource):
class CronTriggerManager(base.ResourceManager): class CronTriggerManager(base.ResourceManager):
resource_class = CronTrigger resource_class = CronTrigger
def create(self, name, pattern, workflow_name, workflow_input=None): def create(self, name, workflow_name, workflow_input=None, pattern=None,
first_time=None, count=None):
self._ensure_not_empty( self._ensure_not_empty(
name=name, name=name,
pattern=pattern,
workflow_name=workflow_name workflow_name=workflow_name
) )
data = { data = {
'name': name, 'name': name,
'pattern': pattern,
'workflow_name': workflow_name, 'workflow_name': workflow_name,
'pattern': pattern,
'first_execution_time': first_time,
'remaining_executions': count
} }
if workflow_input: if workflow_input:

View File

@@ -33,11 +33,12 @@ def format_list(trigger=None):
def format(trigger=None, lister=False): def format(trigger=None, lister=False):
columns = ( columns = (
'Name', 'Name',
'Pattern',
'Workflow', 'Workflow',
'Pattern',
# TODO(rakhmerov): Uncomment when passwords are handled properly. # TODO(rakhmerov): Uncomment when passwords are handled properly.
# TODO(rakhmerov): Add 'Workflow input' column. # TODO(rakhmerov): Add 'Workflow input' column.
'Next execution time', 'Next execution time',
'Remaining executions',
'Created at', 'Created at',
'Updated at' 'Updated at'
) )
@@ -49,11 +50,12 @@ def format(trigger=None, lister=False):
data = ( data = (
trigger.name, trigger.name,
trigger.pattern,
trigger.workflow_name, trigger.workflow_name,
trigger.pattern,
# TODO(rakhmerov): Uncomment when passwords are handled properly. # TODO(rakhmerov): Uncomment when passwords are handled properly.
# TODo(rakhmerov): Add 'wf_input' here. # TODo(rakhmerov): Add 'wf_input' here.
trigger.next_execution_time, trigger.next_execution_time,
trigger.remaining_executions,
trigger.created_at, trigger.created_at,
) )
@@ -100,7 +102,6 @@ class Create(show.ShowOne):
parser = super(Create, self).get_parser(prog_name) parser = super(Create, self).get_parser(prog_name)
parser.add_argument('name', help='Cron trigger name') parser.add_argument('name', help='Cron trigger name')
parser.add_argument('pattern', help='Cron trigger pattern')
parser.add_argument('workflow_name', help='Workflow name') parser.add_argument('workflow_name', help='Workflow name')
parser.add_argument( parser.add_argument(
@@ -109,6 +110,25 @@ class Create(show.ShowOne):
help='Workflow input' help='Workflow input'
) )
parser.add_argument(
'--pattern',
type=str,
help='Cron trigger pattern',
metavar='<* * * * *>'
)
parser.add_argument(
'--first-time',
type=str,
help="Date and time of the first execution",
metavar='<YYYY-MM-DD HH:MM>'
)
parser.add_argument(
'--count',
type=int,
help="Number of wanted executions",
metavar='<integer>'
)
return parser return parser
def take_action(self, parsed_args): def take_action(self, parsed_args):
@@ -124,9 +144,11 @@ class Create(show.ShowOne):
trigger = mgr.create( trigger = mgr.create(
parsed_args.name, parsed_args.name,
parsed_args.pattern,
parsed_args.workflow_name, parsed_args.workflow_name,
wf_input wf_input,
parsed_args.pattern,
parsed_args.first_time,
parsed_args.count
) )
return format(trigger) return format(trigger)

View File

@@ -115,12 +115,20 @@ class MistralClientTestBase(base.MistralCLIAuth, base.MistralCLIAltAuth):
return acts return acts
def cron_trigger_create(self, name, pattern, def cron_trigger_create(self, name, wf_name, wf_input, pattern=None,
wf_name, wf_input, admin=True): count=None, first_time=None, admin=True):
optional_params = ""
if pattern:
optional_params += ' --pattern "{}"'.format(pattern)
if count:
optional_params += ' --count {}'.format(count)
if first_time:
optional_params += ' --first-time "{}"'.format(first_time)
trigger = self.mistral_cli( trigger = self.mistral_cli(
admin, admin,
'cron-trigger-create', 'cron-trigger-create',
params='%s "%s" %s %s' % (name, pattern, wf_name, wf_input)) params='{} {} {} {}'.format(name, wf_name, wf_input,
optional_params))
self.addCleanup(self.mistral_cli, self.addCleanup(self.mistral_cli,
admin, admin,
'cron-trigger-delete', 'cron-trigger-delete',

View File

@@ -224,7 +224,7 @@ class CronTriggerIsolationCLITests(base_v2.MistralClientTestBase):
def test_cron_trigger_name_uniqueness(self): def test_cron_trigger_name_uniqueness(self):
wf = self.workflow_create(self.wf_def) wf = self.workflow_create(self.wf_def)
self.cron_trigger_create( self.cron_trigger_create(
"trigger", "5 * * * *", wf[0]["Name"], "{}") "trigger", wf[0]["Name"], "{}", "5 * * * *")
self.assertRaises( self.assertRaises(
exceptions.CommandFailed, exceptions.CommandFailed,
@@ -236,23 +236,20 @@ class CronTriggerIsolationCLITests(base_v2.MistralClientTestBase):
) )
wf = self.workflow_create(self.wf_def, admin=False) wf = self.workflow_create(self.wf_def, admin=False)
self.cron_trigger_create( self.cron_trigger_create("trigger", wf[0]["Name"], "{}", "5 * * * *",
"trigger", "5 * * * *", wf[0]["Name"], "{}", admin=False) None, None, admin=False)
self.assertRaises( self.assertRaises(
exceptions.CommandFailed, exceptions.CommandFailed,
self.cron_trigger_create, self.cron_trigger_create,
"trigger", "trigger", wf[0]["Name"], "{}", "5 * * * *",
"5 * * * *", None, None, admin=False
wf[0]["Name"],
"{}",
admin=False
) )
def test_cron_trigger_isolation(self): def test_cron_trigger_isolation(self):
wf = self.workflow_create(self.wf_def) wf = self.workflow_create(self.wf_def)
self.cron_trigger_create( self.cron_trigger_create(
"trigger", "5 * * * *", wf[0]["Name"], "{}") "trigger", wf[0]["Name"], "{}", "5 * * * *")
alt_trs = self.mistral_alt_user("cron-trigger-list") alt_trs = self.mistral_alt_user("cron-trigger-list")

View File

@@ -67,8 +67,8 @@ class SimpleMistralCLITests(base.MistralCLIAuth):
self.mistral('cron-trigger-list')) self.mistral('cron-trigger-list'))
self.assertTableStruct( self.assertTableStruct(
triggers, triggers,
['Name', 'Pattern', 'Workflow', 'Next execution time', ['Name', 'Workflow', 'Pattern', 'Next execution time',
'Created at', 'Updated at'] 'Remaining executions', 'Created at', 'Updated at']
) )
def test_actions_list(self): def test_actions_list(self):
@@ -369,16 +369,21 @@ class CronTriggerCLITests(base_v2.MistralClientTestBase):
def test_cron_trigger_create_delete(self): def test_cron_trigger_create_delete(self):
trigger = self.mistral_admin( trigger = self.mistral_admin(
'cron-trigger-create', 'cron-trigger-create',
params='trigger "5 * * * *" %s {}' % self.wf_name) params=('trigger %s {} --pattern "5 * * * *" --count 5'
' --first-time "4242-12-25 13:37"' % self.wf_name))
self.assertTableStruct(trigger, ['Field', 'Value']) self.assertTableStruct(trigger, ['Field', 'Value'])
tr_name = self.get_value_of_field(trigger, 'Name') tr_name = self.get_value_of_field(trigger, 'Name')
wf_name = self.get_value_of_field(trigger, 'Workflow') wf_name = self.get_value_of_field(trigger, 'Workflow')
created_at = self.get_value_of_field(trigger, 'Created at') created_at = self.get_value_of_field(trigger, 'Created at')
remain = self.get_value_of_field(trigger, 'Remaining executions')
next_time = self.get_value_of_field(trigger, 'Next execution time')
self.assertEqual('trigger', tr_name) self.assertEqual('trigger', tr_name)
self.assertEqual(self.wf_name, wf_name) self.assertEqual(self.wf_name, wf_name)
self.assertIsNotNone(created_at) self.assertIsNotNone(created_at)
self.assertEqual("4242-12-25 13:37:00", next_time)
self.assertEqual("5", remain)
trgs = self.mistral_admin('cron-trigger-list') trgs = self.mistral_admin('cron-trigger-list')
self.assertIn(tr_name, [tr['Name'] for tr in trgs]) self.assertIn(tr_name, [tr['Name'] for tr in trgs])
@@ -391,9 +396,9 @@ class CronTriggerCLITests(base_v2.MistralClientTestBase):
def test_two_cron_triggers_for_one_wf(self): def test_two_cron_triggers_for_one_wf(self):
self.cron_trigger_create( self.cron_trigger_create(
'trigger1', "5 * * * *", self.wf_name, '{}') 'trigger1', self.wf_name, '{}', "5 * * * *")
self.cron_trigger_create( self.cron_trigger_create(
'trigger2', "15 * * * *", self.wf_name, '{}') 'trigger2', self.wf_name, '{}', "15 * * * *")
trgs = self.mistral_admin('cron-trigger-list') trgs = self.mistral_admin('cron-trigger-list')
self.assertIn("trigger1", [tr['Name'] for tr in trgs]) self.assertIn("trigger1", [tr['Name'] for tr in trgs])
@@ -401,7 +406,7 @@ class CronTriggerCLITests(base_v2.MistralClientTestBase):
def test_cron_trigger_get(self): def test_cron_trigger_get(self):
trigger = self.cron_trigger_create( trigger = self.cron_trigger_create(
'trigger', "5 * * * *", self.wf_name, '{}') 'trigger', self.wf_name, '{}', "5 * * * *")
self.assertTableStruct(trigger, ['Field', 'Value']) self.assertTableStruct(trigger, ['Field', 'Value'])
tr_name = self.get_value_of_field(trigger, 'Name') tr_name = self.get_value_of_field(trigger, 'Name')
@@ -857,27 +862,28 @@ class NegativeCLITests(base_v2.MistralClientTestBase):
self.assertRaises(exceptions.CommandFailed, self.assertRaises(exceptions.CommandFailed,
self.mistral_admin, self.mistral_admin,
'cron-trigger-create', 'cron-trigger-create',
params='tr "" %s {}' % wf[0]['Name']) params='tr %s {}' % wf[0]['Name'])
def test_tr_create_invalid_pattern(self): def test_tr_create_invalid_pattern(self):
wf = self.workflow_create(self.wf_def) wf = self.workflow_create(self.wf_def)
self.assertRaises(exceptions.CommandFailed, self.assertRaises(exceptions.CommandFailed,
self.mistral_admin, self.mistral_admin,
'cron-trigger-create', 'cron-trigger-create',
params='tr "q" %s {}' % wf[0]['Name']) params='tr %s {} --pattern "q"' % wf[0]['Name'])
def test_tr_create_invalid_pattern_value_out_of_range(self): def test_tr_create_invalid_pattern_value_out_of_range(self):
wf = self.workflow_create(self.wf_def) wf = self.workflow_create(self.wf_def)
self.assertRaises(exceptions.CommandFailed, self.assertRaises(exceptions.CommandFailed,
self.mistral_admin, self.mistral_admin,
'cron-trigger-create', 'cron-trigger-create',
params='tr "88 * * * *" %s {}' % wf[0]['Name']) params='tr %s {}'
' --pattern "80 * * * *"' % wf[0]['Name'])
def test_tr_create_nonexistent_wf(self): def test_tr_create_nonexistent_wf(self):
self.assertRaises(exceptions.CommandFailed, self.assertRaises(exceptions.CommandFailed,
self.mistral_admin, self.mistral_admin,
'cron-trigger-create', 'cron-trigger-create',
params='tr "* * * * *" wb.wf1 {}') params='tr wb.wf1 {} --pattern "* * * * *"')
def test_tr_delete_nonexistant_tr(self): def test_tr_delete_nonexistant_tr(self):
self.assertRaises(exceptions.CommandFailed, self.assertRaises(exceptions.CommandFailed,
@@ -891,6 +897,41 @@ class NegativeCLITests(base_v2.MistralClientTestBase):
'cron-trigger-get', 'cron-trigger-get',
params='tr') params='tr')
def test_tr_create_invalid_count(self):
self.assertRaises(exceptions.CommandFailed,
self.mistral_admin,
'cron-trigger-create',
params='tr wb.wf1 {}'
' --pattern "* * * * *" --count q')
def test_tr_create_negative_count(self):
self.assertRaises(exceptions.CommandFailed,
self.mistral_admin,
'cron-trigger-create',
params='tr wb.wf1 {}'
' --pattern "* * * * *" --count -1')
def test_tr_create_invalid_first_date(self):
self.assertRaises(exceptions.CommandFailed,
self.mistral_admin,
'cron-trigger-create',
params='tr wb.wf1 {}'
' --pattern "* * * * *"'
' --first-date "q"')
def test_tr_create_count_only(self):
self.assertRaises(exceptions.CommandFailed,
self.mistral_admin,
'cron-trigger-create',
params='tr wb.wf1 {} --count 42')
def test_tr_create_date_and_count_without_pattern(self):
self.assertRaises(exceptions.CommandFailed,
self.mistral_admin,
'cron-trigger-create',
params='tr wb.wf1 {} --count 42'
' --first-time "4242-12-25 13:37"')
def test_action_get_nonexistent(self): def test_action_get_nonexistent(self):
self.assertRaises(exceptions.CommandFailed, self.assertRaises(exceptions.CommandFailed,
self.mistral, self.mistral,

View File

@@ -23,10 +23,11 @@ from mistralclient.tests.unit import base
TRIGGER_DICT = { TRIGGER_DICT = {
'name': 'my_trigger', 'name': 'my_trigger',
'pattern': '* * * * *',
'workflow_name': 'flow1', 'workflow_name': 'flow1',
'workflow_input': {}, 'workflow_input': {},
'next_execution_time': '1', 'pattern': '* * * * *',
'next_execution_time': '4242-12-20 13:37',
'remaining_executions': 5,
'created_at': '1', 'created_at': '1',
'updated_at': '1' 'updated_at': '1'
} }
@@ -44,11 +45,13 @@ class TestCLIWorkbooksV2(base.BaseCommandTest):
result = self.call( result = self.call(
cron_triggers_cmd.Create, cron_triggers_cmd.Create,
app_args=['my_trigger', '* * * * *', 'flow1'] app_args=['my_trigger', 'flow1', '--pattern', '* * * * *',
'--count', '5', '--first-time', '4242-12-20 13:37']
) )
self.assertEqual( self.assertEqual(
('my_trigger', '* * * * *', 'flow1', '1', '1', '1'), ('my_trigger', 'flow1', '* * * * *', '4242-12-20 13:37', 5, '1',
'1'),
result[1] result[1]
) )
@@ -59,7 +62,8 @@ class TestCLIWorkbooksV2(base.BaseCommandTest):
result = self.call(cron_triggers_cmd.List) result = self.call(cron_triggers_cmd.List)
self.assertEqual( self.assertEqual(
[('my_trigger', '* * * * *', 'flow1', '1', '1', '1')], [('my_trigger', 'flow1', '* * * * *', '4242-12-20 13:37', 5, '1',
'1')],
result[1] result[1]
) )
@@ -70,7 +74,8 @@ class TestCLIWorkbooksV2(base.BaseCommandTest):
result = self.call(cron_triggers_cmd.Get, app_args=['name']) result = self.call(cron_triggers_cmd.Get, app_args=['name'])
self.assertEqual( self.assertEqual(
('my_trigger', '* * * * *', 'flow1', '1', '1', '1'), ('my_trigger', 'flow1', '* * * * *', '4242-12-20 13:37', 5, '1',
'1'),
result[1] result[1]
) )