Drop default URI, make either a session or inspector_url required
While convenient sometimes, the previous default behavior of assuming 127.0.0.1 is too unrealistic for production and may cause confusion. Change-Id: I32496bc57f0127e680bbd1823401ab0211f93114
This commit is contained in:
parent
98ee6670f1
commit
e9bcb68bab
@ -12,4 +12,4 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from .v1 import ClientV1, DEFAULT_API_VERSION, MAX_API_VERSION # noqa
|
from .v1 import ClientV1, DEFAULT_API_VERSION, MAX_API_VERSION # noqa
|
||||||
from .common.http import ClientError, VersionNotSupported # noqa
|
from .common.http import ClientError, EndpointNotFound, VersionNotSupported # noqa
|
||||||
|
@ -18,14 +18,12 @@ import logging
|
|||||||
|
|
||||||
from keystoneauth1 import exceptions as ks_exc
|
from keystoneauth1 import exceptions as ks_exc
|
||||||
from keystoneauth1 import session as ks_session
|
from keystoneauth1 import session as ks_session
|
||||||
from oslo_utils import netutils
|
|
||||||
import requests
|
import requests
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from ironic_inspector_client.common.i18n import _
|
from ironic_inspector_client.common.i18n import _
|
||||||
|
|
||||||
|
|
||||||
_DEFAULT_URL = 'http://' + netutils.get_my_ipv4() + ':5050'
|
|
||||||
_ERROR_ENCODING = 'utf-8'
|
_ERROR_ENCODING = 'utf-8'
|
||||||
LOG = logging.getLogger('ironic_inspector_client')
|
LOG = logging.getLogger('ironic_inspector_client')
|
||||||
|
|
||||||
@ -85,6 +83,19 @@ class VersionNotSupported(Exception):
|
|||||||
super(Exception, self).__init__(msg)
|
super(Exception, self).__init__(msg)
|
||||||
|
|
||||||
|
|
||||||
|
class EndpointNotFound(Exception):
|
||||||
|
"""Denotes that endpoint for the introspection service was not found.
|
||||||
|
|
||||||
|
ivar service_type: requested service type
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, service_type):
|
||||||
|
self.service_type = service_type
|
||||||
|
msg = _('Endpoint of type %s was not found in the service catalog '
|
||||||
|
'and was not provided explicitly') % service_type
|
||||||
|
super(Exception, self).__init__(msg)
|
||||||
|
|
||||||
|
|
||||||
class BaseClient(object):
|
class BaseClient(object):
|
||||||
"""Base class for clients, provides common HTTP code."""
|
"""Base class for clients, provides common HTTP code."""
|
||||||
|
|
||||||
@ -105,8 +116,11 @@ class BaseClient(object):
|
|||||||
:param interface: interface type (public, internal, etc) to use when
|
:param interface: interface type (public, internal, etc) to use when
|
||||||
looking up the URL
|
looking up the URL
|
||||||
:param region_name: region name to use when looking up the URL
|
:param region_name: region name to use when looking up the URL
|
||||||
|
:raises: EndpointNotFound if the introspection service endpoint
|
||||||
|
was not provided via inspector_url and was not found in the
|
||||||
|
service catalog.
|
||||||
"""
|
"""
|
||||||
self._base_url = inspector_url or _DEFAULT_URL
|
self._base_url = inspector_url
|
||||||
|
|
||||||
if session is None:
|
if session is None:
|
||||||
self._session = ks_session.Session(None)
|
self._session = ks_session.Session(None)
|
||||||
@ -117,11 +131,21 @@ class BaseClient(object):
|
|||||||
self._base_url = session.get_endpoint(
|
self._base_url = session.get_endpoint(
|
||||||
service_type=service_type,
|
service_type=service_type,
|
||||||
interface=interface,
|
interface=interface,
|
||||||
region_name=region_name) or _DEFAULT_URL
|
region_name=region_name)
|
||||||
except ks_exc.EndpointNotFound:
|
except ks_exc.CatalogException as exc:
|
||||||
LOG.warning('Endpoint for service %s was not found, '
|
LOG.error('%(iface)s endpoint for %(stype)s in region '
|
||||||
'falling back to local host on port 5050',
|
'%(region)s was not found in the service '
|
||||||
service_type)
|
'catalog: %(error)s',
|
||||||
|
{'iface': interface,
|
||||||
|
'stype': service_type,
|
||||||
|
'region': region_name,
|
||||||
|
'error': exc})
|
||||||
|
raise EndpointNotFound(service_type=service_type)
|
||||||
|
|
||||||
|
if not self._base_url:
|
||||||
|
# This handles the case when session=None and no inspector_url is
|
||||||
|
# provided, as well as keystoneauth plugins that may return None.
|
||||||
|
raise EndpointNotFound(service_type=service_type)
|
||||||
|
|
||||||
self._base_url = self._base_url.rstrip('/')
|
self._base_url = self._base_url.rstrip('/')
|
||||||
self._api_version = self._check_api_version(api_version)
|
self._api_version = self._check_api_version(api_version)
|
||||||
|
@ -25,17 +25,21 @@ import unittest
|
|||||||
from ironic_inspector.common import swift
|
from ironic_inspector.common import swift
|
||||||
from ironic_inspector import introspection_state as istate
|
from ironic_inspector import introspection_state as istate
|
||||||
from ironic_inspector.test import functional
|
from ironic_inspector.test import functional
|
||||||
|
from keystoneauth1 import session as ks_session
|
||||||
|
from keystoneauth1 import token_endpoint
|
||||||
from oslo_concurrency import processutils
|
from oslo_concurrency import processutils
|
||||||
|
|
||||||
import ironic_inspector_client as client
|
import ironic_inspector_client as client
|
||||||
from ironic_inspector_client.common import http
|
|
||||||
from ironic_inspector_client import shell
|
from ironic_inspector_client import shell
|
||||||
|
|
||||||
|
|
||||||
class TestV1PythonAPI(functional.Base):
|
class TestV1PythonAPI(functional.Base):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestV1PythonAPI, self).setUp()
|
super(TestV1PythonAPI, self).setUp()
|
||||||
self.client = client.ClientV1()
|
self.auth = token_endpoint.Token(endpoint='http://127.0.0.1:5050',
|
||||||
|
token='token')
|
||||||
|
self.session = ks_session.Session(self.auth)
|
||||||
|
self.client = client.ClientV1(session=self.session)
|
||||||
functional.cfg.CONF.set_override('store_data', 'none', 'processing')
|
functional.cfg.CONF.set_override('store_data', 'none', 'processing')
|
||||||
|
|
||||||
def my_status_index(self, statuses):
|
def my_status_index(self, statuses):
|
||||||
@ -192,15 +196,18 @@ class TestV1PythonAPI(functional.Base):
|
|||||||
|
|
||||||
def test_client_init(self):
|
def test_client_init(self):
|
||||||
self.assertRaises(client.VersionNotSupported,
|
self.assertRaises(client.VersionNotSupported,
|
||||||
client.ClientV1, api_version=(1, 999))
|
client.ClientV1, session=self.session,
|
||||||
|
api_version=(1, 999))
|
||||||
self.assertRaises(client.VersionNotSupported,
|
self.assertRaises(client.VersionNotSupported,
|
||||||
client.ClientV1, api_version=2)
|
client.ClientV1, session=self.session,
|
||||||
|
api_version=2)
|
||||||
|
|
||||||
self.assertTrue(client.ClientV1(api_version=1).server_api_versions())
|
self.assertTrue(client.ClientV1(
|
||||||
self.assertTrue(client.ClientV1(api_version='1.0')
|
api_version=1, session=self.session).server_api_versions())
|
||||||
.server_api_versions())
|
self.assertTrue(client.ClientV1(
|
||||||
self.assertTrue(client.ClientV1(api_version=(1, 0))
|
api_version='1.0', session=self.session).server_api_versions())
|
||||||
.server_api_versions())
|
self.assertTrue(client.ClientV1(
|
||||||
|
api_version=(1, 0), session=self.session).server_api_versions())
|
||||||
|
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
client.ClientV1(inspector_url='http://127.0.0.1:5050')
|
client.ClientV1(inspector_url='http://127.0.0.1:5050')
|
||||||
@ -455,6 +462,4 @@ class TestCLI(BaseCLITest):
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
with functional.mocked_server():
|
with functional.mocked_server():
|
||||||
# Make links predictable
|
|
||||||
http._DEFAULT_URL = 'http://127.0.0.1:5050'
|
|
||||||
unittest.main(verbosity=2)
|
unittest.main(verbosity=2)
|
||||||
|
@ -25,7 +25,7 @@ class TestCheckVersion(unittest.TestCase):
|
|||||||
@mock.patch.object(http.BaseClient, 'server_api_versions',
|
@mock.patch.object(http.BaseClient, 'server_api_versions',
|
||||||
lambda *args, **kwargs: ((1, 0), (1, 99)))
|
lambda *args, **kwargs: ((1, 0), (1, 99)))
|
||||||
def _check(self, version):
|
def _check(self, version):
|
||||||
cli = http.BaseClient(1)
|
cli = http.BaseClient(1, inspector_url='http://127.0.0.1:5050')
|
||||||
return cli._check_api_version(version)
|
return cli._check_api_version(version)
|
||||||
|
|
||||||
def test_tuple(self):
|
def test_tuple(self):
|
||||||
@ -64,7 +64,9 @@ FAKE_HEADERS = {
|
|||||||
'return_value.headers': FAKE_HEADERS})
|
'return_value.headers': FAKE_HEADERS})
|
||||||
class TestServerApiVersions(unittest.TestCase):
|
class TestServerApiVersions(unittest.TestCase):
|
||||||
def _check(self, current=1):
|
def _check(self, current=1):
|
||||||
return http.BaseClient(api_version=current).server_api_versions()
|
return http.BaseClient(
|
||||||
|
api_version=current,
|
||||||
|
inspector_url='http://127.0.0.1:5050').server_api_versions()
|
||||||
|
|
||||||
def test_no_headers(self, mock_get):
|
def test_no_headers(self, mock_get):
|
||||||
mock_get.return_value.headers = {}
|
mock_get.return_value.headers = {}
|
||||||
@ -102,7 +104,7 @@ class TestServerApiVersions(unittest.TestCase):
|
|||||||
|
|
||||||
|
|
||||||
class TestRequest(unittest.TestCase):
|
class TestRequest(unittest.TestCase):
|
||||||
base_url = http._DEFAULT_URL + '/v1'
|
base_url = 'http://127.0.0.1:5050/v1'
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestRequest, self).setUp()
|
super(TestRequest, self).setUp()
|
||||||
@ -131,24 +133,18 @@ class TestRequest(unittest.TestCase):
|
|||||||
service_type='baremetal-introspection',
|
service_type='baremetal-introspection',
|
||||||
interface=None, region_name=None)
|
interface=None, region_name=None)
|
||||||
|
|
||||||
def test_ok_no_endpoint(self):
|
def test_no_endpoint(self):
|
||||||
self.session.get_endpoint.return_value = None
|
self.session.get_endpoint.return_value = None
|
||||||
res = self.get_client().request('get', '/foo/bar')
|
self.assertRaises(http.EndpointNotFound, self.get_client)
|
||||||
|
|
||||||
self.assertIs(self.req.return_value, res)
|
|
||||||
self.req.assert_called_once_with(self.base_url + '/foo/bar', 'get',
|
|
||||||
raise_exc=False, headers=self.headers)
|
|
||||||
self.session.get_endpoint.assert_called_once_with(
|
self.session.get_endpoint.assert_called_once_with(
|
||||||
service_type='baremetal-introspection',
|
service_type='baremetal-introspection',
|
||||||
interface=None, region_name=None)
|
interface=None, region_name=None)
|
||||||
|
|
||||||
def test_ok_endpoint_not_found(self):
|
def test_endpoint_not_found(self):
|
||||||
self.session.get_endpoint.side_effect = exceptions.EndpointNotFound()
|
self.session.get_endpoint.side_effect = exceptions.EndpointNotFound()
|
||||||
res = self.get_client().request('get', '/foo/bar')
|
self.assertRaises(http.EndpointNotFound, self.get_client)
|
||||||
|
|
||||||
self.assertIs(self.req.return_value, res)
|
|
||||||
self.req.assert_called_once_with(self.base_url + '/foo/bar', 'get',
|
|
||||||
raise_exc=False, headers=self.headers)
|
|
||||||
self.session.get_endpoint.assert_called_once_with(
|
self.session.get_endpoint.assert_called_once_with(
|
||||||
service_type='baremetal-introspection',
|
service_type='baremetal-introspection',
|
||||||
interface=None, region_name=None)
|
interface=None, region_name=None)
|
||||||
@ -156,11 +152,13 @@ class TestRequest(unittest.TestCase):
|
|||||||
@mock.patch.object(session.Session, 'request', autospec=True,
|
@mock.patch.object(session.Session, 'request', autospec=True,
|
||||||
**{'return_value.status_code': 200})
|
**{'return_value.status_code': 200})
|
||||||
def test_ok_no_auth(self, mock_req):
|
def test_ok_no_auth(self, mock_req):
|
||||||
res = self.get_client(use_session=False).request('get', '/foo/bar')
|
res = self.get_client(
|
||||||
|
use_session=False,
|
||||||
|
inspector_url='http://some/host').request('get', '/foo/bar')
|
||||||
|
|
||||||
self.assertIs(mock_req.return_value, res)
|
self.assertIs(mock_req.return_value, res)
|
||||||
mock_req.assert_called_once_with(mock.ANY,
|
mock_req.assert_called_once_with(mock.ANY,
|
||||||
self.base_url + '/foo/bar', 'get',
|
'http://some/host/v1/foo/bar', 'get',
|
||||||
raise_exc=False, headers=self.headers)
|
raise_exc=False, headers=self.headers)
|
||||||
|
|
||||||
def test_explicit_version(self):
|
def test_explicit_version(self):
|
||||||
@ -171,22 +169,6 @@ class TestRequest(unittest.TestCase):
|
|||||||
self.req.assert_called_once_with(self.base_url + '/foo/bar', 'get',
|
self.req.assert_called_once_with(self.base_url + '/foo/bar', 'get',
|
||||||
raise_exc=False, headers=self.headers)
|
raise_exc=False, headers=self.headers)
|
||||||
|
|
||||||
def test_explicit_url(self):
|
|
||||||
res = self.get_client(inspector_url='http://host').request(
|
|
||||||
'get', '/foo/bar')
|
|
||||||
|
|
||||||
self.assertIs(self.req.return_value, res)
|
|
||||||
self.req.assert_called_once_with('http://host/v1/foo/bar', 'get',
|
|
||||||
raise_exc=False, headers=self.headers)
|
|
||||||
|
|
||||||
def test_explicit_url_with_version(self):
|
|
||||||
res = self.get_client(inspector_url='http://host/v1').request(
|
|
||||||
'get', '/foo/bar')
|
|
||||||
|
|
||||||
self.assertIs(self.req.return_value, res)
|
|
||||||
self.req.assert_called_once_with('http://host/v1/foo/bar', 'get',
|
|
||||||
raise_exc=False, headers=self.headers)
|
|
||||||
|
|
||||||
def test_error(self):
|
def test_error(self):
|
||||||
self.req.return_value.status_code = 400
|
self.req.return_value.status_code = 400
|
||||||
self.req.return_value.content = json.dumps(
|
self.req.return_value.content = json.dumps(
|
||||||
|
@ -23,6 +23,7 @@ class TestExposedAPI(unittest.TestCase):
|
|||||||
if not x.startswith('__') and
|
if not x.startswith('__') and
|
||||||
not isinstance(getattr(ironic_inspector_client, x),
|
not isinstance(getattr(ironic_inspector_client, x),
|
||||||
types.ModuleType)}
|
types.ModuleType)}
|
||||||
self.assertEqual({'ClientV1', 'ClientError', 'VersionNotSupported',
|
self.assertEqual({'ClientV1', 'ClientError', 'EndpointNotFound',
|
||||||
|
'VersionNotSupported',
|
||||||
'MAX_API_VERSION', 'DEFAULT_API_VERSION'},
|
'MAX_API_VERSION', 'DEFAULT_API_VERSION'},
|
||||||
exposed)
|
exposed)
|
||||||
|
@ -38,6 +38,7 @@ class TestInit(unittest.TestCase):
|
|||||||
my_ip = 'http://' + netutils.get_my_ipv4() + ':5050'
|
my_ip = 'http://' + netutils.get_my_ipv4() + ':5050'
|
||||||
|
|
||||||
def get_client(self, **kwargs):
|
def get_client(self, **kwargs):
|
||||||
|
kwargs.setdefault('inspector_url', self.my_ip)
|
||||||
return ironic_inspector_client.ClientV1(**kwargs)
|
return ironic_inspector_client.ClientV1(**kwargs)
|
||||||
|
|
||||||
def test_ok(self, mock_get):
|
def test_ok(self, mock_get):
|
||||||
@ -74,6 +75,7 @@ class BaseTest(unittest.TestCase):
|
|||||||
@mock.patch.object(http.BaseClient, 'server_api_versions',
|
@mock.patch.object(http.BaseClient, 'server_api_versions',
|
||||||
lambda self: ((1, 0), (1, 99)))
|
lambda self: ((1, 0), (1, 99)))
|
||||||
def get_client(self, **kwargs):
|
def get_client(self, **kwargs):
|
||||||
|
kwargs.setdefault('inspector_url', self.my_ip)
|
||||||
return ironic_inspector_client.ClientV1(**kwargs)
|
return ironic_inspector_client.ClientV1(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
11
releasenotes/notes/no-default-uri-861f675ccb75e05d.yaml
Normal file
11
releasenotes/notes/no-default-uri-861f675ccb75e05d.yaml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
upgrade:
|
||||||
|
- |
|
||||||
|
There is no longer a default introspection API endpoint. Previously,
|
||||||
|
if no endpoint was requested and no endpoint found in the service catalog,
|
||||||
|
``127.0.0.1:5050`` was used by default.
|
||||||
|
other:
|
||||||
|
- |
|
||||||
|
The ``ClientV1`` constructor now raises a new ``EndpointNotFound``
|
||||||
|
exception when no introspection API endpoint can be detected. Previously
|
||||||
|
this condition was ignored and ``127.0.0.1:5050`` was used as a fallback.
|
Loading…
Reference in New Issue
Block a user