diff --git a/functionaltests/resources/v2/wf_single_v2.yaml b/functionaltests/resources/v2/wf_single_v2.yaml new file mode 100644 index 00000000..ad213d59 --- /dev/null +++ b/functionaltests/resources/v2/wf_single_v2.yaml @@ -0,0 +1,11 @@ +--- +version: '2.0' + +wf_single: + type: direct + + tasks: + hello: + action: std.echo output="Hello" + publish: + result: <% $.hello %> diff --git a/mistralclient/api/v2/workflows.py b/mistralclient/api/v2/workflows.py index 273c2b0c..6cef0693 100644 --- a/mistralclient/api/v2/workflows.py +++ b/mistralclient/api/v2/workflows.py @@ -43,11 +43,13 @@ class WorkflowManager(base.ResourceManager): return [self.resource_class(self, resource_data) for resource_data in base.extract_json(resp, 'workflows')] - def update(self, definition, scope='private'): + def update(self, definition, scope='private', id=None): self._ensure_not_empty(definition=definition) + url_pre = ('/workflows/%s' % id) if id else '/workflows' + resp = self.client.http_client.put( - '/workflows?scope=%s' % scope, + '%s?scope=%s' % (url_pre, scope), definition, headers={'content-type': 'text/plain'} ) @@ -55,6 +57,9 @@ class WorkflowManager(base.ResourceManager): if resp.status_code != 200: self._raise_api_exception(resp) + if id: + return self.resource_class(self, base.extract_json(resp, None)) + return [self.resource_class(self, resource_data) for resource_data in base.extract_json(resp, 'workflows')] @@ -81,15 +86,15 @@ class WorkflowManager(base.ResourceManager): response_key='workflows', ) - def get(self, name): - self._ensure_not_empty(name=name) + def get(self, identifier): + self._ensure_not_empty(identifier=identifier) - return self._get('/workflows/%s' % name) + return self._get('/workflows/%s' % identifier) - def delete(self, name): - self._ensure_not_empty(name=name) + def delete(self, identifier): + self._ensure_not_empty(identifier=identifier) - self._delete('/workflows/%s' % name) + self._delete('/workflows/%s' % identifier) def validate(self, definition): self._ensure_not_empty(definition=definition) diff --git a/mistralclient/commands/v2/base.py b/mistralclient/commands/v2/base.py index a17f7002..f1661f90 100644 --- a/mistralclient/commands/v2/base.py +++ b/mistralclient/commands/v2/base.py @@ -41,7 +41,11 @@ class MistralLister(lister.Lister): f = self._get_format_function() - data = [f(r)[1] for r in self._get_resources(parsed_args)] + ret = self._get_resources(parsed_args) + if not isinstance(ret, list): + ret = [ret] + + data = [f(r)[1] for r in ret] if data: return f()[0], data diff --git a/mistralclient/commands/v2/workflows.py b/mistralclient/commands/v2/workflows.py index 3ff6ab57..f90bb906 100644 --- a/mistralclient/commands/v2/workflows.py +++ b/mistralclient/commands/v2/workflows.py @@ -80,13 +80,13 @@ class Get(show.ShowOne): def get_parser(self, prog_name): parser = super(Get, self).get_parser(prog_name) - parser.add_argument('name', help='Workflow name') + parser.add_argument('identifier', help='Workflow ID or name.') return parser def take_action(self, parsed_args): mistral_client = self.app.client_manager.workflow_engine - wf = mistral_client.workflows.get(parsed_args.name) + wf = mistral_client.workflows.get(parsed_args.identifier) return format(wf) @@ -100,7 +100,7 @@ class Create(base.MistralLister): parser.add_argument( 'definition', type=argparse.FileType('r'), - help='Workflow definition file' + help='Workflow definition file.' ) parser.add_argument( '--public', @@ -134,7 +134,11 @@ class Delete(command.Command): def get_parser(self, prog_name): parser = super(Delete, self).get_parser(prog_name) - parser.add_argument('name', nargs='+', help='Name of workflow(s).') + parser.add_argument( + 'identifier', + nargs='+', + help='Name or ID of workflow(s).' + ) return parser @@ -142,7 +146,7 @@ class Delete(command.Command): mistral_client = self.app.client_manager.workflow_engine utils.do_action_on_many( lambda s: mistral_client.workflows.delete(s), - parsed_args.name, + parsed_args.identifier, "Request to delete workflow %s has been accepted.", "Unable to delete the specified workflow(s)." ) @@ -159,6 +163,7 @@ class Update(base.MistralLister): type=argparse.FileType('r'), help='Workflow definition' ) + parser.add_argument('--id', help='Workflow ID.') parser.add_argument( '--public', action='store_true', @@ -176,7 +181,8 @@ class Update(base.MistralLister): return mistral_client.workflows.update( parsed_args.definition.read(), - scope=scope + scope=scope, + id=parsed_args.id ) @@ -186,15 +192,15 @@ class GetDefinition(command.Command): def get_parser(self, prog_name): parser = super(GetDefinition, self).get_parser(prog_name) - parser.add_argument('name', help='Workflow name') + parser.add_argument('identifier', help='Workflow ID or name.') return parser def take_action(self, parsed_args): mistral_client = self.app.client_manager.workflow_engine - definition = mistral_client.workflows.get(parsed_args.name).definition + wf = mistral_client.workflows.get(parsed_args.identifier) - self.app.stdout.write(definition or "\n") + self.app.stdout.write(wf.definition or "\n") class Validate(show.ShowOne): diff --git a/mistralclient/tests/functional/cli/v2/base_v2.py b/mistralclient/tests/functional/cli/v2/base_v2.py index 34a7d423..62c685a5 100644 --- a/mistralclient/tests/functional/cli/v2/base_v2.py +++ b/mistralclient/tests/functional/cli/v2/base_v2.py @@ -40,6 +40,9 @@ class MistralClientTestBase(base.MistralCLIAuth, base.MistralCLIAltAuth): cls.wf_def = os.path.relpath( 'functionaltests/resources/v2/wf_v2.yaml', os.getcwd()) + cls.wf_single_def = os.path.relpath( + 'functionaltests/resources/v2/wf_single_v2.yaml', os.getcwd()) + cls.wf_with_delay_def = os.path.relpath( 'functionaltests/resources/v2/wf_delay_v2.yaml', os.getcwd()) @@ -109,7 +112,7 @@ class MistralClientTestBase(base.MistralCLIAuth, base.MistralCLIAltAuth): self.mistral_cli, admin, 'workflow-delete', - params=workflow['Name'] + params=workflow['ID'] ) return wf diff --git a/mistralclient/tests/functional/cli/v2/cli_multi_tenancy_tests.py b/mistralclient/tests/functional/cli/v2/cli_multi_tenancy_tests.py index a68f111e..eef82e34 100644 --- a/mistralclient/tests/functional/cli/v2/cli_multi_tenancy_tests.py +++ b/mistralclient/tests/functional/cli/v2/cli_multi_tenancy_tests.py @@ -153,7 +153,7 @@ class WorkflowIsolationCLITests(base_v2.MistralClientTestBase): exceptions.CommandFailed, self.mistral_alt_user, "workflow-get", - params=wf[0]["Name"] + params=wf[0]["ID"] ) def test_create_public_workflow(self): @@ -176,7 +176,7 @@ class WorkflowIsolationCLITests(base_v2.MistralClientTestBase): exceptions.CommandFailed, self.mistral_alt_user, "workflow-delete", - params=wf[0]["Name"] + params=wf[0]["ID"] ) @@ -246,30 +246,45 @@ class ActionIsolationCLITests(base_v2.MistralClientTestBase): class CronTriggerIsolationCLITests(base_v2.MistralClientTestBase): - def test_cron_trigger_name_uniqueness(self): wf = self.workflow_create(self.wf_def) self.cron_trigger_create( - "trigger", wf[0]["Name"], "{}", "5 * * * *") + "admin_trigger", + wf[0]["ID"], + "{}", + "5 * * * *" + ) self.assertRaises( exceptions.CommandFailed, self.cron_trigger_create, - "trigger", - "5 * * * *", - wf[0]["Name"], + "admin_trigger", + wf[0]["ID"], "{}" + "5 * * * *", ) wf = self.workflow_create(self.wf_def, admin=False) - self.cron_trigger_create("trigger", wf[0]["Name"], "{}", "5 * * * *", - None, None, admin=False) + self.cron_trigger_create( + "user_trigger", + wf[0]["ID"], + "{}", + "5 * * * *", + None, + None, + admin=False + ) self.assertRaises( exceptions.CommandFailed, self.cron_trigger_create, - "trigger", wf[0]["Name"], "{}", "5 * * * *", - None, None, admin=False + "user_trigger", + wf[0]["ID"], + "{}", + "5 * * * *", + None, + None, + admin=False ) def test_cron_trigger_isolation(self): diff --git a/mistralclient/tests/functional/cli/v2/cli_tests_v2.py b/mistralclient/tests/functional/cli/v2/cli_tests_v2.py index 2ecc2432..6c949f6a 100644 --- a/mistralclient/tests/functional/cli/v2/cli_tests_v2.py +++ b/mistralclient/tests/functional/cli/v2/cli_tests_v2.py @@ -241,37 +241,85 @@ class WorkflowCLITests(base_v2.MistralClientTestBase): def test_workflow_update(self): wf = self.workflow_create(self.wf_def) wf_name = wf[0]['Name'] + wf_id = wf[0]['ID'] created_wf_info = self.get_item_info( - get_from=wf, get_by='Name', value=wf_name) + get_from=wf, + get_by='Name', + value=wf_name + ) + # Update a workflow with definition unchanged. upd_wf = self.mistral_admin( - 'workflow-update', params='{0}'.format(self.wf_def)) + 'workflow-update', + params='{0}'.format(self.wf_def) + ) + self.assertTableStruct(upd_wf, ['Name', 'Created at', 'Updated at']) updated_wf_info = self.get_item_info( - get_from=upd_wf, get_by='Name', value=wf_name) + get_from=upd_wf, + get_by='Name', + value=wf_name + ) self.assertEqual(wf_name, upd_wf[0]['Name']) + self.assertEqual( + created_wf_info['Created at'].split(".")[0], + updated_wf_info['Created at'] + ) + self.assertEqual( + created_wf_info['Updated at'], + updated_wf_info['Updated at'] + ) - self.assertEqual(created_wf_info['Created at'].split(".")[0], - updated_wf_info['Created at']) - self.assertEqual(created_wf_info['Updated at'], - updated_wf_info['Updated at']) - + # Update a workflow with definition changed. upd_wf = self.mistral_admin( - 'workflow-update', params='{0}'.format(self.wf_with_delay_def)) + 'workflow-update', + params='{0}'.format(self.wf_with_delay_def) + ) + self.assertTableStruct(upd_wf, ['Name', 'Created at', 'Updated at']) updated_wf_info = self.get_item_info( - get_from=upd_wf, get_by='Name', value=wf_name) + get_from=upd_wf, + get_by='Name', + value=wf_name + ) self.assertEqual(wf_name, upd_wf[0]['Name']) + self.assertEqual( + created_wf_info['Created at'].split(".")[0], + updated_wf_info['Created at'] + ) + self.assertNotEqual( + created_wf_info['Updated at'], + updated_wf_info['Updated at'] + ) - self.assertEqual(created_wf_info['Created at'].split(".")[0], - updated_wf_info['Created at']) - self.assertNotEqual(created_wf_info['Updated at'], - updated_wf_info['Updated at']) + # Update a workflow with uuid. + upd_wf = self.mistral_admin( + 'workflow-update', + params='{0} --id {1}'.format(self.wf_with_delay_def, wf_id) + ) + + self.assertTableStruct(upd_wf, ['Name', 'Created at', 'Updated at']) + + updated_wf_info = self.get_item_info( + get_from=upd_wf, + get_by='ID', + value=wf_id + ) + + self.assertEqual(wf_name, upd_wf[0]['Name']) + self.assertEqual( + created_wf_info['Created at'].split(".")[0], + updated_wf_info['Created at'] + ) + self.assertNotEqual( + created_wf_info['Updated at'], + updated_wf_info['Updated at'] + ) def test_workflow_update_truncate_input(self): input_value = "very_long_input_parameter_name_that_should_be_truncated" @@ -301,6 +349,15 @@ class WorkflowCLITests(base_v2.MistralClientTestBase): fetched_wf_name = self.get_value_of_field(fetched, 'Name') self.assertEqual(wf_name, fetched_wf_name) + def test_workflow_get_with_id(self): + created = self.workflow_create(self.wf_def) + wf_name = created[0]['Name'] + wf_id = created[0]['ID'] + + fetched = self.mistral_admin('workflow-get', params=wf_id) + fetched_wf_name = self.get_value_of_field(fetched, 'Name') + self.assertEqual(wf_name, fetched_wf_name) + def test_workflow_get_definition(self): wf = self.workflow_create(self.wf_def) wf_name = wf[0]['Name'] diff --git a/mistralclient/tests/unit/v2/test_cli_action_execs.py b/mistralclient/tests/unit/v2/test_cli_action_execs.py index c1acdbe0..dfb051aa 100644 --- a/mistralclient/tests/unit/v2/test_cli_action_execs.py +++ b/mistralclient/tests/unit/v2/test_cli_action_execs.py @@ -93,7 +93,7 @@ class TestCLIActionExecutions(base.BaseCommandTest): ) def test_list(self): - self.client.action_executions.list.return_value = (ACTION_EX,) + self.client.action_executions.list.return_value = [ACTION_EX] result = self.call(action_ex_cmd.List) diff --git a/mistralclient/tests/unit/v2/test_cli_actions.py b/mistralclient/tests/unit/v2/test_cli_actions.py index c5b88d3b..5466dff6 100644 --- a/mistralclient/tests/unit/v2/test_cli_actions.py +++ b/mistralclient/tests/unit/v2/test_cli_actions.py @@ -52,7 +52,7 @@ ACTION_WITH_DEF = actions.Action(mock, ACTION_WITH_DEF_DICT) class TestCLIActionsV2(base.BaseCommandTest): @mock.patch('argparse.open', create=True) def test_create(self, mock_open): - self.client.actions.create.return_value = (ACTION,) + self.client.actions.create.return_value = [ACTION] result = self.call(action_cmd.Create, app_args=['1.txt']) @@ -63,7 +63,7 @@ class TestCLIActionsV2(base.BaseCommandTest): @mock.patch('argparse.open', create=True) def test_create_public(self, mock_open): - self.client.actions.create.return_value = (ACTION,) + self.client.actions.create.return_value = [ACTION] result = self.call( action_cmd.Create, @@ -91,7 +91,7 @@ class TestCLIActionsV2(base.BaseCommandTest): mock.Mock(), action_long_input_dict ) - self.client.actions.create.return_value = (workflow_long_input,) + self.client.actions.create.return_value = [workflow_long_input] result = self.call(action_cmd.Create, app_args=['1.txt']) @@ -103,7 +103,7 @@ class TestCLIActionsV2(base.BaseCommandTest): @mock.patch('argparse.open', create=True) def test_update(self, mock_open): - self.client.actions.update.return_value = (ACTION,) + self.client.actions.update.return_value = [ACTION] result = self.call(action_cmd.Update, app_args=['my_action.yaml']) @@ -114,7 +114,7 @@ class TestCLIActionsV2(base.BaseCommandTest): @mock.patch('argparse.open', create=True) def test_update_public(self, mock_open): - self.client.actions.update.return_value = (ACTION,) + self.client.actions.update.return_value = [ACTION] result = self.call( action_cmd.Update, @@ -132,7 +132,7 @@ class TestCLIActionsV2(base.BaseCommandTest): ) def test_list(self): - self.client.actions.list.return_value = (ACTION,) + self.client.actions.list.return_value = [ACTION] result = self.call(action_cmd.List) diff --git a/mistralclient/tests/unit/v2/test_cli_cron_triggers.py b/mistralclient/tests/unit/v2/test_cli_cron_triggers.py index fcfc9039..f1635838 100644 --- a/mistralclient/tests/unit/v2/test_cli_cron_triggers.py +++ b/mistralclient/tests/unit/v2/test_cli_cron_triggers.py @@ -58,7 +58,7 @@ class TestCLITriggersV2(base.BaseCommandTest): ) def test_list(self): - self.client.cron_triggers.list.return_value = (TRIGGER,) + self.client.cron_triggers.list.return_value = [TRIGGER] result = self.call(cron_triggers_cmd.List) diff --git a/mistralclient/tests/unit/v2/test_cli_environments.py b/mistralclient/tests/unit/v2/test_cli_environments.py index 16a8dab5..4c24618e 100644 --- a/mistralclient/tests/unit/v2/test_cli_environments.py +++ b/mistralclient/tests/unit/v2/test_cli_environments.py @@ -92,7 +92,7 @@ class TestCLIEnvironmentsV2(base.BaseCommandTest): self._test_update(yml) def test_list(self): - self.client.environments.list.return_value = (ENVIRONMENT,) + self.client.environments.list.return_value = [ENVIRONMENT] expected = (ENVIRONMENT_DICT['name'], ENVIRONMENT_DICT['description'], ENVIRONMENT_DICT['scope'], diff --git a/mistralclient/tests/unit/v2/test_cli_executions.py b/mistralclient/tests/unit/v2/test_cli_executions.py index 9a22e605..2babeea4 100644 --- a/mistralclient/tests/unit/v2/test_cli_executions.py +++ b/mistralclient/tests/unit/v2/test_cli_executions.py @@ -139,7 +139,7 @@ class TestCLIExecutionsV2(base.BaseCommandTest): ) def test_list(self): - self.client.executions.list.return_value = (EXEC, SUB_WF_EXEC) + self.client.executions.list.return_value = [EXEC, SUB_WF_EXEC] result = self.call(execution_cmd.List) @@ -149,7 +149,7 @@ class TestCLIExecutionsV2(base.BaseCommandTest): ) def test_list_with_pagination(self): - self.client.executions.list.return_value = (EXEC,) + self.client.executions.list.return_value = [EXEC] self.call(execution_cmd.List) self.client.executions.list.assert_called_once_with( diff --git a/mistralclient/tests/unit/v2/test_cli_services.py b/mistralclient/tests/unit/v2/test_cli_services.py index 79228c0c..09e4e862 100644 --- a/mistralclient/tests/unit/v2/test_cli_services.py +++ b/mistralclient/tests/unit/v2/test_cli_services.py @@ -29,7 +29,7 @@ SERVICE = services.Service(mock, SERVICE_DICT) class TestCLIServicesV2(base.BaseCommandTest): def test_list(self): - self.client.services.list.return_value = (SERVICE,) + self.client.services.list.return_value = [SERVICE] expected = (SERVICE_DICT['name'], SERVICE_DICT['type'],) result = self.call(service_cmd.List) diff --git a/mistralclient/tests/unit/v2/test_cli_tasks.py b/mistralclient/tests/unit/v2/test_cli_tasks.py index 8543c06c..10f97ff5 100644 --- a/mistralclient/tests/unit/v2/test_cli_tasks.py +++ b/mistralclient/tests/unit/v2/test_cli_tasks.py @@ -49,14 +49,14 @@ EXPECTED_TASK_RESULT = ('123', 'some', 'thing', '321', 'RUNNING', None) class TestCLITasksV2(base.BaseCommandTest): def test_list(self): - self.client.tasks.list.return_value = (TASK,) + self.client.tasks.list.return_value = [TASK] result = self.call(task_cmd.List) self.assertEqual([EXPECTED_TASK_RESULT], result[1]) def test_list_with_workflow_execution(self): - self.client.tasks.list.return_value = (TASK,) + self.client.tasks.list.return_value = [TASK] result = self.call(task_cmd.List, app_args=['workflow_execution']) diff --git a/mistralclient/tests/unit/v2/test_cli_workbooks.py b/mistralclient/tests/unit/v2/test_cli_workbooks.py index f2e5e43d..a7899393 100644 --- a/mistralclient/tests/unit/v2/test_cli_workbooks.py +++ b/mistralclient/tests/unit/v2/test_cli_workbooks.py @@ -65,7 +65,7 @@ class TestCLIWorkbooksV2(base.BaseCommandTest): self.assertEqual(('a', 'a, b', '1', '1'), result[1]) def test_list(self): - self.client.workbooks.list.return_value = (WORKBOOK,) + self.client.workbooks.list.return_value = [WORKBOOK] result = self.call(workbook_cmd.List) diff --git a/mistralclient/tests/unit/v2/test_cli_workflows.py b/mistralclient/tests/unit/v2/test_cli_workflows.py index 9eab9406..38c5fa55 100644 --- a/mistralclient/tests/unit/v2/test_cli_workflows.py +++ b/mistralclient/tests/unit/v2/test_cli_workflows.py @@ -50,7 +50,7 @@ WORKFLOW_WITH_DEF = workflows.Workflow(mock, WF_WITH_DEF_DICT) class TestCLIWorkflowsV2(base.BaseCommandTest): @mock.patch('argparse.open', create=True) def test_create(self, mock_open): - self.client.workflows.create.return_value = (WORKFLOW,) + self.client.workflows.create.return_value = [WORKFLOW] result = self.call(workflow_cmd.Create, app_args=['1.txt']) @@ -61,7 +61,7 @@ class TestCLIWorkflowsV2(base.BaseCommandTest): @mock.patch('argparse.open', create=True) def test_create_public(self, mock_open): - self.client.workflows.create.return_value = (WORKFLOW,) + self.client.workflows.create.return_value = [WORKFLOW] result = self.call( workflow_cmd.Create, @@ -86,7 +86,7 @@ class TestCLIWorkflowsV2(base.BaseCommandTest): ) wf_long_input_dict['input'] = long_input workflow_long_input = workflows.Workflow(mock, wf_long_input_dict) - self.client.workflows.create.return_value = (workflow_long_input,) + self.client.workflows.create.return_value = [workflow_long_input] result = self.call(workflow_cmd.Create, app_args=['1.txt']) @@ -98,7 +98,7 @@ class TestCLIWorkflowsV2(base.BaseCommandTest): @mock.patch('argparse.open', create=True) def test_update(self, mock_open): - self.client.workflows.update.return_value = (WORKFLOW,) + self.client.workflows.update.return_value = [WORKFLOW] result = self.call(workflow_cmd.Update, app_args=['1.txt']) @@ -109,7 +109,7 @@ class TestCLIWorkflowsV2(base.BaseCommandTest): @mock.patch('argparse.open', create=True) def test_update_public(self, mock_open): - self.client.workflows.update.return_value = (WORKFLOW,) + self.client.workflows.update.return_value = [WORKFLOW] result = self.call( workflow_cmd.Update, @@ -126,8 +126,22 @@ class TestCLIWorkflowsV2(base.BaseCommandTest): self.client.workflows.update.call_args[1]['scope'] ) + @mock.patch('argparse.open', create=True) + def test_update_with_id(self, mock_open): + self.client.workflows.update.return_value = WORKFLOW + + result = self.call( + workflow_cmd.Update, + app_args=['1.txt', '--id', '1-2-3-4'] + ) + + self.assertEqual( + [('1-2-3-4', 'a', '12345', 'a, b', 'param', '1', '1')], + result[1] + ) + def test_list(self): - self.client.workflows.list.return_value = (WORKFLOW,) + self.client.workflows.list.return_value = [WORKFLOW] result = self.call(workflow_cmd.List) diff --git a/mistralclient/tests/unit/v2/test_workflows.py b/mistralclient/tests/unit/v2/test_workflows.py index bc592b1b..c4fc2f02 100644 --- a/mistralclient/tests/unit/v2/test_workflows.py +++ b/mistralclient/tests/unit/v2/test_workflows.py @@ -68,6 +68,20 @@ class TestWorkflowsV2(base.BaseClientV2Test): headers={'content-type': 'text/plain'} ) + def test_update_with_id(self): + mock = self.mock_http_put(content=WORKFLOW) + + wf = self.workflows.update(WF_DEF, id='123') + + self.assertIsNotNone(wf) + self.assertEqual(WF_DEF, wf.definition) + + mock.assert_called_once_with( + '/workflows/123?scope=private', + WF_DEF, + headers={'content-type': 'text/plain'} + ) + def test_list(self): mock = self.mock_http_get(content={'workflows': [WORKFLOW]})