Change interface to explicitly pass legacy_headers
Based on suggestion from @sdague TODO: more tests, positive and negative
This commit is contained in:
parent
49b44934b9
commit
d2c4bb7605
15
README.rst
15
README.rst
|
@ -8,22 +8,21 @@ A simple parser for OpenStack microversion headers::
|
||||||
# headers is a dict of headers with folded (comma-separated
|
# headers is a dict of headers with folded (comma-separated
|
||||||
# values) or a list of header, value tuples
|
# values) or a list of header, value tuples
|
||||||
version = microversion_parse.get_version(
|
version = microversion_parse.get_version(
|
||||||
headers, service_type='compute', legacy_type='nova')
|
headers, service_type='compute',
|
||||||
|
legacy_headers=['x-openstack-nova-api-version'])
|
||||||
|
|
||||||
It processes microversion headers with the standard form::
|
It processes microversion headers with the standard form::
|
||||||
|
|
||||||
OpenStack-API-Version: compute 2.1
|
OpenStack-API-Version: compute 2.1
|
||||||
|
|
||||||
It also deals with several older formats, depending on the values of
|
If provided with a ``legacy_headers`` argument, this is treated as
|
||||||
the service_type and legacy_type arguments::
|
a list of headers to check for microversions. Some examples of
|
||||||
|
headers include:
|
||||||
|
|
||||||
OpenStack-compute-api-version: 2.1
|
OpenStack-telemetry-api-version: 2.1
|
||||||
OpenStack-nova-api-version: 2.1
|
OpenStack-nova-api-version: 2.1
|
||||||
X-OpenStack-nova-api-version: 2.1
|
X-OpenStack-nova-api-version: 2.1
|
||||||
|
|
||||||
.. note:: The X prefixed version does not currently parse for
|
If a version string cannot be found, ``None`` will be returned. If
|
||||||
service type named headers, only project named headers.
|
|
||||||
|
|
||||||
If a version string cannot be found ``None`` will be returned. If
|
|
||||||
the input is incorrect usual Python exceptions (ValueError,
|
the input is incorrect usual Python exceptions (ValueError,
|
||||||
TypeError) are allowed to raise to the caller.
|
TypeError) are allowed to raise to the caller.
|
||||||
|
|
|
@ -17,58 +17,53 @@ import collections
|
||||||
STANDARD_HEADER = 'openstack-api-version'
|
STANDARD_HEADER = 'openstack-api-version'
|
||||||
|
|
||||||
|
|
||||||
def get_version(headers, service_type=None, legacy_type=None):
|
def get_version(headers, service_type=None, legacy_headers=None):
|
||||||
"""Parse a microversion out of headers
|
"""Parse a microversion out of headers
|
||||||
|
|
||||||
:param headers: The headers of a request, dict or list
|
:param headers: The headers of a request, dict or list
|
||||||
:param service_type: The service type being looked for in the headers
|
:param service_type: The service type being looked for in the headers
|
||||||
:param legacy_type: The project name to use when looking for fallback
|
:param legacy_headers: Other headers to look at for a version
|
||||||
headers.
|
|
||||||
:returns: a version string or "latest"
|
:returns: a version string or "latest"
|
||||||
:raises: ValueError
|
:raises: ValueError
|
||||||
|
|
||||||
|
If headers is not a dict we assume is an iterator of
|
||||||
|
tuple-like headers, which we will fold into a dict.
|
||||||
|
|
||||||
|
The flow is that we first look for the new standard singular
|
||||||
|
header:
|
||||||
|
|
||||||
|
* openstack-api-version: <service> <version>
|
||||||
|
|
||||||
|
If that's not present we fall back to the headers listed in
|
||||||
|
legacy_headers. These often look like this:
|
||||||
|
|
||||||
|
* openstack-<service>-api-version: <version>
|
||||||
|
* openstack-<legacy>-api-version: <version>
|
||||||
|
* x-openstack-<legacy>-api-version: <version>
|
||||||
|
|
||||||
|
Folded headers are joined by ','.
|
||||||
"""
|
"""
|
||||||
# If headers is not a dict we assume is an iterator of
|
|
||||||
# tuple-like headers, which we will fold into a dict.
|
assert service_type, 'service type required'
|
||||||
#
|
|
||||||
# The flow is that we first look for the new standard singular
|
|
||||||
# header:
|
|
||||||
# * openstack-api-version: <service> <version>
|
|
||||||
# If that's not present we fall back, in order, to:
|
|
||||||
# * openstack-<service>-api-version: <version>
|
|
||||||
# * openstack-<legacy>-api-version: <version>
|
|
||||||
# * x-openstack-<legacy>-api-version: <version>
|
|
||||||
#
|
|
||||||
# Folded headers are joined by ,
|
|
||||||
folded_headers = fold_headers(headers)
|
folded_headers = fold_headers(headers)
|
||||||
|
|
||||||
version = check_standard_header(folded_headers, service_type)
|
version = check_standard_header(folded_headers, service_type)
|
||||||
if version:
|
if version:
|
||||||
return version
|
return version
|
||||||
|
|
||||||
extra_headers = build_headers(service_type, legacy_type)
|
if legacy_headers:
|
||||||
version = check_legacy_headers(folded_headers, extra_headers)
|
version = check_legacy_headers(folded_headers, legacy_headers)
|
||||||
return version
|
return version
|
||||||
|
|
||||||
|
return None
|
||||||
def build_headers(service_type, legacy_type=None):
|
|
||||||
"""Create the headers to be looked at."""
|
|
||||||
headers = [
|
|
||||||
'openstack-%s-api-version' % service_type
|
|
||||||
]
|
|
||||||
if legacy_type:
|
|
||||||
legacy_headers = [
|
|
||||||
'openstack-%s-api-version' % legacy_type,
|
|
||||||
'x-openstack-%s-api-version' % legacy_type
|
|
||||||
]
|
|
||||||
headers.extend(legacy_headers)
|
|
||||||
return headers
|
|
||||||
|
|
||||||
|
|
||||||
def check_legacy_headers(headers, legacy_headers):
|
def check_legacy_headers(headers, legacy_headers):
|
||||||
"""Gather values from old headers."""
|
"""Gather values from old headers."""
|
||||||
for legacy_header in legacy_headers:
|
for legacy_header in legacy_headers:
|
||||||
try:
|
try:
|
||||||
value = headers[legacy_header]
|
value = headers[legacy_header.lower()]
|
||||||
return value.split(',')[-1].strip()
|
return value.split(',')[-1].strip()
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -16,23 +16,6 @@ import testtools
|
||||||
import microversion_parse
|
import microversion_parse
|
||||||
|
|
||||||
|
|
||||||
class TestBuildHeaders(testtools.TestCase):
|
|
||||||
|
|
||||||
def test_build_header_list_service(self):
|
|
||||||
headers = microversion_parse.build_headers('alpha')
|
|
||||||
|
|
||||||
self.assertEqual(1, len(headers))
|
|
||||||
self.assertEqual('openstack-alpha-api-version', headers[0])
|
|
||||||
|
|
||||||
def test_build_header_list_legacy(self):
|
|
||||||
headers = microversion_parse.build_headers('alpha', 'beta')
|
|
||||||
|
|
||||||
self.assertEqual(3, len(headers))
|
|
||||||
self.assertEqual('openstack-alpha-api-version', headers[0])
|
|
||||||
self.assertEqual('openstack-beta-api-version', headers[1])
|
|
||||||
self.assertEqual('x-openstack-beta-api-version', headers[2])
|
|
||||||
|
|
||||||
|
|
||||||
class TestFoldHeaders(testtools.TestCase):
|
class TestFoldHeaders(testtools.TestCase):
|
||||||
|
|
||||||
def test_dict_headers(self):
|
def test_dict_headers(self):
|
||||||
|
@ -142,7 +125,8 @@ class TestLegacyHeaders(testtools.TestCase):
|
||||||
'header-two': 'beta',
|
'header-two': 'beta',
|
||||||
}
|
}
|
||||||
version = microversion_parse.get_version(
|
version = microversion_parse.get_version(
|
||||||
headers, service_type='compute')
|
headers, service_type='compute',
|
||||||
|
legacy_headers=['openstack-CoMpUte-api-version'])
|
||||||
self.assertEqual('2.1', version)
|
self.assertEqual('2.1', version)
|
||||||
|
|
||||||
def test_legacy_headers_folded(self):
|
def test_legacy_headers_folded(self):
|
||||||
|
@ -152,28 +136,21 @@ class TestLegacyHeaders(testtools.TestCase):
|
||||||
'header-two': 'beta',
|
'header-two': 'beta',
|
||||||
}
|
}
|
||||||
version = microversion_parse.get_version(
|
version = microversion_parse.get_version(
|
||||||
headers, service_type='compute')
|
headers, service_type='compute',
|
||||||
|
legacy_headers=['openstack-compute-api-version'])
|
||||||
self.assertEqual('9.2', version)
|
self.assertEqual('9.2', version)
|
||||||
|
|
||||||
def test_older_legacy_headers_with_service(self):
|
def test_older_legacy_headers(self):
|
||||||
headers = {
|
|
||||||
'header-one': 'alpha',
|
|
||||||
'x-openstack-compute-api-version': ' 2.1, 9.2 ',
|
|
||||||
'header-two': 'beta',
|
|
||||||
}
|
|
||||||
version = microversion_parse.get_version(
|
|
||||||
headers, service_type='compute')
|
|
||||||
# We don't do x- for service types.
|
|
||||||
self.assertEqual(None, version)
|
|
||||||
|
|
||||||
def test_legacy_headers_project(self):
|
|
||||||
headers = {
|
headers = {
|
||||||
'header-one': 'alpha',
|
'header-one': 'alpha',
|
||||||
'x-openstack-nova-api-version': ' 2.1, 9.2 ',
|
'x-openstack-nova-api-version': ' 2.1, 9.2 ',
|
||||||
'header-two': 'beta',
|
'header-two': 'beta',
|
||||||
}
|
}
|
||||||
version = microversion_parse.get_version(
|
version = microversion_parse.get_version(
|
||||||
headers, service_type='compute', legacy_type='nova')
|
headers, service_type='compute',
|
||||||
|
legacy_headers=['openstack-nova-api-version',
|
||||||
|
'x-openstack-nova-api-version'])
|
||||||
|
# We don't do x- for service types.
|
||||||
self.assertEqual('9.2', version)
|
self.assertEqual('9.2', version)
|
||||||
|
|
||||||
def test_legacy_headers_prefer(self):
|
def test_legacy_headers_prefer(self):
|
||||||
|
@ -184,8 +161,15 @@ class TestLegacyHeaders(testtools.TestCase):
|
||||||
'header-two': 'beta',
|
'header-two': 'beta',
|
||||||
}
|
}
|
||||||
version = microversion_parse.get_version(
|
version = microversion_parse.get_version(
|
||||||
headers, service_type='compute', legacy_type='nova')
|
headers, service_type='compute',
|
||||||
|
legacy_headers=['openstack-compute-api-version',
|
||||||
|
'x-openstack-nova-api-version'])
|
||||||
self.assertEqual('3.7', version)
|
self.assertEqual('3.7', version)
|
||||||
|
version = microversion_parse.get_version(
|
||||||
|
headers, service_type='compute',
|
||||||
|
legacy_headers=['x-openstack-nova-api-version',
|
||||||
|
'openstack-compute-api-version'])
|
||||||
|
self.assertEqual('9.2', version)
|
||||||
|
|
||||||
|
|
||||||
class TestGetHeaders(testtools.TestCase):
|
class TestGetHeaders(testtools.TestCase):
|
||||||
|
@ -199,9 +183,21 @@ class TestGetHeaders(testtools.TestCase):
|
||||||
'header-two': 'beta',
|
'header-two': 'beta',
|
||||||
}
|
}
|
||||||
version = microversion_parse.get_version(
|
version = microversion_parse.get_version(
|
||||||
headers, service_type='compute', legacy_type='nova')
|
headers, service_type='compute',
|
||||||
|
legacy_headers=['openstack-compute-api-version',
|
||||||
|
'x-openstack-nova-api-version'])
|
||||||
self.assertEqual('11.12', version)
|
self.assertEqual('11.12', version)
|
||||||
|
|
||||||
|
def test_no_headers(self):
|
||||||
|
headers = {}
|
||||||
|
version = microversion_parse.get_version(
|
||||||
|
headers, service_type='compute')
|
||||||
|
self.assertEqual(None, version)
|
||||||
|
|
||||||
|
self.assertRaises(AssertionError,
|
||||||
|
microversion_parse.get_version,
|
||||||
|
headers)
|
||||||
|
|
||||||
def test_unfolded_service(self):
|
def test_unfolded_service(self):
|
||||||
headers = [
|
headers = [
|
||||||
('header-one', 'alpha'),
|
('header-one', 'alpha'),
|
||||||
|
@ -210,16 +206,17 @@ class TestGetHeaders(testtools.TestCase):
|
||||||
('openstack-api-version', '3.0'),
|
('openstack-api-version', '3.0'),
|
||||||
]
|
]
|
||||||
version = microversion_parse.get_version(
|
version = microversion_parse.get_version(
|
||||||
headers, service_type='compute', legacy_type='nova')
|
headers, service_type='compute')
|
||||||
self.assertEqual('2.0', version)
|
self.assertEqual('2.0', version)
|
||||||
|
|
||||||
def test_unfolded_in_name(self):
|
def test_unfolded_in_name(self):
|
||||||
headers = [
|
headers = [
|
||||||
('header-one', 'alpha'),
|
('header-one', 'alpha'),
|
||||||
('openstack-compute-api-version', '1.0'),
|
('x-openstack-nova-api-version', '1.0'),
|
||||||
('openstack-compute-api-version', '2.0'),
|
('x-openstack-nova-api-version', '2.0'),
|
||||||
('openstack-telemetry-api-version', '3.0'),
|
('openstack-telemetry-api-version', '3.0'),
|
||||||
]
|
]
|
||||||
version = microversion_parse.get_version(
|
version = microversion_parse.get_version(
|
||||||
headers, service_type='compute', legacy_type='nova')
|
headers, service_type='compute',
|
||||||
|
legacy_headers=['x-openstack-nova-api-version'])
|
||||||
self.assertEqual('2.0', version)
|
self.assertEqual('2.0', version)
|
||||||
|
|
Loading…
Reference in New Issue