Expose versioning information on GET / endpoint

This returns 'min_version' (minimum supported version), 'version'
(current/maximum supported version), and 'status' (of the version,
is 'CURRENT' for v1) in the response to a GET / request.
It makes our response to / very close to Nova's one[0], and will allow
us to implement a `ironic version-list` command similar to Nova's.

As this change is needed for proper versioning support on client
side, it's not versioned itself. Otherwise we'll have a
chicken-and-egg problem.

[0]http://specs.openstack.org/openstack/nova-specs/specs/kilo/implemented/api-microversions.html#versioning

Closes-Bug: #1489172
Co-Authored-By: Ruby Loo <rloo@yahoo-inc.com>
Change-Id: I6dd1ea20e23137d9a9566355adc0784067d2cb5b
This commit is contained in:
Dmitry Tantsur 2015-08-04 13:49:48 +02:00 committed by Ruby Loo
parent 56ef2a001c
commit 91467d3135
2 changed files with 50 additions and 16 deletions

View File

@ -21,25 +21,47 @@ from wsme import types as wtypes
from ironic.api.controllers import base from ironic.api.controllers import base
from ironic.api.controllers import link from ironic.api.controllers import link
from ironic.api.controllers import v1 from ironic.api.controllers import v1
from ironic.api.controllers.v1 import versions
from ironic.api import expose from ironic.api import expose
ID_VERSION1 = 'v1'
class Version(base.APIBase): class Version(base.APIBase):
"""An API version representation.""" """An API version representation.
This class represents an API version, including the minimum and
maximum minor versions that are supported within the major version.
"""
id = wtypes.text id = wtypes.text
"""The ID of the version, also acts as the release number""" """The ID of the (major) version, also acts as the release number"""
links = [link.Link] links = [link.Link]
"""A Link that point to a specific version of the API""" """A Link that point to a specific version of the API"""
@staticmethod status = wtypes.text
def convert(id): """Status of the version.
version = Version()
version.id = id One of:
version.links = [link.Link.make_link('self', pecan.request.public_url, * CURRENT - the latest version of API,
id, '', bookmark=True)] * SUPPORTED - supported, but not latest, version of API,
return version * DEPRECATED - supported, but deprecated, version of API.
"""
version = wtypes.text
"""The current, maximum supported (major.minor) version of API."""
min_version = wtypes.text
"""Minimum supported (major.minor) version of API."""
def __init__(self, id, min_version, version, status='CURRENT'):
self.id = id
self.links = [link.Link.make_link('self', pecan.request.public_url,
self.id, '', bookmark=True)]
self.status = status
self.version = version
self.min_version = min_version
class Root(base.APIBase): class Root(base.APIBase):
@ -62,17 +84,19 @@ class Root(base.APIBase):
root.name = "OpenStack Ironic API" root.name = "OpenStack Ironic API"
root.description = ("Ironic is an OpenStack project which aims to " root.description = ("Ironic is an OpenStack project which aims to "
"provision baremetal machines.") "provision baremetal machines.")
root.versions = [Version.convert('v1')] root.default_version = Version(ID_VERSION1,
root.default_version = Version.convert('v1') versions.MIN_VERSION_STRING,
versions.MAX_VERSION_STRING)
root.versions = [root.default_version]
return root return root
class RootController(rest.RestController): class RootController(rest.RestController):
_versions = ['v1'] _versions = [ID_VERSION1]
"""All supported API versions""" """All supported API versions"""
_default_version = 'v1' _default_version = ID_VERSION1
"""The default API version""" """The default API version"""
v1 = v1.Controller() v1 = v1.Controller()

View File

@ -13,16 +13,26 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from ironic.api.controllers.v1 import versions
from ironic.tests.unit.api import base from ironic.tests.unit.api import base
class TestRoot(base.BaseApiTest): class TestRoot(base.BaseApiTest):
def test_get_root(self): def test_get_root(self):
data = self.get_json('/', path_prefix='') response = self.get_json('/', path_prefix='')
self.assertEqual('v1', data['default_version']['id'])
# Check fields are not empty # Check fields are not empty
[self.assertNotIn(f, ['', []]) for f in data.keys()] [self.assertNotIn(f, ['', []]) for f in response]
self.assertEqual('OpenStack Ironic API', response['name'])
self.assertTrue(response['description'])
self.assertEqual([response['default_version']], response['versions'])
version1 = response['default_version']
self.assertEqual('v1', version1['id'])
self.assertEqual('CURRENT', version1['status'])
self.assertEqual(versions.MIN_VERSION_STRING, version1['min_version'])
self.assertEqual(versions.MAX_VERSION_STRING, version1['version'])
class TestV1Root(base.BaseApiTest): class TestV1Root(base.BaseApiTest):