Query endpoint id from keystone
Endpoint id is not predictable so users can't configure the endpoint_id option until keystone endpoints are created. This requires redundant steps in deployment. For example both keystone and glance are run by httpd + mod_wsgi then you first have to deploy keystone and then create glance endpoints, until you can install glance and restart httpd. This introduces a few new options to look up the target endpoint from Keystone. All these options accept predictable values. Closes-bug: #1931875 Change-Id: I0411d4aa6abd86cb38bf3c1999f2bae213983078
This commit is contained in:
parent
ba8b9aba0b
commit
9575a24796
@ -18,6 +18,7 @@ from collections import namedtuple
|
||||
from keystoneauth1 import exceptions as ksa_exceptions
|
||||
from keystoneauth1 import loading
|
||||
from openstack import connection
|
||||
from openstack import exceptions as os_exceptions
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log
|
||||
|
||||
@ -262,16 +263,69 @@ class _EnforcerUtils(object):
|
||||
# {resource_name: registered_limit}
|
||||
self.rlimit_cache = {}
|
||||
|
||||
# get and cache endpoint info
|
||||
endpoint_id = CONF.oslo_limit.endpoint_id
|
||||
if not endpoint_id:
|
||||
raise ValueError("endpoint_id is not configured")
|
||||
self._endpoint = self.connection.get_endpoint(endpoint_id)
|
||||
if not self._endpoint:
|
||||
raise ValueError("can't find endpoint for %s" % endpoint_id)
|
||||
self._endpoint = self._get_endpoint()
|
||||
self._service_id = self._endpoint.service_id
|
||||
self._region_id = self._endpoint.region_id
|
||||
|
||||
def _get_endpoint(self):
|
||||
endpoint = self._get_endpoint_by_id()
|
||||
if endpoint is not None:
|
||||
return endpoint
|
||||
|
||||
return self._get_endpoint_by_service_lookup()
|
||||
|
||||
def _get_endpoint_by_id(self):
|
||||
endpoint_id = CONF.oslo_limit.endpoint_id
|
||||
if endpoint_id is None:
|
||||
return None
|
||||
|
||||
try:
|
||||
endpoint = self.connection.get_endpoint(endpoint_id)
|
||||
except os_exceptions.ResourceNotFound:
|
||||
raise ValueError("Can't find endpoint for %s" % endpoint_id)
|
||||
return endpoint
|
||||
|
||||
def _get_endpoint_by_service_lookup(self):
|
||||
service_type = CONF.oslo_limit.endpoint_service_type
|
||||
service_name = CONF.oslo_limit.endpoint_service_name
|
||||
if not service_type and not service_name:
|
||||
raise ValueError(
|
||||
"Either service_type or service_name should be set")
|
||||
|
||||
try:
|
||||
services = self.connection.services(type=service_type,
|
||||
name=service_name)
|
||||
if len(services) > 1:
|
||||
raise ValueError("Multiple services found")
|
||||
service_id = services[0].id
|
||||
except os_exceptions.ResourceNotFound:
|
||||
raise ValueError("Service not found")
|
||||
|
||||
if CONF.oslo_limit.endpoint_region_name is not None:
|
||||
try:
|
||||
regions = self.connection.regions(
|
||||
name=CONF.oslo_limit.endpoint_region_name)
|
||||
if len(regions) > 1:
|
||||
raise ValueError("Multiple regions found")
|
||||
region_id = regions[0].id
|
||||
except os_exceptions.ResourceNotFound:
|
||||
raise ValueError("Region not found")
|
||||
else:
|
||||
region_id = None
|
||||
|
||||
try:
|
||||
endpoints = self.connection.endpoints(
|
||||
service_id=service_id, region_id=region_id,
|
||||
interface=CONF.oslo_limit.endpoint_interface,
|
||||
)
|
||||
except os_exceptions.ResourceNotFound:
|
||||
raise ValueError("Endpoint not found")
|
||||
|
||||
if len(endpoints) > 1:
|
||||
raise ValueError("Multiple endpoints found")
|
||||
|
||||
return endpoints[0]
|
||||
|
||||
@staticmethod
|
||||
def enforce_limits(project_id, limits, current_usage, deltas):
|
||||
"""Check that proposed usage is not over given limits
|
||||
|
@ -24,12 +24,25 @@ __all__ = [
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
endpoint_id = cfg.StrOpt(
|
||||
'endpoint_id',
|
||||
help=_("The service's endpoint id which is registered in Keystone."))
|
||||
|
||||
_options = [
|
||||
endpoint_id,
|
||||
cfg.StrOpt(
|
||||
'endpoint_id',
|
||||
help=_("The service's endpoint id which is registered in Keystone.")),
|
||||
cfg.StrOpt(
|
||||
'endpoint_service_name',
|
||||
help=_("Service name for endpoint discovery")),
|
||||
cfg.StrOpt(
|
||||
'endpoint_service_type',
|
||||
help=_("Service type for endpoint discovery")),
|
||||
cfg.StrOpt(
|
||||
'endpoint_region_name',
|
||||
help=_("Region to which the endpoint belongs")),
|
||||
cfg.StrOpt(
|
||||
'endpoint_interface',
|
||||
default='publicURL',
|
||||
choices=['public', 'publicURL', 'internal', 'internalURL',
|
||||
'admin', 'adminURL'],
|
||||
help=_("The interface for endpoint discovery")),
|
||||
]
|
||||
|
||||
_option_group = 'oslo_limit'
|
||||
|
@ -21,9 +21,12 @@ Tests for `limit` module.
|
||||
from unittest import mock
|
||||
import uuid
|
||||
|
||||
from openstack import exceptions as os_exceptions
|
||||
from openstack.identity.v3 import endpoint
|
||||
from openstack.identity.v3 import limit as klimit
|
||||
from openstack.identity.v3 import region
|
||||
from openstack.identity.v3 import registered_limit
|
||||
from openstack.identity.v3 import service
|
||||
from oslo_config import cfg
|
||||
from oslo_config import fixture as config_fixture
|
||||
from oslotest import base
|
||||
@ -337,6 +340,244 @@ class TestEnforcerUtils(base.BaseTestCase):
|
||||
|
||||
self.assertEqual(fake_endpoint, utils._endpoint)
|
||||
self.mock_conn.get_endpoint.assert_called_once_with('ENDPOINT_ID')
|
||||
self.mock_conn.services.assert_not_called()
|
||||
self.mock_conn.endpoints.assert_not_called()
|
||||
|
||||
def test_get_endpoint_no_id(self):
|
||||
self.config_fixture.config(group='oslo_limit', endpoint_id=None)
|
||||
self.mock_conn.get_endpoint.side_effect = \
|
||||
os_exceptions.ResourceNotFound
|
||||
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
limit._EnforcerUtils
|
||||
)
|
||||
self.mock_conn.get_endpoint.assert_not_called()
|
||||
self.mock_conn.services.assert_not_called()
|
||||
self.mock_conn.endpoints.assert_not_called()
|
||||
|
||||
def test_get_endpoint_missing(self):
|
||||
self.mock_conn.get_endpoint.side_effect = \
|
||||
os_exceptions.ResourceNotFound
|
||||
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
limit._EnforcerUtils
|
||||
)
|
||||
self.mock_conn.get_endpoint.assert_called_once_with('ENDPOINT_ID')
|
||||
self.mock_conn.services.assert_not_called()
|
||||
self.mock_conn.endpoints.assert_not_called()
|
||||
|
||||
def test_get_endpoint_lookup_without_service_opts(self):
|
||||
self.config_fixture.config(group='oslo_limit', endpoint_id=None)
|
||||
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
limit._EnforcerUtils
|
||||
)
|
||||
|
||||
self.mock_conn.get_endpoint.assert_not_called()
|
||||
self.mock_conn.services.assert_not_called()
|
||||
self.mock_conn.endpoints.assert_not_called()
|
||||
|
||||
def test_get_endpoint_lookup(self):
|
||||
self.config_fixture.config(group='oslo_limit', endpoint_id=None)
|
||||
self.config_fixture.config(
|
||||
group='oslo_limit', endpoint_service_type='SERVICE_TYPE'
|
||||
)
|
||||
self.config_fixture.config(
|
||||
group='oslo_limit', endpoint_service_name='SERVICE_NAME'
|
||||
)
|
||||
fake_service = service.Service(id='SERVICE_ID')
|
||||
self.mock_conn.services.return_value = [fake_service]
|
||||
fake_endpoint = endpoint.Endpoint()
|
||||
self.mock_conn.endpoints.return_value = [fake_endpoint]
|
||||
|
||||
utils = limit._EnforcerUtils()
|
||||
|
||||
self.assertEqual(fake_endpoint, utils._endpoint)
|
||||
self.mock_conn.get_endpoint.assert_not_called()
|
||||
self.mock_conn.services.assert_called_once_with(type='SERVICE_TYPE',
|
||||
name='SERVICE_NAME')
|
||||
self.mock_conn.endpoints.assert_called_once_with(
|
||||
service_id='SERVICE_ID', region_id=None, interface='publicURL')
|
||||
|
||||
def test_get_endpoint_lookup_multiple_endpoints(self):
|
||||
self.config_fixture.config(group='oslo_limit', endpoint_id=None)
|
||||
self.config_fixture.config(
|
||||
group='oslo_limit', endpoint_service_type='SERVICE_TYPE'
|
||||
)
|
||||
self.config_fixture.config(
|
||||
group='oslo_limit', endpoint_service_name='SERVICE_NAME'
|
||||
)
|
||||
fake_service = service.Service(id='SERVICE_ID')
|
||||
self.mock_conn.services.return_value = [fake_service]
|
||||
self.mock_conn.endpoints.return_value = [
|
||||
endpoint.Endpoint(), endpoint.Endpoint()
|
||||
]
|
||||
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
limit._EnforcerUtils
|
||||
)
|
||||
|
||||
self.mock_conn.get_endpoint.assert_not_called()
|
||||
self.mock_conn.services.assert_called_once_with(type='SERVICE_TYPE',
|
||||
name='SERVICE_NAME')
|
||||
self.mock_conn.endpoints.assert_called_once_with(
|
||||
service_id='SERVICE_ID', region_id=None, interface='publicURL')
|
||||
|
||||
def test_get_endpoint_lookup_endpoint_not_found(self):
|
||||
self.config_fixture.config(group='oslo_limit', endpoint_id=None)
|
||||
self.config_fixture.config(
|
||||
group='oslo_limit', endpoint_service_type='SERVICE_TYPE'
|
||||
)
|
||||
self.config_fixture.config(
|
||||
group='oslo_limit', endpoint_service_name='SERVICE_NAME'
|
||||
)
|
||||
fake_service = service.Service(id='SERVICE_ID')
|
||||
self.mock_conn.services.return_value = [fake_service]
|
||||
self.mock_conn.endpoints.side_effect = os_exceptions.ResourceNotFound
|
||||
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
limit._EnforcerUtils
|
||||
)
|
||||
|
||||
self.mock_conn.get_endpoint.assert_not_called()
|
||||
self.mock_conn.services.assert_called_once_with(type='SERVICE_TYPE',
|
||||
name='SERVICE_NAME')
|
||||
self.mock_conn.endpoints.assert_called_once_with(
|
||||
service_id='SERVICE_ID', region_id=None, interface='publicURL')
|
||||
|
||||
def test_get_endpoint_lookup_multiple_service(self):
|
||||
self.config_fixture.config(group='oslo_limit', endpoint_id=None)
|
||||
self.config_fixture.config(
|
||||
group='oslo_limit', endpoint_service_type='SERVICE_TYPE'
|
||||
)
|
||||
self.config_fixture.config(
|
||||
group='oslo_limit', endpoint_service_name='SERVICE_NAME'
|
||||
)
|
||||
self.mock_conn.services.side_effect = [
|
||||
service.Service(id='SERVICE_ID1'),
|
||||
service.Service(id='SERVICE_ID2')
|
||||
]
|
||||
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
limit._EnforcerUtils
|
||||
)
|
||||
|
||||
self.mock_conn.get_endpoint.assert_not_called()
|
||||
self.mock_conn.services.assert_called_once_with(type='SERVICE_TYPE',
|
||||
name='SERVICE_NAME')
|
||||
self.mock_conn.endpoints.assert_not_called()
|
||||
|
||||
def test_get_endpoint_lookup_service_not_found(self):
|
||||
self.config_fixture.config(group='oslo_limit', endpoint_id=None)
|
||||
self.config_fixture.config(
|
||||
group='oslo_limit', endpoint_service_type='SERVICE_TYPE'
|
||||
)
|
||||
self.config_fixture.config(
|
||||
group='oslo_limit', endpoint_service_name='SERVICE_NAME'
|
||||
)
|
||||
self.mock_conn.services.side_effect = os_exceptions.ResourceNotFound
|
||||
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
limit._EnforcerUtils
|
||||
)
|
||||
|
||||
self.mock_conn.get_endpoint.assert_not_called()
|
||||
self.mock_conn.services.assert_called_once_with(type='SERVICE_TYPE',
|
||||
name='SERVICE_NAME')
|
||||
self.mock_conn.endpoints.assert_not_called()
|
||||
|
||||
def test_get_endpoint_lookup_with_region(self):
|
||||
self.config_fixture.config(group='oslo_limit', endpoint_id=None)
|
||||
self.config_fixture.config(
|
||||
group='oslo_limit', endpoint_service_type='SERVICE_TYPE'
|
||||
)
|
||||
self.config_fixture.config(
|
||||
group='oslo_limit', endpoint_service_name='SERVICE_NAME'
|
||||
)
|
||||
self.config_fixture.config(
|
||||
group='oslo_limit', endpoint_region_name='regionOne'
|
||||
)
|
||||
fake_service = service.Service(id='SERVICE_ID')
|
||||
self.mock_conn.services.return_value = [fake_service]
|
||||
fake_endpoint = endpoint.Endpoint()
|
||||
self.mock_conn.endpoints.return_value = [fake_endpoint]
|
||||
fake_region = region.Region(id='REGION_ID')
|
||||
self.mock_conn.regions.return_value = [fake_region]
|
||||
|
||||
utils = limit._EnforcerUtils()
|
||||
|
||||
self.assertEqual(fake_endpoint, utils._endpoint)
|
||||
self.mock_conn.get_endpoint.assert_not_called()
|
||||
self.mock_conn.services.assert_called_once_with(type='SERVICE_TYPE',
|
||||
name='SERVICE_NAME')
|
||||
self.mock_conn.regions.assert_called_once_with(name='regionOne')
|
||||
self.mock_conn.endpoints.assert_called_once_with(
|
||||
service_id='SERVICE_ID', region_id='REGION_ID',
|
||||
interface='publicURL')
|
||||
|
||||
def test_get_endpoint_lookup_with_region_not_found(self):
|
||||
self.config_fixture.config(group='oslo_limit', endpoint_id=None)
|
||||
self.config_fixture.config(
|
||||
group='oslo_limit', endpoint_service_type='SERVICE_TYPE'
|
||||
)
|
||||
self.config_fixture.config(
|
||||
group='oslo_limit', endpoint_service_name='SERVICE_NAME'
|
||||
)
|
||||
self.config_fixture.config(
|
||||
group='oslo_limit', endpoint_region_name='regionOne'
|
||||
)
|
||||
fake_service = service.Service(id='SERVICE_ID')
|
||||
self.mock_conn.services.return_value = [fake_service]
|
||||
fake_endpoint = endpoint.Endpoint()
|
||||
self.mock_conn.endpoints.return_value = [fake_endpoint]
|
||||
self.mock_conn.regions.side_effect = os_exceptions.ResourceNotFound
|
||||
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
limit._EnforcerUtils
|
||||
)
|
||||
|
||||
self.mock_conn.get_endpoint.assert_not_called()
|
||||
self.mock_conn.services.assert_called_once_with(type='SERVICE_TYPE',
|
||||
name='SERVICE_NAME')
|
||||
self.mock_conn.regions.assert_called_once_with(name='regionOne')
|
||||
self.mock_conn.endpoints.assert_not_called()
|
||||
|
||||
def test_get_endpoint_lookup_with_mutliple_regions(self):
|
||||
self.config_fixture.config(group='oslo_limit', endpoint_id=None)
|
||||
self.config_fixture.config(
|
||||
group='oslo_limit', endpoint_service_type='SERVICE_TYPE'
|
||||
)
|
||||
self.config_fixture.config(
|
||||
group='oslo_limit', endpoint_service_name='SERVICE_NAME'
|
||||
)
|
||||
self.config_fixture.config(
|
||||
group='oslo_limit', endpoint_region_name='regionOne'
|
||||
)
|
||||
fake_service = service.Service(id='SERVICE_ID')
|
||||
self.mock_conn.services.return_value = [fake_service]
|
||||
fake_endpoint = endpoint.Endpoint()
|
||||
self.mock_conn.endpoints.return_value = [fake_endpoint]
|
||||
self.mock_conn.regions.return_value = [
|
||||
region.Region(id='REGION_ID1'), region.Region(id='REGION_ID2')]
|
||||
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
limit._EnforcerUtils
|
||||
)
|
||||
|
||||
self.mock_conn.get_endpoint.assert_not_called()
|
||||
self.mock_conn.services.assert_called_once_with(type='SERVICE_TYPE',
|
||||
name='SERVICE_NAME')
|
||||
self.mock_conn.regions.assert_called_once_with(name='regionOne')
|
||||
self.mock_conn.endpoints.assert_not_called()
|
||||
|
||||
def test_get_registered_limit_empty(self):
|
||||
self.mock_conn.registered_limits.return_value = iter([])
|
||||
@ -357,9 +598,8 @@ class TestEnforcerUtils(base.BaseTestCase):
|
||||
self.assertEqual(foo, reg_limit)
|
||||
|
||||
def test_get_registered_limits(self):
|
||||
fake_endpoint = endpoint.Endpoint()
|
||||
fake_endpoint.service_id = "service_id"
|
||||
fake_endpoint.region_id = "region_id"
|
||||
fake_endpoint = endpoint.Endpoint(service_id='service_id',
|
||||
region_id='region_id')
|
||||
self.mock_conn.get_endpoint.return_value = fake_endpoint
|
||||
|
||||
# a and c have limits, b doesn't have one
|
||||
@ -384,9 +624,8 @@ class TestEnforcerUtils(base.BaseTestCase):
|
||||
self.assertEqual([('a', 1), ('b', 0), ('c', 2)], limits)
|
||||
|
||||
def test_get_project_limits(self):
|
||||
fake_endpoint = endpoint.Endpoint()
|
||||
fake_endpoint.service_id = "service_id"
|
||||
fake_endpoint.region_id = "region_id"
|
||||
fake_endpoint = endpoint.Endpoint(service_id='service_id',
|
||||
region_id='region_id')
|
||||
self.mock_conn.get_endpoint.return_value = fake_endpoint
|
||||
project_id = uuid.uuid4().hex
|
||||
|
||||
|
@ -0,0 +1,11 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
The following options have been added to the ``[oslo_limit]`` section.
|
||||
When these options are set instead of the ``endpoint_id`` option, endpoint
|
||||
id is looked up from keystone API.
|
||||
|
||||
- ``endpoint_service_name``
|
||||
- ``endpoint_service_type``
|
||||
- ``endpoint_region_name``
|
||||
- ``endpoint_interface``
|
Loading…
Reference in New Issue
Block a user