Remove templated catalog driver
The driver was deprecated in 2024.1 release because of the unmaintained default template[1]. [1] 307296af5e170ca6b0d44fd5ec85a39bd6b5e572 Change-Id: If7b040a39679b2707565ecd0bd8a06b7db7503c1
This commit is contained in:
parent
b9e1f8d0fb
commit
39746b7417
@ -1,37 +0,0 @@
|
||||
# config for templated.Catalog, using camelCase because I don't want to do
|
||||
# translations for keystone compat
|
||||
catalog.RegionOne.identity.publicURL = http://localhost:5000/v3
|
||||
catalog.RegionOne.identity.adminURL = http://localhost:5000/v3
|
||||
catalog.RegionOne.identity.internalURL = http://localhost:5000/v3
|
||||
catalog.RegionOne.identity.name = Identity Service
|
||||
|
||||
# fake compute service for now to help novaclient tests work
|
||||
catalog.RegionOne.computev21.publicURL = http://localhost:8774/v2.1
|
||||
catalog.RegionOne.computev21.adminURL = http://localhost:8774/v2.1
|
||||
catalog.RegionOne.computev21.internalURL = http://localhost:8774/v2.1
|
||||
catalog.RegionOne.computev21.name = Compute Service V2.1
|
||||
|
||||
catalog.RegionOne.volumev3.publicURL = http://localhost:8776/v3
|
||||
catalog.RegionOne.volumev3.adminURL = http://localhost:8776/v3
|
||||
catalog.RegionOne.volumev3.internalURL = http://localhost:8776/v3
|
||||
catalog.RegionOne.volumev3.name = Volume Service V3
|
||||
|
||||
catalog.RegionOne.image.publicURL = http://localhost:9292
|
||||
catalog.RegionOne.image.adminURL = http://localhost:9292
|
||||
catalog.RegionOne.image.internalURL = http://localhost:9292
|
||||
catalog.RegionOne.image.name = Image Service
|
||||
|
||||
catalog.RegionOne.network.publicURL = http://localhost:9696
|
||||
catalog.RegionOne.network.adminURL = http://localhost:9696
|
||||
catalog.RegionOne.network.internalURL = http://localhost:9696
|
||||
catalog.RegionOne.network.name = Network Service
|
||||
|
||||
catalog.RegionOne.placement.publicURL = http://localhost:8778
|
||||
catalog.RegionOne.placement.adminURL = http://localhost:8778
|
||||
catalog.RegionOne.placement.internalURL = http://localhost:8778
|
||||
catalog.RegionOne.placement.name = Placement Service
|
||||
|
||||
catalog.RegionOne.orchestration.publicURL = http://localhost:8004/v1/$(tenant_id)s
|
||||
catalog.RegionOne.orchestration.adminURL = http://localhost:8004/v1/$(tenant_id)s
|
||||
catalog.RegionOne.orchestration.internalURL = http://localhost:8004/v1/$(tenant_id)s
|
||||
catalog.RegionOne.orchestration.name = Orchestration Service
|
@ -1,345 +0,0 @@
|
||||
# Copyright 2012 OpenStack Foundation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import os.path
|
||||
|
||||
from oslo_log import log
|
||||
|
||||
from keystone.catalog.backends import base
|
||||
from keystone.common import utils
|
||||
import keystone.conf
|
||||
from keystone import exception
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
CONF = keystone.conf.CONF
|
||||
|
||||
|
||||
def parse_templates(template_lines):
|
||||
o = {}
|
||||
for line in template_lines:
|
||||
if ' = ' not in line:
|
||||
continue
|
||||
|
||||
k, v = line.strip().split(' = ')
|
||||
if not k.startswith('catalog.'):
|
||||
continue
|
||||
|
||||
parts = k.split('.')
|
||||
|
||||
region = parts[1]
|
||||
# NOTE(termie): object-store insists on having a dash
|
||||
service = parts[2].replace('_', '-')
|
||||
key = parts[3]
|
||||
|
||||
region_ref = o.get(region, {})
|
||||
service_ref = region_ref.get(service, {})
|
||||
service_ref[key] = v
|
||||
|
||||
region_ref[service] = service_ref
|
||||
o[region] = region_ref
|
||||
|
||||
return o
|
||||
|
||||
|
||||
class Catalog(base.CatalogDriverBase):
|
||||
"""A backend that generates endpoints for the Catalog based on templates.
|
||||
|
||||
It is usually configured via config entries that look like:
|
||||
|
||||
catalog.$REGION.$SERVICE.$key = $value
|
||||
|
||||
and is stored in a similar looking hierarchy.
|
||||
|
||||
When expanding the template it will pass in a dict made up of the conf
|
||||
instance plus a few additional key-values, notably project_id and user_id.
|
||||
|
||||
It does not care what the keys and values are but it is worth noting that
|
||||
keystone_compat will expect certain keys to be there so that it can munge
|
||||
them into the output format keystone expects. These keys are:
|
||||
|
||||
name - the name of the service, most likely repeated for all services of
|
||||
the same type, across regions.
|
||||
|
||||
adminURL - the url of the admin endpoint
|
||||
|
||||
publicURL - the url of the public endpoint
|
||||
|
||||
internalURL - the url of the internal endpoint
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, templates=None):
|
||||
super().__init__()
|
||||
LOG.warning(
|
||||
'The templated catalog driver has been deprecated and '
|
||||
'will be removed in a future release.'
|
||||
)
|
||||
if templates:
|
||||
self.templates = templates
|
||||
else:
|
||||
template_file = CONF.catalog.template_file
|
||||
if not os.path.exists(template_file):
|
||||
template_file = CONF.find_file(template_file)
|
||||
self._load_templates(template_file)
|
||||
|
||||
def _load_templates(self, template_file):
|
||||
try:
|
||||
with open(template_file) as f:
|
||||
self.templates = parse_templates(f)
|
||||
except OSError:
|
||||
LOG.critical('Unable to open template file %s', template_file)
|
||||
raise
|
||||
|
||||
# region crud
|
||||
|
||||
def create_region(self, region_ref):
|
||||
raise exception.NotImplemented()
|
||||
|
||||
def list_regions(self, hints):
|
||||
return [
|
||||
{'id': region_id, 'description': '', 'parent_region_id': ''}
|
||||
for region_id in self.templates
|
||||
]
|
||||
|
||||
def get_region(self, region_id):
|
||||
if region_id in self.templates:
|
||||
return {'id': region_id, 'description': '', 'parent_region_id': ''}
|
||||
raise exception.RegionNotFound(region_id=region_id)
|
||||
|
||||
def update_region(self, region_id, region_ref):
|
||||
raise exception.NotImplemented()
|
||||
|
||||
def delete_region(self, region_id):
|
||||
raise exception.NotImplemented()
|
||||
|
||||
# service crud
|
||||
|
||||
def create_service(self, service_id, service_ref):
|
||||
raise exception.NotImplemented()
|
||||
|
||||
def _list_services(self, hints):
|
||||
for region_ref in self.templates.values():
|
||||
for service_type, service_ref in region_ref.items():
|
||||
yield {
|
||||
'id': service_type,
|
||||
'enabled': True,
|
||||
'name': service_ref.get('name', ''),
|
||||
'description': service_ref.get('description', ''),
|
||||
'type': service_type,
|
||||
}
|
||||
|
||||
def list_services(self, hints):
|
||||
return list(self._list_services(hints=None))
|
||||
|
||||
def get_service(self, service_id):
|
||||
for service in self._list_services(hints=None):
|
||||
if service['id'] == service_id:
|
||||
return service
|
||||
raise exception.ServiceNotFound(service_id=service_id)
|
||||
|
||||
def update_service(self, service_id, service_ref):
|
||||
raise exception.NotImplemented()
|
||||
|
||||
def delete_service(self, service_id):
|
||||
raise exception.NotImplemented()
|
||||
|
||||
# endpoint crud
|
||||
|
||||
def create_endpoint(self, endpoint_id, endpoint_ref):
|
||||
raise exception.NotImplemented()
|
||||
|
||||
def _list_endpoints(self):
|
||||
for region_id, region_ref in self.templates.items():
|
||||
for service_type, service_ref in region_ref.items():
|
||||
for key in service_ref:
|
||||
if key.endswith('URL'):
|
||||
interface = key[:-3]
|
||||
endpoint_id = f'{region_id}-{service_type}-{interface}'
|
||||
yield {
|
||||
'id': endpoint_id,
|
||||
'service_id': service_type,
|
||||
'interface': interface,
|
||||
'url': service_ref[key],
|
||||
'legacy_endpoint_id': None,
|
||||
'region_id': region_id,
|
||||
'enabled': True,
|
||||
}
|
||||
|
||||
def list_endpoints(self, hints):
|
||||
return list(self._list_endpoints())
|
||||
|
||||
def get_endpoint(self, endpoint_id):
|
||||
for endpoint in self._list_endpoints():
|
||||
if endpoint['id'] == endpoint_id:
|
||||
return endpoint
|
||||
raise exception.EndpointNotFound(endpoint_id=endpoint_id)
|
||||
|
||||
def update_endpoint(self, endpoint_id, endpoint_ref):
|
||||
raise exception.NotImplemented()
|
||||
|
||||
def delete_endpoint(self, endpoint_id):
|
||||
raise exception.NotImplemented()
|
||||
|
||||
def get_catalog(self, user_id, project_id):
|
||||
"""Retrieve and format the V2 service catalog.
|
||||
|
||||
:param user_id: The id of the user who has been authenticated for
|
||||
creating service catalog.
|
||||
:param project_id: The id of the project. 'project_id' will be None in
|
||||
the case this being called to create a catalog to go in a domain
|
||||
scoped token. In this case, any endpoint that requires a project_id
|
||||
as part of their URL will be skipped.
|
||||
|
||||
:returns: A nested dict representing the service catalog or an
|
||||
empty dict.
|
||||
|
||||
"""
|
||||
substitutions = dict(CONF.items())
|
||||
substitutions.update({'user_id': user_id})
|
||||
silent_keyerror_failures = []
|
||||
if project_id:
|
||||
substitutions.update(
|
||||
{'tenant_id': project_id, 'project_id': project_id}
|
||||
)
|
||||
else:
|
||||
silent_keyerror_failures = ['tenant_id', 'project_id']
|
||||
|
||||
catalog = {}
|
||||
# TODO(davechen): If there is service with no endpoints, we should
|
||||
# skip the service instead of keeping it in the catalog.
|
||||
# see bug #1436704.
|
||||
for region, region_ref in self.templates.items():
|
||||
catalog[region] = {}
|
||||
for service, service_ref in region_ref.items():
|
||||
service_data = {}
|
||||
try:
|
||||
for k, v in service_ref.items():
|
||||
formatted_value = utils.format_url(
|
||||
v,
|
||||
substitutions,
|
||||
silent_keyerror_failures=silent_keyerror_failures,
|
||||
)
|
||||
if formatted_value:
|
||||
service_data[k] = formatted_value
|
||||
except exception.MalformedEndpoint: # nosec(tkelsey)
|
||||
continue # this failure is already logged in format_url()
|
||||
catalog[region][service] = service_data
|
||||
|
||||
return catalog
|
||||
|
||||
def get_v3_catalog(self, user_id, project_id):
|
||||
"""Retrieve and format the current V3 service catalog.
|
||||
|
||||
This implementation builds the V3 catalog from the V2 catalog.
|
||||
|
||||
:param user_id: The id of the user who has been authenticated for
|
||||
creating service catalog.
|
||||
:param project_id: The id of the project. 'project_id' will be None in
|
||||
the case this being called to create a catalog to go in a domain
|
||||
scoped token. In this case, any endpoint that requires a project_id
|
||||
as part of their URL will be skipped.
|
||||
|
||||
:returns: A list representing the service catalog or an empty list
|
||||
|
||||
"""
|
||||
v2_catalog = self.get_catalog(user_id, project_id)
|
||||
v3_catalog = {}
|
||||
|
||||
for region_name, region in v2_catalog.items():
|
||||
for service_type, service in region.items():
|
||||
if service_type not in v3_catalog:
|
||||
v3_catalog[service_type] = {
|
||||
'type': service_type,
|
||||
'endpoints': [],
|
||||
}
|
||||
|
||||
for attr, value in service.items():
|
||||
# Attributes that end in URL are interfaces. In the V2
|
||||
# catalog, these are internalURL, publicURL, and adminURL.
|
||||
# For example, <region_name>.publicURL=<URL> in the V2
|
||||
# catalog becomes the V3 interface for the service:
|
||||
# { 'interface': 'public', 'url': '<URL>', 'region':
|
||||
# 'region: '<region_name>' }
|
||||
if attr.endswith('URL'):
|
||||
v3_interface = attr[: -len('URL')]
|
||||
v3_catalog[service_type]['endpoints'].append(
|
||||
{
|
||||
'interface': v3_interface,
|
||||
'region': region_name,
|
||||
'url': value,
|
||||
}
|
||||
)
|
||||
continue
|
||||
|
||||
# Other attributes are copied to the service.
|
||||
v3_catalog[service_type][attr] = value
|
||||
|
||||
return list(v3_catalog.values())
|
||||
|
||||
def add_endpoint_to_project(self, endpoint_id, project_id):
|
||||
raise exception.NotImplemented()
|
||||
|
||||
def remove_endpoint_from_project(self, endpoint_id, project_id):
|
||||
raise exception.NotImplemented()
|
||||
|
||||
def check_endpoint_in_project(self, endpoint_id, project_id):
|
||||
raise exception.NotImplemented()
|
||||
|
||||
def list_endpoints_for_project(self, project_id):
|
||||
raise exception.NotImplemented()
|
||||
|
||||
def list_projects_for_endpoint(self, endpoint_id):
|
||||
raise exception.NotImplemented()
|
||||
|
||||
def delete_association_by_endpoint(self, endpoint_id):
|
||||
raise exception.NotImplemented()
|
||||
|
||||
def delete_association_by_project(self, project_id):
|
||||
raise exception.NotImplemented()
|
||||
|
||||
def create_endpoint_group(self, endpoint_group):
|
||||
raise exception.NotImplemented()
|
||||
|
||||
def get_endpoint_group(self, endpoint_group_id):
|
||||
raise exception.NotImplemented()
|
||||
|
||||
def update_endpoint_group(self, endpoint_group_id, endpoint_group):
|
||||
raise exception.NotImplemented()
|
||||
|
||||
def delete_endpoint_group(self, endpoint_group_id):
|
||||
raise exception.NotImplemented()
|
||||
|
||||
def add_endpoint_group_to_project(self, endpoint_group_id, project_id):
|
||||
raise exception.NotImplemented()
|
||||
|
||||
def get_endpoint_group_in_project(self, endpoint_group_id, project_id):
|
||||
raise exception.NotImplemented()
|
||||
|
||||
def list_endpoint_groups(self, hints):
|
||||
raise exception.NotImplemented()
|
||||
|
||||
def list_endpoint_groups_for_project(self, project_id):
|
||||
raise exception.NotImplemented()
|
||||
|
||||
def list_projects_associated_with_endpoint_group(self, endpoint_group_id):
|
||||
raise exception.NotImplemented()
|
||||
|
||||
def remove_endpoint_group_from_project(
|
||||
self, endpoint_group_id, project_id
|
||||
):
|
||||
raise exception.NotImplemented()
|
||||
|
||||
def delete_endpoint_group_association_by_project(self, project_id):
|
||||
raise exception.NotImplemented()
|
@ -14,17 +14,6 @@ from oslo_config import cfg
|
||||
|
||||
from keystone.conf import utils
|
||||
|
||||
template_file = cfg.StrOpt(
|
||||
'template_file',
|
||||
default='default_catalog.templates',
|
||||
help=utils.fmt(
|
||||
"""
|
||||
Absolute path to the file used for the templated catalog backend. This option
|
||||
is only used if the `[catalog] driver` is set to `templated`.
|
||||
"""
|
||||
),
|
||||
)
|
||||
|
||||
driver = cfg.StrOpt(
|
||||
'driver',
|
||||
default='sql',
|
||||
@ -32,9 +21,8 @@ driver = cfg.StrOpt(
|
||||
"""
|
||||
Entry point for the catalog driver in the `keystone.catalog` namespace.
|
||||
Keystone provides a `sql` option (which supports basic CRUD operations through
|
||||
SQL), a `templated` option (which loads the catalog from a templated catalog
|
||||
file on disk), and a `endpoint_filter.sql` option (which supports arbitrary
|
||||
service catalogs per project).
|
||||
SQL) and a `endpoint_filter.sql` option (which supports arbitrary service
|
||||
catalogs per project).
|
||||
"""
|
||||
),
|
||||
)
|
||||
@ -75,7 +63,7 @@ have enough services or endpoints to exceed a reasonable limit.
|
||||
|
||||
|
||||
GROUP_NAME = __name__.split('.')[-1]
|
||||
ALL_OPTS = [template_file, driver, caching, cache_time, list_limit]
|
||||
ALL_OPTS = [driver, caching, cache_time, list_limit]
|
||||
|
||||
|
||||
def register_opts(conf):
|
||||
|
@ -897,11 +897,7 @@ class TestCase(BaseTestCase):
|
||||
enabled=True,
|
||||
proxies=['oslo_cache.testing.CacheIsolatingProxy'],
|
||||
)
|
||||
self.config_fixture.config(
|
||||
group='catalog',
|
||||
driver='sql',
|
||||
template_file=dirs.tests('default_catalog.templates'),
|
||||
)
|
||||
self.config_fixture.config(group='catalog', driver='sql')
|
||||
self.config_fixture.config(
|
||||
group='saml', certfile=signing_certfile, keyfile=signing_keyfile
|
||||
)
|
||||
|
@ -1,14 +0,0 @@
|
||||
# config for templated.Catalog, using camelCase because I don't want to do
|
||||
# translations for keystone compat
|
||||
catalog.RegionOne.identity.publicURL = http://localhost:5000/v3
|
||||
catalog.RegionOne.identity.adminURL = http://localhost:35357/v3
|
||||
catalog.RegionOne.identity.internalURL = http://localhost:35357/v3
|
||||
catalog.RegionOne.identity.name = 'Identity Service'
|
||||
catalog.RegionOne.identity.id = 1
|
||||
|
||||
# fake compute service for now to help novaclient tests work
|
||||
catalog.RegionOne.compute.publicURL = http://localhost:8774/v1.1/$(tenant_id)s
|
||||
catalog.RegionOne.compute.adminURL = http://localhost:8774/v1.1/$(tenant_id)s
|
||||
catalog.RegionOne.compute.internalURL = http://localhost:8774/v1.1/$(tenant_id)s
|
||||
catalog.RegionOne.compute.name = 'Compute Service'
|
||||
catalog.RegionOne.compute.id = 2
|
@ -1,27 +0,0 @@
|
||||
# config for templated.Catalog, using camelCase because I don't want to do
|
||||
# translations for keystone compat
|
||||
catalog.RegionOne.identity.publicURL = http://region-one:5000/v3
|
||||
catalog.RegionOne.identity.adminURL = http://region-one:35357/v3
|
||||
catalog.RegionOne.identity.internalURL = http://region-one:35357/v3
|
||||
catalog.RegionOne.identity.name = 'Identity Service'
|
||||
catalog.RegionOne.identity.id = 1
|
||||
|
||||
# fake compute service for now to help novaclient tests work
|
||||
catalog.RegionOne.compute.publicURL = http://region-one:8774/v1.1/$(tenant_id)s
|
||||
catalog.RegionOne.compute.adminURL = http://region-one:8774/v1.1/$(tenant_id)s
|
||||
catalog.RegionOne.compute.internalURL = http://region-one:8774/v1.1/$(tenant_id)s
|
||||
catalog.RegionOne.compute.name = 'Compute Service'
|
||||
catalog.RegionOne.compute.id = 2
|
||||
|
||||
# second region for multi-region testing
|
||||
catalog.RegionTwo.identity.publicURL = http://region-two:5000/v3
|
||||
catalog.RegionTwo.identity.adminURL = http://region-two:35357/v3
|
||||
catalog.RegionTwo.identity.internalURL = http://region-two:35357/v3
|
||||
catalog.RegionTwo.identity.name = 'Identity Service'
|
||||
catalog.RegionTwo.identity.id = 1
|
||||
|
||||
catalog.RegionTwo.compute.publicURL = http://region-two:8774/v1.1/$(tenant_id)s
|
||||
catalog.RegionTwo.compute.adminURL = http://region-two:8774/v1.1/$(tenant_id)s
|
||||
catalog.RegionTwo.compute.internalURL = http://region-two:8774/v1.1/$(tenant_id)s
|
||||
catalog.RegionTwo.compute.name = 'Compute Service'
|
||||
catalog.RegionTwo.compute.id = 2
|
@ -1,415 +0,0 @@
|
||||
# Copyright 2012 OpenStack Foundation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from unittest import mock
|
||||
import uuid
|
||||
|
||||
from keystone.catalog.backends import base as catalog_base
|
||||
from keystone.common import provider_api
|
||||
from keystone.tests import unit
|
||||
from keystone.tests.unit.catalog import test_backends as catalog_tests
|
||||
from keystone.tests.unit import default_fixtures
|
||||
from keystone.tests.unit.ksfixtures import database
|
||||
|
||||
PROVIDERS = provider_api.ProviderAPIs
|
||||
BROKEN_WRITE_FUNCTIONALITY_MSG = (
|
||||
"Templated backend doesn't correctly implement write operations"
|
||||
)
|
||||
|
||||
|
||||
class TestTemplatedCatalog(unit.TestCase, catalog_tests.CatalogTests):
|
||||
DEFAULT_FIXTURE = {
|
||||
'RegionOne': {
|
||||
'compute': {
|
||||
'adminURL': 'http://localhost:8774/v1.1/bar',
|
||||
'publicURL': 'http://localhost:8774/v1.1/bar',
|
||||
'internalURL': 'http://localhost:8774/v1.1/bar',
|
||||
'name': "'Compute Service'",
|
||||
'id': '2',
|
||||
},
|
||||
'identity': {
|
||||
'adminURL': 'http://localhost:35357/v3',
|
||||
'publicURL': 'http://localhost:5000/v3',
|
||||
'internalURL': 'http://localhost:35357/v3',
|
||||
'name': "'Identity Service'",
|
||||
'id': '1',
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.useFixture(database.Database())
|
||||
self.load_backends()
|
||||
self.load_fixtures(default_fixtures)
|
||||
|
||||
def config_overrides(self):
|
||||
super().config_overrides()
|
||||
self.config_fixture.config(
|
||||
group='catalog',
|
||||
driver='templated',
|
||||
template_file=unit.dirs.tests('default_catalog.templates'),
|
||||
)
|
||||
|
||||
def test_get_catalog(self):
|
||||
catalog_ref = PROVIDERS.catalog_api.get_catalog('foo', 'bar')
|
||||
self.assertDictEqual(self.DEFAULT_FIXTURE, catalog_ref)
|
||||
|
||||
# NOTE(lbragstad): This test is skipped because the catalog is being
|
||||
# modified within the test and not through the API.
|
||||
@unit.skip_if_cache_is_enabled('catalog')
|
||||
def test_catalog_ignored_malformed_urls(self):
|
||||
# both endpoints are in the catalog
|
||||
catalog_ref = PROVIDERS.catalog_api.get_catalog('foo', 'bar')
|
||||
self.assertEqual(2, len(catalog_ref['RegionOne']))
|
||||
|
||||
region = PROVIDERS.catalog_api.driver.templates['RegionOne']
|
||||
region['compute']['adminURL'] = 'http://localhost:8774/v1.1/$(tenant)s'
|
||||
|
||||
# the malformed one has been removed
|
||||
catalog_ref = PROVIDERS.catalog_api.get_catalog('foo', 'bar')
|
||||
self.assertEqual(1, len(catalog_ref['RegionOne']))
|
||||
|
||||
def test_get_v3_catalog_endpoint_disabled(self):
|
||||
self.skip_test_overrides(
|
||||
"Templated backend doesn't have disabled endpoints"
|
||||
)
|
||||
|
||||
def assert_catalogs_equal(self, expected, observed):
|
||||
def sort_key(d):
|
||||
return d['id']
|
||||
|
||||
for e, o in zip(
|
||||
sorted(expected, key=sort_key), sorted(observed, key=sort_key)
|
||||
):
|
||||
expected_endpoints = e.pop('endpoints')
|
||||
observed_endpoints = o.pop('endpoints')
|
||||
self.assertDictEqual(e, o)
|
||||
self.assertCountEqual(expected_endpoints, observed_endpoints)
|
||||
|
||||
def test_get_v3_catalog(self):
|
||||
user_id = uuid.uuid4().hex
|
||||
project_id = uuid.uuid4().hex
|
||||
catalog_ref = PROVIDERS.catalog_api.get_v3_catalog(user_id, project_id)
|
||||
exp_catalog = [
|
||||
{
|
||||
'endpoints': [
|
||||
{
|
||||
'interface': 'admin',
|
||||
'region': 'RegionOne',
|
||||
'url': f'http://localhost:8774/v1.1/{project_id}',
|
||||
},
|
||||
{
|
||||
'interface': 'public',
|
||||
'region': 'RegionOne',
|
||||
'url': f'http://localhost:8774/v1.1/{project_id}',
|
||||
},
|
||||
{
|
||||
'interface': 'internal',
|
||||
'region': 'RegionOne',
|
||||
'url': f'http://localhost:8774/v1.1/{project_id}',
|
||||
},
|
||||
],
|
||||
'type': 'compute',
|
||||
'name': "'Compute Service'",
|
||||
'id': '2',
|
||||
},
|
||||
{
|
||||
'endpoints': [
|
||||
{
|
||||
'interface': 'admin',
|
||||
'region': 'RegionOne',
|
||||
'url': 'http://localhost:35357/v3',
|
||||
},
|
||||
{
|
||||
'interface': 'public',
|
||||
'region': 'RegionOne',
|
||||
'url': 'http://localhost:5000/v3',
|
||||
},
|
||||
{
|
||||
'interface': 'internal',
|
||||
'region': 'RegionOne',
|
||||
'url': 'http://localhost:35357/v3',
|
||||
},
|
||||
],
|
||||
'type': 'identity',
|
||||
'name': "'Identity Service'",
|
||||
'id': '1',
|
||||
},
|
||||
]
|
||||
self.assert_catalogs_equal(exp_catalog, catalog_ref)
|
||||
|
||||
def test_get_multi_region_v3_catalog(self):
|
||||
user_id = uuid.uuid4().hex
|
||||
project_id = uuid.uuid4().hex
|
||||
|
||||
catalog_api = PROVIDERS.catalog_api
|
||||
|
||||
# Load the multi-region catalog.
|
||||
catalog_api._load_templates(
|
||||
unit.dirs.tests('default_catalog_multi_region.templates')
|
||||
)
|
||||
|
||||
catalog_ref = catalog_api.get_v3_catalog(user_id, project_id)
|
||||
exp_catalog = [
|
||||
{
|
||||
'endpoints': [
|
||||
{
|
||||
'interface': 'admin',
|
||||
'region': 'RegionOne',
|
||||
'url': f'http://region-one:8774/v1.1/{project_id}',
|
||||
},
|
||||
{
|
||||
'interface': 'public',
|
||||
'region': 'RegionOne',
|
||||
'url': f'http://region-one:8774/v1.1/{project_id}',
|
||||
},
|
||||
{
|
||||
'interface': 'internal',
|
||||
'region': 'RegionOne',
|
||||
'url': f'http://region-one:8774/v1.1/{project_id}',
|
||||
},
|
||||
{
|
||||
'interface': 'admin',
|
||||
'region': 'RegionTwo',
|
||||
'url': f'http://region-two:8774/v1.1/{project_id}',
|
||||
},
|
||||
{
|
||||
'interface': 'public',
|
||||
'region': 'RegionTwo',
|
||||
'url': f'http://region-two:8774/v1.1/{project_id}',
|
||||
},
|
||||
{
|
||||
'interface': 'internal',
|
||||
'region': 'RegionTwo',
|
||||
'url': f'http://region-two:8774/v1.1/{project_id}',
|
||||
},
|
||||
],
|
||||
'type': 'compute',
|
||||
'name': "'Compute Service'",
|
||||
'id': '2',
|
||||
},
|
||||
{
|
||||
'endpoints': [
|
||||
{
|
||||
'interface': 'admin',
|
||||
'region': 'RegionOne',
|
||||
'url': 'http://region-one:35357/v3',
|
||||
},
|
||||
{
|
||||
'interface': 'public',
|
||||
'region': 'RegionOne',
|
||||
'url': 'http://region-one:5000/v3',
|
||||
},
|
||||
{
|
||||
'interface': 'internal',
|
||||
'region': 'RegionOne',
|
||||
'url': 'http://region-one:35357/v3',
|
||||
},
|
||||
{
|
||||
'interface': 'admin',
|
||||
'region': 'RegionTwo',
|
||||
'url': 'http://region-two:35357/v3',
|
||||
},
|
||||
{
|
||||
'interface': 'public',
|
||||
'region': 'RegionTwo',
|
||||
'url': 'http://region-two:5000/v3',
|
||||
},
|
||||
{
|
||||
'interface': 'internal',
|
||||
'region': 'RegionTwo',
|
||||
'url': 'http://region-two:35357/v3',
|
||||
},
|
||||
],
|
||||
'type': 'identity',
|
||||
'name': "'Identity Service'",
|
||||
'id': '1',
|
||||
},
|
||||
]
|
||||
self.assert_catalogs_equal(exp_catalog, catalog_ref)
|
||||
|
||||
def test_get_catalog_ignores_endpoints_with_invalid_urls(self):
|
||||
user_id = uuid.uuid4().hex
|
||||
project_id = None
|
||||
# If the URL has no 'project_id' to substitute, we will skip the
|
||||
# endpoint which contains this kind of URL.
|
||||
catalog_ref = PROVIDERS.catalog_api.get_v3_catalog(user_id, project_id)
|
||||
exp_catalog = [
|
||||
{
|
||||
'endpoints': [],
|
||||
'type': 'compute',
|
||||
'name': "'Compute Service'",
|
||||
'id': '2',
|
||||
},
|
||||
{
|
||||
'endpoints': [
|
||||
{
|
||||
'interface': 'admin',
|
||||
'region': 'RegionOne',
|
||||
'url': 'http://localhost:35357/v3',
|
||||
},
|
||||
{
|
||||
'interface': 'public',
|
||||
'region': 'RegionOne',
|
||||
'url': 'http://localhost:5000/v3',
|
||||
},
|
||||
{
|
||||
'interface': 'internal',
|
||||
'region': 'RegionOne',
|
||||
'url': 'http://localhost:35357/v3',
|
||||
},
|
||||
],
|
||||
'type': 'identity',
|
||||
'name': "'Identity Service'",
|
||||
'id': '1',
|
||||
},
|
||||
]
|
||||
self.assert_catalogs_equal(exp_catalog, catalog_ref)
|
||||
|
||||
def test_list_regions_filtered_by_parent_region_id(self):
|
||||
self.skip_test_overrides('Templated backend does not support hints')
|
||||
|
||||
def test_service_filtering(self):
|
||||
self.skip_test_overrides("Templated backend doesn't support filtering")
|
||||
|
||||
def test_list_services_with_hints(self):
|
||||
hints = {}
|
||||
services = PROVIDERS.catalog_api.list_services(hints=hints)
|
||||
exp_services = [
|
||||
{
|
||||
'type': 'compute',
|
||||
'description': '',
|
||||
'enabled': True,
|
||||
'name': "'Compute Service'",
|
||||
'id': 'compute',
|
||||
},
|
||||
{
|
||||
'type': 'identity',
|
||||
'description': '',
|
||||
'enabled': True,
|
||||
'name': "'Identity Service'",
|
||||
'id': 'identity',
|
||||
},
|
||||
]
|
||||
self.assertCountEqual(exp_services, services)
|
||||
|
||||
# NOTE(dstanek): the following methods have been overridden
|
||||
# from unit.catalog.test_backends.CatalogTests.
|
||||
|
||||
def test_region_crud(self):
|
||||
self.skip_test_overrides(BROKEN_WRITE_FUNCTIONALITY_MSG)
|
||||
|
||||
@unit.skip_if_cache_disabled('catalog')
|
||||
def test_cache_layer_region_crud(self):
|
||||
self.skip_test_overrides(BROKEN_WRITE_FUNCTIONALITY_MSG)
|
||||
|
||||
@unit.skip_if_cache_disabled('catalog')
|
||||
def test_invalidate_cache_when_updating_region(self):
|
||||
self.skip_test_overrides(BROKEN_WRITE_FUNCTIONALITY_MSG)
|
||||
|
||||
def test_update_region_extras(self):
|
||||
self.skip_test_overrides(BROKEN_WRITE_FUNCTIONALITY_MSG)
|
||||
|
||||
def test_create_region_with_duplicate_id(self):
|
||||
self.skip_test_overrides(BROKEN_WRITE_FUNCTIONALITY_MSG)
|
||||
|
||||
def test_delete_region_returns_not_found(self):
|
||||
self.skip_test_overrides(BROKEN_WRITE_FUNCTIONALITY_MSG)
|
||||
|
||||
def test_create_region_invalid_parent_region_returns_not_found(self):
|
||||
self.skip_test_overrides(BROKEN_WRITE_FUNCTIONALITY_MSG)
|
||||
|
||||
def test_avoid_creating_circular_references_in_regions_update(self):
|
||||
self.skip_test_overrides(BROKEN_WRITE_FUNCTIONALITY_MSG)
|
||||
|
||||
@mock.patch.object(
|
||||
catalog_base.CatalogDriverBase,
|
||||
"_ensure_no_circle_in_hierarchical_regions",
|
||||
)
|
||||
def test_circular_regions_can_be_deleted(self, mock_ensure_on_circle):
|
||||
self.skip_test_overrides(BROKEN_WRITE_FUNCTIONALITY_MSG)
|
||||
|
||||
def test_service_crud(self):
|
||||
self.skip_test_overrides(BROKEN_WRITE_FUNCTIONALITY_MSG)
|
||||
|
||||
@unit.skip_if_cache_disabled('catalog')
|
||||
def test_cache_layer_service_crud(self):
|
||||
self.skip_test_overrides(BROKEN_WRITE_FUNCTIONALITY_MSG)
|
||||
|
||||
@unit.skip_if_cache_disabled('catalog')
|
||||
def test_invalidate_cache_when_updating_service(self):
|
||||
self.skip_test_overrides(BROKEN_WRITE_FUNCTIONALITY_MSG)
|
||||
|
||||
def test_delete_service_with_endpoint(self):
|
||||
self.skip_test_overrides(BROKEN_WRITE_FUNCTIONALITY_MSG)
|
||||
|
||||
def test_cache_layer_delete_service_with_endpoint(self):
|
||||
self.skip_test_overrides(BROKEN_WRITE_FUNCTIONALITY_MSG)
|
||||
|
||||
def test_delete_service_returns_not_found(self):
|
||||
self.skip_test_overrides(BROKEN_WRITE_FUNCTIONALITY_MSG)
|
||||
|
||||
def test_update_endpoint_nonexistent_service(self):
|
||||
self.skip_test_overrides(BROKEN_WRITE_FUNCTIONALITY_MSG)
|
||||
|
||||
def test_create_endpoint_nonexistent_region(self):
|
||||
self.skip_test_overrides(BROKEN_WRITE_FUNCTIONALITY_MSG)
|
||||
|
||||
def test_update_endpoint_nonexistent_region(self):
|
||||
self.skip_test_overrides(BROKEN_WRITE_FUNCTIONALITY_MSG)
|
||||
|
||||
def test_get_endpoint_returns_not_found(self):
|
||||
self.skip_test_overrides(
|
||||
"Templated backend doesn't use IDs for endpoints."
|
||||
)
|
||||
|
||||
def test_delete_endpoint_returns_not_found(self):
|
||||
self.skip_test_overrides(BROKEN_WRITE_FUNCTIONALITY_MSG)
|
||||
|
||||
def test_create_endpoint(self):
|
||||
self.skip_test_overrides(BROKEN_WRITE_FUNCTIONALITY_MSG)
|
||||
|
||||
def test_update_endpoint(self):
|
||||
self.skip_test_overrides(BROKEN_WRITE_FUNCTIONALITY_MSG)
|
||||
|
||||
def test_list_endpoints(self):
|
||||
expected_urls = {
|
||||
'http://localhost:5000/v3',
|
||||
'http://localhost:35357/v3',
|
||||
'http://localhost:8774/v1.1/$(tenant_id)s',
|
||||
}
|
||||
endpoints = PROVIDERS.catalog_api.list_endpoints()
|
||||
self.assertEqual(expected_urls, {e['url'] for e in endpoints})
|
||||
|
||||
@unit.skip_if_cache_disabled('catalog')
|
||||
def test_invalidate_cache_when_updating_endpoint(self):
|
||||
self.skip_test_overrides(BROKEN_WRITE_FUNCTIONALITY_MSG)
|
||||
|
||||
def test_delete_endpoint_group_association_by_project(self):
|
||||
# Deleting endpoint group association is not supported by the templated
|
||||
# driver, but it should be silent about it and not raise an error.
|
||||
PROVIDERS.catalog_api.delete_endpoint_group_association_by_project(
|
||||
uuid.uuid4().hex
|
||||
)
|
||||
|
||||
def test_delete_association_by_endpoint(self):
|
||||
# Deleting endpoint association is not supported by the templated
|
||||
# driver, but it should be silent about it and not raise an error.
|
||||
PROVIDERS.catalog_api.delete_association_by_endpoint(uuid.uuid4().hex)
|
||||
|
||||
def test_delete_association_by_project(self):
|
||||
# Deleting endpoint association is not supported by the templated
|
||||
# driver, but it should be silent about it and not raise an error.
|
||||
PROVIDERS.catalog_api.delete_association_by_project(uuid.uuid4().hex)
|
@ -1030,34 +1030,3 @@ class TestCatalogAPISQLRegions(unit.TestCase):
|
||||
for k in keys:
|
||||
self.assertEqual(ref.get(k), entity[k], k)
|
||||
self.assertEqual(entity['region_id'], entity['region'])
|
||||
|
||||
|
||||
class TestCatalogAPITemplatedProject(test_v3.RestfulTestCase):
|
||||
"""Templated Catalog doesn't support full API.
|
||||
|
||||
Eg. No region/endpoint creation.
|
||||
|
||||
"""
|
||||
|
||||
def config_overrides(self):
|
||||
super().config_overrides()
|
||||
self.config_fixture.config(group='catalog', driver='templated')
|
||||
|
||||
def load_fixtures(self, fixtures):
|
||||
self.load_sample_data(create_region_and_endpoints=False)
|
||||
|
||||
def test_project_delete(self):
|
||||
"""Deleting a project should not result in an 500 ISE.
|
||||
|
||||
Deleting a project will create a notification, which the EndpointFilter
|
||||
functionality will use to clean up any project->endpoint and
|
||||
project->endpoint_group relationships. The templated catalog does not
|
||||
support such relationships, but the act of attempting to delete them
|
||||
should not cause a NotImplemented exception to be exposed to an API
|
||||
caller.
|
||||
|
||||
Deleting an endpoint has a similar notification and clean up
|
||||
mechanism, but since we do not allow deletion of endpoints with the
|
||||
templated catalog, there is no testing to do for that action.
|
||||
"""
|
||||
self.delete(f'/projects/{self.project_id}')
|
||||
|
@ -0,0 +1,6 @@
|
||||
---
|
||||
upgrade:
|
||||
- |
|
||||
The templated catalog driver has been removed.
|
||||
The ``[catalog] template_file`` option, which was used by the templated
|
||||
catalog driver has also been removed.
|
Loading…
x
Reference in New Issue
Block a user