From f1ca20421a11d6ed87c43ed94abd35a49b2dc04b Mon Sep 17 00:00:00 2001 From: Mikhail Date: Wed, 9 Nov 2016 16:40:42 +0300 Subject: [PATCH] Add ability to create role for cluster Add -e/--env to all role commands DocImpact Change-Id: Ibab8720b3b48fd61dabccbd30952b3be582b010f Implements: blueprint role-decomposition --- fuelclient/cli/actions/role.py | 80 ++++++-- fuelclient/commands/role.py | 116 +++++++---- fuelclient/objects/role.py | 33 +++- fuelclient/tests/unit/v1/test_roles.py | 100 ++++++++-- fuelclient/tests/unit/v2/cli/test_role.py | 222 +++++++++++++++++++--- fuelclient/tests/unit/v2/lib/test_role.py | 123 +++++++++--- fuelclient/v1/role.py | 27 +-- 7 files changed, 557 insertions(+), 144 deletions(-) diff --git a/fuelclient/cli/actions/role.py b/fuelclient/cli/actions/role.py index d3dcedbc..a8fd644a 100644 --- a/fuelclient/cli/actions/role.py +++ b/fuelclient/cli/actions/role.py @@ -15,6 +15,7 @@ from fuelclient.cli.actions.base import Action from fuelclient.cli.actions.base import check_all +from fuelclient.cli.actions.base import check_any import fuelclient.cli.arguments as Args from fuelclient.cli.arguments import group from fuelclient.cli.formatting import format_table @@ -23,18 +24,26 @@ from fuelclient.objects.role import Role class RoleAction(Action): - """List all roles for specific release + """List all roles for specific release or cluster """ action_name = "role" + fields_mapper = ( + ('env', 'clusters'), + ('release', 'releases') + ) + def __init__(self): # NOTE(dshulyak) this serializers are really messed up # it gets overwritten in several places self.file_serializer = FileFormatBasedSerializer() self.args = [ Args.get_list_arg("List all roles"), - - Args.get_release_arg("Release id"), + group( + Args.get_env_arg(), + Args.get_release_arg("Release id"), + required=True + ), Args.get_str_arg("role", help="Name of the role"), Args.get_file_arg("File with role description"), @@ -52,13 +61,21 @@ class RoleAction(Action): (None, self.list), ) - @check_all('release') + def parse_model(self, args): + for param, role_class in self.fields_mapper: + model_id = getattr(args, param) + if model_id: + return role_class, model_id + + @check_any('release', 'env') def list(self, params): - """Print all available roles + """Print all available roles for release or cluster fuel role --rel 1 + fuel role --env 1 """ - roles = Role(obj_id=params.release).data + model, model_id = self.parse_model(params) + roles = Role(owner_type=model, owner_id=model_id).get_all() acceptable_keys = ("name", ) @@ -70,46 +87,69 @@ class RoleAction(Action): ) ) - @check_all('role', 'release', 'file') + @check_all('role', 'file') + @check_any('release', 'env') def item(self, params): """Save full role description to file fuel role --rel 1 --role controller --file some.yaml + fuel role --env 1 --role controller --file some.yaml """ - role = Role(obj_id=params.release).get_role(params.role) + model, model_id = self.parse_model(params) + role = Role(owner_type=model, owner_id=model_id).get_role(params.role) self.file_serializer.write_to_file(params.file, role) self.file_serializer.print_to_output( role, - "Role successfully saved to {0}.".format(params.file)) + "Role {0} for {1} successfully saved to {2}.".format( + params.role, + model, + params.file)) - @check_all('file', 'release') + @check_all('file') + @check_any('release', 'env') def create(self, params): """Create a role from file description fuel role --rel 1 --create --file some.yaml + fuel role --env 1 --create --file some.yaml """ + model, model_id = self.parse_model(params) role = self.file_serializer.read_from_file(params.file) - role = Role(obj_id=params.release).create_role(role) + role = Role(owner_type=model, owner_id=model_id).create_role(role) self.file_serializer.print_to_output( role, - "Role {0} successfully created from {1}.".format( - role['name'], params.file)) + "Role {0} for {1} successfully created from {2}.".format( + role['name'], model, params.file)) - @check_all('file', 'release') + @check_all('file') + @check_any('release', 'env') def update(self, params): """Update a role from file description - fuel role --rel 1 --update --file some.yaml + fuel role --rel 1 --create --file some.yaml + fuel role --env 1 --create --file some.yaml """ + model, model_id = self.parse_model(params) role = self.file_serializer.read_from_file(params.file) - role = Role(obj_id=params.release).update_role(role['name'], role) + role = Role(owner_type=model, owner_id=model_id).update_role( + role['name'], + role) self.file_serializer.print_to_output( role, - "Role successfully updated from {0}.".format(params.file)) + "Role {0} for {1} successfully updated from {2}.".format( + params.role, + model, + params.file)) - @check_all('role', 'release') + @check_all('role') + @check_any('release', 'env') def delete(self, params): """Delete role from fuel fuel role --delete --role controller --rel 1 + fuel role --delete --role controller --env 1 """ - Role(obj_id=params.release).delete_role(params.role) + model, model_id = self.parse_model(params) + Role(owner_type=model, owner_id=model_id).delete_role(params.role) self.file_serializer.print_to_output( {}, - "Role with id {0} successfully deleted.".format(params.role)) + "Role {0} for {1} with id {2} successfully deleted.".format( + params.role, + model, + model_id)) diff --git a/fuelclient/commands/role.py b/fuelclient/commands/role.py index 6b2cf3c9..555e7f39 100644 --- a/fuelclient/commands/role.py +++ b/fuelclient/commands/role.py @@ -26,14 +26,24 @@ from fuelclient.common import data_utils class RoleMixIn(object): - entity_name = 'role' supported_file_formats = ('json', 'yaml') + fields_mapper = ( + ('env', 'clusters'), + ('release', 'releases') + ) + + def parse_model(self, args): + for param, role_class in self.fields_mapper: + model_id = getattr(args, param) + if model_id: + return role_class, model_id @staticmethod - def get_file_path(directory, release_id, role_name, file_format): + def get_file_path(directory, owner_type, owner_id, role_name, file_format): return os.path.join(os.path.abspath(directory), - 'release_{id}'.format(id=release_id), + '{owner}_{id}'.format(owner=owner_type, + id=owner_id), '{}.{}'.format(role_name, file_format)) @@ -53,11 +63,15 @@ class BaseUploadCommand(RoleMixIn, base.BaseCommand): def get_parser(self, prog_name): parser = super(BaseUploadCommand, self).get_parser(prog_name) - parser.add_argument('-r', - '--release', - type=int, - required=True, - help='Id of the release') + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument('-r', + '--release', + type=int, + help='Id of the release') + group.add_argument('-e', + '--env', + type=int, + help='Id of the environment') parser.add_argument('-n', '--name', required=True, @@ -76,11 +90,14 @@ class BaseUploadCommand(RoleMixIn, base.BaseCommand): return parser def take_action(self, parsed_args): - params = {"release_id": parsed_args.release, + model, model_id = self.parse_model(parsed_args) + params = {"owner_type": model, + "owner_id": model_id, "role_name": parsed_args.name} file_path = self.get_file_path(parsed_args.directory, - parsed_args.release, + model, + model_id, parsed_args.name, parsed_args.format) @@ -93,16 +110,17 @@ class BaseUploadCommand(RoleMixIn, base.BaseCommand): parsed_args.name, file_path) raise error.InvalidFileException(msg) - msg = ("Description of role '{role}' for release with id {id} was " + msg = ("Description of role '{role}' for {owner} with id {id} was " "{action}d from {file_path}\n".format(role=parsed_args.name, - id=parsed_args.release, + owner=model, + id=model_id, action=self.action, file_path=file_path)) self.app.stdout.write(msg) class RoleList(RoleMixIn, base.BaseListCommand): - """Show list of all available roles for release.""" + """Show list of all available roles for release or cluster.""" columns = ("name", "group", @@ -111,15 +129,21 @@ class RoleList(RoleMixIn, base.BaseListCommand): def get_parser(self, prog_name): parser = super(RoleList, self).get_parser(prog_name) - parser.add_argument('-r', - '--release', - type=int, - required=True, - help='Id of the release') + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument('-r', + '--release', + type=int, + help='Id of the release') + group.add_argument('-e', + '--env', + type=int, + help='Id of the environment' + ) return parser def take_action(self, parsed_args): - data = self.client.get_all(parsed_args.release) + model, model_id = self.parse_model(parsed_args) + data = self.client.get_all(model, model_id) data = data_utils.get_display_data_multi(self.columns, data) return self.columns, data @@ -130,11 +154,16 @@ class RoleDownload(RoleMixIn, base.BaseCommand): def get_parser(self, prog_name): parser = super(RoleDownload, self).get_parser(prog_name) - parser.add_argument('-r', - '--release', - type=int, - required=True, - help='Id of the release') + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument('-r', + '--release', + type=int, + help='Id of the release') + group.add_argument('-e', + '--env', + type=int, + help='Id of the environment' + ) parser.add_argument('-n', '--name', required=True, @@ -153,11 +182,15 @@ class RoleDownload(RoleMixIn, base.BaseCommand): return parser def take_action(self, parsed_args): + model, model_id = self.parse_model(parsed_args) file_path = self.get_file_path(parsed_args.directory, - parsed_args.release, + model, + model_id, parsed_args.name, parsed_args.format) - data = self.client.get_one(parsed_args.release, parsed_args.name) + data = self.client.get_one(model, + model_id, + parsed_args.name) try: fileutils.ensure_tree(os.path.dirname(file_path)) @@ -170,9 +203,10 @@ class RoleDownload(RoleMixIn, base.BaseCommand): "for role {} at {}".format(parsed_args.name, file_path)) raise error.InvalidFileException(msg) - msg = ("Description data of role '{}' within release id {} " + msg = ("Description data of role '{}' within {} id {} " "was stored in {}\n".format(parsed_args.name, - parsed_args.release, + model, + model_id, file_path)) self.app.stdout.write(msg) @@ -198,15 +232,20 @@ class RoleCreate(BaseUploadCommand): class RoleDelete(RoleMixIn, base.BaseCommand): - """Delete a role from release""" + """Delete a role from release or cluster""" def get_parser(self, prog_name): parser = super(RoleDelete, self).get_parser(prog_name) - parser.add_argument('-r', - '--release', - type=int, - required=True, - help='Id of the release') + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument('-r', + '--release', + type=int, + help='Id of the release') + group.add_argument('-e', + '--env', + type=int, + help='Id of the environment' + ) parser.add_argument('-n', '--name', required=True, @@ -214,8 +253,11 @@ class RoleDelete(RoleMixIn, base.BaseCommand): return parser def take_action(self, parsed_args): - self.client.delete(parsed_args.release, parsed_args.name) + model, model_id = self.parse_model(parsed_args) + self.client.delete(model, + model_id, + parsed_args.name) - msg = "Role '{}' was deleted from release with id {}\n".format( - parsed_args.name, parsed_args.release) + msg = "Role '{}' was deleted from {} with id {}\n".format( + parsed_args.name, model, model_id) self.app.stdout.write(msg) diff --git a/fuelclient/objects/role.py b/fuelclient/objects/role.py index 65096fce..1e6d7615 100644 --- a/fuelclient/objects/role.py +++ b/fuelclient/objects/role.py @@ -18,21 +18,42 @@ from fuelclient.objects.base import BaseObject class Role(BaseObject): - instance_api_path = "releases/{0}/roles/" - role_api_path = "releases/{0}/roles/{1}/" + instance_api_path = "{owner_type}/{owner_id}/roles/" + class_api_path = "{owner_type}/{owner_id}/roles/{role_name}/" + + def __init__(self, owner_type, owner_id, **kwargs): + super(Role, self).__init__(owner_id, **kwargs) + self.owner_type = owner_type + + def get_all(self): + return self.connection.get_request( + self.instance_api_path.format( + owner_type=self.owner_type, + owner_id=self.id)) def get_role(self, role_name): return self.connection.get_request( - self.role_api_path.format(self.id, role_name)) + self.class_api_path.format( + owner_type=self.owner_type, + owner_id=self.id, + role_name=role_name)) def update_role(self, role_name, data): return self.connection.put_request( - self.role_api_path.format(self.id, role_name), data) + self.class_api_path.format( + owner_type=self.owner_type, + owner_id=self.id, + role_name=role_name), + data) def create_role(self, data): return self.connection.post_request( - self.instance_api_path.format(self.id), data) + self.instance_api_path.format( + owner_type=self.owner_type, owner_id=self.id), data) def delete_role(self, role_name): return self.connection.delete_request( - self.role_api_path.format(self.id, role_name)) + self.class_api_path.format( + owner_type=self.owner_type, + owner_id=self.id, + role_name=role_name)) diff --git a/fuelclient/tests/unit/v1/test_roles.py b/fuelclient/tests/unit/v1/test_roles.py index 1ba7c7dc..a4de90c4 100644 --- a/fuelclient/tests/unit/v1/test_roles.py +++ b/fuelclient/tests/unit/v1/test_roles.py @@ -26,11 +26,21 @@ API_OUT = yaml.load(API_IN) class TestRoleActions(base.UnitTestCase): - release_id = 2 + owner_id = 2 - def test_list_roles(self): - url = '/api/v1/releases/{0}/roles/'.format(self.release_id) - cmd = 'fuel role --rel {0}'.format(self.release_id) + def test_list_release_roles(self): + url = '/api/v1/releases/{0}/roles/'.format(self.owner_id) + cmd = 'fuel role --rel {0}'.format(self.owner_id) + get_request = self.m_request.get(url, json=[API_OUT]) + + self.execute(cmd.split()) + + self.assertTrue(get_request.called) + self.assertIn(url, get_request.last_request.url) + + def test_list_cluster_roles(self): + url = '/api/v1/clusters/{0}/roles/'.format(self.owner_id) + cmd = 'fuel role --env {0}'.format(self.owner_id) get_request = self.m_request.get(url, json=[API_OUT]) self.execute(cmd.split()) @@ -39,10 +49,10 @@ class TestRoleActions(base.UnitTestCase): self.assertIn(url, get_request.last_request.url) @patch('fuelclient.cli.serializers.open', create=True) - def test_get_role(self, mopen): - url = '/api/v1/releases/{0}/roles/my_role/'.format(self.release_id) + def test_get_release_role(self, mopen): + url = '/api/v1/releases/{0}/roles/my_role/'.format(self.owner_id) cmd = 'fuel role --role my_role --file myfile.yaml --rel {0}'.format( - self.release_id) + self.owner_id) get_request = self.m_request.get(url, json=API_OUT) self.execute(cmd.split()) @@ -52,10 +62,23 @@ class TestRoleActions(base.UnitTestCase): self.assertIn(url, get_request.last_request.url) @patch('fuelclient.cli.serializers.open', create=True) - def test_create_role(self, mopen): - url = '/api/v1/releases/{0}/roles/'.format(self.release_id) + def test_get_cluster_role(self, mopen): + url = '/api/v1/clusters/{0}/roles/my_role/'.format(self.owner_id) + cmd = 'fuel role --role my_role --file myfile.yaml --env {0}'.format( + self.owner_id) + get_request = self.m_request.get(url, json=API_OUT) + + self.execute(cmd.split()) + + mopen().__enter__().write.assert_called_once_with(API_IN) + self.assertTrue(get_request.called) + self.assertIn(url, get_request.last_request.url) + + @patch('fuelclient.cli.serializers.open', create=True) + def test_create_release_role(self, mopen): + url = '/api/v1/releases/{0}/roles/'.format(self.owner_id) cmd = 'fuel role --create --file myfile.yaml --rel {0}'.format( - self.release_id) + self.owner_id) mopen().__enter__().read.return_value = API_IN post_request = self.m_request.post(url, json=API_OUT) @@ -67,10 +90,25 @@ class TestRoleActions(base.UnitTestCase): API_OUT, post_request.last_request.json()) @patch('fuelclient.cli.serializers.open', create=True) - def test_update_role(self, mopen): - url = '/api/v1/releases/{0}/roles/my_role/'.format(self.release_id) + def test_create_cluster_role(self, mopen): + url = '/api/v1/clusters/{0}/roles/'.format(self.owner_id) + cmd = 'fuel role --create --file myfile.yaml --env {0}'.format( + self.owner_id) + mopen().__enter__().read.return_value = API_IN + post_request = self.m_request.post(url, json=API_OUT) + + self.execute(cmd.split()) + + self.assertTrue(post_request.called) + self.assertIn(url, post_request.last_request.url) + self.assertEqual( + API_OUT, post_request.last_request.json()) + + @patch('fuelclient.cli.serializers.open', create=True) + def test_update_release_role(self, mopen): + url = '/api/v1/releases/{0}/roles/my_role/'.format(self.owner_id) cmd = 'fuel role --update --file myfile.yaml --rel {0}'.format( - self.release_id) + self.owner_id) mopen().__enter__().read.return_value = API_IN put_request = self.m_request.put(url, json=API_OUT) @@ -81,10 +119,36 @@ class TestRoleActions(base.UnitTestCase): self.assertEqual( API_OUT, put_request.last_request.json()) - def test_delete_role(self): - url = '/api/v1/releases/{0}/roles/my_role/'.format(self.release_id) + @patch('fuelclient.cli.serializers.open', create=True) + def test_update_cluster_role(self, mopen): + url = '/api/v1/clusters/{0}/roles/my_role/'.format(self.owner_id) + cmd = 'fuel role --update --file myfile.yaml --env {0}'.format( + self.owner_id) + mopen().__enter__().read.return_value = API_IN + put_request = self.m_request.put(url, json=API_OUT) + + self.execute(cmd.split()) + + self.assertTrue(put_request.called) + self.assertIn(url, put_request.last_request.url) + self.assertEqual( + API_OUT, put_request.last_request.json()) + + def test_delete_release_role(self): + url = '/api/v1/releases/{0}/roles/my_role/'.format(self.owner_id) cmd = 'fuel role --delete --role my_role --rel {0}'.format( - self.release_id) + self.owner_id) + delete_request = self.m_request.delete(url, json=API_OUT) + + self.execute(cmd.split()) + + self.assertTrue(delete_request.called) + self.assertIn(url, delete_request.last_request.url) + + def test_delete_cluster_role(self): + url = '/api/v1/clusters/{0}/roles/my_role/'.format(self.owner_id) + cmd = 'fuel role --delete --role my_role --env {0}'.format( + self.owner_id) delete_request = self.m_request.delete(url, json=API_OUT) self.execute(cmd.split()) @@ -93,8 +157,8 @@ class TestRoleActions(base.UnitTestCase): self.assertIn(url, delete_request.last_request.url) def test_formatting_for_list_roles(self): - url = '/api/v1/releases/{0}/roles/'.format(self.release_id) - cmd = 'fuel role --rel {0}'.format(self.release_id) + url = '/api/v1/releases/{0}/roles/'.format(self.owner_id) + cmd = 'fuel role --rel {0}'.format(self.owner_id) get_request = self.m_request.get(url, json=[API_OUT]) with patch.object(Serializer, 'print_to_output') as mock_print: diff --git a/fuelclient/tests/unit/v2/cli/test_role.py b/fuelclient/tests/unit/v2/cli/test_role.py index dea9a49b..c2af5e12 100644 --- a/fuelclient/tests/unit/v2/cli/test_role.py +++ b/fuelclient/tests/unit/v2/cli/test_role.py @@ -39,25 +39,49 @@ class TestRoleCommand(test_engine.BaseCLITest): release_id = 45 args = 'role list -r {id}'.format(id=release_id) self.exec_command(args) - self.m_client.get_all.assert_called_once_with(release_id) + self.m_client.get_all.assert_called_once_with('releases', release_id) + self.m_get_client.assert_called_once_with('role', mock.ANY) + + def test_role_list_for_cluster(self): + self.m_client.get_all.return_value = [ + {"name": "fake_role_1", + "group": "fake_group", + "conflicts": ["fake_role_2", "fake_role_3"], + "description": "some fake description"}, + {"name": "fake_role_2", + "group": "fake_group", + "conflicts": ["fake_role_1", "fake_role_3"], + "description": "some fake description"} + ] + env_id = 45 + args = 'role list -e {id}'.format(id=env_id) + self.exec_command(args) + self.m_client.get_all.assert_called_once_with('clusters', env_id) self.m_get_client.assert_called_once_with('role', mock.ANY) @mock.patch('sys.stderr') - def test_role_list_for_release_fail(self, mocked_stderr): + def test_role_list_fail(self, mocked_stderr): args = 'role list' self.assertRaises(SystemExit, self.exec_command, args) - self.assertIn('-r/--release', + self.assertIn('-r/--release -e/--env', + mocked_stderr.write.call_args_list[-1][0][0]) + + @mock.patch('sys.stderr') + def test_role_list_with_mutually_exclusive_params(self, mocked_stderr): + args = 'role list -e 1 -r 2' + self.assertRaises(SystemExit, self.exec_command, args) + self.assertIn('not allowed', mocked_stderr.write.call_args_list[-1][0][0]) @mock.patch('json.dump') - def test_role_download_json(self, m_dump): + def test_release_role_download_json(self, m_dump): release_id = 45 role_name = 'fake_role' test_data = fake_role.get_fake_role(fake_role) args = 'role download -r {} -n {} -f json -d /tmp'.format(release_id, role_name) - expected_path = '/tmp/release_{id}/{name}.json'.format(id=release_id, - name=role_name) + expected_path = '/tmp/releases_{id}/{name}.json'.format(id=release_id, + name=role_name) self.m_client.get_one.return_value = test_data @@ -68,17 +92,42 @@ class TestRoleCommand(test_engine.BaseCLITest): m_open.assert_called_once_with(expected_path, 'w') m_dump.assert_called_once_with(test_data, mock.ANY, indent=4) self.m_get_client.assert_called_once_with('role', mock.ANY) - self.m_client.get_one.assert_called_once_with(release_id, role_name) + self.m_client.get_one.assert_called_once_with('releases', + release_id, + role_name) + + @mock.patch('json.dump') + def test_cluster_role_download_json(self, m_dump): + env_id = 45 + role_name = 'fake_role' + test_data = fake_role.get_fake_role(fake_role) + args = 'role download -e {} -n {} -f json -d /tmp'.format(env_id, + role_name) + expected_path = '/tmp/clusters_{id}/{name}.json'.format(id=env_id, + name=role_name) + + self.m_client.get_one.return_value = test_data + + m_open = mock.mock_open() + with mock.patch('fuelclient.commands.role.open', m_open, create=True): + self.exec_command(args) + + m_open.assert_called_once_with(expected_path, 'w') + m_dump.assert_called_once_with(test_data, mock.ANY, indent=4) + self.m_get_client.assert_called_once_with('role', mock.ANY) + self.m_client.get_one.assert_called_once_with('clusters', + env_id, + role_name) @mock.patch('yaml.safe_dump') - def test_role_download_yaml(self, m_safe_dump): + def test_release_role_download_yaml(self, m_safe_dump): release_id = 45 role_name = 'fake_role' test_data = fake_role.get_fake_role(fake_role) args = 'role download -r {} -n {} -f yaml -d /tmp'.format(release_id, role_name) - expected_path = '/tmp/release_{id}/{name}.yaml'.format(id=release_id, - name=role_name) + expected_path = '/tmp/releases_{id}/{name}.yaml'.format(id=release_id, + name=role_name) self.m_client.get_one.return_value = test_data @@ -90,16 +139,44 @@ class TestRoleCommand(test_engine.BaseCLITest): m_safe_dump.assert_called_once_with(test_data, mock.ANY, default_flow_style=False) self.m_get_client.assert_called_once_with('role', mock.ANY) - self.m_client.get_one.assert_called_once_with(release_id, role_name) + self.m_client.get_one.assert_called_once_with('releases', + release_id, + role_name) - def test_role_update_json(self): + @mock.patch('yaml.safe_dump') + def test_cluster_role_download_yaml(self, m_safe_dump): + env_id = 45 + role_name = 'fake_role' + test_data = fake_role.get_fake_role(fake_role) + args = 'role download -e {} -n {} -f yaml -d /tmp'.format(env_id, + role_name) + expected_path = '/tmp/clusters_{id}/{name}.yaml'.format(id=env_id, + name=role_name) + + self.m_client.get_one.return_value = test_data + + m_open = mock.mock_open() + with mock.patch('fuelclient.commands.role.open', m_open, create=True): + self.exec_command(args) + + m_open.assert_called_once_with(expected_path, 'w') + m_safe_dump.assert_called_once_with(test_data, mock.ANY, + default_flow_style=False) + self.m_get_client.assert_called_once_with('role', mock.ANY) + self.m_client.get_one.assert_called_once_with('clusters', + env_id, + role_name) + + def test_release_role_update_json(self): release_id = 45 role_name = 'fake_role' - params = {"release_id": release_id, "role_name": role_name} + params = {"owner_type": "releases", + "owner_id": release_id, + "role_name": role_name} args = 'role update -r {} -n {} -f json -d /tmp'.format(release_id, role_name) test_data = fake_role.get_fake_role(role_name) - expected_path = '/tmp/release_{}/fake_role.json'.format(release_id) + expected_path = '/tmp/releases_{}/fake_role.json'.format(release_id) m_open = mock.mock_open(read_data=json.dumps(test_data)) with mock.patch('fuelclient.commands.role.open', m_open, create=True): @@ -109,14 +186,35 @@ class TestRoleCommand(test_engine.BaseCLITest): self.m_get_client.assert_called_once_with('role', mock.ANY) self.m_client.update.assert_called_once_with(test_data, **params) - def test_role_update_yaml(self): + def test_cluster_role_update_json(self): + env_id = 45 + role_name = 'fake_role' + params = {"owner_type": "clusters", + "owner_id": env_id, + "role_name": role_name} + args = 'role update -e {} -n {} -f json -d /tmp'.format(env_id, + role_name) + test_data = fake_role.get_fake_role(role_name) + expected_path = '/tmp/clusters_{}/fake_role.json'.format(env_id) + + m_open = mock.mock_open(read_data=json.dumps(test_data)) + with mock.patch('fuelclient.commands.role.open', m_open, create=True): + self.exec_command(args) + + m_open.assert_called_once_with(expected_path, 'r') + self.m_get_client.assert_called_once_with('role', mock.ANY) + self.m_client.update.assert_called_once_with(test_data, **params) + + def test_release_role_update_yaml(self): release_id = 45 role_name = 'fake_role' - params = {"release_id": release_id, "role_name": role_name} + params = {"owner_type": "releases", + "owner_id": release_id, + "role_name": role_name} args = 'role update -r {} -n {} -f yaml -d /tmp'.format(release_id, role_name) test_data = fake_role.get_fake_role(role_name) - expected_path = '/tmp/release_{}/fake_role.yaml'.format(release_id) + expected_path = '/tmp/releases_{}/fake_role.yaml'.format(release_id) m_open = mock.mock_open(read_data=yaml.safe_dump(test_data)) with mock.patch('fuelclient.commands.role.open', m_open, create=True): @@ -126,14 +224,35 @@ class TestRoleCommand(test_engine.BaseCLITest): self.m_get_client.assert_called_once_with('role', mock.ANY) self.m_client.update.assert_called_once_with(test_data, **params) - def test_role_create_json(self): + def test_cluster_role_update_yaml(self): + env_id = 45 + role_name = 'fake_role' + params = {"owner_type": "clusters", + "owner_id": env_id, + "role_name": role_name} + args = 'role update -e {} -n {} -f yaml -d /tmp'.format(env_id, + role_name) + test_data = fake_role.get_fake_role(role_name) + expected_path = '/tmp/clusters_{}/fake_role.yaml'.format(env_id) + + m_open = mock.mock_open(read_data=yaml.safe_dump(test_data)) + with mock.patch('fuelclient.commands.role.open', m_open, create=True): + self.exec_command(args) + + m_open.assert_called_once_with(expected_path, 'r') + self.m_get_client.assert_called_once_with('role', mock.ANY) + self.m_client.update.assert_called_once_with(test_data, **params) + + def test_release_role_create_json(self): release_id = 45 role_name = 'fake_role' - params = {"release_id": release_id, "role_name": role_name} + params = {"owner_type": "releases", + "owner_id": release_id, + "role_name": role_name} args = 'role create -r {} -n {} -f json -d /tmp'.format(release_id, role_name) test_data = fake_role.get_fake_role(role_name) - expected_path = '/tmp/release_{}/fake_role.json'.format(release_id) + expected_path = '/tmp/releases_{}/fake_role.json'.format(release_id) m_open = mock.mock_open(read_data=json.dumps(test_data)) with mock.patch('fuelclient.commands.role.open', m_open, create=True): @@ -143,14 +262,35 @@ class TestRoleCommand(test_engine.BaseCLITest): self.m_get_client.assert_called_once_with('role', mock.ANY) self.m_client.create.assert_called_once_with(test_data, **params) - def test_role_create_yaml(self): + def test_cluster_role_create_json(self): + env_id = 45 + role_name = 'fake_role' + params = {"owner_type": "clusters", + "owner_id": env_id, + "role_name": role_name} + args = 'role create -e {} -n {} -f json -d /tmp'.format(env_id, + role_name) + test_data = fake_role.get_fake_role(role_name) + expected_path = '/tmp/clusters_{}/fake_role.json'.format(env_id) + + m_open = mock.mock_open(read_data=json.dumps(test_data)) + with mock.patch('fuelclient.commands.role.open', m_open, create=True): + self.exec_command(args) + + m_open.assert_called_once_with(expected_path, 'r') + self.m_get_client.assert_called_once_with('role', mock.ANY) + self.m_client.create.assert_called_once_with(test_data, **params) + + def test_release_role_create_yaml(self): release_id = 45 role_name = 'fake_role' - params = {"release_id": release_id, "role_name": role_name} + params = {"owner_type": "releases", + "owner_id": release_id, + "role_name": role_name} args = 'role create -r {} -n {} -f yaml -d /tmp'.format(release_id, role_name) test_data = fake_role.get_fake_role(role_name) - expected_path = '/tmp/release_{}/fake_role.yaml'.format(release_id) + expected_path = '/tmp/releases_{}/fake_role.yaml'.format(release_id) m_open = mock.mock_open(read_data=yaml.safe_dump(test_data)) with mock.patch('fuelclient.commands.role.open', m_open, create=True): @@ -160,11 +300,43 @@ class TestRoleCommand(test_engine.BaseCLITest): self.m_get_client.assert_called_once_with('role', mock.ANY) self.m_client.create.assert_called_once_with(test_data, **params) - def test_role_delete(self): + def test_cluster_role_create_yaml(self): + env_id = 45 + role_name = 'fake_role' + params = {"owner_type": "clusters", + "owner_id": env_id, + "role_name": role_name} + args = 'role create -e {} -n {} -f yaml -d /tmp'.format(env_id, + role_name) + test_data = fake_role.get_fake_role(role_name) + expected_path = '/tmp/clusters_{}/fake_role.yaml'.format(env_id) + + m_open = mock.mock_open(read_data=yaml.safe_dump(test_data)) + with mock.patch('fuelclient.commands.role.open', m_open, create=True): + self.exec_command(args) + + m_open.assert_called_once_with(expected_path, 'r') + self.m_get_client.assert_called_once_with('role', mock.ANY) + self.m_client.create.assert_called_once_with(test_data, **params) + + def test_release_role_delete(self): release_id = 45 role_name = 'fake_role' args = 'role delete -r {} -n {}'.format(release_id, role_name) self.exec_command(args) self.m_get_client.assert_called_once_with('role', mock.ANY) - self.m_client.delete.assert_called_once_with(release_id, role_name) + self.m_client.delete.assert_called_once_with('releases', + release_id, + role_name) + + def test_cluster_role_delete(self): + env_id = 45 + role_name = 'fake_role' + args = 'role delete -e {} -n {}'.format(env_id, role_name) + + self.exec_command(args) + self.m_get_client.assert_called_once_with('role', mock.ANY) + self.m_client.delete.assert_called_once_with('clusters', + env_id, + role_name) diff --git a/fuelclient/tests/unit/v2/lib/test_role.py b/fuelclient/tests/unit/v2/lib/test_role.py index 73352582..fcd83686 100644 --- a/fuelclient/tests/unit/v2/lib/test_role.py +++ b/fuelclient/tests/unit/v2/lib/test_role.py @@ -25,39 +25,67 @@ class TestRoleFacade(test_api.BaseLibTest): super(TestRoleFacade, self).setUp() self.version = 'v1' - self.res_uri = '/api/{version}/releases/'.format( - version=self.version) self.role_name = 'fake_role' self.fake_role = utils.get_fake_role(self.role_name) self.fake_roles = utils.get_fake_roles(10) self.client = fuelclient.get_client('role', self.version) - def test_role_list(self): - release_id = 42 - expected_uri = self.get_object_uri(self.res_uri, release_id, '/roles/') + def get_uri(self, owner): + return '/api/{version}/{owner}/'.format(version=self.version, + owner=owner) + + def test_release_role_list(self): + owner, owner_id = 'releases', 42 + expected_uri = self.get_object_uri(self.get_uri(owner), + owner_id, + '/roles/') matcher = self.m_request.get(expected_uri, json=self.fake_roles) - self.client.get_all(release_id) + self.client.get_all(owner, owner_id) self.assertTrue(matcher.called) - def test_role_download(self): - release_id = 45 - expected_uri = self.get_object_uri(self.res_uri, - release_id, + def test_cluster_role_list(self): + owner, owner_id = 'clusters', 42 + expected_uri = self.get_object_uri(self.get_uri(owner), + owner_id, + '/roles/') + matcher = self.m_request.get(expected_uri, json=self.fake_roles) + self.client.get_all(owner_type=owner, owner_id=owner_id) + + self.assertTrue(matcher.called) + + def test_release_role_download(self): + owner, owner_id = 'releases', 45 + expected_uri = self.get_object_uri(self.get_uri(owner), + owner_id, '/roles/{}/'.format(self.role_name)) role_matcher = self.m_request.get(expected_uri, json=self.fake_role) - role = self.client.get_one(release_id, self.role_name) + role = self.client.get_one(owner, owner_id, self.role_name) self.assertTrue(expected_uri, role_matcher.called) self.assertEqual(role, self.fake_role) - def test_role_update(self): - release_id = 45 - params = {"release_id": release_id, "role_name": self.role_name} - expected_uri = self.get_object_uri(self.res_uri, - release_id, + def test_cluster_role_download(self): + owner, owner_id = 'clusters', 45 + expected_uri = self.get_object_uri(self.get_uri(owner), + owner_id, + '/roles/{}/'.format(self.role_name)) + role_matcher = self.m_request.get(expected_uri, json=self.fake_role) + + role = self.client.get_one(owner, owner_id, self.role_name) + + self.assertTrue(expected_uri, role_matcher.called) + self.assertEqual(role, self.fake_role) + + def test_release_role_update(self): + owner, owner_id = 'releases', 45 + params = {"owner_type": owner, + "owner_id": owner_id, + "role_name": self.role_name} + expected_uri = self.get_object_uri(self.get_uri(owner), + owner_id, '/roles/{}/'.format(self.role_name)) upd_matcher = self.m_request.put(expected_uri, json=self.fake_role) @@ -66,12 +94,28 @@ class TestRoleFacade(test_api.BaseLibTest): self.assertTrue(upd_matcher.called) self.assertEqual(self.fake_role, upd_matcher.last_request.json()) - def test_role_create(self): - release_id = 45 - params = {"release_id": release_id} - expected_uri = self.get_object_uri(self.res_uri, - release_id, - '/roles/'.format(self.role_name)) + def test_cluster_role_update(self): + owner, owner_id = 'clusters', 45 + params = {"owner_type": owner, + "owner_id": owner_id, + "role_name": self.role_name} + expected_uri = self.get_object_uri(self.get_uri(owner), + owner_id, + '/roles/{}/'.format(self.role_name)) + upd_matcher = self.m_request.put(expected_uri, json=self.fake_role) + + self.client.update(self.fake_role, **params) + + self.assertTrue(upd_matcher.called) + self.assertEqual(self.fake_role, upd_matcher.last_request.json()) + + def test_release_role_create(self): + owner, owner_id = 'releases', 45 + params = {"owner_type": owner, + "owner_id": owner_id} + expected_uri = self.get_object_uri(self.get_uri(owner), + owner_id, + '/roles/') post_matcher = self.m_request.post(expected_uri, json=self.fake_role) self.client.create(self.fake_role, **params) @@ -79,13 +123,38 @@ class TestRoleFacade(test_api.BaseLibTest): self.assertTrue(post_matcher.called) self.assertEqual(self.fake_role, post_matcher.last_request.json()) - def test_role_delete(self): - release_id = 45 - expected_uri = self.get_object_uri(self.res_uri, - release_id, + def test_cluster_role_create(self): + owner, owner_id = 'clusters', 45 + params = {"owner_type": owner, + "owner_id": owner_id} + expected_uri = self.get_object_uri(self.get_uri(owner), + owner_id, + '/roles/') + post_matcher = self.m_request.post(expected_uri, json=self.fake_role) + + self.client.create(self.fake_role, **params) + + self.assertTrue(post_matcher.called) + self.assertEqual(self.fake_role, post_matcher.last_request.json()) + + def test_release_role_delete(self): + owner, owner_id = 'releases', 45 + expected_uri = self.get_object_uri(self.get_uri(owner), + owner_id, '/roles/{}/'.format(self.role_name)) delete_matcher = self.m_request.delete(expected_uri, json={}) - self.client.delete(release_id, self.role_name) + self.client.delete(owner, owner_id, self.role_name) + + self.assertTrue(delete_matcher.called) + + def test_cluster_role_delete(self): + owner, owner_id = 'clusters', 45 + expected_uri = self.get_object_uri(self.get_uri(owner), + owner_id, + '/roles/{}/'.format(self.role_name)) + delete_matcher = self.m_request.delete(expected_uri, json={}) + + self.client.delete(owner, owner_id, self.role_name) self.assertTrue(delete_matcher.called) diff --git a/fuelclient/v1/role.py b/fuelclient/v1/role.py index c173debb..f38fe4cf 100644 --- a/fuelclient/v1/role.py +++ b/fuelclient/v1/role.py @@ -23,15 +23,17 @@ class RoleClient(base_v1.BaseV1Client): _entity_wrapper = objects.Role - def get_all(self, release_id): - """Get all available roles for specific release. + def get_all(self, owner_type, owner_id): + """Get all available roles for specific release or cluster. - :param release_id: Id of release - :type release_id: int + :param owner_type: release or cluster + :type owner_id: int :return: roles data as a list of dict :rtype: list """ - data = self._entity_wrapper(obj_id=release_id).data + + data = self._entity_wrapper(owner_type, owner_id).get_all() + # Retrieve nested data from 'meta' and add it as a new key-value pair for role in data: role_meta = role.pop('meta') @@ -41,20 +43,23 @@ class RoleClient(base_v1.BaseV1Client): return data - def get_one(self, release_id, role_name): - role = self._entity_wrapper(obj_id=release_id) + def get_one(self, owner_type, owner_id, role_name): + role = self._entity_wrapper(owner_type, owner_id) return role.get_role(role_name) def update(self, data, **kwargs): - role = self._entity_wrapper(obj_id=kwargs['release_id']) + role = self._entity_wrapper(owner_type=kwargs['owner_type'], + owner_id=kwargs['owner_id']) return role.update_role(kwargs['role_name'], data) def create(self, data, **kwargs): - role = self._entity_wrapper(obj_id=kwargs['release_id']) + role = self._entity_wrapper(owner_type=kwargs['owner_type'], + owner_id=kwargs['owner_id']) return role.create_role(data) - def delete(self, release_id, role_name): - role = self._entity_wrapper(obj_id=release_id) + def delete(self, owner_type, owner_id, role_name): + role = self._entity_wrapper(owner_type=owner_type, + owner_id=owner_id) return role.delete_role(role_name)