Show v3 endpoints in v2 endpoint list

v3 endpoints that are not migrated from v2 endpoints, were not shown in
v2 endpoint list. While it was more clear, separating v2 and v3
endpoints, it was not easy for tools or services which are still using
v2 APIs to get the endpoints created by v3 API.
Since it won't do harm to return more information, this commit is to
show v3 endpoints in API call to list v2 endpoints.
NOTE: public interface is required in a v2 endpoints, so only v3
endpoints with public interface are added into the list.

Change-Id: Id9b3822f87e973b6eb7dd9fc715c2dba76b9fe04
Closes-Bug: #1480270
(cherry picked from commit 0d13a85e93)
This commit is contained in:
Tony Wang 2015-08-21 23:41:32 -04:00 committed by Steve Martinelli
parent ec3ceb5956
commit 4a239f23e3
2 changed files with 135 additions and 17 deletions

View File

@ -68,25 +68,59 @@ class Endpoint(controller.V2Controller):
"""Merge matching v3 endpoint refs into legacy refs."""
self.assert_admin(context)
legacy_endpoints = {}
v3_endpoints = {}
for endpoint in self.catalog_api.list_endpoints():
if not endpoint.get('legacy_endpoint_id'):
# endpoints created in v3 should not appear on the v2 API
if not endpoint.get('legacy_endpoint_id'): # pure v3 endpoint
# tell endpoints apart by the combination of
# service_id and region_id.
# NOTE(muyu): in theory, it's possible that there are more than
# one endpoint of one service, one region and one interface,
# but in practice, it makes no sense because only one will be
# used.
key = (endpoint['service_id'], endpoint['region_id'])
v3_endpoints.setdefault(key, []).append(endpoint)
else: # legacy endpoint
if endpoint['legacy_endpoint_id'] not in legacy_endpoints:
legacy_ep = endpoint.copy()
legacy_ep['id'] = legacy_ep.pop('legacy_endpoint_id')
legacy_ep.pop('interface')
legacy_ep.pop('url')
legacy_ep['region'] = legacy_ep.pop('region_id')
legacy_endpoints[endpoint['legacy_endpoint_id']] = (
legacy_ep)
else:
legacy_ep = (
legacy_endpoints[endpoint['legacy_endpoint_id']])
# add the legacy endpoint with an interface url
legacy_ep['%surl' % endpoint['interface']] = endpoint['url']
# convert collected v3 endpoints into v2 endpoints
for endpoints in v3_endpoints.values():
legacy_ep = {}
# For v3 endpoints in the same group, contents of extra attributes
# can be different, which may cause confusion if a random one is
# used. So only necessary attributes are used here.
# It's different for legacy v2 endpoints, which are created
# with the same "extra" value when being migrated.
for key in ('service_id', 'enabled'):
legacy_ep[key] = endpoints[0][key]
legacy_ep['region'] = endpoints[0]['region_id']
for endpoint in endpoints:
# Public URL is required for v2 endpoints, so the generated v2
# endpoint uses public endpoint's id as its id, which can also
# be an indicator whether a public v3 endpoint is present.
# It's safe to do so is also because that there is no v2 API to
# get an endpoint by endpoint ID.
if endpoint['interface'] == 'public':
legacy_ep['id'] = endpoint['id']
legacy_ep['%surl' % endpoint['interface']] = endpoint['url']
# this means there is no public URL of this group of v3 endpoints
if 'id' not in legacy_ep:
continue
# is this is a legacy endpoint we haven't indexed yet?
if endpoint['legacy_endpoint_id'] not in legacy_endpoints:
legacy_ep = endpoint.copy()
legacy_ep['id'] = legacy_ep.pop('legacy_endpoint_id')
legacy_ep.pop('interface')
legacy_ep.pop('url')
legacy_ep['region'] = legacy_ep.pop('region_id')
legacy_endpoints[endpoint['legacy_endpoint_id']] = legacy_ep
else:
legacy_ep = legacy_endpoints[endpoint['legacy_endpoint_id']]
# add the legacy endpoint with an interface url
legacy_ep['%surl' % endpoint['interface']] = endpoint['url']
legacy_endpoints[legacy_ep['id']] = legacy_ep
return {'endpoints': list(legacy_endpoints.values())}
@controller.v2_deprecated

