From 2e7b4f2027a1147ca28301e4f88adf8274b39a1f Mon Sep 17 00:00:00 2001 From: DUVAL Thomas Date: Thu, 9 Jun 2016 09:11:50 +0200 Subject: Update Keystone core to Mitaka. Change-Id: Ia10d6add16f4a9d25d1f42d420661c46332e69db --- .../test_associate_project_endpoint_extension.py | 453 +++++++++++++++++---- 1 file changed, 377 insertions(+), 76 deletions(-) (limited to 'keystone-moon/keystone/tests/unit/test_associate_project_endpoint_extension.py') diff --git a/keystone-moon/keystone/tests/unit/test_associate_project_endpoint_extension.py b/keystone-moon/keystone/tests/unit/test_associate_project_endpoint_extension.py index 24fc82dd..79065863 100644 --- a/keystone-moon/keystone/tests/unit/test_associate_project_endpoint_extension.py +++ b/keystone-moon/keystone/tests/unit/test_associate_project_endpoint_extension.py @@ -15,24 +15,25 @@ import copy import uuid +import mock +from oslo_log import versionutils from six.moves import http_client from testtools import matchers +from keystone.contrib.endpoint_filter import routers +from keystone.tests import unit from keystone.tests.unit import test_v3 -class TestExtensionCase(test_v3.RestfulTestCase): - - EXTENSION_NAME = 'endpoint_filter' - EXTENSION_TO_ADD = 'endpoint_filter_extension' +class EndpointFilterTestCase(test_v3.RestfulTestCase): def config_overrides(self): - super(TestExtensionCase, self).config_overrides() + super(EndpointFilterTestCase, self).config_overrides() self.config_fixture.config( group='catalog', driver='endpoint_filter.sql') def setUp(self): - super(TestExtensionCase, self).setUp() + super(EndpointFilterTestCase, self).setUp() self.default_request_url = ( '/OS-EP-FILTER/projects/%(project_id)s' '/endpoints/%(endpoint_id)s' % { @@ -40,7 +41,17 @@ class TestExtensionCase(test_v3.RestfulTestCase): 'endpoint_id': self.endpoint_id}) -class EndpointFilterCRUDTestCase(TestExtensionCase): +class EndpointFilterDeprecateTestCase(test_v3.RestfulTestCase): + + @mock.patch.object(versionutils, 'report_deprecated_feature') + def test_exception_happens(self, mock_deprecator): + routers.EndpointFilterExtension(mock.ANY) + mock_deprecator.assert_called_once_with(mock.ANY, mock.ANY) + args, _kwargs = mock_deprecator.call_args + self.assertIn("Remove endpoint_filter_extension from", args[1]) + + +class EndpointFilterCRUDTestCase(EndpointFilterTestCase): def test_create_endpoint_project_association(self): """PUT /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id} @@ -48,8 +59,7 @@ class EndpointFilterCRUDTestCase(TestExtensionCase): Valid endpoint and project id test case. """ - self.put(self.default_request_url, - expected_status=204) + self.put(self.default_request_url) def test_create_endpoint_project_association_with_invalid_project(self): """PUT OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id} @@ -82,8 +92,7 @@ class EndpointFilterCRUDTestCase(TestExtensionCase): """ self.put(self.default_request_url, - body={'project_id': self.default_domain_project_id}, - expected_status=204) + body={'project_id': self.default_domain_project_id}) def test_check_endpoint_project_association(self): """HEAD /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id} @@ -91,13 +100,11 @@ class EndpointFilterCRUDTestCase(TestExtensionCase): Valid project and endpoint id test case. """ - self.put(self.default_request_url, - expected_status=204) + self.put(self.default_request_url) self.head('/OS-EP-FILTER/projects/%(project_id)s' '/endpoints/%(endpoint_id)s' % { 'project_id': self.default_domain_project_id, - 'endpoint_id': self.endpoint_id}, - expected_status=204) + 'endpoint_id': self.endpoint_id}) def test_check_endpoint_project_association_with_invalid_project(self): """HEAD /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id} @@ -169,8 +176,7 @@ class EndpointFilterCRUDTestCase(TestExtensionCase): """ r = self.get('/OS-EP-FILTER/endpoints/%(endpoint_id)s/projects' % - {'endpoint_id': self.endpoint_id}, - expected_status=200) + {'endpoint_id': self.endpoint_id}) self.assertValidProjectListResponse(r, expected_length=0) def test_list_projects_associated_with_invalid_endpoint(self): @@ -193,8 +199,7 @@ class EndpointFilterCRUDTestCase(TestExtensionCase): self.delete('/OS-EP-FILTER/projects/%(project_id)s' '/endpoints/%(endpoint_id)s' % { 'project_id': self.default_domain_project_id, - 'endpoint_id': self.endpoint_id}, - expected_status=204) + 'endpoint_id': self.endpoint_id}) def test_remove_endpoint_project_association_with_invalid_project(self): """DELETE /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id} @@ -226,35 +231,167 @@ class EndpointFilterCRUDTestCase(TestExtensionCase): self.put(self.default_request_url) association_url = ('/OS-EP-FILTER/endpoints/%(endpoint_id)s/projects' % {'endpoint_id': self.endpoint_id}) - r = self.get(association_url, expected_status=200) + r = self.get(association_url) self.assertValidProjectListResponse(r, expected_length=1) self.delete('/projects/%(project_id)s' % { 'project_id': self.default_domain_project_id}) - r = self.get(association_url, expected_status=200) + r = self.get(association_url) self.assertValidProjectListResponse(r, expected_length=0) def test_endpoint_project_association_cleanup_when_endpoint_deleted(self): self.put(self.default_request_url) association_url = '/OS-EP-FILTER/projects/%(project_id)s/endpoints' % { 'project_id': self.default_domain_project_id} - r = self.get(association_url, expected_status=200) + r = self.get(association_url) self.assertValidEndpointListResponse(r, expected_length=1) self.delete('/endpoints/%(endpoint_id)s' % { 'endpoint_id': self.endpoint_id}) - r = self.get(association_url, expected_status=200) + r = self.get(association_url) self.assertValidEndpointListResponse(r, expected_length=0) + @unit.skip_if_cache_disabled('catalog') + def test_create_endpoint_project_association_invalidates_cache(self): + # NOTE(davechen): create another endpoint which will be added to + # default project, this should be done at first since + # `create_endpoint` will also invalidate cache. + endpoint_id2 = uuid.uuid4().hex + endpoint2 = unit.new_endpoint_ref(service_id=self.service_id, + region_id=self.region_id, + interface='public', + id=endpoint_id2) + self.catalog_api.create_endpoint(endpoint_id2, endpoint2.copy()) + + # create endpoint project association. + self.put(self.default_request_url) -class EndpointFilterTokenRequestTestCase(TestExtensionCase): + # should get back only one endpoint that was just created. + user_id = uuid.uuid4().hex + catalog = self.catalog_api.get_v3_catalog( + user_id, + self.default_domain_project_id) + + # there is only one endpoints associated with the default project. + self.assertEqual(1, len(catalog[0]['endpoints'])) + self.assertEqual(self.endpoint_id, catalog[0]['endpoints'][0]['id']) + + # add the second endpoint to default project, bypassing + # catalog_api API manager. + self.catalog_api.driver.add_endpoint_to_project( + endpoint_id2, + self.default_domain_project_id) + + # but, we can just get back one endpoint from the cache, since the + # catalog is pulled out from cache and its haven't been invalidated. + catalog = self.catalog_api.get_v3_catalog( + user_id, + self.default_domain_project_id) + + self.assertEqual(1, len(catalog[0]['endpoints'])) + + # remove the endpoint2 from the default project, and add it again via + # catalog_api API manager. + self.catalog_api.driver.remove_endpoint_from_project( + endpoint_id2, + self.default_domain_project_id) + + # add second endpoint to default project, this can be done by calling + # the catalog_api API manager directly but call the REST API + # instead for consistency. + self.put('/OS-EP-FILTER/projects/%(project_id)s' + '/endpoints/%(endpoint_id)s' % { + 'project_id': self.default_domain_project_id, + 'endpoint_id': endpoint_id2}) + + # should get back two endpoints since the cache has been + # invalidated when the second endpoint was added to default project. + catalog = self.catalog_api.get_v3_catalog( + user_id, + self.default_domain_project_id) + + self.assertEqual(2, len(catalog[0]['endpoints'])) + + ep_id_list = [catalog[0]['endpoints'][0]['id'], + catalog[0]['endpoints'][1]['id']] + self.assertItemsEqual([self.endpoint_id, endpoint_id2], ep_id_list) + + @unit.skip_if_cache_disabled('catalog') + def test_remove_endpoint_from_project_invalidates_cache(self): + endpoint_id2 = uuid.uuid4().hex + endpoint2 = unit.new_endpoint_ref(service_id=self.service_id, + region_id=self.region_id, + interface='public', + id=endpoint_id2) + self.catalog_api.create_endpoint(endpoint_id2, endpoint2.copy()) + # create endpoint project association. + self.put(self.default_request_url) + + # add second endpoint to default project. + self.put('/OS-EP-FILTER/projects/%(project_id)s' + '/endpoints/%(endpoint_id)s' % { + 'project_id': self.default_domain_project_id, + 'endpoint_id': endpoint_id2}) + + # should get back only one endpoint that was just created. + user_id = uuid.uuid4().hex + catalog = self.catalog_api.get_v3_catalog( + user_id, + self.default_domain_project_id) + + # there are two endpoints associated with the default project. + ep_id_list = [catalog[0]['endpoints'][0]['id'], + catalog[0]['endpoints'][1]['id']] + self.assertEqual(2, len(catalog[0]['endpoints'])) + self.assertItemsEqual([self.endpoint_id, endpoint_id2], ep_id_list) + + # remove the endpoint2 from the default project, bypassing + # catalog_api API manager. + self.catalog_api.driver.remove_endpoint_from_project( + endpoint_id2, + self.default_domain_project_id) + + # but, we can just still get back two endpoints from the cache, + # since the catalog is pulled out from cache and its haven't + # been invalidated. + catalog = self.catalog_api.get_v3_catalog( + user_id, + self.default_domain_project_id) + + self.assertEqual(2, len(catalog[0]['endpoints'])) + + # add back the endpoint2 to the default project, and remove it by + # catalog_api API manage. + self.catalog_api.driver.add_endpoint_to_project( + endpoint_id2, + self.default_domain_project_id) + + # remove the endpoint2 from the default project, this can be done + # by calling the catalog_api API manager directly but call + # the REST API instead for consistency. + self.delete('/OS-EP-FILTER/projects/%(project_id)s' + '/endpoints/%(endpoint_id)s' % { + 'project_id': self.default_domain_project_id, + 'endpoint_id': endpoint_id2}) + + # should only get back one endpoint since the cache has been + # invalidated after the endpoint project association was removed. + catalog = self.catalog_api.get_v3_catalog( + user_id, + self.default_domain_project_id) + + self.assertEqual(1, len(catalog[0]['endpoints'])) + self.assertEqual(self.endpoint_id, catalog[0]['endpoints'][0]['id']) + + +class EndpointFilterTokenRequestTestCase(EndpointFilterTestCase): def test_project_scoped_token_using_endpoint_filter(self): """Verify endpoints from project scoped token filtered.""" # create a project to work with - ref = self.new_project_ref(domain_id=self.domain_id) + ref = unit.new_project_ref(domain_id=self.domain_id) r = self.post('/projects', body={'project': ref}) project = self.assertValidProjectResponse(r, ref) @@ -276,8 +413,7 @@ class EndpointFilterTokenRequestTestCase(TestExtensionCase): self.put('/OS-EP-FILTER/projects/%(project_id)s' '/endpoints/%(endpoint_id)s' % { 'project_id': project['id'], - 'endpoint_id': self.endpoint_id}, - expected_status=204) + 'endpoint_id': self.endpoint_id}) # attempt to authenticate without requesting a project auth_data = self.build_authentication_request( @@ -289,7 +425,7 @@ class EndpointFilterTokenRequestTestCase(TestExtensionCase): require_catalog=True, endpoint_filter=True, ep_filter_assoc=1) - self.assertEqual(r.result['token']['project']['id'], project['id']) + self.assertEqual(project['id'], r.result['token']['project']['id']) def test_default_scoped_token_using_endpoint_filter(self): """Verify endpoints from default scoped token filtered.""" @@ -297,8 +433,7 @@ class EndpointFilterTokenRequestTestCase(TestExtensionCase): self.put('/OS-EP-FILTER/projects/%(project_id)s' '/endpoints/%(endpoint_id)s' % { 'project_id': self.project['id'], - 'endpoint_id': self.endpoint_id}, - expected_status=204) + 'endpoint_id': self.endpoint_id}) auth_data = self.build_authentication_request( user_id=self.user['id'], @@ -310,16 +445,24 @@ class EndpointFilterTokenRequestTestCase(TestExtensionCase): require_catalog=True, endpoint_filter=True, ep_filter_assoc=1) - self.assertEqual(r.result['token']['project']['id'], - self.project['id']) + self.assertEqual(self.project['id'], + r.result['token']['project']['id']) + + # Ensure name of the service exists + self.assertIn('name', r.result['token']['catalog'][0]) + + # region and region_id should be the same in endpoints + endpoint = r.result['token']['catalog'][0]['endpoints'][0] + self.assertIn('region', endpoint) + self.assertIn('region_id', endpoint) + self.assertEqual(endpoint['region'], endpoint['region_id']) def test_scoped_token_with_no_catalog_using_endpoint_filter(self): """Verify endpoint filter does not affect no catalog.""" self.put('/OS-EP-FILTER/projects/%(project_id)s' '/endpoints/%(endpoint_id)s' % { 'project_id': self.project['id'], - 'endpoint_id': self.endpoint_id}, - expected_status=204) + 'endpoint_id': self.endpoint_id}) auth_data = self.build_authentication_request( user_id=self.user['id'], @@ -329,8 +472,8 @@ class EndpointFilterTokenRequestTestCase(TestExtensionCase): self.assertValidProjectScopedTokenResponse( r, require_catalog=False) - self.assertEqual(r.result['token']['project']['id'], - self.project['id']) + self.assertEqual(self.project['id'], + r.result['token']['project']['id']) def test_invalid_endpoint_project_association(self): """Verify an invalid endpoint-project association is handled.""" @@ -338,28 +481,26 @@ class EndpointFilterTokenRequestTestCase(TestExtensionCase): self.put('/OS-EP-FILTER/projects/%(project_id)s' '/endpoints/%(endpoint_id)s' % { 'project_id': self.project['id'], - 'endpoint_id': self.endpoint_id}, - expected_status=204) + 'endpoint_id': self.endpoint_id}) # create a second temporary endpoint - self.endpoint_id2 = uuid.uuid4().hex - self.endpoint2 = self.new_endpoint_ref(service_id=self.service_id) - self.endpoint2['id'] = self.endpoint_id2 - self.catalog_api.create_endpoint( - self.endpoint_id2, - self.endpoint2.copy()) + endpoint_id2 = uuid.uuid4().hex + endpoint2 = unit.new_endpoint_ref(service_id=self.service_id, + region_id=self.region_id, + interface='public', + id=endpoint_id2) + self.catalog_api.create_endpoint(endpoint_id2, endpoint2.copy()) # add second endpoint to default project self.put('/OS-EP-FILTER/projects/%(project_id)s' '/endpoints/%(endpoint_id)s' % { 'project_id': self.project['id'], - 'endpoint_id': self.endpoint_id2}, - expected_status=204) + 'endpoint_id': endpoint_id2}) # remove the temporary reference # this will create inconsistency in the endpoint filter table # which is fixed during the catalog creation for token request - self.catalog_api.delete_endpoint(self.endpoint_id2) + self.catalog_api.delete_endpoint(endpoint_id2) auth_data = self.build_authentication_request( user_id=self.user['id'], @@ -371,8 +512,8 @@ class EndpointFilterTokenRequestTestCase(TestExtensionCase): require_catalog=True, endpoint_filter=True, ep_filter_assoc=1) - self.assertEqual(r.result['token']['project']['id'], - self.project['id']) + self.assertEqual(self.project['id'], + r.result['token']['project']['id']) def test_disabled_endpoint(self): """Test that a disabled endpoint is handled.""" @@ -380,8 +521,7 @@ class EndpointFilterTokenRequestTestCase(TestExtensionCase): self.put('/OS-EP-FILTER/projects/%(project_id)s' '/endpoints/%(endpoint_id)s' % { 'project_id': self.project['id'], - 'endpoint_id': self.endpoint_id}, - expected_status=204) + 'endpoint_id': self.endpoint_id}) # Add a disabled endpoint to the default project. @@ -399,8 +539,7 @@ class EndpointFilterTokenRequestTestCase(TestExtensionCase): self.put('/OS-EP-FILTER/projects/%(project_id)s' '/endpoints/%(endpoint_id)s' % { 'project_id': self.project['id'], - 'endpoint_id': disabled_endpoint_id}, - expected_status=204) + 'endpoint_id': disabled_endpoint_id}) # Authenticate to get token with catalog auth_data = self.build_authentication_request( @@ -416,7 +555,9 @@ class EndpointFilterTokenRequestTestCase(TestExtensionCase): def test_multiple_endpoint_project_associations(self): def _create_an_endpoint(): - endpoint_ref = self.new_endpoint_ref(service_id=self.service_id) + endpoint_ref = unit.new_endpoint_ref(service_id=self.service_id, + interface='public', + region_id=self.region_id) r = self.post('/endpoints', body={'endpoint': endpoint_ref}) return r.result['endpoint']['id'] @@ -429,13 +570,11 @@ class EndpointFilterTokenRequestTestCase(TestExtensionCase): self.put('/OS-EP-FILTER/projects/%(project_id)s' '/endpoints/%(endpoint_id)s' % { 'project_id': self.project['id'], - 'endpoint_id': endpoint_id1}, - expected_status=204) + 'endpoint_id': endpoint_id1}) self.put('/OS-EP-FILTER/projects/%(project_id)s' '/endpoints/%(endpoint_id)s' % { 'project_id': self.project['id'], - 'endpoint_id': endpoint_id2}, - expected_status=204) + 'endpoint_id': endpoint_id2}) # there should be only two endpoints in token catalog auth_data = self.build_authentication_request( @@ -454,8 +593,7 @@ class EndpointFilterTokenRequestTestCase(TestExtensionCase): self.put('/OS-EP-FILTER/projects/%(project_id)s' '/endpoints/%(endpoint_id)s' % { 'project_id': self.project['id'], - 'endpoint_id': self.endpoint_id}, - expected_status=204) + 'endpoint_id': self.endpoint_id}) auth_data = self.build_authentication_request( user_id=self.user['id'], @@ -474,7 +612,7 @@ class EndpointFilterTokenRequestTestCase(TestExtensionCase): auth_catalog.result['catalog']) -class JsonHomeTests(TestExtensionCase, test_v3.JsonHomeTestMixin): +class JsonHomeTests(EndpointFilterTestCase, test_v3.JsonHomeTestMixin): JSON_HOME_DATA = { 'http://docs.openstack.org/api/openstack-identity/3/ext/OS-EP-FILTER/' '1.0/rel/endpoint_projects': { @@ -545,7 +683,7 @@ class JsonHomeTests(TestExtensionCase, test_v3.JsonHomeTestMixin): } -class EndpointGroupCRUDTestCase(TestExtensionCase): +class EndpointGroupCRUDTestCase(EndpointFilterTestCase): DEFAULT_ENDPOINT_GROUP_BODY = { 'endpoint_group': { @@ -638,7 +776,7 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): self.DEFAULT_ENDPOINT_GROUP_URL, self.DEFAULT_ENDPOINT_GROUP_BODY) url = '/OS-EP-FILTER/endpoint_groups/%(endpoint_group_id)s' % { 'endpoint_group_id': endpoint_group_id} - self.head(url, expected_status=200) + self.head(url, expected_status=http_client.OK) def test_check_invalid_endpoint_group(self): """HEAD /OS-EP-FILTER/endpoint_groups/{endpoint_group_id} @@ -832,7 +970,7 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): self.project_id) url = self._get_project_endpoint_group_url( endpoint_group_id, self.project_id) - self.head(url, expected_status=200) + self.head(url, expected_status=http_client.OK) def test_check_endpoint_group_to_project_with_invalid_project_id(self): """Test HEAD with an invalid endpoint group and project association.""" @@ -891,7 +1029,7 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): """ # create a service - service_ref = self.new_service_ref() + service_ref = unit.new_service_ref() response = self.post( '/services', body={'service': service_ref}) @@ -899,10 +1037,10 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): service_id = response.result['service']['id'] # create an endpoint - endpoint_ref = self.new_endpoint_ref(service_id=service_id) - response = self.post( - '/endpoints', - body={'endpoint': endpoint_ref}) + endpoint_ref = unit.new_endpoint_ref(service_id=service_id, + interface='public', + region_id=self.region_id) + response = self.post('/endpoints', body={'endpoint': endpoint_ref}) endpoint_id = response.result['endpoint']['id'] # create an endpoint group @@ -929,7 +1067,7 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): """ # create a temporary service - service_ref = self.new_service_ref() + service_ref = unit.new_service_ref() response = self.post('/services', body={'service': service_ref}) service_id2 = response.result['service']['id'] @@ -957,7 +1095,16 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): 'project_id': self.default_domain_project_id} r = self.get(endpoints_url) endpoints = self.assertValidEndpointListResponse(r) - self.assertEqual(len(endpoints), 2) + self.assertEqual(2, len(endpoints)) + + # Ensure catalog includes the endpoints from endpoint_group project + # association, this is needed when a project scoped token is issued + # and "endpoint_filter.sql" backend driver is in place. + user_id = uuid.uuid4().hex + catalog_list = self.catalog_api.get_v3_catalog( + user_id, + self.default_domain_project_id) + self.assertEqual(2, len(catalog_list)) # Now remove project endpoint group association url = self._get_project_endpoint_group_url( @@ -971,7 +1118,12 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): r = self.get(endpoints_url) endpoints = self.assertValidEndpointListResponse(r) - self.assertEqual(len(endpoints), 1) + self.assertEqual(1, len(endpoints)) + + catalog_list = self.catalog_api.get_v3_catalog( + user_id, + self.default_domain_project_id) + self.assertEqual(1, len(catalog_list)) def test_endpoint_group_project_cleanup_with_project(self): # create endpoint group @@ -979,7 +1131,7 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): self.DEFAULT_ENDPOINT_GROUP_URL, self.DEFAULT_ENDPOINT_GROUP_BODY) # create new project and associate with endpoint_group - project_ref = self.new_project_ref(domain_id=self.domain_id) + project_ref = unit.new_project_ref(domain_id=self.domain_id) r = self.post('/projects', body={'project': project_ref}) project = self.assertValidProjectResponse(r, project_ref) url = self._get_project_endpoint_group_url(endpoint_group_id, @@ -1001,7 +1153,7 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): self.DEFAULT_ENDPOINT_GROUP_URL, self.DEFAULT_ENDPOINT_GROUP_BODY) # create new project and associate with endpoint_group - project_ref = self.new_project_ref(domain_id=self.domain_id) + project_ref = unit.new_project_ref(domain_id=self.domain_id) r = self.post('/projects', body={'project': project_ref}) project = self.assertValidProjectResponse(r, project_ref) url = self._get_project_endpoint_group_url(endpoint_group_id, @@ -1049,6 +1201,153 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): self.get(project_endpoint_group_url, expected_status=http_client.NOT_FOUND) + @unit.skip_if_cache_disabled('catalog') + def test_add_endpoint_group_to_project_invalidates_catalog_cache(self): + # create another endpoint with 'admin' interface which matches + # 'filters' definition in endpoint group, then there should be two + # endpoints returned when retrieving v3 catalog if cache works as + # expected. + # this should be done at first since `create_endpoint` will also + # invalidate cache. + endpoint_id2 = uuid.uuid4().hex + endpoint2 = unit.new_endpoint_ref(service_id=self.service_id, + region_id=self.region_id, + interface='admin', + id=endpoint_id2) + self.catalog_api.create_endpoint(endpoint_id2, endpoint2) + + # create a project and endpoint association. + self.put(self.default_request_url) + + # there is only one endpoint associated with the default project. + user_id = uuid.uuid4().hex + catalog = self.catalog_api.get_v3_catalog( + user_id, + self.default_domain_project_id) + + self.assertThat(catalog[0]['endpoints'], matchers.HasLength(1)) + + # create an endpoint group. + endpoint_group_id = self._create_valid_endpoint_group( + self.DEFAULT_ENDPOINT_GROUP_URL, self.DEFAULT_ENDPOINT_GROUP_BODY) + + # add the endpoint group to default project, bypassing + # catalog_api API manager. + self.catalog_api.driver.add_endpoint_group_to_project( + endpoint_group_id, + self.default_domain_project_id) + + # can get back only one endpoint from the cache, since the catalog + # is pulled out from cache. + invalid_catalog = self.catalog_api.get_v3_catalog( + user_id, + self.default_domain_project_id) + + self.assertThat(invalid_catalog[0]['endpoints'], + matchers.HasLength(1)) + self.assertEqual(catalog, invalid_catalog) + + # remove the endpoint group from default project, and add it again via + # catalog_api API manager. + self.catalog_api.driver.remove_endpoint_group_from_project( + endpoint_group_id, + self.default_domain_project_id) + + # add the endpoint group to default project. + self.catalog_api.add_endpoint_group_to_project( + endpoint_group_id, + self.default_domain_project_id) + + catalog = self.catalog_api.get_v3_catalog( + user_id, + self.default_domain_project_id) + + # now, it will return 2 endpoints since the cache has been + # invalidated. + self.assertThat(catalog[0]['endpoints'], matchers.HasLength(2)) + + ep_id_list = [catalog[0]['endpoints'][0]['id'], + catalog[0]['endpoints'][1]['id']] + self.assertItemsEqual([self.endpoint_id, endpoint_id2], ep_id_list) + + @unit.skip_if_cache_disabled('catalog') + def test_remove_endpoint_group_from_project_invalidates_cache(self): + # create another endpoint with 'admin' interface which matches + # 'filters' definition in endpoint group, then there should be two + # endpoints returned when retrieving v3 catalog. But only one + # endpoint will return after the endpoint group's deletion if cache + # works as expected. + # this should be done at first since `create_endpoint` will also + # invalidate cache. + endpoint_id2 = uuid.uuid4().hex + endpoint2 = unit.new_endpoint_ref(service_id=self.service_id, + region_id=self.region_id, + interface='admin', + id=endpoint_id2) + self.catalog_api.create_endpoint(endpoint_id2, endpoint2) + + # create project and endpoint association. + self.put(self.default_request_url) + + # create an endpoint group. + endpoint_group_id = self._create_valid_endpoint_group( + self.DEFAULT_ENDPOINT_GROUP_URL, self.DEFAULT_ENDPOINT_GROUP_BODY) + + # add the endpoint group to default project. + self.catalog_api.add_endpoint_group_to_project( + endpoint_group_id, + self.default_domain_project_id) + + # should get back two endpoints, one from endpoint project + # association, the other one is from endpoint_group project + # association. + user_id = uuid.uuid4().hex + catalog = self.catalog_api.get_v3_catalog( + user_id, + self.default_domain_project_id) + + self.assertThat(catalog[0]['endpoints'], matchers.HasLength(2)) + + ep_id_list = [catalog[0]['endpoints'][0]['id'], + catalog[0]['endpoints'][1]['id']] + self.assertItemsEqual([self.endpoint_id, endpoint_id2], ep_id_list) + + # remove endpoint_group project association, bypassing + # catalog_api API manager. + self.catalog_api.driver.remove_endpoint_group_from_project( + endpoint_group_id, + self.default_domain_project_id) + + # still get back two endpoints, since the catalog is pulled out + # from cache and the cache haven't been invalidated. + invalid_catalog = self.catalog_api.get_v3_catalog( + user_id, + self.default_domain_project_id) + + self.assertThat(invalid_catalog[0]['endpoints'], + matchers.HasLength(2)) + self.assertEqual(catalog, invalid_catalog) + + # add back the endpoint_group project association and remove it from + # manager. + self.catalog_api.driver.add_endpoint_group_to_project( + endpoint_group_id, + self.default_domain_project_id) + + self.catalog_api.remove_endpoint_group_from_project( + endpoint_group_id, + self.default_domain_project_id) + + # should only get back one endpoint since the cache has been + # invalidated after the endpoint_group project association was + # removed. + catalog = self.catalog_api.get_v3_catalog( + user_id, + self.default_domain_project_id) + + self.assertThat(catalog[0]['endpoints'], matchers.HasLength(1)) + self.assertEqual(self.endpoint_id, catalog[0]['endpoints'][0]['id']) + def _create_valid_endpoint_group(self, url, body): r = self.post(url, body=body) return r.result['endpoint_group']['id'] @@ -1072,13 +1371,15 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): """Creates an endpoint associated with service and project.""" if not service_id: # create a new service - service_ref = self.new_service_ref() + service_ref = unit.new_service_ref() response = self.post( '/services', body={'service': service_ref}) service_id = response.result['service']['id'] # create endpoint - endpoint_ref = self.new_endpoint_ref(service_id=service_id) + endpoint_ref = unit.new_endpoint_ref(service_id=service_id, + interface='public', + region_id=self.region_id) response = self.post('/endpoints', body={'endpoint': endpoint_ref}) endpoint = response.result['endpoint'] -- cgit 1.2.3-korg