nova/doc/source/devref/api_microversions.rst

157 lines
5.1 KiB
ReStructuredText
Raw Normal View History

API Microversions
=================
Background
----------
Nova uses a framework we call 'API Microversions' for allowing changes
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``.
If a user makes a request without specifying a version, they will get
the ``DEFAULT_API_VERSION`` as defined in
``nova/api/openstack/wsgi.py``. This value is currently ``2.1`` and
is expected to remain so for quite a long time.
There is a special value ``latest`` which can be specified, which will
allow a client to always recieve the most recent version of API
responses from the server.
For full details please read the `Kilo spec for microversions
<http://git.openstack.org/cgit/openstack/nova-specs/tree/specs/kilo/approved/api-microversions.rst>`_
In Code
-------
In ``nova/api/openstack/wsgi.py`` we define an ``@api_version`` decorator
which is intended to be used on top-level Controller methods. It is
not appropriate for lower-level methods. Some examples:
Adding a new API method
~~~~~~~~~~~~~~~~~~~~~~~
In the controller class::
@wsgi.Controller.api_version("2.4")
def my_api_method(self, req, id):
....
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
lower version (or not specified it and received the default of ``2.1``)
the server would respond with ``HTTP/404``.
Removing an API method
~~~~~~~~~~~~~~~~~~~~~~
In the controller class::
@wsgi.Controller.api_version("2.1", "2.4")
def my_api_method(self, req, id):
....
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
is specified the server will respond with ``HTTP/404``.
Changing a method's behaviour
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In the controller class::
@wsgi.Controller.api_version("2.1", "2.3")
def my_api_method(self, req, id):
.... method_1 ...
@wsgi.Controller.api_version("2.4") #noqa
def my_api_method(self, req, id):
.... method_2 ...
If a caller specified ``2.1``, ``2.2`` or ``2.3`` (or received the
default of ``2.1``) they would see the result from ``method_1``,
``2.4`` or later ``method_2``.
It is vital that the two methods have the same name, so the second of
them will need ``#noqa`` to avoid failing flake8's ``F811`` rule. The
two methods may be different in any kind of semantics (schema
validation, return values, response codes, etc)
A method with only small changes between versions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A method may have only small changes between microversions, in which
case you can decorate a private method::
@api_version("2.1", "2.4")
def _version_specific_func(self, req, arg1):
pass
@api_version(min_version="2.5") #noqa
def _version_specific_func(self, req, arg1):
pass
def show(self, req, id):
.... common stuff ....
self._version_specific_func(req, "foo")
.... common stuff ....
A change in schema only
~~~~~~~~~~~~~~~~~~~~~~~
If there is no change to the method, only to the schema that is used for
validation, you can add a version range to the ``validation.schema``
decorator::
@wsgi.Controller.api_version("2.1")
@validation.schema(dummy_schema.dummy, "2.3", "2.8")
@validation.schema(dummy_schema.dummy2, "2.9")
def update(self, req, id, body):
....
This method will be available from version ``2.1``, validated according to
``dummy_schema.dummy`` from ``2.3`` to ``2.8``, and validated according to
``dummy_schema.dummy2`` from ``2.9`` onward.
Other necessary changes
-----------------------
If you are adding a patch which adds a new microversion, it is
necessary to add changes to other places which describe your change:
* Update ``REST_API_VERSION_HISTORY`` in
``nova/api/openstack/api_version_request.py``
* Update ``_MAX_API_VERSION`` in
``nova/api/openstack/api_version_request.py``
* Add a verbose description to
``nova/api/openstack/rest_api_version_history.rst``. There should
be enough information that it could be used by the docs team for
release notes.
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``
header, for example::
req = fakes.HTTPRequest.blank('/testable/url/endpoint')
req.headers = {'X-OpenStack-Nova-API-Version': '2.2'}
req.api_version_request = api_version.APIVersionRequest('2.6')
controller = controller.TestableController()
res = controller.index(req)
... assertions about the response ...
For many examples of testing, the canonical examples are in
``nova/tests/unit/api/openstack/compute/test_microversions.py``.