Make versions aware of enabled pipelines.
Updates to make our versions controller a bit smarter so that it only returns information on API versions which are actually running. With these changes a user can disable the v2.0 or v3 API versions in their pipeline, restart keystone, and then have versions return information only for the versions which are actually running. This is important because auth_token now uses info from the keystone versions controller (in some cases) to dynamically select an API version. Fixes LP Bug #1158470. Change-Id: I0fa8a82f08e7247c44fb7f4ff8dbb7d4ad58b9cc
This commit is contained in:
parent
ba3f41f068
commit
620e6e3780
|
@ -19,13 +19,14 @@ from keystone.common import logging
|
|||
from keystone import config
|
||||
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'
|
||||
|
||||
_VERSIONS = []
|
||||
|
||||
|
||||
class Extensions(wsgi.Application):
|
||||
"""Base extensions controller to be extended by public and admin API's."""
|
||||
|
@ -76,7 +77,12 @@ class PublicExtensions(Extensions):
|
|||
pass
|
||||
|
||||
|
||||
def register_version(version):
|
||||
_VERSIONS.append(version)
|
||||
|
||||
|
||||
class Version(wsgi.Application):
|
||||
|
||||
def __init__(self, version_type):
|
||||
self.endpoint_url_type = version_type
|
||||
|
||||
|
@ -92,57 +98,60 @@ class Version(wsgi.Application):
|
|||
def _get_versions_list(self, context):
|
||||
"""The list of versions is dependent on the context."""
|
||||
versions = {}
|
||||
versions['v2.0'] = {
|
||||
'id': 'v2.0',
|
||||
'status': 'stable',
|
||||
'updated': '2013-03-06T00:00:00Z',
|
||||
'links': [
|
||||
{
|
||||
'rel': 'self',
|
||||
'href': self._get_identity_url(version='v2.0'),
|
||||
}, {
|
||||
'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': MEDIA_TYPE_JSON % 'v2.0'
|
||||
}, {
|
||||
'base': 'application/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'
|
||||
}
|
||||
]
|
||||
}
|
||||
if 'v2.0' in _VERSIONS:
|
||||
versions['v2.0'] = {
|
||||
'id': 'v2.0',
|
||||
'status': 'stable',
|
||||
'updated': '2013-03-06T00:00:00Z',
|
||||
'links': [
|
||||
{
|
||||
'rel': 'self',
|
||||
'href': self._get_identity_url(version='v2.0'),
|
||||
}, {
|
||||
'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': MEDIA_TYPE_JSON % 'v2.0'
|
||||
}, {
|
||||
'base': 'application/xml',
|
||||
'type': MEDIA_TYPE_XML % 'v2.0'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
if 'v3' in _VERSIONS:
|
||||
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'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
return versions
|
||||
|
||||
|
@ -156,12 +165,18 @@ class Version(wsgi.Application):
|
|||
|
||||
def get_version_v2(self, context):
|
||||
versions = self._get_versions_list(context)
|
||||
return wsgi.render_response(body={
|
||||
'version': versions['v2.0']
|
||||
})
|
||||
if 'v2.0' in _VERSIONS:
|
||||
return wsgi.render_response(body={
|
||||
'version': versions['v2.0']
|
||||
})
|
||||
else:
|
||||
raise exception.VersionNotFound(version='v2.0')
|
||||
|
||||
def get_version_v3(self, context):
|
||||
versions = self._get_versions_list(context)
|
||||
return wsgi.render_response(body={
|
||||
'version': versions['v3']
|
||||
})
|
||||
if 'v3' in _VERSIONS:
|
||||
return wsgi.render_response(body={
|
||||
'version': versions['v3']
|
||||
})
|
||||
else:
|
||||
raise exception.VersionNotFound(version='v3')
|
||||
|
|
|
@ -204,6 +204,10 @@ class CredentialNotFound(NotFound):
|
|||
"""Could not find credential: %(credential_id)s"""
|
||||
|
||||
|
||||
class VersionNotFound(NotFound):
|
||||
"""Could not find version: %(version)s"""
|
||||
|
||||
|
||||
class Conflict(Error):
|
||||
"""Conflict occurred attempting to store %(type)s.
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ import routes
|
|||
from keystone import auth
|
||||
from keystone import catalog
|
||||
from keystone import config
|
||||
from keystone import controllers
|
||||
from keystone.common import logging
|
||||
from keystone.common import wsgi
|
||||
from keystone.contrib import ec2
|
||||
|
@ -43,6 +44,7 @@ DRIVERS = dict(
|
|||
|
||||
@logging.fail_gracefully
|
||||
def public_app_factory(global_conf, **local_conf):
|
||||
controllers.register_version('v2.0')
|
||||
conf = global_conf.copy()
|
||||
conf.update(local_conf)
|
||||
return wsgi.ComposingRouter(routes.Mapper(),
|
||||
|
@ -81,6 +83,7 @@ def admin_version_app_factory(global_conf, **local_conf):
|
|||
|
||||
@logging.fail_gracefully
|
||||
def v3_app_factory(global_conf, **local_conf):
|
||||
controllers.register_version('v3')
|
||||
conf = global_conf.copy()
|
||||
conf.update(local_conf)
|
||||
mapper = routes.Mapper()
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
# limitations under the License.
|
||||
|
||||
from keystone import config
|
||||
from keystone import controllers
|
||||
from keystone.openstack.common import jsonutils
|
||||
from keystone import test
|
||||
|
||||
|
@ -193,3 +194,65 @@ class VersionTestCase(test.TestCase):
|
|||
self._paste_in_port(expected['version'],
|
||||
'http://localhost:%s/v3/' % CONF.admin_port)
|
||||
self.assertEqual(data, expected)
|
||||
|
||||
def test_v2_disabled(self):
|
||||
self.stubs.Set(controllers, '_VERSIONS', ['v3'])
|
||||
client = self.client(self.public_app)
|
||||
# request to /v2.0 should fail
|
||||
resp = client.get('/v2.0/')
|
||||
self.assertEqual(resp.status_int, 404)
|
||||
|
||||
# request to /v3 should pass
|
||||
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)
|
||||
|
||||
# only v3 information should be displayed by requests to /
|
||||
v3_only_response = {
|
||||
"versions": {
|
||||
"values": [
|
||||
v3_EXPECTED_RESPONSE
|
||||
]
|
||||
}
|
||||
}
|
||||
self._paste_in_port(v3_only_response['versions']['values'][0],
|
||||
'http://localhost:%s/v3/' % CONF.public_port)
|
||||
resp = client.get('/')
|
||||
self.assertEqual(resp.status_int, 300)
|
||||
data = jsonutils.loads(resp.body)
|
||||
self.assertEqual(data, v3_only_response)
|
||||
|
||||
def test_v3_disabled(self):
|
||||
self.stubs.Set(controllers, '_VERSIONS', ['v2.0'])
|
||||
client = self.client(self.public_app)
|
||||
# request to /v3 should fail
|
||||
resp = client.get('/v3/')
|
||||
self.assertEqual(resp.status_int, 404)
|
||||
|
||||
# request to /v2.0 should pass
|
||||
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)
|
||||
|
||||
# only v2 information should be displayed by requests to /
|
||||
v2_only_response = {
|
||||
"versions": {
|
||||
"values": [
|
||||
v2_EXPECTED_RESPONSE
|
||||
]
|
||||
}
|
||||
}
|
||||
self._paste_in_port(v2_only_response['versions']['values'][0],
|
||||
'http://localhost:%s/v2.0/' % CONF.public_port)
|
||||
resp = client.get('/')
|
||||
self.assertEqual(resp.status_int, 300)
|
||||
data = jsonutils.loads(resp.body)
|
||||
self.assertEqual(data, v2_only_response)
|
||||
|
|
Loading…
Reference in New Issue