diff --git a/mistralclient/api/v2/workbooks.py b/mistralclient/api/v2/workbooks.py index d046bb88..c890d777 100644 --- a/mistralclient/api/v2/workbooks.py +++ b/mistralclient/api/v2/workbooks.py @@ -25,7 +25,22 @@ class Workbook(base.Resource): class WorkbookManager(base.ResourceManager): resource_class = Workbook - def create(self, definition, scope='private'): + def _get_workbooks_url(self, resource=None, namespace=None, scope=None): + path = '/workbooks' + + if resource: + path += '/%s' % resource + + if scope and namespace: + path += '?scope=%s&namespace=%s' % (scope, namespace) + elif scope: + path += '?scope=%s' % scope + elif namespace: + path += '?namespace=%s' % namespace + + return path + + def create(self, definition, namespace='', scope='private'): self._ensure_not_empty(definition=definition) # If the specified definition is actually a file, read in the @@ -34,7 +49,7 @@ class WorkbookManager(base.ResourceManager): try: resp = self.http_client.post( - '/workbooks?scope=%s' % scope, + self._get_workbooks_url(None, namespace, scope), definition, headers={'content-type': 'text/plain'} ) @@ -46,7 +61,7 @@ class WorkbookManager(base.ResourceManager): return self.resource_class(self, base.extract_json(resp, None)) - def update(self, definition, scope='private'): + def update(self, definition, namespace='', scope='private'): self._ensure_not_empty(definition=definition) # If the specified definition is actually a file, read in the @@ -55,7 +70,7 @@ class WorkbookManager(base.ResourceManager): try: resp = self.http_client.put( - '/workbooks?scope=%s' % scope, + self._get_workbooks_url(None, namespace, scope), definition, headers={'content-type': 'text/plain'} ) @@ -67,18 +82,23 @@ class WorkbookManager(base.ResourceManager): return self.resource_class(self, base.extract_json(resp, None)) - def list(self): - return self._list('/workbooks', response_key='workbooks') + def list(self, namespace=''): + return self._list( + self._get_workbooks_url(None, namespace), + response_key='workbooks' + ) - def get(self, name): + def get(self, name, namespace=''): self._ensure_not_empty(name=name) - return self._get('/workbooks/%s' % name) + return self._get( + self._get_workbooks_url(name, namespace) + ) - def delete(self, name): + def delete(self, name, namespace=''): self._ensure_not_empty(name=name) - self._delete('/workbooks/%s' % name) + self._delete(self._get_workbooks_url(name, namespace)) def validate(self, definition): self._ensure_not_empty(definition=definition) diff --git a/mistralclient/commands/v2/workbooks.py b/mistralclient/commands/v2/workbooks.py index 8e290100..3c015373 100644 --- a/mistralclient/commands/v2/workbooks.py +++ b/mistralclient/commands/v2/workbooks.py @@ -24,6 +24,7 @@ from mistralclient import utils def format(workbook=None): columns = ( 'Name', + 'Namespace', 'Tags', 'Scope', 'Created at', @@ -33,6 +34,7 @@ def format(workbook=None): if workbook: data = ( workbook.name, + workbook.namespace, base.wrap(', '.join(workbook.tags or '')) or '', workbook.scope, workbook.created_at, @@ -66,16 +68,23 @@ class Get(command.ShowOne): def get_parser(self, prog_name): parser = super(Get, self).get_parser(prog_name) + parser.add_argument('workbook', help='Workbook name') + parser.add_argument( - 'workbook', - help='Workbook name' + '--namespace', + nargs='?', + default='', + help="Namespace to get the workbook from." ) return parser def take_action(self, parsed_args): mistral_client = self.app.client_manager.workflow_engine - workbook = mistral_client.workbooks.get(parsed_args.workbook) + workbook = mistral_client.workbooks.get( + parsed_args.workbook, + parsed_args.namespace + ) return format(workbook) @@ -97,6 +106,13 @@ class Create(command.ShowOne): help='With this flag workbook will be marked as "public".' ) + parser.add_argument( + '--namespace', + nargs='?', + default='', + help="Namespace to create the workbook within." + ) + return parser def take_action(self, parsed_args): @@ -105,6 +121,7 @@ class Create(command.ShowOne): mistral_client = self.app.client_manager.workflow_engine workbook = mistral_client.workbooks.create( parsed_args.definition.read(), + namespace=parsed_args.namespace, scope=scope ) @@ -118,13 +135,20 @@ class Delete(command.Command): parser = super(Delete, self).get_parser(prog_name) parser.add_argument('workbook', nargs='+', help='Name of workbook(s).') + parser.add_argument( + '--namespace', + nargs='?', + default=None, + help="Namespace to delete the workbook(s) from." + ) return parser def take_action(self, parsed_args): mistral_client = self.app.client_manager.workflow_engine utils.do_action_on_many( - lambda s: mistral_client.workbooks.delete(s), + lambda s: mistral_client.workbooks.delete(s, + parsed_args.namespace), parsed_args.workbook, "Request to delete workbook %s has been accepted.", "Unable to delete the specified workbook(s)." @@ -142,6 +166,12 @@ class Update(command.ShowOne): type=argparse.FileType('r'), help='Workbook definition file' ) + parser.add_argument( + '--namespace', + nargs='?', + default=None, + help="Namespace to update the workbook in." + ) parser.add_argument( '--public', action='store_true', @@ -156,6 +186,7 @@ class Update(command.ShowOne): mistral_client = self.app.client_manager.workflow_engine workbook = mistral_client.workbooks.update( parsed_args.definition.read(), + namespace=parsed_args.namespace, scope=scope ) diff --git a/mistralclient/tests/functional/cli/v2/base_v2.py b/mistralclient/tests/functional/cli/v2/base_v2.py index f4095dd7..9eee93cf 100644 --- a/mistralclient/tests/functional/cli/v2/base_v2.py +++ b/mistralclient/tests/functional/cli/v2/base_v2.py @@ -111,12 +111,21 @@ class MistralClientTestBase(base.MistralCLIAuth, base.MistralCLIAltAuth): else: return self.mistral_alt_user(cmd, params) - def workbook_create(self, wb_def, admin=True, scope='private'): + def workbook_create(self, wb_def, namespace='', admin=True, + scope='private'): + params = '{0}'.format(wb_def) + namespace_params = '' + + if namespace: + namespace_params += ' --namespace {}'.format(namespace) + if scope == 'public': params += ' --public' + params += namespace_params + wb = self.mistral_cli( admin, 'workbook-create', @@ -125,11 +134,13 @@ class MistralClientTestBase(base.MistralCLIAuth, base.MistralCLIAltAuth): wb_name = self.get_field_value(wb, "Name") + wb_delete_params = wb_name + namespace_params + self.addCleanup( self.mistral_cli, admin, 'workbook-delete', - params=wb_name + params=wb_delete_params ) self.addCleanup( diff --git a/mistralclient/tests/unit/v2/test_cli_workbooks.py b/mistralclient/tests/unit/v2/test_cli_workbooks.py index 4a946d9f..c4a97cc4 100644 --- a/mistralclient/tests/unit/v2/test_cli_workbooks.py +++ b/mistralclient/tests/unit/v2/test_cli_workbooks.py @@ -22,6 +22,7 @@ from mistralclient.tests.unit import base WORKBOOK_DICT = { 'name': 'a', + 'namespace': '', 'tags': ['a', 'b'], 'scope': 'private', 'created_at': '1', @@ -55,7 +56,7 @@ class TestCLIWorkbooksV2(base.BaseCommandTest): result = self.call(workbook_cmd.Create, app_args=['wb.yaml']) - self.assertEqual(('a', 'a, b', 'private', '1', '1'), result[1]) + self.assertEqual(('a', '', 'a, b', 'private', '1', '1'), result[1]) @mock.patch('argparse.open', create=True) def test_create_public(self, mock_open): @@ -69,7 +70,7 @@ class TestCLIWorkbooksV2(base.BaseCommandTest): app_args=['wb.yaml', '--public'] ) - self.assertEqual(('a', 'a, b', 'public', '1', '1'), result[1]) + self.assertEqual(('a', '', 'a, b', 'public', '1', '1'), result[1]) self.assertEqual( 'public', @@ -82,7 +83,7 @@ class TestCLIWorkbooksV2(base.BaseCommandTest): result = self.call(workbook_cmd.Update, app_args=['definition']) - self.assertEqual(('a', 'a, b', 'private', '1', '1'), result[1]) + self.assertEqual(('a', '', 'a, b', 'private', '1', '1'), result[1]) @mock.patch('argparse.open', create=True) def test_update_public(self, mock_open): @@ -96,7 +97,7 @@ class TestCLIWorkbooksV2(base.BaseCommandTest): app_args=['definition', '--public'] ) - self.assertEqual(('a', 'a, b', 'public', '1', '1'), result[1]) + self.assertEqual(('a', '', 'a, b', 'public', '1', '1'), result[1]) self.assertEqual( 'public', @@ -108,26 +109,26 @@ class TestCLIWorkbooksV2(base.BaseCommandTest): result = self.call(workbook_cmd.List) - self.assertEqual([('a', 'a, b', 'private', '1', '1')], result[1]) + self.assertEqual([('a', '', 'a, b', 'private', '1', '1')], result[1]) def test_get(self): self.client.workbooks.get.return_value = WORKBOOK result = self.call(workbook_cmd.Get, app_args=['name']) - self.assertEqual(('a', 'a, b', 'private', '1', '1'), result[1]) + self.assertEqual(('a', '', 'a, b', 'private', '1', '1'), result[1]) def test_delete(self): self.call(workbook_cmd.Delete, app_args=['name']) - self.client.workbooks.delete.assert_called_once_with('name') + self.client.workbooks.delete.assert_called_once_with('name', None) def test_delete_with_multi_names(self): self.call(workbook_cmd.Delete, app_args=['name1', 'name2']) self.assertEqual(2, self.client.workbooks.delete.call_count) self.assertEqual( - [mock.call('name1'), mock.call('name2')], + [mock.call('name1', None), mock.call('name2', None)], self.client.workbooks.delete.call_args_list ) diff --git a/releasenotes/notes/add_namespace_option_to_workbook_command-202c40625dd24ecb.yaml b/releasenotes/notes/add_namespace_option_to_workbook_command-202c40625dd24ecb.yaml new file mode 100644 index 00000000..6593d531 --- /dev/null +++ b/releasenotes/notes/add_namespace_option_to_workbook_command-202c40625dd24ecb.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Add namespace parameter to workbook commands. Namespace parameter allows + to create multiple workbooks with same name under different namespaces.