Use microversion parse 0.2.1
That change extracts much of the microversion handling code from placement and uses code from the microversion-parse library instead. The code in microversion-parse was taken from placement and lightly adjusted to make it more generally useful. Depends-On: https://review.openstack.org/555332 Change-Id: I5a70ebf2843d2b50dccb44e66d50e1e5105005f4
This commit is contained in:
parent
000391041f
commit
16e1910d6e
|
@ -11,6 +11,7 @@
|
|||
# under the License.
|
||||
"""Deployment handling for Placmenent API."""
|
||||
|
||||
from microversion_parse import middleware as mp_middleware
|
||||
import oslo_middleware
|
||||
from oslo_middleware import cors
|
||||
|
||||
|
@ -19,6 +20,7 @@ from nova.api.openstack.placement import fault_wrap
|
|||
from nova.api.openstack.placement import handler
|
||||
from nova.api.openstack.placement import microversion
|
||||
from nova.api.openstack.placement import requestlog
|
||||
from nova.api.openstack.placement import util
|
||||
|
||||
|
||||
# TODO(cdent): NAME points to the config project being used, so for
|
||||
|
@ -49,11 +51,15 @@ def deploy(conf):
|
|||
|
||||
context_middleware = auth.PlacementKeystoneContext
|
||||
req_id_middleware = oslo_middleware.RequestId
|
||||
microversion_middleware = microversion.MicroversionMiddleware
|
||||
microversion_middleware = mp_middleware.MicroversionMiddleware
|
||||
fault_middleware = fault_wrap.FaultWrapper
|
||||
request_log = requestlog.RequestLog
|
||||
|
||||
application = handler.PlacementHandler()
|
||||
# configure microversion middleware in the old school way
|
||||
application = microversion_middleware(
|
||||
application, microversion.SERVICE_TYPE, microversion.VERSIONS,
|
||||
json_error_formatter=util.json_error_formatter)
|
||||
|
||||
# NOTE(cdent): The ordering here is important. The list is ordered
|
||||
# from the inside out. For a single request req_id_middleware is called
|
||||
|
@ -63,8 +69,7 @@ def deploy(conf):
|
|||
# order the request went in. This order ensures that log messages
|
||||
# all see the same contextual information including request id and
|
||||
# authentication information.
|
||||
for middleware in (microversion_middleware,
|
||||
fault_middleware,
|
||||
for middleware in (fault_middleware,
|
||||
request_log,
|
||||
context_middleware,
|
||||
auth_middleware,
|
||||
|
|
|
@ -22,11 +22,6 @@ import inspect
|
|||
import microversion_parse
|
||||
import webob
|
||||
|
||||
# NOTE(cdent): avoid cyclical import conflict between util and
|
||||
# microversion
|
||||
import nova.api.openstack.placement.util
|
||||
from nova.i18n import _
|
||||
|
||||
|
||||
SERVICE_TYPE = 'placement'
|
||||
MICROVERSION_ENVIRON = '%s.microversion' % SERVICE_TYPE
|
||||
|
@ -77,119 +72,6 @@ def min_version_string():
|
|||
return VERSIONS[0]
|
||||
|
||||
|
||||
def parse_version_string(version_string):
|
||||
"""Turn a version string into a Version
|
||||
|
||||
:param version_string: A string of two numerals, X.Y, or 'latest'
|
||||
:returns: a Version
|
||||
:raises: TypeError
|
||||
"""
|
||||
if version_string == 'latest':
|
||||
version_string = max_version_string()
|
||||
try:
|
||||
# The combination of int and a limited split with the
|
||||
# named tuple means that this incantation will raise
|
||||
# ValueError or TypeError when the incoming data is
|
||||
# poorly formed but will, however, naturally adapt to
|
||||
# extraneous whitespace.
|
||||
return Version(*(int(value) for value
|
||||
in version_string.split('.', 1)))
|
||||
except (ValueError, TypeError) as exc:
|
||||
raise TypeError(
|
||||
_('invalid version string: %(version_string)s; %(exc)s') %
|
||||
{'version_string': version_string, 'exc': exc})
|
||||
|
||||
|
||||
class MicroversionMiddleware(object):
|
||||
"""WSGI middleware for getting microversion info."""
|
||||
|
||||
def __init__(self, application):
|
||||
self.application = application
|
||||
|
||||
@webob.dec.wsgify
|
||||
def __call__(self, req):
|
||||
util = nova.api.openstack.placement.util
|
||||
try:
|
||||
microversion = extract_version(req.headers)
|
||||
except ValueError as exc:
|
||||
raise webob.exc.HTTPNotAcceptable(
|
||||
_('Invalid microversion: %(error)s') % {'error': exc},
|
||||
json_formatter=util.json_error_formatter)
|
||||
except TypeError as exc:
|
||||
raise webob.exc.HTTPBadRequest(
|
||||
_('Invalid microversion: %(error)s') % {'error': exc},
|
||||
json_formatter=util.json_error_formatter)
|
||||
|
||||
req.environ[MICROVERSION_ENVIRON] = microversion
|
||||
microversion_header = '%s %s' % (SERVICE_TYPE, microversion)
|
||||
|
||||
try:
|
||||
response = req.get_response(self.application)
|
||||
except webob.exc.HTTPError as exc:
|
||||
# If there was an error in the application we still need
|
||||
# to send the microversion header, so add the header and
|
||||
# re-raise the exception.
|
||||
exc.headers.add(Version.HEADER, microversion_header)
|
||||
raise exc
|
||||
|
||||
response.headers.add(Version.HEADER, microversion_header)
|
||||
response.headers.add('vary', Version.HEADER)
|
||||
return response
|
||||
|
||||
|
||||
class Version(collections.namedtuple('Version', 'major minor')):
|
||||
"""A namedtuple containing major and minor values.
|
||||
|
||||
Since it is a tuple is automatically comparable.
|
||||
"""
|
||||
|
||||
HEADER = 'OpenStack-API-Version'
|
||||
|
||||
MIN_VERSION = None
|
||||
MAX_VERSION = None
|
||||
|
||||
def __str__(self):
|
||||
return '%s.%s' % (self.major, self.minor)
|
||||
|
||||
@property
|
||||
def max_version(self):
|
||||
if not self.MAX_VERSION:
|
||||
self.MAX_VERSION = parse_version_string(max_version_string())
|
||||
return self.MAX_VERSION
|
||||
|
||||
@property
|
||||
def min_version(self):
|
||||
if not self.MIN_VERSION:
|
||||
self.MIN_VERSION = parse_version_string(min_version_string())
|
||||
return self.MIN_VERSION
|
||||
|
||||
def matches(self, min_version=None, max_version=None):
|
||||
if min_version is None:
|
||||
min_version = self.min_version
|
||||
if max_version is None:
|
||||
max_version = self.max_version
|
||||
return min_version <= self <= max_version
|
||||
|
||||
|
||||
def extract_version(headers):
|
||||
"""Extract the microversion from Version.HEADER
|
||||
|
||||
There may be multiple headers and some which don't match our
|
||||
service.
|
||||
"""
|
||||
found_version = microversion_parse.get_version(headers,
|
||||
service_type=SERVICE_TYPE)
|
||||
|
||||
version_string = found_version or min_version_string()
|
||||
request_version = parse_version_string(version_string)
|
||||
# We need a version that is in VERSION and within MIX and MAX.
|
||||
# This gives us the option to administratively disable a
|
||||
# version if we really need to.
|
||||
if (str(request_version) in VERSIONS and request_version.matches()):
|
||||
return request_version
|
||||
raise ValueError(_('Unacceptable version header: %s') % version_string)
|
||||
|
||||
|
||||
# From twisted
|
||||
# https://github.com/twisted/twisted/blob/trunk/twisted/python/deprecate.py
|
||||
def _fully_qualified_name(obj):
|
||||
|
@ -253,11 +135,12 @@ def version_handler(min_ver, max_ver=None, status_code=404):
|
|||
:param status_code: A status code to indicate error, 404 by default
|
||||
"""
|
||||
def decorator(f):
|
||||
min_version = parse_version_string(min_ver)
|
||||
min_version = microversion_parse.parse_version_string(min_ver)
|
||||
if max_ver:
|
||||
max_version = parse_version_string(max_ver)
|
||||
max_version = microversion_parse.parse_version_string(max_ver)
|
||||
else:
|
||||
max_version = parse_version_string(max_version_string())
|
||||
max_version = microversion_parse.parse_version_string(
|
||||
max_version_string())
|
||||
qualified_name = _fully_qualified_name(f)
|
||||
VERSIONED_METHODS[qualified_name].append(
|
||||
(min_version, max_version, f))
|
||||
|
|
|
@ -13,7 +13,7 @@ tests:
|
|||
- name: root has microversion header
|
||||
GET: /
|
||||
response_headers:
|
||||
vary: /OpenStack-API-Version/
|
||||
vary: /openstack-api-version/
|
||||
openstack-api-version: /^placement \d+\.\d+$/
|
||||
|
||||
- name: root has microversion info
|
||||
|
@ -44,7 +44,7 @@ tests:
|
|||
request_headers:
|
||||
openstack-api-version: placement latest
|
||||
response_headers:
|
||||
vary: /OpenStack-API-Version/
|
||||
vary: /openstack-api-version/
|
||||
openstack-api-version: placement 1.21
|
||||
|
||||
- name: other accept header bad version
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
# under the License.
|
||||
"""Unit tests for the functions used by the placement API handlers."""
|
||||
|
||||
import microversion_parse
|
||||
import mock
|
||||
import routes
|
||||
import webob
|
||||
|
@ -38,7 +39,7 @@ def _environ(path='/moo', method='GET'):
|
|||
'wsgi.url_scheme': 'http',
|
||||
# The microversion version value is not used, but it
|
||||
# needs to be set to avoid a KeyError.
|
||||
microversion.MICROVERSION_ENVIRON: microversion.Version(1, 12),
|
||||
microversion.MICROVERSION_ENVIRON: microversion_parse.Version(1, 12),
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ import collections
|
|||
import operator
|
||||
import webob
|
||||
|
||||
import microversion_parse
|
||||
import mock
|
||||
|
||||
# import the handlers to load up handler decorators
|
||||
|
@ -54,13 +55,16 @@ class TestMicroversionDecoration(test.NoDBTestCase):
|
|||
|
||||
stored_method_data = methods_data[-1]
|
||||
self.assertEqual(2, len(methods_data))
|
||||
self.assertEqual(microversion.Version(1, 1), stored_method_data[0])
|
||||
self.assertEqual(microversion.Version(1, 10), stored_method_data[1])
|
||||
self.assertEqual(microversion_parse.Version(1, 1),
|
||||
stored_method_data[0])
|
||||
self.assertEqual(microversion_parse.Version(1, 10),
|
||||
stored_method_data[1])
|
||||
self.assertEqual(handler, stored_method_data[2])
|
||||
self.assertEqual(microversion.Version(2, 0), methods_data[0][0])
|
||||
self.assertEqual(microversion_parse.Version(2, 0),
|
||||
methods_data[0][0])
|
||||
|
||||
def test_version_handler_float_exception(self):
|
||||
self.assertRaises(AttributeError,
|
||||
self.assertRaises(TypeError,
|
||||
microversion.version_handler(1.1),
|
||||
handler)
|
||||
|
||||
|
@ -70,7 +74,7 @@ class TestMicroversionDecoration(test.NoDBTestCase):
|
|||
handler)
|
||||
|
||||
def test_version_handler_tuple_exception(self):
|
||||
self.assertRaises(AttributeError,
|
||||
self.assertRaises(TypeError,
|
||||
microversion.version_handler((1, 1)),
|
||||
handler)
|
||||
|
||||
|
@ -140,7 +144,7 @@ class MicroversionSequentialTest(test.NoDBTestCase):
|
|||
for method_name, method_list in microversion.VERSIONED_METHODS.items():
|
||||
previous_min_version = method_list[0][0]
|
||||
for method in method_list[1:]:
|
||||
previous_min_version = microversion.parse_version_string(
|
||||
previous_min_version = microversion_parse.parse_version_string(
|
||||
'%s.%s' % (previous_min_version.major,
|
||||
previous_min_version.minor - 1))
|
||||
self.assertEqual(previous_min_version, method[1],
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
import datetime
|
||||
|
||||
import fixtures
|
||||
import microversion_parse
|
||||
import mock
|
||||
from oslo_middleware import request_id
|
||||
from oslo_utils import timeutils
|
||||
|
@ -228,7 +229,7 @@ class TestJSONErrorFormatter(test.NoDBTestCase):
|
|||
# parsing was successful), no version info
|
||||
# required.
|
||||
status = '406 Not Acceptable'
|
||||
version_obj = microversion.parse_version_string('2.3')
|
||||
version_obj = microversion_parse.parse_version_string('2.3')
|
||||
self.environ[microversion.MICROVERSION_ENVIRON] = version_obj
|
||||
|
||||
result = util.json_error_formatter(
|
||||
|
|
|
@ -57,7 +57,7 @@ os-traits>=0.4.0 # Apache-2.0
|
|||
os-vif!=1.8.0,>=1.7.0 # Apache-2.0
|
||||
os-win>=3.0.0 # Apache-2.0
|
||||
castellan>=0.16.0 # Apache-2.0
|
||||
microversion-parse>=0.1.2 # Apache-2.0
|
||||
microversion-parse>=0.2.1 # Apache-2.0
|
||||
os-xenapi>=0.3.1 # Apache-2.0
|
||||
tooz>=1.58.0 # Apache-2.0
|
||||
cursive>=0.2.1 # Apache-2.0
|
||||
|
|
Loading…
Reference in New Issue