diff --git a/ironicclient/common/http.py b/ironicclient/common/http.py index 5e677ad24..122dcef49 100644 --- a/ironicclient/common/http.py +++ b/ironicclient/common/http.py @@ -18,6 +18,7 @@ import hashlib import logging import os +import re import socket import ssl import textwrap @@ -50,7 +51,8 @@ USER_AGENT = 'python-ironicclient' CHUNKSIZE = 1024 * 64 # 64kB -API_VERSION = '/v1' +_MAJOR_VERSION = 1 +API_VERSION = '/v%d' % _MAJOR_VERSION API_VERSION_SELECTED_STATES = ('user', 'negotiated', 'cached', 'default') @@ -61,10 +63,12 @@ SUPPORTED_ENDPOINT_SCHEME = ('http', 'https') +_API_VERSION_RE = re.compile(r'/+(v%d)?/*$' % _MAJOR_VERSION) + def _trim_endpoint_api_version(url): """Trim API version and trailing slash from endpoint.""" - return url.rstrip('/').rstrip(API_VERSION).rstrip('/') + return re.sub(_API_VERSION_RE, '', url) def _extract_error_json(body): @@ -604,6 +608,9 @@ def __init__(self, 'removed in Stein. Please use "endpoint_override" ' 'instead.') self.endpoint = endpoint + if isinstance(kwargs.get('endpoint_override'), six.string_types): + kwargs['endpoint_override'] = _trim_endpoint_api_version( + kwargs['endpoint_override']) super(SessionClient, self).__init__(**kwargs) @@ -634,10 +641,7 @@ def _http_request(self, url, method, **kwargs): kwargs.setdefault('user_agent', USER_AGENT) kwargs.setdefault('auth', self.auth) if isinstance(self.endpoint_override, six.string_types): - kwargs.setdefault( - 'endpoint_override', - _trim_endpoint_api_version(self.endpoint_override) - ) + kwargs.setdefault('endpoint_override', self.endpoint_override) if getattr(self, 'os_ironic_api_version', None): kwargs['headers'].setdefault('X-OpenStack-Ironic-API-Version', diff --git a/ironicclient/tests/unit/test_client.py b/ironicclient/tests/unit/test_client.py index 47759a2ee..1d12d709d 100644 --- a/ironicclient/tests/unit/test_client.py +++ b/ironicclient/tests/unit/test_client.py @@ -93,14 +93,14 @@ def test_get_client_only_ironic_url(self): client = self._test_get_client(auth='none', warn_mock_call_count=1, **kwargs) self.assertIsInstance(client.http_client, http.SessionClient) - self.assertEqual('http://localhost:6385/v1', + self.assertEqual('http://localhost:6385', client.http_client.endpoint_override) def test_get_client_only_endpoint(self): kwargs = {'endpoint': 'http://localhost:6385/v1'} client = self._test_get_client(auth='none', **kwargs) self.assertIsInstance(client.http_client, http.SessionClient) - self.assertEqual('http://localhost:6385/v1', + self.assertEqual('http://localhost:6385', client.http_client.endpoint_override) def test_get_client_with_auth_token_ironic_url(self): @@ -113,7 +113,7 @@ def test_get_client_with_auth_token_ironic_url(self): warn_mock_call_count=2, **kwargs) self.assertIsInstance(client.http_client, http.SessionClient) - self.assertEqual('http://localhost:6385/v1', + self.assertEqual('http://localhost:6385', client.http_client.endpoint_override) def test_get_client_no_auth_token(self): diff --git a/releasenotes/notes/endpoint-strip-dea59ccb05628a35.yaml b/releasenotes/notes/endpoint-strip-dea59ccb05628a35.yaml new file mode 100644 index 000000000..6400da81f --- /dev/null +++ b/releasenotes/notes/endpoint-strip-dea59ccb05628a35.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + Prevent trying to access endpoints with ``/v1/v1`` when using endpoint + overrides containing ``/v1``.