upstream/openstack/python-openstackclient/centos/patches/0002-Add-support-for-endpoi...

615 lines
23 KiB
Diff

From 8d106e1f1b3e536127818e98e495343e3c85f6b1 Mon Sep 17 00:00:00 2001
From: Jose Castro Leon <jose.castro.leon@cern.ch>
Date: Wed, 25 Oct 2017 15:39:44 +0200
Subject: [PATCH] Add support for endpoing filter commands
Implements the commands that allow to link and endpoint to
a project for endpoint filter management.
Implements: blueprint keystone-endpoint-filter
Change-Id: Iecf61495664fb8413d35ef69f07ea929d190d002
Signed-off-by: Kam Nasim <kam.nasim@windriver.com>
---
doc/source/cli/command-objects/endpoint.rst | 79 +++++++++++
openstackclient/identity/v3/endpoint.py | 147 ++++++++++++++++++---
.../tests/functional/identity/v3/common.py | 1 +
.../tests/functional/identity/v3/test_endpoint.py | 42 ++++++
openstackclient/tests/unit/identity/v3/fakes.py | 27 ++++
.../tests/unit/identity/v3/test_endpoint.py | 139 +++++++++++++++++++
.../keystone-endpoint-filter-e930a7b72276fa2c.yaml | 5 +
setup.cfg | 9 +-
8 files changed, 429 insertions(+), 20 deletions(-)
create mode 100644 releasenotes/notes/keystone-endpoint-filter-e930a7b72276fa2c.yaml
diff --git a/doc/source/cli/command-objects/endpoint.rst b/doc/source/cli/command-objects/endpoint.rst
index 02a75be..030947c 100644
--- a/doc/source/cli/command-objects/endpoint.rst
+++ b/doc/source/cli/command-objects/endpoint.rst
@@ -4,6 +4,34 @@ endpoint
Identity v2, v3
+endpoint add project
+--------------------
+
+Associate a project to and endpoint for endpoint filtering
+
+.. program:: endpoint add project
+.. code:: bash
+
+ openstack endpoint add project
+ [--project-domain <project-domain>]
+ <endpoint>
+ <project>
+
+.. option:: --project-domain <project-domain>
+
+ Domain the project belongs to (name or ID).
+ This can be used in case collisions between project names exist.
+
+.. _endpoint_add_project-endpoint:
+.. describe:: <endpoint>
+
+ Endpoint to associate with specified project (name or ID)
+
+.. _endpoint_add_project-project:
+.. describe:: <project>
+
+ Project to associate with specified endpoint (name or ID)
+
endpoint create
---------------
@@ -107,6 +135,8 @@ List endpoints
[--interface <interface>]
[--region <region-id>]
[--long]
+ [--endpoint <endpoint> |
+ --project <project> [--project-domain <project-domain>]]
.. option:: --service <service>
@@ -132,6 +162,55 @@ List endpoints
*Identity version 2 only*
+.. option:: --endpoint
+
+ List projects that have access to that endpoint using
+ endpoint filtering
+
+ *Identity version 3 only*
+
+.. option:: --project
+
+ List endpoints available for the project using
+ endpoint filtering
+
+ *Identity version 3 only*
+
+.. option:: --project-domain
+
+ Domain the project belongs to (name or ID).
+ This can be used in case collisions between project names exist.
+
+ *Identity version 3 only*
+
+endpoint remove project
+-----------------------
+
+Dissociate a project from an endpoint.
+
+.. program:: endpoint remove project
+.. code:: bash
+
+ openstack endpoint remove project
+ [--project-domain <project-domain>]
+ <endpoint>
+ <project>
+
+.. option:: --project-domain <project-domain>
+
+ Domain the project belongs to (name or ID).
+ This can be used in case collisions between project names exist.
+
+.. _endpoint_remove_project-endpoint:
+.. describe:: <endpoint>
+
+ Endpoint to dissociate with specified project (name or ID)
+
+.. _endpoint_remove_project-project:
+.. describe:: <project>
+
+ Project to dissociate with specified endpoint (name or ID)
+
endpoint set
------------
diff --git a/openstackclient/identity/v3/endpoint.py b/openstackclient/identity/v3/endpoint.py
index 3b4dd0d..649a230 100644
--- a/openstackclient/identity/v3/endpoint.py
+++ b/openstackclient/identity/v3/endpoint.py
@@ -36,6 +36,42 @@ def get_service_name(service):
return ''
+class AddProjectToEndpoint(command.Command):
+ _description = _("Associate a project to an endpoint")
+
+ def get_parser(self, prog_name):
+ parser = super(
+ AddProjectToEndpoint, self).get_parser(prog_name)
+ parser.add_argument(
+ 'endpoint',
+ metavar='<endpoint>',
+ help=_('Endpoint to associate with '
+ 'specified project (name or ID)'),
+ )
+ parser.add_argument(
+ 'project',
+ metavar='<project>',
+ help=_('Project to associate with '
+ 'specified endpoint name or ID)'),
+ )
+ common.add_project_domain_option_to_parser(parser)
+ return parser
+
+ def take_action(self, parsed_args):
+ client = self.app.client_manager.identity
+
+ endpoint = utils.find_resource(client.endpoints,
+ parsed_args.endpoint)
+
+ project = common.find_project(client,
+ parsed_args.project,
+ parsed_args.project_domain)
+
+ client.endpoint_filter.add_endpoint_to_project(
+ project=project.id,
+ endpoint=endpoint.id)
+
+
class CreateEndpoint(command.ShowOne):
_description = _("Create new endpoint")
@@ -152,27 +188,68 @@ class ListEndpoint(command.Lister):
metavar='<region-id>',
help=_('Filter by region ID'),
)
+ list_group = parser.add_mutually_exclusive_group()
+ list_group.add_argument(
+ '--endpoint',
+ metavar='<endpoint-group>',
+ help=_('Endpoint to list filters'),
+ )
+ list_group.add_argument(
+ '--project',
+ metavar='<project>',
+ help=_('Project to list filters (name or ID)'),
+ )
+ common.add_project_domain_option_to_parser(list_group)
return parser
def take_action(self, parsed_args):
identity_client = self.app.client_manager.identity
- columns = ('ID', 'Region', 'Service Name', 'Service Type',
- 'Enabled', 'Interface', 'URL')
- kwargs = {}
- if parsed_args.service:
- service = common.find_service(identity_client, parsed_args.service)
- kwargs['service'] = service.id
- if parsed_args.interface:
- kwargs['interface'] = parsed_args.interface
- if parsed_args.region:
- kwargs['region'] = parsed_args.region
- data = identity_client.endpoints.list(**kwargs)
- service_list = identity_client.services.list()
-
- for ep in data:
- service = common.find_service_in_list(service_list, ep.service_id)
- ep.service_name = get_service_name(service)
- ep.service_type = service.type
+
+ endpoint = None
+ if parsed_args.endpoint:
+ endpoint = utils.find_resource(identity_client.endpoints,
+ parsed_args.endpoint)
+ project = None
+ if parsed_args.project:
+ project = common.find_project(identity_client,
+ parsed_args.project,
+ parsed_args.project_domain)
+
+ if endpoint:
+ columns = ('ID', 'Name')
+ data = (
+ identity_client.endpoint_filter
+ .list_projects_for_endpoint(endpoint=endpoint.id)
+ )
+ else:
+ columns = ('ID', 'Region', 'Service Name', 'Service Type',
+ 'Enabled', 'Interface', 'URL')
+ kwargs = {}
+ if parsed_args.service:
+ service = common.find_service(identity_client,
+ parsed_args.service)
+ kwargs['service'] = service.id
+ if parsed_args.interface:
+ kwargs['interface'] = parsed_args.interface
+ if parsed_args.region:
+ kwargs['region'] = parsed_args.region
+
+ if project:
+ data = (
+ identity_client.endpoint_filter
+ .list_endpoints_for_project(project=project.id)
+ )
+ else:
+ data = identity_client.endpoints.list(**kwargs)
+
+ service_list = identity_client.services.list()
+
+ for ep in data:
+ service = common.find_service_in_list(service_list,
+ ep.service_id)
+ ep.service_name = get_service_name(service)
+ ep.service_type = service.type
+
return (columns,
(utils.get_item_properties(
s, columns,
@@ -180,6 +257,42 @@ class ListEndpoint(command.Lister):
) for s in data))
+class RemoveProjectFromEndpoint(command.Command):
+ _description = _("Dissociate a project from an endpoint")
+
+ def get_parser(self, prog_name):
+ parser = super(
+ RemoveProjectFromEndpoint, self).get_parser(prog_name)
+ parser.add_argument(
+ 'endpoint',
+ metavar='<endpoint>',
+ help=_('Endpoint to dissociate from '
+ 'specified project (name or ID)'),
+ )
+ parser.add_argument(
+ 'project',
+ metavar='<project>',
+ help=_('Project to dissociate from '
+ 'specified endpoint name or ID)'),
+ )
+ common.add_project_domain_option_to_parser(parser)
+ return parser
+
+ def take_action(self, parsed_args):
+ client = self.app.client_manager.identity
+
+ endpoint = utils.find_resource(client.endpoints,
+ parsed_args.endpoint)
+
+ project = common.find_project(client,
+ parsed_args.project,
+ parsed_args.project_domain)
+
+ client.endpoint_filter.delete_endpoint_from_project(
+ project=project.id,
+ endpoint=endpoint.id)
+
+
class SetEndpoint(command.Command):
_description = _("Set endpoint properties")
diff --git a/openstackclient/tests/functional/identity/v3/common.py b/openstackclient/tests/functional/identity/v3/common.py
index 6d7896d..33cb5d8 100644
--- a/openstackclient/tests/functional/identity/v3/common.py
+++ b/openstackclient/tests/functional/identity/v3/common.py
@@ -42,6 +42,7 @@ class IdentityTests(base.TestCase):
REGION_LIST_HEADERS = ['Region', 'Parent Region', 'Description']
ENDPOINT_LIST_HEADERS = ['ID', 'Region', 'Service Name', 'Service Type',
'Enabled', 'Interface', 'URL']
+ ENDPOINT_LIST_PROJECT_HEADERS = ['ID', 'Name']
IDENTITY_PROVIDER_FIELDS = ['description', 'enabled', 'id', 'remote_ids',
'domain_id']
diff --git a/openstackclient/tests/functional/identity/v3/test_endpoint.py b/openstackclient/tests/functional/identity/v3/test_endpoint.py
index 22dc1b6..41f0b4c 100644
--- a/openstackclient/tests/functional/identity/v3/test_endpoint.py
+++ b/openstackclient/tests/functional/identity/v3/test_endpoint.py
@@ -42,6 +42,29 @@ class EndpointTests(common.IdentityTests):
items = self.parse_listing(raw_output)
self.assert_table_structure(items, self.ENDPOINT_LIST_HEADERS)
+ def test_endpoint_list_filter(self):
+ endpoint_id = self._create_dummy_endpoint(add_clean_up=False)
+ project_id = self._create_dummy_project(add_clean_up=False)
+ raw_output = self.openstack(
+ 'endpoint add project '
+ '%(endpoint_id)s '
+ '%(project_id)s' % {
+ 'project_id': project_id,
+ 'endpoint_id': endpoint_id})
+ self.assertEqual(0, len(raw_output))
+ raw_output = self.openstack(
+ 'endpoint list --endpoint %s' % endpoint_id)
+ self.assertIn(project_id, raw_output)
+ items = self.parse_listing(raw_output)
+ self.assert_table_structure(items,
+ self.ENDPOINT_LIST_PROJECT_HEADERS)
+
+ raw_output = self.openstack(
+ 'endpoint list --project %s' % project_id)
+ self.assertIn(endpoint_id, raw_output)
+ items = self.parse_listing(raw_output)
+ self.assert_table_structure(items, self.ENDPOINT_LIST_HEADERS)
+
def test_endpoint_set(self):
endpoint_id = self._create_dummy_endpoint()
new_endpoint_url = data_utils.rand_url()
@@ -65,3 +88,22 @@ class EndpointTests(common.IdentityTests):
raw_output = self.openstack('endpoint show %s' % endpoint_id)
items = self.parse_show(raw_output)
self.assert_show_fields(items, self.ENDPOINT_FIELDS)
+
+ def test_endpoint_add_remove_project(self):
+ endpoint_id = self._create_dummy_endpoint(add_clean_up=False)
+ project_id = self._create_dummy_project(add_clean_up=False)
+ raw_output = self.openstack(
+ 'endpoint add project '
+ '%(endpoint_id)s '
+ '%(project_id)s' % {
+ 'project_id': project_id,
+ 'endpoint_id': endpoint_id})
+ self.assertEqual(0, len(raw_output))
+
+ raw_output = self.openstack(
+ 'endpoint remove project '
+ '%(endpoint_id)s '
+ '%(project_id)s' % {
+ 'project_id': project_id,
+ 'endpoint_id': endpoint_id})
+ self.assertEqual(0, len(raw_output))
diff --git a/openstackclient/tests/unit/identity/v3/fakes.py b/openstackclient/tests/unit/identity/v3/fakes.py
index c7d2988..549a1aa 100644
--- a/openstackclient/tests/unit/identity/v3/fakes.py
+++ b/openstackclient/tests/unit/identity/v3/fakes.py
@@ -491,6 +491,8 @@ class FakeIdentityv3Client(object):
self.credentials.resource_class = fakes.FakeResource(None, {})
self.endpoints = mock.Mock()
self.endpoints.resource_class = fakes.FakeResource(None, {})
+ self.endpoint_filter = mock.Mock()
+ self.endpoint_filter.resource_class = fakes.FakeResource(None, {})
self.groups = mock.Mock()
self.groups.resource_class = fakes.FakeResource(None, {})
self.oauth1 = mock.Mock()
@@ -909,6 +911,31 @@ class FakeEndpoint(object):
loaded=True)
return endpoint
+ @staticmethod
+ def create_one_endpoint_filter(attrs=None):
+ """Create a fake endpoint project relationship.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes of endpoint filter
+ :return:
+ A FakeResource object with project, endpoint and so on
+ """
+ attrs = attrs or {}
+
+ # Set default attribute
+ endpoint_filter_info = {
+ 'project': 'project-id-' + uuid.uuid4().hex,
+ 'endpoint': 'endpoint-id-' + uuid.uuid4().hex,
+ }
+
+ # Overwrite default attributes if there are some attributes set
+ endpoint_filter_info.update(attrs)
+
+ endpoint_filter = fakes.FakeModel(
+ copy.deepcopy(endpoint_filter_info))
+
+ return endpoint_filter
+
class FakeService(object):
"""Fake one or more service."""
diff --git a/openstackclient/tests/unit/identity/v3/test_endpoint.py b/openstackclient/tests/unit/identity/v3/test_endpoint.py
index fad53fc..bfe930d 100644
--- a/openstackclient/tests/unit/identity/v3/test_endpoint.py
+++ b/openstackclient/tests/unit/identity/v3/test_endpoint.py
@@ -22,11 +22,23 @@ class TestEndpoint(identity_fakes.TestIdentityv3):
# Get a shortcut to the EndpointManager Mock
self.endpoints_mock = self.app.client_manager.identity.endpoints
self.endpoints_mock.reset_mock()
+ self.ep_filter_mock = (
+ self.app.client_manager.identity.endpoint_filter
+ )
+ self.ep_filter_mock.reset_mock()
# Get a shortcut to the ServiceManager Mock
self.services_mock = self.app.client_manager.identity.services
self.services_mock.reset_mock()
+ # Get a shortcut to the DomainManager Mock
+ self.domains_mock = self.app.client_manager.identity.domains
+ self.domains_mock.reset_mock()
+
+ # Get a shortcut to the ProjectManager Mock
+ self.projects_mock = self.app.client_manager.identity.projects
+ self.projects_mock.reset_mock()
+
class TestEndpointCreate(TestEndpoint):
@@ -750,3 +762,130 @@ class TestEndpointShowServiceWithoutName(TestEndpointShow):
# Get the command object to test
self.cmd = endpoint.ShowEndpoint(self.app, None)
+
+
+class TestAddProjectToEndpoint(TestEndpoint):
+
+ project = identity_fakes.FakeProject.create_one_project()
+ domain = identity_fakes.FakeDomain.create_one_domain()
+ service = identity_fakes.FakeService.create_one_service()
+ endpoint = identity_fakes.FakeEndpoint.create_one_endpoint(
+ attrs={'service_id': service.id})
+
+ new_ep_filter = identity_fakes.FakeEndpoint.create_one_endpoint_filter(
+ attrs={'endpoint': endpoint.id,
+ 'project': project.id}
+ )
+
+ def setUp(self):
+ super(TestAddProjectToEndpoint, self).setUp()
+
+ # This is the return value for utils.find_resource()
+ self.endpoints_mock.get.return_value = self.endpoint
+
+ # Update the image_id in the MEMBER dict
+ self.ep_filter_mock.create.return_value = self.new_ep_filter
+ self.projects_mock.get.return_value = self.project
+ self.domains_mock.get.return_value = self.domain
+ # Get the command object to test
+ self.cmd = endpoint.AddProjectToEndpoint(self.app, None)
+
+ def test_add_project_to_endpoint_no_option(self):
+ arglist = [
+ self.endpoint.id,
+ self.project.id,
+ ]
+ verifylist = [
+ ('endpoint', self.endpoint.id),
+ ('project', self.project.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+ self.ep_filter_mock.add_endpoint_to_project.assert_called_with(
+ project=self.project.id,
+ endpoint=self.endpoint.id
+ )
+ self.assertIsNone(result)
+
+ def test_add_project_to_endpoint_with_option(self):
+ arglist = [
+ self.endpoint.id,
+ self.project.id,
+ '--project-domain', self.domain.id,
+ ]
+ verifylist = [
+ ('endpoint', self.endpoint.id),
+ ('project', self.project.id),
+ ('project_domain', self.domain.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+ self.ep_filter_mock.add_endpoint_to_project.assert_called_with(
+ project=self.project.id,
+ endpoint=self.endpoint.id
+ )
+ self.assertIsNone(result)
+
+
+class TestRemoveProjectEndpoint(TestEndpoint):
+
+ project = identity_fakes.FakeProject.create_one_project()
+ domain = identity_fakes.FakeDomain.create_one_domain()
+ service = identity_fakes.FakeService.create_one_service()
+ endpoint = identity_fakes.FakeEndpoint.create_one_endpoint(
+ attrs={'service_id': service.id})
+
+ def setUp(self):
+ super(TestRemoveProjectEndpoint, self).setUp()
+
+ # This is the return value for utils.find_resource()
+ self.endpoints_mock.get.return_value = self.endpoint
+
+ self.projects_mock.get.return_value = self.project
+ self.domains_mock.get.return_value = self.domain
+ self.ep_filter_mock.delete.return_value = None
+
+ # Get the command object to test
+ self.cmd = endpoint.RemoveProjectFromEndpoint(self.app, None)
+
+ def test_remove_project_endpoint_no_options(self):
+ arglist = [
+ self.endpoint.id,
+ self.project.id,
+ ]
+ verifylist = [
+ ('endpoint', self.endpoint.id),
+ ('project', self.project.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ self.ep_filter_mock.delete_endpoint_from_project.assert_called_with(
+ project=self.project.id,
+ endpoint=self.endpoint.id,
+ )
+ self.assertIsNone(result)
+
+ def test_remove_project_endpoint_with_options(self):
+ arglist = [
+ self.endpoint.id,
+ self.project.id,
+ '--project-domain', self.domain.id,
+ ]
+ verifylist = [
+ ('endpoint', self.endpoint.id),
+ ('project', self.project.id),
+ ('project_domain', self.domain.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ self.ep_filter_mock.delete_endpoint_from_project.assert_called_with(
+ project=self.project.id,
+ endpoint=self.endpoint.id,
+ )
+ self.assertIsNone(result)
diff --git a/releasenotes/notes/keystone-endpoint-filter-e930a7b72276fa2c.yaml b/releasenotes/notes/keystone-endpoint-filter-e930a7b72276fa2c.yaml
new file mode 100644
index 0000000..5a633ee
--- /dev/null
+++ b/releasenotes/notes/keystone-endpoint-filter-e930a7b72276fa2c.yaml
@@ -0,0 +1,5 @@
+---
+features:
+ - |
+ Add ``endpoint add project``, ``endpoint remove project`` and ``endpoint
+ list`` commands to manage endpoint filters in identity v3.
diff --git a/setup.cfg b/setup.cfg
index 1b8e006..d60657f 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -195,12 +195,15 @@ openstack.identity.v3 =
ec2_credentials_delete = openstackclient.identity.v3.ec2creds:DeleteEC2Creds
ec2_credentials_list = openstackclient.identity.v3.ec2creds:ListEC2Creds
ec2_credentials_show = openstackclient.identity.v3.ec2creds:ShowEC2Creds
- endpoint_create = openstackclient.identity.v3.endpoint:CreateEndpoint
+
+ endpoint_add_project = openstackclient.identity.v3.endpoint:AddProjectToEndpoint
+ endpoint_create = openstackclient.identity.v3.endpoint:CreateEndpoint
endpoint_delete = openstackclient.identity.v3.endpoint:DeleteEndpoint
+ endpoint_list = openstackclient.identity.v3.endpoint:ListEndpoint
+ endpoint_remove_project = openstackclient.identity.v3.endpoint:RemoveProjectFromEndpoint
endpoint_set = openstackclient.identity.v3.endpoint:SetEndpoint
endpoint_show = openstackclient.identity.v3.endpoint:ShowEndpoint
- endpoint_list = openstackclient.identity.v3.endpoint:ListEndpoint
- group_add_user = openstackclient.identity.v3.group:AddUserToGroup
+ group_add_user = openstackclient.identity.v3.group:AddUserToGroup
group_contains_user = openstackclient.identity.v3.group:CheckUserInGroup
group_create = openstackclient.identity.v3.group:CreateGroup
group_delete = openstackclient.identity.v3.group:DeleteGroup
--
1.8.3.1