77a8e5d582
Provide a decorator that allows Placement API handlers to express a microversion window within which that method should be used. This makes it possible to define N different methods (with the same name) to present different behaviors dependent on what microversion is requested by the user-agent. If there is no match between a valid requested microversion and available versioned handlers a 404 response is returned. The mechanism for managing the versioned handlers is borrowed from the compute api but adjusted to allow for the fact that placement handlers are module level functions. Some code is borrowed from twisted to get fully qualified names of methods for the VERSION_METHODS dictionary. Tests are added which validate that versioned handlers do not intersect. In the rest of nova this is done at runtime, but in the placement context this isn't necessary. Doing it at testime is sufficient. Since there are currently no versioned handlers in addition to the faked tests of the intersection handling I also manually forced some tests with real handlers, just to be sure. Change-Id: I713c139ee12bb7f5301edd85951f8960fda84ac3
108 lines
4.1 KiB
Python
108 lines
4.1 KiB
Python
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
# implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
"""Tests for placement microversion handling."""
|
|
|
|
import collections
|
|
import mock
|
|
import operator
|
|
|
|
# import the handlers to load up handler decorators
|
|
import nova.api.openstack.placement.handler # noqa
|
|
from nova.api.openstack.placement import microversion
|
|
from nova import test
|
|
|
|
|
|
def handler():
|
|
return True
|
|
|
|
|
|
class TestMicroversionDecoration(test.NoDBTestCase):
|
|
|
|
@mock.patch('nova.api.openstack.placement.microversion.VERSIONED_METHODS',
|
|
new=collections.defaultdict(list))
|
|
def test_methods_structure(self):
|
|
"""Test that VERSIONED_METHODS gets data as expected."""
|
|
self.assertEqual(0, len(microversion.VERSIONED_METHODS))
|
|
fully_qualified_method = microversion._fully_qualified_name(
|
|
handler)
|
|
microversion.version_handler('1.0', '1.9')(handler)
|
|
microversion.version_handler('2.0')(handler)
|
|
|
|
methods_data = microversion.VERSIONED_METHODS[fully_qualified_method]
|
|
|
|
stored_method_data = methods_data[-1]
|
|
self.assertEqual(2, len(methods_data))
|
|
self.assertEqual(1.0, stored_method_data[0])
|
|
self.assertEqual(1.9, stored_method_data[1])
|
|
self.assertEqual(handler, stored_method_data[2])
|
|
self.assertEqual(2.0, methods_data[0][0])
|
|
|
|
|
|
class TestMicroversionIntersection(test.NoDBTestCase):
|
|
"""Test that there are no overlaps in the versioned handlers."""
|
|
|
|
# If you add versioned handlers you need to update this value to
|
|
# reflect the change. The value is the total number of methods
|
|
# with different names, not the total number overall. That is,
|
|
# if you add two different versions of method 'foobar' the
|
|
# number only goes up by one if no other version foobar yet
|
|
# exists. This operates as a simple sanity check.
|
|
TOTAL_VERSIONED_METHODS = 0
|
|
|
|
def test_methods_versioned(self):
|
|
methods_data = microversion.VERSIONED_METHODS
|
|
self.assertEqual(self.TOTAL_VERSIONED_METHODS, len(methods_data))
|
|
|
|
@staticmethod
|
|
def _check_intersection(method_info):
|
|
# See check_for_versions_intersection in
|
|
# nova.api.openstack.wsgi.
|
|
pairs = []
|
|
counter = 0
|
|
for min_ver, max_ver, func in method_info:
|
|
pairs.append((min_ver, 1, func))
|
|
pairs.append((max_ver, -1, func))
|
|
|
|
pairs.sort(key=operator.itemgetter(0))
|
|
|
|
for p in pairs:
|
|
counter += p[1]
|
|
if counter > 1:
|
|
return True
|
|
return False
|
|
|
|
@mock.patch('nova.api.openstack.placement.microversion.VERSIONED_METHODS',
|
|
new=collections.defaultdict(list))
|
|
def test_faked_intersection(self):
|
|
microversion.version_handler('1.0', '1.9')(handler)
|
|
microversion.version_handler('1.8', '2.0')(handler)
|
|
|
|
for method_info in microversion.VERSIONED_METHODS.values():
|
|
self.assertTrue(self._check_intersection(method_info))
|
|
|
|
@mock.patch('nova.api.openstack.placement.microversion.VERSIONED_METHODS',
|
|
new=collections.defaultdict(list))
|
|
def test_faked_non_intersection(self):
|
|
microversion.version_handler('1.0', '1.8')(handler)
|
|
microversion.version_handler('1.9', '2.0')(handler)
|
|
|
|
for method_info in microversion.VERSIONED_METHODS.values():
|
|
self.assertFalse(self._check_intersection(method_info))
|
|
|
|
def test_check_real_for_intersection(self):
|
|
"""Check the real handlers to make sure there is no intersctions."""
|
|
for method_name, method_info in microversion.VERSIONED_METHODS.items():
|
|
self.assertFalse(
|
|
self._check_intersection(method_info),
|
|
'method %s has intersecting versioned handlers' % method_name)
|