Merge "Support for both microversion headers"
This commit is contained in:
commit
dbf9bf0703
@ -107,17 +107,28 @@ HTTP header::
|
||||
|
||||
X-OpenStack-Nova-API-Version: 2.4
|
||||
|
||||
Starting with microversion `2.27` it is also correct to use the
|
||||
following header to specify the microversion::
|
||||
|
||||
OpenStack-API-Version: compute 2.27
|
||||
|
||||
.. note:: For more detail on this newer form see the `Microversion Specification
|
||||
<http://specs.openstack.org/openstack/api-wg/guidelines/microversion_specification.html>`_.
|
||||
|
||||
This acts conceptually like the "Accept" header. Semantically this means:
|
||||
|
||||
* If `X-OpenStack-Nova-API-Version` is not provided, act as if the minimum
|
||||
supported microversion was specified.
|
||||
* If neither `X-OpenStack-Nova-API-Version` nor `OpenStack-API-Version`
|
||||
(specifying `compute`) is provided, act as if the minimum supported
|
||||
microversion was specified.
|
||||
|
||||
* If `X-OpenStack-Nova-API-Version` is provided, respond with the API at
|
||||
that microversion. If that's outside of the range of microversions supported,
|
||||
return 406 Not Acceptable.
|
||||
* If both headers are provided, `OpenStack-API-Version` will be preferred.
|
||||
|
||||
* If `X-OpenStack-Nova-API-Version` is ``latest`` (special keyword), act as
|
||||
if maximum was specified.
|
||||
* If `X-OpenStack-Nova-API-Version` or `OpenStack-API-Version` is provided,
|
||||
respond with the API at that microversion. If that's outside of the range
|
||||
of microversions supported, return 406 Not Acceptable.
|
||||
|
||||
* If `X-OpenStack-Nova-API-Version` or `OpenStack-API-Version` has a value
|
||||
of ``latest`` (special keyword), act as if maximum was specified.
|
||||
|
||||
.. warning:: The ``latest`` value is mostly meant for integration testing and
|
||||
would be dangerous to rely on in client code since microversions are not
|
||||
@ -129,14 +140,21 @@ This means that out of the box, an old client without any knowledge of
|
||||
microversions can work with an OpenStack installation with microversions
|
||||
support.
|
||||
|
||||
Two extra headers are always returned in the response:
|
||||
In microversions prior to `2.27` two extra headers are always returned in
|
||||
the response::
|
||||
|
||||
* X-OpenStack-Nova-API-Version: microversion_number
|
||||
* Vary: X-OpenStack-Nova-API-Version
|
||||
X-OpenStack-Nova-API-Version: microversion_number
|
||||
Vary: X-OpenStack-Nova-API-Version
|
||||
|
||||
The first header specifies the microversion number of the API which was
|
||||
executed.
|
||||
|
||||
The second header is used as a hint to caching proxies that the response
|
||||
is also dependent on the X-OpenStack-Nova-API-Version and not just
|
||||
the body and query parameters. See :rfc:`2616` section 14.44 for details.
|
||||
The `Vary` header is used as a hint to caching proxies that the response
|
||||
is also dependent on the microversion and not just the body and query
|
||||
parameters. See :rfc:`2616` section 14.44 for details.
|
||||
|
||||
From microversion `2.27` two additional headers are added to the
|
||||
response::
|
||||
|
||||
OpenStack-API-Version: compute microversion_number
|
||||
Vary: OpenStack-API-Version
|
||||
|
@ -12,7 +12,8 @@ supports versioning. There are two kinds of versions in Nova.
|
||||
|
||||
- ''major versions'', which have dedicated urls
|
||||
- ''microversions'', which can be requested through the use of the
|
||||
``X-OpenStack-Nova-API-Version`` header
|
||||
``X-OpenStack-Nova-API-Version`` header or since microversion 2.27
|
||||
the ``OpenStack-API-Version`` header may also be used.
|
||||
|
||||
For more detail about Microversion, please reference:
|
||||
`Microversions
|
||||
|
@ -19,7 +19,7 @@
|
||||
}
|
||||
],
|
||||
"status": "CURRENT",
|
||||
"version": "2.26",
|
||||
"version": "2.27",
|
||||
"min_version": "2.1",
|
||||
"updated": "2013-07-23T11:33:21Z"
|
||||
}
|
||||
|
@ -22,7 +22,7 @@
|
||||
}
|
||||
],
|
||||
"status": "CURRENT",
|
||||
"version": "2.26",
|
||||
"version": "2.27",
|
||||
"min_version": "2.1",
|
||||
"updated": "2013-07-23T11:33:21Z"
|
||||
}
|
||||
|
@ -9,9 +9,12 @@ to the API while preserving backward compatibility. The basic idea is
|
||||
that a user has to explicitly ask for their request to be treated with
|
||||
a particular version of the API. So breaking changes can be added to
|
||||
the API without breaking users who don't specifically ask for it. This
|
||||
is done with an HTTP header ``X-OpenStack-Nova-API-Version`` which
|
||||
is a monotonically increasing semantic version number starting from
|
||||
``2.1``.
|
||||
is done with an HTTP header ``OpenStack-API-Version`` which has as its
|
||||
value a string containing the name of the service, ``compute``, and a
|
||||
monotonically increasing semantic version number starting from ``2.1``.
|
||||
The full form of the header takes the form::
|
||||
|
||||
OpenStack-API-Version: compute 2.1
|
||||
|
||||
If a user makes a request without specifying a version, they will get
|
||||
the ``DEFAULT_API_VERSION`` as defined in
|
||||
@ -29,8 +32,21 @@ responses from the server.
|
||||
microversion but limit what is acceptable to the version range that it
|
||||
understands at the time.
|
||||
|
||||
.. warning:: To maintain compatibility, an earlier form of the microversion
|
||||
header is acceptable. It takes the form::
|
||||
|
||||
X-OpenStack-Nova-API-Version: 2.1
|
||||
|
||||
This form will continue to be supported until the ``DEFAULT_API_VERSION``
|
||||
is raised to version ``2.27`` or higher.
|
||||
|
||||
Clients accessing deployments of the Nova API which are not yet
|
||||
providing microversion ``2.27`` must use the older form.
|
||||
|
||||
For full details please read the `Kilo spec for microversions
|
||||
<http://git.openstack.org/cgit/openstack/nova-specs/tree/specs/kilo/implemented/api-microversions.rst>`_
|
||||
and `Microversion Specification
|
||||
<http://specs.openstack.org/openstack/api-wg/guidelines/microversion_specification.html>`_.
|
||||
|
||||
When do I need a new Microversion?
|
||||
----------------------------------
|
||||
@ -217,7 +233,7 @@ In the controller class::
|
||||
....
|
||||
|
||||
This method would only be available if the caller had specified an
|
||||
``X-OpenStack-Nova-API-Version`` of >= ``2.4``. If they had specified a
|
||||
``OpenStack-API-Version`` of >= ``2.4``. If they had specified a
|
||||
lower version (or not specified it and received the default of ``2.1``)
|
||||
the server would respond with ``HTTP/404``.
|
||||
|
||||
@ -231,7 +247,7 @@ In the controller class::
|
||||
....
|
||||
|
||||
This method would only be available if the caller had specified an
|
||||
``X-OpenStack-Nova-API-Version`` of <= ``2.4``. If ``2.5`` or later
|
||||
``OpenStack-API-Version`` of <= ``2.4``. If ``2.5`` or later
|
||||
is specified the server will respond with ``HTTP/404``.
|
||||
|
||||
Changing a method's behavior
|
||||
@ -333,9 +349,9 @@ necessary to add changes to other places which describe your change:
|
||||
* Update the expected versions in affected tests, for example in
|
||||
``nova/tests/unit/api/openstack/compute/test_versions.py``.
|
||||
|
||||
* Update the get versions api sample files:
|
||||
* Update the get versions api sample file:
|
||||
``doc/api_samples/versions/versions-get-resp.json`` and
|
||||
``nova/tests/functional/api_samples/versions/versions-get-resp.json.tpl``.
|
||||
``doc/api_samples/versions/v21-version-get-resp.json``.
|
||||
|
||||
* Make a new commit to python-novaclient and update corresponding
|
||||
files to enable the newly added microversion API.
|
||||
@ -361,11 +377,11 @@ Testing Microversioned API Methods
|
||||
----------------------------------
|
||||
|
||||
Testing a microversioned API method is very similar to a normal controller
|
||||
method test, you just need to add the ``X-OpenStack-Nova-API-Version``
|
||||
method test, you just need to add the ``OpenStack-API-Version``
|
||||
header, for example::
|
||||
|
||||
req = fakes.HTTPRequest.blank('/testable/url/endpoint')
|
||||
req.headers = {'X-OpenStack-Nova-API-Version': '2.2'}
|
||||
req.headers = {'OpenStack-API-Version': 'compute 2.28'}
|
||||
req.api_version_request = api_version.APIVersionRequest('2.6')
|
||||
|
||||
controller = controller.TestableController()
|
||||
|
@ -113,11 +113,14 @@ class LegacyV2CompatibleWrapper(base_wsgi.Middleware):
|
||||
|
||||
def _filter_request_headers(self, req):
|
||||
"""For keeping same behavior with v2 API, ignores microversions
|
||||
HTTP header X-OpenStack-Nova-API-Version in the request.
|
||||
HTTP headers X-OpenStack-Nova-API-Version and OpenStack-API-Version
|
||||
in the request.
|
||||
"""
|
||||
|
||||
if wsgi.API_VERSION_REQUEST_HEADER in req.headers:
|
||||
del req.headers[wsgi.API_VERSION_REQUEST_HEADER]
|
||||
if wsgi.LEGACY_API_VERSION_REQUEST_HEADER in req.headers:
|
||||
del req.headers[wsgi.LEGACY_API_VERSION_REQUEST_HEADER]
|
||||
return req
|
||||
|
||||
def _filter_response_headers(self, response):
|
||||
@ -127,13 +130,16 @@ class LegacyV2CompatibleWrapper(base_wsgi.Middleware):
|
||||
|
||||
if wsgi.API_VERSION_REQUEST_HEADER in response.headers:
|
||||
del response.headers[wsgi.API_VERSION_REQUEST_HEADER]
|
||||
if wsgi.LEGACY_API_VERSION_REQUEST_HEADER in response.headers:
|
||||
del response.headers[wsgi.LEGACY_API_VERSION_REQUEST_HEADER]
|
||||
|
||||
if 'Vary' in response.headers:
|
||||
vary_headers = response.headers['Vary'].split(',')
|
||||
filtered_vary = []
|
||||
for vary in vary_headers:
|
||||
vary = vary.strip()
|
||||
if vary == wsgi.API_VERSION_REQUEST_HEADER:
|
||||
if (vary == wsgi.API_VERSION_REQUEST_HEADER or
|
||||
vary == wsgi.LEGACY_API_VERSION_REQUEST_HEADER):
|
||||
continue
|
||||
filtered_vary.append(vary)
|
||||
if filtered_vary:
|
||||
|
@ -72,6 +72,8 @@ REST_API_VERSION_HISTORY = """REST API Version History:
|
||||
* 2.25 - Make block_migration support 'auto' and remove
|
||||
disk_over_commit for os-migrateLive.
|
||||
* 2.26 - Adds support of server tags
|
||||
* 2.27 - Adds support for new-style microversion headers while
|
||||
keeping support for the original style.
|
||||
"""
|
||||
|
||||
# The minimum and maximum versions of the API supported
|
||||
@ -80,7 +82,7 @@ REST_API_VERSION_HISTORY = """REST API Version History:
|
||||
# Note(cyeoh): This only applies for the v2.1 API once microversions
|
||||
# support is fully merged. It does not affect the V2 API.
|
||||
_MIN_API_VERSION = "2.1"
|
||||
_MAX_API_VERSION = "2.26"
|
||||
_MAX_API_VERSION = "2.27"
|
||||
DEFAULT_API_VERSION = _MIN_API_VERSION
|
||||
|
||||
|
||||
|
@ -283,3 +283,11 @@ user documentation.
|
||||
These filters can be combined. Also user can use more than one string tags
|
||||
for each filter. In this case string tags for each filter must be separated
|
||||
by comma: GET /servers?tags=red&tags-any=green,orange
|
||||
|
||||
2.27
|
||||
----
|
||||
|
||||
Added support for the new form of microversion headers described in the
|
||||
`Microversion Specification
|
||||
<http://specs.openstack.org/openstack/api-wg/guidelines/microversion_specification.html>`_.
|
||||
Both the original form of header and the new form is supported.
|
||||
|
@ -71,9 +71,10 @@ DEFAULT_API_VERSION = "2.1"
|
||||
# name of attribute to keep version method information
|
||||
VER_METHOD_ATTR = 'versioned_methods'
|
||||
|
||||
# Name of header used by clients to request a specific version
|
||||
# Names of headers used by clients to request a specific version
|
||||
# of the REST API
|
||||
API_VERSION_REQUEST_HEADER = 'X-OpenStack-Nova-API-Version'
|
||||
API_VERSION_REQUEST_HEADER = 'OpenStack-API-Version'
|
||||
LEGACY_API_VERSION_REQUEST_HEADER = 'X-OpenStack-Nova-API-Version'
|
||||
|
||||
|
||||
ENV_LEGACY_V2 = 'openstack.legacy_v2'
|
||||
@ -230,7 +231,7 @@ class Request(wsgi.Request):
|
||||
"""Set API version request based on the request header information."""
|
||||
hdr_string = microversion_parse.get_version(
|
||||
self.headers, service_type='compute',
|
||||
legacy_headers=[API_VERSION_REQUEST_HEADER])
|
||||
legacy_headers=[LEGACY_API_VERSION_REQUEST_HEADER])
|
||||
|
||||
if hdr_string is None:
|
||||
self.api_version_request = api_version.APIVersionRequest(
|
||||
@ -767,8 +768,11 @@ class Resource(wsgi.Application):
|
||||
|
||||
if not request.api_version_request.is_null():
|
||||
response.headers[API_VERSION_REQUEST_HEADER] = \
|
||||
'compute ' + request.api_version_request.get_string()
|
||||
response.headers[LEGACY_API_VERSION_REQUEST_HEADER] = \
|
||||
request.api_version_request.get_string()
|
||||
response.headers['Vary'] = API_VERSION_REQUEST_HEADER
|
||||
response.headers.add('Vary', API_VERSION_REQUEST_HEADER)
|
||||
response.headers.add('Vary', LEGACY_API_VERSION_REQUEST_HEADER)
|
||||
|
||||
return response
|
||||
|
||||
@ -1121,9 +1125,12 @@ class Fault(webob.exc.HTTPException):
|
||||
|
||||
if not req.api_version_request.is_null():
|
||||
self.wrapped_exc.headers[API_VERSION_REQUEST_HEADER] = \
|
||||
'compute ' + req.api_version_request.get_string()
|
||||
self.wrapped_exc.headers[LEGACY_API_VERSION_REQUEST_HEADER] = \
|
||||
req.api_version_request.get_string()
|
||||
self.wrapped_exc.headers['Vary'] = \
|
||||
API_VERSION_REQUEST_HEADER
|
||||
self.wrapped_exc.headers.add('Vary', API_VERSION_REQUEST_HEADER)
|
||||
self.wrapped_exc.headers.add('Vary',
|
||||
LEGACY_API_VERSION_REQUEST_HEADER)
|
||||
|
||||
self.wrapped_exc.content_type = 'application/json'
|
||||
self.wrapped_exc.charset = 'UTF-8'
|
||||
|
@ -171,11 +171,13 @@ class TestOpenStackClient(object):
|
||||
|
||||
headers = kwargs.setdefault('headers', {})
|
||||
headers['X-Auth-Token'] = auth_result['x-auth-token']
|
||||
if 'X-OpenStack-Nova-API-Version' in headers:
|
||||
raise Exception('X-OpenStack-Nova-API-Version should be set on '
|
||||
if ('X-OpenStack-Nova-API-Version' in headers or
|
||||
'OpenStack-API-Version' in headers):
|
||||
raise Exception('Microversion should be set via '
|
||||
'microversion attribute in API client.')
|
||||
elif self.microversion:
|
||||
headers['X-OpenStack-Nova-API-Version'] = self.microversion
|
||||
headers['OpenStack-API-Version'] = 'compute %s' % self.microversion
|
||||
|
||||
response = self.request(full_uri, **kwargs)
|
||||
|
||||
|
@ -34,6 +34,8 @@ class LegacyV2CompatibleTestBase(test_servers.ServersTestBase):
|
||||
response = self.api.api_post('os-keypairs',
|
||||
{"keypair": {"name": "test"}})
|
||||
self.assertNotIn(wsgi.API_VERSION_REQUEST_HEADER, response.headers)
|
||||
self.assertNotIn(wsgi.LEGACY_API_VERSION_REQUEST_HEADER,
|
||||
response.headers)
|
||||
self.assertNotIn('Vary', response.headers)
|
||||
self.assertNotIn('type', response.body["keypair"])
|
||||
|
||||
@ -42,6 +44,8 @@ class LegacyV2CompatibleTestBase(test_servers.ServersTestBase):
|
||||
response = self.api.api_post('os-keypairs',
|
||||
{"keypair": {"name": "test", "foooooo": "barrrrrr"}})
|
||||
self.assertNotIn(wsgi.API_VERSION_REQUEST_HEADER, response.headers)
|
||||
self.assertNotIn(wsgi.LEGACY_API_VERSION_REQUEST_HEADER,
|
||||
response.headers)
|
||||
self.assertNotIn('Vary', response.headers)
|
||||
self.assertNotIn('type', response.body["keypair"])
|
||||
|
||||
|
@ -89,7 +89,7 @@ class ExtendedServerAttributesTestV21(test.TestCase):
|
||||
req = fakes.HTTPRequest.blank(url)
|
||||
req.headers['Accept'] = self.content_type
|
||||
req.headers = {os_wsgi.API_VERSION_REQUEST_HEADER:
|
||||
self.wsgi_api_version}
|
||||
'compute %s' % self.wsgi_api_version}
|
||||
res = req.get_response(
|
||||
fakes.wsgi_app_v21(init_only=('servers',
|
||||
'os-extended-server-attributes')))
|
||||
|
@ -117,7 +117,7 @@ class ExtendedVolumesTestV21(test.TestCase):
|
||||
req = webob.Request.blank('/v2/fake/servers' + url)
|
||||
req.headers['Accept'] = self.content_type
|
||||
req.headers = {os_wsgi.API_VERSION_REQUEST_HEADER:
|
||||
self.wsgi_api_version}
|
||||
'compute %s' % self.wsgi_api_version}
|
||||
if body:
|
||||
req.body = jsonutils.dump_as_bytes(body)
|
||||
req.method = 'POST'
|
||||
|
@ -20,7 +20,7 @@ from nova import test
|
||||
from nova.tests.unit.api.openstack import fakes
|
||||
|
||||
|
||||
class MicroversionsTest(test.NoDBTestCase):
|
||||
class LegacyMicroversionsTest(test.NoDBTestCase):
|
||||
|
||||
header_name = 'X-OpenStack-Nova-API-Version'
|
||||
|
||||
@ -30,10 +30,19 @@ class MicroversionsTest(test.NoDBTestCase):
|
||||
res = req.get_response(app)
|
||||
self.assertEqual(ret_code, res.status_int)
|
||||
if ret_header:
|
||||
if 'nova' not in self.header_name.lower():
|
||||
ret_header = 'compute %s' % ret_header
|
||||
self.assertEqual(ret_header,
|
||||
res.headers[self.header_name])
|
||||
return res
|
||||
|
||||
def _make_header(self, req_header):
|
||||
if 'nova' in self.header_name.lower():
|
||||
headers = {self.header_name: req_header}
|
||||
else:
|
||||
headers = {self.header_name: 'compute %s' % req_header}
|
||||
return headers
|
||||
|
||||
@mock.patch("nova.api.openstack.APIRouterV21.api_extension_namespace",
|
||||
return_value='nova.api.v21.test_extensions')
|
||||
def test_microversions_no_header(self, mock_namespace):
|
||||
@ -53,8 +62,11 @@ class MicroversionsTest(test.NoDBTestCase):
|
||||
self.assertEqual(200, res.status_int)
|
||||
resp_json = jsonutils.loads(res.body)
|
||||
self.assertEqual('val', resp_json['param'])
|
||||
self.assertEqual("2.1", res.headers[self.header_name])
|
||||
self.assertEqual(self.header_name, res.headers['Vary'])
|
||||
if 'nova' in self.header_name.lower():
|
||||
self.assertEqual("2.1", res.headers[self.header_name])
|
||||
else:
|
||||
self.assertEqual("compute 2.1", res.headers[self.header_name])
|
||||
self.assertIn(self.header_name, res.headers.getall('Vary'))
|
||||
|
||||
@mock.patch("nova.api.openstack.api_version_request.max_api_version")
|
||||
@mock.patch("nova.api.openstack.APIRouterV21.api_extension_namespace",
|
||||
@ -65,13 +77,16 @@ class MicroversionsTest(test.NoDBTestCase):
|
||||
|
||||
app = fakes.wsgi_app_v21(init_only='test-microversions')
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/microversions')
|
||||
req.headers = {self.header_name: '2.3'}
|
||||
req.headers = self._make_header('2.3')
|
||||
res = req.get_response(app)
|
||||
self.assertEqual(200, res.status_int)
|
||||
resp_json = jsonutils.loads(res.body)
|
||||
self.assertEqual('val2', resp_json['param'])
|
||||
self.assertEqual("2.3", res.headers[self.header_name])
|
||||
self.assertEqual(self.header_name, res.headers['Vary'])
|
||||
if 'nova' in self.header_name.lower():
|
||||
self.assertEqual("2.3", res.headers[self.header_name])
|
||||
else:
|
||||
self.assertEqual("compute 2.3", res.headers[self.header_name])
|
||||
self.assertIn(self.header_name, res.headers.getall('Vary'))
|
||||
|
||||
@mock.patch("nova.api.openstack.api_version_request.max_api_version")
|
||||
@mock.patch("nova.api.openstack.APIRouterV21.api_extension_namespace",
|
||||
@ -82,11 +97,14 @@ class MicroversionsTest(test.NoDBTestCase):
|
||||
|
||||
app = fakes.wsgi_app_v21(init_only='test-microversions')
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/microversions')
|
||||
req.headers = {self.header_name: '3.0'}
|
||||
req.headers = self._make_header('3.0')
|
||||
res = req.get_response(app)
|
||||
self.assertEqual(400, res.status_int)
|
||||
self.assertEqual("3.0", res.headers[self.header_name])
|
||||
self.assertEqual(self.header_name, res.headers['Vary'])
|
||||
if 'nova' in self.header_name.lower():
|
||||
self.assertEqual("3.0", res.headers[self.header_name])
|
||||
else:
|
||||
self.assertEqual("compute 3.0", res.headers[self.header_name])
|
||||
self.assertIn(self.header_name, res.headers.getall('Vary'))
|
||||
|
||||
@mock.patch("nova.api.openstack.api_version_request.max_api_version")
|
||||
@mock.patch("nova.api.openstack.APIRouterV21.api_extension_namespace",
|
||||
@ -97,7 +115,7 @@ class MicroversionsTest(test.NoDBTestCase):
|
||||
|
||||
app = fakes.wsgi_app_v21(init_only='test-microversions')
|
||||
req = fakes.HTTPRequest.blank(url)
|
||||
req.headers = {self.header_name: req_version}
|
||||
req.headers = self._make_header(req_version)
|
||||
res = req.get_response(app)
|
||||
self.assertEqual(200, res.status_int)
|
||||
resp_json = jsonutils.loads(res.body)
|
||||
@ -123,7 +141,7 @@ class MicroversionsTest(test.NoDBTestCase):
|
||||
|
||||
app = fakes.wsgi_app_v21(init_only='test-microversions')
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/microversions2')
|
||||
req.headers = {self.header_name: '3.0'}
|
||||
req.headers = self._make_header('3.0')
|
||||
res = req.get_response(app)
|
||||
self.assertEqual(202, res.status_int)
|
||||
resp_json = jsonutils.loads(res.body)
|
||||
@ -160,7 +178,7 @@ class MicroversionsTest(test.NoDBTestCase):
|
||||
|
||||
app = fakes.wsgi_app_v21(init_only='test-microversions')
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/microversions2')
|
||||
req.headers = {self.header_name: '3.7'}
|
||||
req.headers = self._make_header('3.7')
|
||||
res = req.get_response(app)
|
||||
self.assertEqual(406, res.status_int)
|
||||
res_json = jsonutils.loads(res.body)
|
||||
@ -177,7 +195,7 @@ class MicroversionsTest(test.NoDBTestCase):
|
||||
app = fakes.wsgi_app_v21(init_only='test-microversions')
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/microversions3')
|
||||
req.method = 'POST'
|
||||
req.headers = {self.header_name: '2.2'}
|
||||
req.headers = self._make_header('2.2')
|
||||
req.environ['CONTENT_TYPE'] = "application/json"
|
||||
req.body = jsonutils.dump_as_bytes({'dummy': {'val': 'foo'}})
|
||||
|
||||
@ -185,8 +203,11 @@ class MicroversionsTest(test.NoDBTestCase):
|
||||
self.assertEqual(200, res.status_int)
|
||||
resp_json = jsonutils.loads(res.body)
|
||||
self.assertEqual('create_val1', resp_json['param'])
|
||||
self.assertEqual("2.2", res.headers[self.header_name])
|
||||
self.assertEqual(self.header_name, res.headers['Vary'])
|
||||
if 'nova' in self.header_name.lower():
|
||||
self.assertEqual("2.2", res.headers[self.header_name])
|
||||
else:
|
||||
self.assertEqual("compute 2.2", res.headers[self.header_name])
|
||||
self.assertIn(self.header_name, res.headers.getall('Vary'))
|
||||
|
||||
@mock.patch("nova.api.openstack.api_version_request.max_api_version")
|
||||
@mock.patch("nova.api.openstack.APIRouterV21.api_extension_namespace",
|
||||
@ -217,7 +238,7 @@ class MicroversionsTest(test.NoDBTestCase):
|
||||
app = fakes.wsgi_app_v21(init_only='test-microversions')
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/microversions3/1')
|
||||
req.method = 'PUT'
|
||||
req.headers = {self.header_name: '2.2'}
|
||||
req.headers = self._make_header('2.2')
|
||||
req.body = jsonutils.dump_as_bytes({'dummy': {'inv_val': 'foo'}})
|
||||
req.environ['CONTENT_TYPE'] = "application/json"
|
||||
|
||||
@ -225,7 +246,10 @@ class MicroversionsTest(test.NoDBTestCase):
|
||||
self.assertEqual(200, res.status_int)
|
||||
resp_json = jsonutils.loads(res.body)
|
||||
self.assertEqual('update_val1', resp_json['param'])
|
||||
self.assertEqual("2.2", res.headers[self.header_name])
|
||||
if 'nova' in self.header_name.lower():
|
||||
self.assertEqual("2.2", res.headers[self.header_name])
|
||||
else:
|
||||
self.assertEqual("compute 2.2", res.headers[self.header_name])
|
||||
|
||||
@mock.patch("nova.api.openstack.api_version_request.max_api_version")
|
||||
@mock.patch("nova.api.openstack.APIRouterV21.api_extension_namespace",
|
||||
@ -236,7 +260,7 @@ class MicroversionsTest(test.NoDBTestCase):
|
||||
|
||||
app = fakes.wsgi_app_v21(init_only='test-microversions')
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/microversions3/1')
|
||||
req.headers = {self.header_name: '2.10'}
|
||||
req.headers = self._make_header('2.10')
|
||||
req.environ['CONTENT_TYPE'] = "application/json"
|
||||
req.method = 'PUT'
|
||||
req.body = jsonutils.dump_as_bytes({'dummy': {'val2': 'foo'}})
|
||||
@ -245,7 +269,10 @@ class MicroversionsTest(test.NoDBTestCase):
|
||||
self.assertEqual(200, res.status_int)
|
||||
resp_json = jsonutils.loads(res.body)
|
||||
self.assertEqual('update_val1', resp_json['param'])
|
||||
self.assertEqual("2.10", res.headers[self.header_name])
|
||||
if 'nova' in self.header_name.lower():
|
||||
self.assertEqual("2.10", res.headers[self.header_name])
|
||||
else:
|
||||
self.assertEqual("compute 2.10", res.headers[self.header_name])
|
||||
|
||||
@mock.patch("nova.api.openstack.api_version_request.max_api_version")
|
||||
@mock.patch("nova.api.openstack.APIRouterV21.api_extension_namespace",
|
||||
@ -256,7 +283,7 @@ class MicroversionsTest(test.NoDBTestCase):
|
||||
mock_maxver.return_value = api_version.APIVersionRequest("2.2")
|
||||
app = fakes.wsgi_app_v21(init_only='test-microversions')
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/microversions4')
|
||||
req.headers = {self.header_name: version}
|
||||
req.headers = self._make_header(version)
|
||||
req.environ['CONTENT_TYPE'] = "application/json"
|
||||
req.method = 'POST'
|
||||
|
||||
@ -264,6 +291,8 @@ class MicroversionsTest(test.NoDBTestCase):
|
||||
self.assertEqual(200, res.status_int)
|
||||
resp_json = jsonutils.loads(res.body)
|
||||
self.assertEqual(expected_resp, resp_json['param'])
|
||||
if 'nova' not in self.header_name.lower():
|
||||
version = 'compute %s' % version
|
||||
self.assertEqual(version, res.headers[self.header_name])
|
||||
|
||||
def test_microversions_inner_function_v22(self):
|
||||
@ -306,7 +335,7 @@ class MicroversionsTest(test.NoDBTestCase):
|
||||
app = fakes.wsgi_app_v21(init_only='test-microversions')
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/microversions3/1/action')
|
||||
if req_header:
|
||||
req.headers = {self.header_name: req_header}
|
||||
req.headers = self._make_header(req_header)
|
||||
req.method = 'POST'
|
||||
req.body = jsonutils.dump_as_bytes({'foo': None})
|
||||
|
||||
@ -324,3 +353,8 @@ class MicroversionsTest(test.NoDBTestCase):
|
||||
|
||||
def test_microversions_actions_no_header(self):
|
||||
self._test_microversions_actions(202, "2.1", None)
|
||||
|
||||
|
||||
class MicroversionsTest(LegacyMicroversionsTest):
|
||||
|
||||
header_name = 'OpenStack-API-Version'
|
||||
|
@ -14,6 +14,7 @@ import inspect
|
||||
|
||||
import mock
|
||||
import six
|
||||
import testscenarios
|
||||
import webob
|
||||
|
||||
from nova.api.openstack import api_version_request as api_version
|
||||
@ -29,8 +30,25 @@ from nova.tests.unit import utils
|
||||
from oslo_serialization import jsonutils
|
||||
|
||||
|
||||
class RequestTest(test.NoDBTestCase):
|
||||
header_name = 'X-OpenStack-Nova-API-Version'
|
||||
class MicroversionedTest(testscenarios.WithScenarios, test.NoDBTestCase):
|
||||
|
||||
scenarios = [
|
||||
('legacy-microverison', {
|
||||
'header_name': 'X-OpenStack-Nova-API-Version',
|
||||
}),
|
||||
('modern-microversion', {
|
||||
'header_name': 'OpenStack-API-Version',
|
||||
})
|
||||
]
|
||||
|
||||
def _make_microversion_header(self, value):
|
||||
if 'nova' in self.header_name.lower():
|
||||
return {self.header_name: value}
|
||||
else:
|
||||
return {self.header_name: 'compute %s' % value}
|
||||
|
||||
|
||||
class RequestTest(MicroversionedTest):
|
||||
|
||||
def test_content_type_missing(self):
|
||||
request = wsgi.Request.blank('/tests/123', method='POST')
|
||||
@ -165,7 +183,7 @@ class RequestTest(test.NoDBTestCase):
|
||||
mock_maxver.return_value = api_version.APIVersionRequest("2.14")
|
||||
|
||||
request = wsgi.Request.blank('/')
|
||||
request.headers = {self.header_name: '2.14'}
|
||||
request.headers = self._make_microversion_header('2.14')
|
||||
request.set_api_version_request()
|
||||
self.assertEqual(api_version.APIVersionRequest("2.14"),
|
||||
request.api_version_request)
|
||||
@ -175,14 +193,14 @@ class RequestTest(test.NoDBTestCase):
|
||||
mock_maxver.return_value = api_version.APIVersionRequest("3.5")
|
||||
|
||||
request = wsgi.Request.blank('/')
|
||||
request.headers = {self.header_name: 'latest'}
|
||||
request.headers = self._make_microversion_header('latest')
|
||||
request.set_api_version_request()
|
||||
self.assertEqual(api_version.APIVersionRequest("3.5"),
|
||||
request.api_version_request)
|
||||
|
||||
def test_api_version_request_header_invalid(self):
|
||||
request = wsgi.Request.blank('/')
|
||||
request.headers = {self.header_name: '2.1.3'}
|
||||
request.headers = self._make_microversion_header('2.1.3')
|
||||
|
||||
self.assertRaises(exception.InvalidAPIVersionString,
|
||||
request.set_api_version_request)
|
||||
@ -269,8 +287,7 @@ class JSONDeserializerTest(test.NoDBTestCase):
|
||||
deserializer.deserialize, data)
|
||||
|
||||
|
||||
class ResourceTest(test.NoDBTestCase):
|
||||
header_name = 'X-OpenStack-Nova-API-Version'
|
||||
class ResourceTest(MicroversionedTest):
|
||||
|
||||
def get_req_id_header_name(self, request):
|
||||
header_name = 'x-openstack-request-id'
|
||||
@ -308,7 +325,7 @@ class ResourceTest(test.NoDBTestCase):
|
||||
|
||||
app = fakes.TestRouterV21(Controller())
|
||||
req = webob.Request.blank('/tests')
|
||||
req.headers = {self.header_name: version}
|
||||
req.headers = self._make_microversion_header(version)
|
||||
response = req.get_response(app)
|
||||
self.assertEqual(b'success', response.body)
|
||||
self.assertEqual(response.status_int, 200)
|
||||
@ -322,7 +339,7 @@ class ResourceTest(test.NoDBTestCase):
|
||||
|
||||
app = fakes.TestRouterV21(Controller())
|
||||
req = webob.Request.blank('/tests')
|
||||
req.headers = {self.header_name: invalid_version}
|
||||
req.headers = self._make_microversion_header(invalid_version)
|
||||
response = req.get_response(app)
|
||||
self.assertEqual(400, response.status_int)
|
||||
|
||||
|
@ -0,0 +1,7 @@
|
||||
---
|
||||
features:
|
||||
- >
|
||||
Microversions may now (with microversion 2.27) be requested with
|
||||
the "OpenStack-API-Version: compute 2.27" header, in alignment with
|
||||
OpenStack-wide standards. The original format,
|
||||
"X-OpenStack-Nova-API-Version: 2.27", may still be used.
|
Loading…
x
Reference in New Issue
Block a user