diff --git a/docs/esi-leap-api-version-history.md b/docs/esi-leap-api-version-history.md new file mode 100644 index 0000000..d86b733 --- /dev/null +++ b/docs/esi-leap-api-version-history.md @@ -0,0 +1,4 @@ +# REST API Version History + +## 1.0 +This is the initial (and current) version of the API. diff --git a/esi_leap/api/controllers/v1/versions.py b/esi_leap/api/controllers/v1/versions.py new file mode 100644 index 0000000..c17af10 --- /dev/null +++ b/esi_leap/api/controllers/v1/versions.py @@ -0,0 +1,48 @@ +# Copyright (c) 2015 Intel Corporation +# All Rights Reserved. +# +# 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. + +# Borrowed from Ironic + +# This is the version 1 API +BASE_VERSION = 1 + +# Here goes a short log of changes in every version. +# Refer to docs/esi-leap-api-version-history.md for a detailed +# explanation of what each version contains. +# +# v1.0: API at the point in time when versioning support was added. + +MINOR_0_INITIAL_VERSION = 0 + +# When adding another version, update: +# - MINOR_MAX_VERSION +# - docs/esi-leap-api-version-history.md with a detailed +# explanation of what changed in the new version + +MINOR_MAX_VERSION = MINOR_0_INITIAL_VERSION + +# String representations of the minor and maximum versions +_MIN_VERSION_STRING = "{}.{}".format(BASE_VERSION, MINOR_0_INITIAL_VERSION) +_MAX_VERSION_STRING = "{}.{}".format(BASE_VERSION, MINOR_MAX_VERSION) + + +def min_version_string(): + """Returns the minimum supported API version (as a string)""" + return _MIN_VERSION_STRING + + +def max_version_string(): + """Returns the maximum supported API version (as a string).""" + return _MAX_VERSION_STRING diff --git a/esi_leap/api/controllers/version.py b/esi_leap/api/controllers/version.py new file mode 100644 index 0000000..68a0c9e --- /dev/null +++ b/esi_leap/api/controllers/version.py @@ -0,0 +1,44 @@ +# 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. + +# Borrowed from Ironic + +ID_VERSION1 = "v1" + + +def all_versions(host_url): + return [default_version(host_url)] + + +def default_version(host_url): + """Return a dict representing the current default version + + id: The ID of the (major) version, also acts as the release number + links: A list containing one link that points to the current version + of the API + + status: Status of the version, one of CURRENT, SUPPORTED, DEPRECATED + + min_version: The current, maximum supported (major.minor) version of API. + + version: Minimum supported (major.minor) version of API. + """ + + from esi_leap.api.controllers.v1 import versions + + return { + "id": ID_VERSION1, + "links": [{"href": "{0}/{1}".format(host_url, ID_VERSION1), "rel": "self"}], + "status": "CURRENT", + "min_version": versions.min_version_string(), + "version": versions.max_version_string(), + } diff --git a/esi_leap/common/health.py b/esi_leap/common/health.py index 9653b50..fd31b74 100644 --- a/esi_leap/common/health.py +++ b/esi_leap/common/health.py @@ -1,17 +1,13 @@ from oslo_serialization import jsonutils +from esi_leap.api.controllers import version def root(host_url): return { "name": "ESI Leap API", "description": "ESI Leap is an OpenStack service for leasing baremetal nodes, designed to run on top of multi-tenant Ironic.", - "versions": [ - { - "id": "v1.0", - "status": "CURRENT", - "links": [{"href": "{0}/v1".format(host_url), "rel": "self"}], - } - ], + "default_version": version.default_version(host_url), + "versions": version.all_versions(host_url), } diff --git a/esi_leap/tests/api/controllers/v1/test_versions.py b/esi_leap/tests/api/controllers/v1/test_versions.py new file mode 100644 index 0000000..a68d744 --- /dev/null +++ b/esi_leap/tests/api/controllers/v1/test_versions.py @@ -0,0 +1,79 @@ +# Copyright (c) 2015 Intel Corporation +# +# 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. +# Borrowed from Ironic + +""" +Tests for the versions constants and methods. +""" + +import re + +from esi_leap.api.controllers.v1 import versions +from esi_leap.tests import base + + +class TestVersionConstants(base.TestCase): + def setUp(self): + super(TestVersionConstants, self).setUp() + + # Get all of our named constants. They all begin with r'MINOR_[0-9]' + self.minor_consts = [x for x in dir(versions) if re.search(r"^MINOR_[0-9]", x)] + + # Sort key needs to be an integer + def minor_key(x): + return int(x.split("_", 2)[1]) + + self.minor_consts.sort(key=minor_key) + + def test_max_ver_str(self): + # Test to make sure _MAX_VERSION_STRING corresponds with the largest + # MINOR_ constant + + max_ver = "1.{}".format(getattr(versions, self.minor_consts[-1])) + self.assertEqual(max_ver, versions._MAX_VERSION_STRING) + + def test_min_ver_str(self): + # Try to make sure someone doesn't change the _MIN_VERSION_STRING by + # accident and make sure it exists + self.assertEqual("1.0", versions._MIN_VERSION_STRING) + + def test_name_value_match(self): + # Test to make sure variable name matches the value. For example + # MINOR_99_FOO should equal 99 + + for var_name in self.minor_consts: + version = int(var_name.split("_", 2)[1]) + self.assertEqual( + version, + getattr(versions, var_name), + 'Constant "{}" does not equal {}'.format(var_name, version), + ) + + def test_duplicates(self): + # Test to make sure no duplicates values + + seen_values = set() + for var_name in self.minor_consts: + value = getattr(versions, var_name) + self.assertNotIn( + value, + seen_values, + "The value {} has been used more than once".format(value), + ) + seen_values.add(value) + + +class TestMaxVersionString(base.TestCase): + def test_max_version_not_pinned(self): + self.assertEqual(versions._MAX_VERSION_STRING, versions.max_version_string()) diff --git a/esi_leap/tests/common/test_health.py b/esi_leap/tests/common/test_health.py index cf35e6f..40ab70b 100644 --- a/esi_leap/tests/common/test_health.py +++ b/esi_leap/tests/common/test_health.py @@ -11,6 +11,7 @@ # under the License. from esi_leap.common import health +from esi_leap.api.controllers import version from esi_leap.tests import base from oslo_serialization import jsonutils @@ -21,13 +22,8 @@ def test_root(self): expected_result = { "name": "ESI Leap API", "description": "ESI Leap is an OpenStack service for leasing baremetal nodes, designed to run on top of multi-tenant Ironic.", - "versions": [ - { - "id": "v1.0", - "status": "CURRENT", - "links": [{"href": "{0}/v1".format(host_url), "rel": "self"}], - } - ], + "default_version": version.default_version(host_url), + "versions": version.all_versions(host_url), } result = health.root(host_url) self.assertEqual(result, expected_result)