Add version discovery information to the /v1 endpoint

Currently we do not provide enough information for keystoneauth
to work on. This makes version discovery impossible e.g. in a standalone
case when the versioned endpoint is supplied by a user.

This change adds the required versioning information based on the actual
keystoneauth implementation and the in-progress API SIG specification:
https://review.openstack.org/#/c/459710/

Note that this change is not microversioned, as it is used to discover
available microversions for the versioned endpoint.

Change-Id: Ibb1e8a1c8973a4742b7ba508dc84de577cb967c9
Story: #2003567
Task: #24859
This commit is contained in:
Dmitry Tantsur 2018-08-27 10:45:31 +02:00
parent d42343deea
commit 1f03a57268
5 changed files with 90 additions and 49 deletions

View File

@ -19,50 +19,10 @@ from pecan import rest
from wsme import types as wtypes
from ironic.api.controllers import base
from ironic.api.controllers import link
from ironic.api.controllers import v1
from ironic.api.controllers.v1 import versions
from ironic.api.controllers import version
from ironic.api import expose
ID_VERSION1 = 'v1'
class Version(base.APIBase):
"""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
"""The ID of the (major) version, also acts as the release number"""
links = [link.Link]
"""A Link that point to a specific version of the API"""
status = wtypes.text
"""Status of the version.
One of:
* CURRENT - the latest version of API,
* SUPPORTED - supported, but not latest, version of API,
* 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):
@ -72,10 +32,10 @@ class Root(base.APIBase):
description = wtypes.text
"""Some information about this API"""
versions = [Version]
versions = [version.Version]
"""Links to all the versions available in this API"""
default_version = Version
default_version = version.Version
"""A link to the default version of the API"""
@staticmethod
@ -84,19 +44,17 @@ class Root(base.APIBase):
root.name = "OpenStack Ironic API"
root.description = ("Ironic is an OpenStack project which aims to "
"provision baremetal machines.")
root.default_version = Version(ID_VERSION1,
versions.min_version_string(),
versions.max_version_string())
root.default_version = version.default_version()
root.versions = [root.default_version]
return root
class RootController(rest.RestController):
_versions = [ID_VERSION1]
_versions = [version.ID_VERSION1]
"""All supported API versions"""
_default_version = ID_VERSION1
_default_version = version.ID_VERSION1
"""The default API version"""
v1 = v1.Controller()

View File

@ -34,6 +34,7 @@ from ironic.api.controllers.v1 import ramdisk
from ironic.api.controllers.v1 import utils
from ironic.api.controllers.v1 import versions
from ironic.api.controllers.v1 import volume
from ironic.api.controllers import version
from ironic.api import expose
from ironic.common.i18n import _
@ -99,6 +100,9 @@ class V1(base.APIBase):
heartbeat = [link.Link]
"""Links to the heartbeat resource"""
version = version.Version
"""Version discovery information."""
@staticmethod
def convert():
v1 = V1()
@ -174,6 +178,7 @@ class V1(base.APIBase):
'heartbeat', '',
bookmark=True)
]
v1.version = version.default_version()
return v1

View File

@ -0,0 +1,65 @@
# 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.
import pecan
from wsme import types as wtypes
from ironic.api.controllers import base
from ironic.api.controllers import link
ID_VERSION1 = 'v1'
class Version(base.APIBase):
"""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
"""The ID of the (major) version, also acts as the release number"""
links = [link.Link]
"""A Link that point to a specific version of the API"""
status = wtypes.text
"""Status of the version.
One of:
* CURRENT - the latest version of API,
* SUPPORTED - supported, but not latest, version of API,
* 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
def default_version():
# NOTE(dtantsur): avoid circular imports
from ironic.api.controllers.v1 import versions
return Version(ID_VERSION1,
versions.min_version_string(),
versions.max_version_string())

View File

@ -49,7 +49,7 @@ class TestV1Root(base.BaseApiTest):
for f in data:
self.assertNotIn(f, ['', []])
# Check if all known resources are present and there are no extra ones.
not_resources = ('id', 'links', 'media_types')
not_resources = ('id', 'links', 'media_types', 'version')
actual_resources = tuple(set(data) - set(not_resources))
expected_resources = (['chassis', 'drivers', 'nodes', 'ports']
+ additional_expected_resources)
@ -57,6 +57,13 @@ class TestV1Root(base.BaseApiTest):
self.assertIn({'type': 'application/vnd.openstack.ironic.v1+json',
'base': 'application/json'}, data['media_types'])
version1 = data['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'])
def test_get_v1_root(self):
self._test_get_root()

View File

@ -0,0 +1,6 @@
---
fixes:
- |
Adds the version discovery information to the versioned API endpoint
(``/v1``). This allows *keystoneauth* version discovery to work on this
endpoint.