Make Keystone return v3 as part of the version api

The keystone "get version" api currently fails to list v3
as a supported api.  It should now do this, along with v2 (which is,
of course, still supported)

Fixes Bug #1148186

Change-Id: Ie88bf941123702d2f7e2ecf6cecb1fa937ca1e52
This commit is contained in:
Henry Nash 2013-03-06 01:54:27 +00:00
parent 756cd5a297
commit 2dd6481a20
4 changed files with 197 additions and 91 deletions

View File

@ -23,6 +23,9 @@ from keystone import exception
LOG = logging.getLogger(__name__)
CONF = config.CONF
MEDIA_TYPE_JSON = 'application/vnd.openstack.identity-%s+json'
MEDIA_TYPE_XML = 'application/vnd.openstack.identity-%s+xml'
class Extensions(wsgi.Application):
"""Base extensions controller to be extended by public and admin API's."""
@ -113,12 +116,30 @@ class Version(wsgi.Application):
'media-types': [
{
'base': 'application/json',
'type': 'application/vnd.openstack.identity-v2.0'
'+json'
'type': MEDIA_TYPE_JSON % 'v2.0'
}, {
'base': 'application/xml',
'type': 'application/vnd.openstack.identity-v2.0'
'+xml'
'type': MEDIA_TYPE_XML % 'v2.0'
}
]
}
versions['v3'] = {
'id': 'v3.0',
'status': 'stable',
'updated': '2013-03-06T00:00:00Z',
'links': [
{
'rel': 'self',
'href': self._get_identity_url(version='v3'),
}
],
'media-types': [
{
'base': 'application/json',
'type': MEDIA_TYPE_JSON % 'v3'
}, {
'base': 'application/xml',
'type': MEDIA_TYPE_XML % 'v3'
}
]
}
@ -133,8 +154,14 @@ class Version(wsgi.Application):
}
})
def get_version(self, context):
def get_version_v2(self, context):
versions = self._get_versions_list(context)
return wsgi.render_response(body={
'version': versions['v2.0']
})
def get_version_v3(self, context):
versions = self._get_versions_list(context)
return wsgi.render_response(body={
'version': versions['v3']
})

View File

@ -47,7 +47,7 @@ class Extension(wsgi.ComposableRouter):
conditions=dict(method=['GET']))
class Version(wsgi.ComposableRouter):
class VersionV2(wsgi.ComposableRouter):
def __init__(self, description):
self.description = description
@ -55,7 +55,18 @@ class Version(wsgi.ComposableRouter):
version_controller = controllers.Version(self.description)
mapper.connect('/',
controller=version_controller,
action='get_version')
action='get_version_v2')
class VersionV3(wsgi.ComposableRouter):
def __init__(self, description):
self.description = description
def add_routes(self, mapper):
version_controller = controllers.Version(self.description)
mapper.connect('/',
controller=version_controller,
action='get_version_v3')
class Versions(wsgi.ComposableRouter):

View File

@ -46,7 +46,7 @@ def public_app_factory(global_conf, **local_conf):
return wsgi.ComposingRouter(routes.Mapper(),
[identity.routers.Public(),
token.routers.Router(),
routers.Version('public'),
routers.VersionV2('public'),
routers.Extension(False)])
@ -57,7 +57,7 @@ def admin_app_factory(global_conf, **local_conf):
return wsgi.ComposingRouter(routes.Mapper(),
[identity.routers.Admin(),
token.routers.Router(),
routers.Version('admin'),
routers.VersionV2('admin'),
routers.Extension()])
@ -85,5 +85,8 @@ def v3_app_factory(global_conf, **local_conf):
v3routers = []
for module in [auth, catalog, identity, policy, trust]:
module.routers.append_v3_routers(mapper, v3routers)
# Add in the v3 version api
v3routers.append(routers.VersionV3('admin'))
v3routers.append(routers.VersionV3('public'))
# TODO(ayoung): put token routes here
return wsgi.ComposingRouter(mapper, v3routers)

View File