View File

@ -76,6 +76,18 @@ class V2CatalogTestCase(rest.RestfulTestCase):
body=body)
return body, r
def _region_create(self):
region_id = uuid.uuid4().hex
self.catalog_api.create_region({'id': region_id})
return region_id
def _service_create(self):
service_id = uuid.uuid4().hex
service = unit.new_service_ref()
service['id'] = service_id
self.catalog_api.create_service(service_id, service)
return service_id
def test_endpoint_create(self):
req_body, response = self._endpoint_create()
self.assertIn('endpoint', response.result)
@ -83,6 +95,78 @@ class V2CatalogTestCase(rest.RestfulTestCase):
for field, value in req_body['endpoint'].items():
self.assertEqual(response.result['endpoint'][field], value)
def test_pure_v3_endpoint_with_publicurl_visible_from_v2(self):
"""Test pure v3 endpoint can be fetched via v2 API.
For those who are using v2 APIs, endpoints created by v3 API should
also be visible as there are no differences about the endpoints
except the format or the internal implementation.
And because public url is required for v2 API, so only the v3 endpoints
of the service which has the public interface endpoint will be
converted into v2 endpoints.
"""
region_id = self._region_create()
service_id = self._service_create()
# create a v3 endpoint with three interfaces
body = {
'endpoint': unit.new_endpoint_ref(service_id,
default_region_id=region_id)
}
for interface in catalog.controllers.INTERFACES:
body['endpoint']['interface'] = interface
self.admin_request(method='POST',
token=self.get_scoped_token(),
path='/v3/endpoints',
expected_status=http_client.CREATED,
body=body)
r = self.admin_request(token=self.get_scoped_token(),
path='/v2.0/endpoints')
# v3 endpoints having public url can be fetched via v2.0 API
self.assertEqual(1, len(r.result['endpoints']))
v2_endpoint = r.result['endpoints'][0]
self.assertEqual(service_id, v2_endpoint['service_id'])
# check urls just in case.
# This is not the focus of this test, so no different urls are used.
self.assertEqual(body['endpoint']['url'], v2_endpoint['publicurl'])
self.assertEqual(body['endpoint']['url'], v2_endpoint['adminurl'])
self.assertEqual(body['endpoint']['url'], v2_endpoint['internalurl'])
self.assertNotIn('name', v2_endpoint)
v3_endpoint = self.catalog_api.get_endpoint(v2_endpoint['id'])
# it's the v3 public endpoint's id as the generated v2 endpoint
self.assertEqual('public', v3_endpoint['interface'])
self.assertEqual(service_id, v3_endpoint['service_id'])
def test_pure_v3_endpoint_without_publicurl_invisible_from_v2(self):
"""Test pure v3 endpoint without public url can't be fetched via v2 API.
V2 API will return endpoints created by v3 API, but because public url
is required for v2 API, so v3 endpoints without public url will be
ignored.
"""
region_id = self._region_create()
service_id = self._service_create()
# create a v3 endpoint without public interface
body = {
'endpoint': unit.new_endpoint_ref(service_id,
default_region_id=region_id)
}
for interface in catalog.controllers.INTERFACES:
if interface == 'public':
continue
body['endpoint']['interface'] = interface
self.admin_request(method='POST',
token=self.get_scoped_token(),
path='/v3/endpoints',
expected_status=http_client.CREATED,
body=body)
r = self.admin_request(token=self.get_scoped_token(),
path='/v2.0/endpoints')
# v3 endpoints without public url won't be fetched via v2.0 API
self.assertEqual(0, len(r.result['endpoints']))
def test_endpoint_create_with_null_adminurl(self):
req_body, response = self._endpoint_create(adminurl=None)
self.assertIsNone(req_body['endpoint']['adminurl'])