@ -22,6 +22,91 @@ from keystone import test
CONF = config.CONF
v2_MEDIA_TYPES = [
{
"base": "application/json",
"type": "application/"
"vnd.openstack.identity-v2.0+json"
}, {
"base": "application/xml",
"type": "application/"
"vnd.openstack.identity-v2.0+xml"
}
]
v2_HTML_DESCRIPTION = {
"rel": "describedby",
"type": "text/html",
"href": "http://docs.openstack.org/api/"
"openstack-identity-service/2.0/"
"content/"
}
v2_PDF_DESCRIPTION = {
"rel": "describedby",
"type": "application/pdf",
"href": "http://docs.openstack.org/api/"
"openstack-identity-service/2.0/"
"identity-dev-guide-2.0.pdf"
}
v2_EXPECTED_RESPONSE = {
"id": "v2.0",
"status": "stable",
"updated": "2013-03-06T00:00:00Z",
"links": [
{
"rel": "self",
"href": "", # Will get filled in after initialization
},
v2_HTML_DESCRIPTION,
v2_PDF_DESCRIPTION
],
"media-types": v2_MEDIA_TYPES
}
v2_VERSION_RESPONSE = {
"version": v2_EXPECTED_RESPONSE
}
v3_MEDIA_TYPES = [
{
"base": "application/json",
"type": "application/"
"vnd.openstack.identity-v3+json"
}, {
"base": "application/xml",
"type": "application/"
"vnd.openstack.identity-v3+xml"
}
]
v3_EXPECTED_RESPONSE = {
"id": "v3.0",
"status": "stable",
"updated": "2013-03-06T00:00:00Z",
"links": [
{
"rel": "self",
"href": "", # Will get filled in after initialization
}
],
"media-types": v3_MEDIA_TYPES
}
v3_VERSION_RESPONSE = {
"version": v3_EXPECTED_RESPONSE
}
VERSIONS_RESPONSE = {
"versions": {
"values": [
v3_EXPECTED_RESPONSE,
v2_EXPECTED_RESPONSE
]
}
}
class VersionTestCase(test.TestCase):
def setUp(self):
@ -33,52 +118,24 @@ class VersionTestCase(test.TestCase):
self.public_server = self.serveapp('keystone', name='main')
self.admin_server = self.serveapp('keystone', name='admin')
def _paste_in_port(self, response, port):
for link in response['links']:
if link['rel'] == 'self':
link['href'] = port
def test_public_versions(self):
client = self.client(self.public_app)
resp = client.get('/')
self.assertEqual(resp.status_int, 300)
data = jsonutils.loads(resp.body)
expected = {
"versions": {
"values": [
{
"id": "v2.0",
"status": "stable",
"updated": '2013-03-06T00:00:00Z',
"links": [
{
"rel": "self",
"href": "http://localhost:%s/v2.0/" %
CONF.public_port,
}, {
"rel": "describedby",
"type": "text/html",
"href": "http://docs.openstack.org/api/"
"openstack-identity-service/2.0/"
"content/"
}, {
"rel": "describedby",
"type": "application/pdf",
"href": "http://docs.openstack.org/api/"
"openstack-identity-service/2.0/"
"identity-dev-guide-2.0.pdf"
}
],
"media-types": [
{
"base": "application/json",
"type": "application/"
"vnd.openstack.identity-v2.0+json"
}, {
"base": "application/xml",
"type": "application/"
"vnd.openstack.identity-v2.0+xml"
}
]
}
]
}
}
expected = VERSIONS_RESPONSE
for version in expected['versions']['values']:
if version['id'] == 'v3.0':
self._paste_in_port(
version, 'http://localhost:%s/v3/' % CONF.public_port)
elif version['id'] == 'v2.0':
self._paste_in_port(
version, 'http://localhost:%s/v2.0/' % CONF.public_port)
self.assertEqual(data, expected)
def test_admin_versions(self):
@ -86,45 +143,53 @@ class VersionTestCase(test.TestCase):
resp = client.get('/')
self.assertEqual(resp.status_int, 300)
data = jsonutils.loads(resp.body)
expected = {
"versions": {
"values": [
{
"id": "v2.0",
"status": "stable",
"updated": '2013-03-06T00:00:00Z',
"links": [
{
"rel": "self",
"href": "http://localhost:%s/v2.0/" %
CONF.admin_port,
}, {
"rel": "describedby",
"type": "text/html",
"href": "http://docs.openstack.org/api/"
"openstack-identity-service/2.0/"
"content/"
}, {
"rel": "describedby",
"type": "application/pdf",
"href": "http://docs.openstack.org/api/"
"openstack-identity-service/2.0/"
"identity-dev-guide-2.0.pdf"
}
],
"media-types": [
{
"base": "application/json",
"type": "application/"
"vnd.openstack.identity-v2.0+json"
}, {
"base": "application/xml",
"type": "application/"
"vnd.openstack.identity-v2.0+xml"
}
]
}
]
}
}
expected = VERSIONS_RESPONSE
for version in expected['versions']['values']:
if version['id'] == 'v3.0':
self._paste_in_port(
version, 'http://localhost:%s/v3/' % CONF.admin_port)
elif version['id'] == 'v2.0':
self._paste_in_port(
version, 'http://localhost:%s/v2.0/' % CONF.admin_port)
self.assertEqual(data, expected)
def test_public_version_v2(self):
client = self.client(self.public_app)
resp = client.get('/v2.0/')
self.assertEqual(resp.status_int, 200)
data = jsonutils.loads(resp.body)
expected = v2_VERSION_RESPONSE
self._paste_in_port(expected['version'],
'http://localhost:%s/v2.0/' % CONF.public_port)
self.assertEqual(data, expected)
def test_admin_version_v2(self):
client = self.client(self.admin_app)
resp = client.get('/v2.0/')
self.assertEqual(resp.status_int, 200)
data = jsonutils.loads(resp.body)
expected = v2_VERSION_RESPONSE
self._paste_in_port(expected['version'],
'http://localhost:%s/v2.0/' % CONF.admin_port)
self.assertEqual(data, expected)
def test_public_version_v3(self):
print CONF.public_port
client = self.client(self.public_app)
resp = client.get('/v3/')
self.assertEqual(resp.status_int, 200)
data = jsonutils.loads(resp.body)
expected = v3_VERSION_RESPONSE
self._paste_in_port(expected['version'],
'http://localhost:%s/v3/' % CONF.public_port)
self.assertEqual(data, expected)
def test_admin_version_v3(self):
client = self.client(self.public_app)
resp = client.get('/v3/')
self.assertEqual(resp.status_int, 200)
data = jsonutils.loads(resp.body)
expected = v3_VERSION_RESPONSE
self._paste_in_port(expected['version'],
'http://localhost:%s/v3/' % CONF.admin_port)
self.assertEqual(data, expected)