diff --git a/keystone/api/__init__.py b/keystone/api/__init__.py new file mode 100644 index 0000000000..69be5d6c4b --- /dev/null +++ b/keystone/api/__init__.py @@ -0,0 +1,16 @@ +# 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. + +from keystone.api import discovery + +__all__ = ('discovery',) +__apis__ = (discovery,) diff --git a/keystone/api/discovery.py b/keystone/api/discovery.py new file mode 100644 index 0000000000..2495c473ec --- /dev/null +++ b/keystone/api/discovery.py @@ -0,0 +1,142 @@ +# 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 flask +from flask import request +from oslo_serialization import jsonutils +from six.moves import http_client + +from keystone.common import json_home +from keystone.common import wsgi +import keystone.conf +from keystone import exception + + +CONF = keystone.conf.CONF +MEDIA_TYPE_JSON = 'application/vnd.openstack.identity-%s+json' +_VERSIONS = [] +_DISCOVERY_BLUEPRINT = flask.Blueprint('Discovery', __name__) + + +def register_version(version): + _VERSIONS.append(version) + + +def _get_versions_list(identity_url): + versions = {} + if 'v3' in _VERSIONS: + versions['v3'] = { + 'id': 'v3.10', + 'status': 'stable', + 'updated': '2018-02-28T00:00:00Z', + 'links': [ + { + 'rel': 'self', + 'href': identity_url, + } + ], + 'media-types': [ + { + 'base': 'application/json', + 'type': MEDIA_TYPE_JSON % 'v3' + } + ] + } + + return versions + + +class MimeTypes(object): + JSON = 'application/json' + JSON_HOME = 'application/json-home' + + +def _v3_json_home_content(): + # TODO(morgan): Eliminate this, we should never be disabling an API version + # now, JSON Home should never be empty. + if 'v3' not in _VERSIONS: + # No V3 Support, so return an empty JSON Home document. + return {'resources': {}} + return json_home.JsonHomeResources.resources() + + +def v3_mime_type_best_match(): + if not request.accept_mimetypes: + return MimeTypes.JSON + + return request.accept_mimetypes.best_match( + [MimeTypes.JSON, MimeTypes.JSON_HOME]) + + +@_DISCOVERY_BLUEPRINT.route('/') +def get_versions(): + if v3_mime_type_best_match() == MimeTypes.JSON_HOME: + # RENDER JSON-Home form, we have a clever client who will + # understand the JSON-Home document. + v3_json_home = _v3_json_home_content() + json_home.translate_urls(v3_json_home, '/v3') + return flask.Response(response=jsonutils.dumps(v3_json_home), + mimetype=MimeTypes.JSON_HOME) + else: + # NOTE(morgan): wsgi.Application.base_url will eventually need to + # be moved to a better "common" location. For now, we'll just lean + # on it for the sake of leaning on common code where possible. + identity_url = '%s/v3/' % wsgi.Application.base_url( + context={'environment': request.environ}) + versions = _get_versions_list(identity_url) + return flask.Response( + response=jsonutils.dumps( + {'versions': { + 'values': list(versions.values())}}), + mimetype=MimeTypes.JSON, + status=http_client.MULTIPLE_CHOICES) + + +@_DISCOVERY_BLUEPRINT.route('/v3') +def get_version_v3(): + if 'v3' not in _VERSIONS: + raise exception.VersionNotFound(version='v3') + + if v3_mime_type_best_match() == MimeTypes.JSON_HOME: + # RENDER JSON-Home form, we have a clever client who will + # understand the JSON-Home document. + content = _v3_json_home_content() + return flask.Response(response=jsonutils.dumps(content), + mimetype=MimeTypes.JSON_HOME) + else: + # NOTE(morgan): wsgi.Application.base_url will eventually need to + # be moved to a better "common" location. For now, we'll just lean + # on it for the sake of leaning on common code where possible. + identity_url = '%s/v3/' % wsgi.Application.base_url( + context={'environment': request.environ}) + versions = _get_versions_list(identity_url) + return flask.Response( + response=jsonutils.dumps({'version': versions['v3']}), + mimetype=MimeTypes.JSON) + + +class DiscoveryAPI(object): + # NOTE(morgan): The Discovery Bits are so special they cannot conform to + # Flask-RESTful-isms. We are using straight flask Blueprint(s) here so that + # we have a lot more control over what the heck is going on. This is just + # a stub object to ensure we can load discovery in the same manner we + # handle the rest of keystone.api + + @staticmethod + def instantiate_and_register_to_app(flask_app): + # This is a lot more magical than the normal setup as the discovery + # API does not lean on flask-restful. We're statically building a + # single blueprint here. + flask_app.register_blueprint(_DISCOVERY_BLUEPRINT) + + +APIs = (DiscoveryAPI,) diff --git a/keystone/catalog/routers.py b/keystone/catalog/routers.py index 62541c36f4..920e4dd1c0 100644 --- a/keystone/catalog/routers.py +++ b/keystone/catalog/routers.py @@ -72,7 +72,7 @@ class Routers(wsgi.RoutersBase): PATH_ENDPOINT_GROUP_PROJECTS = PATH_ENDPOINT_GROUPS + ( '/projects/{project_id}') - _path_prefixes = (PATH_PREFIX, 'regions', 'endpoints', 'services') + _path_prefixes = ('OS-EP-FILTER', 'regions', 'endpoints', 'services') def append_v3_routers(self, mapper, routers): regions_controller = controllers.RegionV3() diff --git a/keystone/server/flask/application.py b/keystone/server/flask/application.py index c195d172ab..8cb1e0e689 100644 --- a/keystone/server/flask/application.py +++ b/keystone/server/flask/application.py @@ -23,7 +23,7 @@ from oslo_middleware import healthcheck import routes import werkzeug.wsgi - +import keystone.api from keystone.application_credential import routers as app_cred_routers from keystone.assignment import routers as assignment_routers from keystone.auth import routers as auth_routers @@ -42,8 +42,6 @@ from keystone.resource import routers as resource_routers from keystone.revoke import routers as revoke_routers from keystone.token import _simple_cert as simple_cert_ext from keystone.trust import routers as trust_routers -from keystone.version import controllers as version_controllers -from keystone.version import routers as version_routers LOG = log.getLogger(__name__) @@ -105,72 +103,53 @@ class KeystoneDispatcherMiddleware(werkzeug.wsgi.DispatcherMiddleware): a non-native flask Mapper. """ + @property + def config(self): + return self.app.config + def __call__(self, environ, start_response): script = environ.get('PATH_INFO', '') original_script_name = environ.get('SCRIPT_NAME', '') last_element = '' path_info = '' - # NOTE(morgan): Special Case root documents per version, these *are* - # special and should never fall through to the legacy dispatcher, they - # must be handled by the version dispatchers. - if script not in ('/v3', '/', '/v2.0'): - while '/' in script: - if script in self.mounts: - LOG.debug('Dispatching request to legacy mapper: %s', - script) - app = self.mounts[script] - # NOTE(morgan): Simply because we're doing something "odd" - # here and internally routing magically to another "wsgi" - # router even though we're already deep in the stack we - # need to re-add the last element pulled off. This is 100% - # legacy and only applies to the "apps" that make up each - # keystone subsystem. - # - # This middleware is only used in support of the transition - # process from webob and home-rolled WSGI framework to - # Flask - if script.rindex('/') > 0: - script, last_element = script.rsplit('/', 1) - last_element = '/%s' % last_element - environ['SCRIPT_NAME'] = original_script_name + script - # Ensure there is only 1 slash between these items, the - # mapper gets horribly confused if we have // in there, - # which occasionally. As this is temporary to dispatch - # to the Legacy mapper, fix the string until we no longer - # need this logic. - environ['PATH_INFO'] = '%s/%s' % (last_element.rstrip('/'), - path_info.strip('/')) - break - script, last_item = script.rsplit('/', 1) - path_info = '/%s%s' % (last_item, path_info) - else: - app = self.mounts.get(script, self.app) - if app != self.app: - LOG.debug('Dispatching (fallthrough) request to legacy ' - 'mapper: %s', script) - else: - LOG.debug('Dispatching back to Flask native app.') + while '/' in script: + if script in self.mounts: + LOG.debug('Dispatching request to legacy mapper: %s', + script) + app = self.mounts[script] + # NOTE(morgan): Simply because we're doing something "odd" + # here and internally routing magically to another "wsgi" + # router even though we're already deep in the stack we + # need to re-add the last element pulled off. This is 100% + # legacy and only applies to the "apps" that make up each + # keystone subsystem. + # + # This middleware is only used in support of the transition + # process from webob and home-rolled WSGI framework to + # Flask + if script.rindex('/') > 0: + script, last_element = script.rsplit('/', 1) + last_element = '/%s' % last_element environ['SCRIPT_NAME'] = original_script_name + script - environ['PATH_INFO'] = path_info + # Ensure there is only 1 slash between these items, the + # mapper gets horribly confused if we have // in there, + # which occasionally. As this is temporary to dispatch + # to the Legacy mapper, fix the string until we no longer + # need this logic. + environ['PATH_INFO'] = '%s/%s' % (last_element.rstrip('/'), + path_info.strip('/')) + break + script, last_item = script.rsplit('/', 1) + path_info = '/%s%s' % (last_item, path_info) else: - # Special casing for version discovery docs. - # REMOVE THIS SPECIAL CASE WHEN VERSION DISCOVERY GOES FLASK NATIVE app = self.mounts.get(script, self.app) - if script == '/': - # ROOT Version Discovery Doc - LOG.debug('Dispatching to legacy root mapper for root version ' - 'discovery document: `%s`', script) - environ['SCRIPT_NAME'] = '/' - environ['PATH_INFO'] = '/' - elif script == '/v3': - LOG.debug('Dispatching to legacy mapper for v3 version ' - 'discovery document: `%s`', script) - # V3 Version Discovery Doc - environ['SCRIPT_NAME'] = '/v3' - environ['PATH_INFO'] = '/' + if app != self.app: + LOG.debug('Dispatching (fallthrough) request to legacy ' + 'mapper: %s', script) else: - LOG.debug('Dispatching to flask native app for version ' - 'discovery document: `%s`', script) + LOG.debug('Dispatching back to Flask native app.') + environ['SCRIPT_NAME'] = original_script_name + script + environ['PATH_INFO'] = path_info # NOTE(morgan): remove extra trailing slashes so the mapper can do the # right thing and get the requests mapped to the right place. For @@ -183,6 +162,18 @@ class KeystoneDispatcherMiddleware(werkzeug.wsgi.DispatcherMiddleware): return app(environ, start_response) +class _ComposibleRouterStub(keystone_wsgi.ComposableRouter): + def __init__(self, routers): + self._routers = routers + + +def _add_vary_x_auth_token_header(response): + # Add the expected Vary Header, this is run after every request in the + # response-phase + response.headers['Vary'] = 'X-Auth-Token' + return response + + @fail_gracefully def application_factory(name='public'): if name not in ('admin', 'public'): @@ -192,6 +183,12 @@ def application_factory(name='public'): # NOTE(morgan): The Flask App actually dispatches nothing until we migrate # some routers to Flask-Blueprints, it is simply a placeholder. app = flask.Flask(name) + app.after_request(_add_vary_x_auth_token_header) + + # NOTE(morgan): Configure the Flask Environment for our needs. + app.config.update( + # We want to bubble up Flask Exceptions (for now) + PROPAGATE_EXCEPTIONS=True) # TODO(morgan): Convert Subsystems over to Flask-Native, for now, we simply # dispatch to another "application" [e.g "keystone"] @@ -215,29 +212,24 @@ def application_factory(name='public'): _routers.append(routers_instance) routers_instance.append_v3_routers(mapper, sub_routers) - # Add in the v3 version api - sub_routers.append(version_routers.VersionV3('public', _routers)) - version_controllers.register_version('v3') + # TODO(morgan): Remove "API version registration". For now this is kept + # for ease of conversion (minimal changes) + keystone.api.discovery.register_version('v3') + + # NOTE(morgan): We add in all the keystone.api blueprints here, this + # replaces (as they are implemented) the legacy dispatcher work. + for api in keystone.api.__apis__: + for api_bp in api.APIs: + api_bp.instantiate_and_register_to_app(app) + + # Build and construct the dispatching for the Legacy dispatching model + sub_routers.append(_ComposibleRouterStub(_routers)) legacy_dispatcher = keystone_wsgi.ComposingRouter(mapper, sub_routers) for pfx in itertools.chain(*[rtr.Routers._path_prefixes for rtr in ALL_API_ROUTERS]): dispatch_map['/v3/%s' % pfx] = legacy_dispatcher - # NOTE(morgan) Move the version routers to Flask Native First! It will - # not work well due to how the dispatcher works unless this is first, - # otherwise nothing falls through to the native flask app. - dispatch_map['/v3'] = legacy_dispatcher - - # NOTE(morgan): The Root Version Discovery Document is special and needs - # it's own mapper/router since the v3 one assumes it owns the root due - # to legacy paste-isms where /v3 would be routed to APP=/v3, PATH=/ - root_version_disc_mapper = routes.Mapper() - root_version_disc_router = version_routers.Versions(name) - root_dispatcher = keystone_wsgi.ComposingRouter( - root_version_disc_mapper, [root_version_disc_router]) - dispatch_map['/'] = root_dispatcher - application = KeystoneDispatcherMiddleware( app, dispatch_map) diff --git a/keystone/server/flask/common.py b/keystone/server/flask/common.py index 907b19f64b..151130a66f 100644 --- a/keystone/server/flask/common.py +++ b/keystone/server/flask/common.py @@ -119,4 +119,4 @@ class APIBase(object): explicitly via normal instantiation where more values may be passed via :meth:`__init__`. """ - flask_app.register(cls()) + flask_app.register_blueprint(cls().blueprint) diff --git a/keystone/server/flask/core.py b/keystone/server/flask/core.py index 6b9f925ed5..4accdf3612 100644 --- a/keystone/server/flask/core.py +++ b/keystone/server/flask/core.py @@ -16,6 +16,7 @@ import os import oslo_i18n from oslo_log import log import stevedore +from werkzeug.contrib import fixers # NOTE(dstanek): i18n.enable_lazy() must be called before @@ -85,7 +86,7 @@ def _get_config_files(env=None): return files -def setup_app_middleware(application): +def setup_app_middleware(app): # NOTE(morgan): Load the middleware, in reverse order, we wrap the app # explicitly; reverse order to ensure the first element in _APP_MIDDLEWARE # processes the request first. @@ -121,8 +122,11 @@ def setup_app_middleware(application): # local_conf, this is all a hold-over from paste-ini and pending # reworking/removal(s) factory_func = loaded.driver.factory({}, **mw.conf) - application = factory_func(application) - return application + app = factory_func(app) + + # Apply werkzeug speficic middleware + app = fixers.ProxyFix(app) + return app def initialize_application(name, post_log_configured_function=lambda: None, diff --git a/keystone/tests/unit/core.py b/keystone/tests/unit/core.py index 0d6b260ad1..832d7f58f0 100644 --- a/keystone/tests/unit/core.py +++ b/keystone/tests/unit/core.py @@ -39,6 +39,7 @@ from sqlalchemy import exc import testtools from testtools import testcase +import keystone.api from keystone.common import context from keystone.common import json_home from keystone.common import provider_api @@ -49,9 +50,8 @@ from keystone import exception from keystone.identity.backends.ldap import common as ks_ldap from keystone import notifications from keystone.resource.backends import base as resource_base +from keystone.server import flask as keystone_flask from keystone.tests.unit import ksfixtures -from keystone.version import controllers -from keystone.version import service keystone.conf.configure() @@ -693,7 +693,7 @@ class TestCase(BaseTestCase): self.addCleanup(notifications.clear_subscribers) self.addCleanup(notifications.reset_notifier) - self.addCleanup(setattr, controllers, '_VERSIONS', []) + self.addCleanup(setattr, keystone.api.discovery, '_VERSIONS', []) def config(self, config_files): sql.initialize() @@ -782,7 +782,9 @@ class TestCase(BaseTestCase): self.addCleanup(self.cleanup_instance(*fixtures_to_cleanup)) def loadapp(self, name='public'): - return service.loadapp(name=name) + app = keystone_flask.application.application_factory(name) + app.config.update(PROPAGATE_EXCEPTIONS=True, testing=True) + return keystone_flask.setup_app_middleware(app) def assertCloseEnoughForGovernmentWork(self, a, b, delta=3): """Assert that two datetimes are nearly equal within a small delta. diff --git a/keystone/tests/unit/test_versions.py b/keystone/tests/unit/test_versions.py index aec4bdcfe6..640a3017ef 100644 --- a/keystone/tests/unit/test_versions.py +++ b/keystone/tests/unit/test_versions.py @@ -23,9 +23,9 @@ from six.moves import http_client from testtools import matchers as tt_matchers import webob +from keystone.api import discovery from keystone.common import json_home from keystone.tests import unit -from keystone.version import controllers v3_MEDIA_TYPES = [ @@ -746,7 +746,7 @@ class VersionTestCase(unit.TestCase): self._paste_in_port(expected['version'], 'http://localhost/v3/') self.assertEqual(expected, data) - @mock.patch.object(controllers, '_VERSIONS', ['v3']) + @mock.patch.object(discovery, '_VERSIONS', ['v3']) def test_v2_disabled(self): # NOTE(morgan): This test should be kept, v2.0 is removed and should # never return, this prevents regression[s]/v2.0 discovery doc @@ -822,8 +822,8 @@ class VersionTestCase(unit.TestCase): self.assertThat(resp.status, tt_matchers.Equals('200 OK')) return resp.headers['Content-Type'] - JSON = controllers.MimeTypes.JSON - JSON_HOME = controllers.MimeTypes.JSON_HOME + JSON = discovery.MimeTypes.JSON + JSON_HOME = discovery.MimeTypes.JSON_HOME JSON_MATCHER = tt_matchers.Equals(JSON) JSON_HOME_MATCHER = tt_matchers.Equals(JSON_HOME) @@ -852,17 +852,13 @@ class VersionTestCase(unit.TestCase): # If request some unknown mime-type, get JSON. self.assertThat(make_request(self.getUniqueString()), JSON_MATCHER) - @mock.patch.object(controllers, '_VERSIONS', []) + @mock.patch.object(discovery, '_VERSIONS', []) def test_no_json_home_document_returned_when_v3_disabled(self): - json_home_document = controllers.request_v3_json_home('some_prefix') + json_home_document = discovery._v3_json_home_content() + json_home.translate_urls(json_home_document, '/v3') expected_document = {'resources': {}} self.assertEqual(expected_document, json_home_document) - def test_extension_property_method_returns_none(self): - extension_obj = controllers.Extensions() - extensions_property = extension_obj.extensions - self.assertIsNone(extensions_property) - class VersionSingleAppTestCase(unit.TestCase): """Test running with a single application loaded. diff --git a/keystone/version/__init__.py b/keystone/version.py similarity index 100% rename from keystone/version/__init__.py rename to keystone/version.py diff --git a/keystone/version/controllers.py b/keystone/version/controllers.py deleted file mode 100644 index 073140986d..0000000000 --- a/keystone/version/controllers.py +++ /dev/null @@ -1,193 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# -# 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. - -from six.moves import http_client - -from keystone.common import extension -from keystone.common import json_home -from keystone.common import wsgi -from keystone import exception - - -MEDIA_TYPE_JSON = 'application/vnd.openstack.identity-%s+json' - -_VERSIONS = [] - -# NOTE(blk-u): latest_app will be set by keystone.version.service.loadapp(). It -# gets set to the application that was just loaded. loadapp() gets called once -# for the public app if this is the public instance or loadapp() gets called -# for the admin app if it's the admin instance. This is used to fetch the /v3 -# JSON Home response. The /v3 JSON Home response is the same whether it's the -# admin or public service so either admin or public works. -latest_app = None - - -def request_v3_json_home(new_prefix): - if 'v3' not in _VERSIONS: - # No V3 support, so return an empty JSON Home document. - return {'resources': {}} - v3_json_home = json_home.JsonHomeResources.resources() - json_home.translate_urls(v3_json_home, new_prefix) - - return v3_json_home - - -class Extensions(wsgi.Application): - """Base extensions controller to be extended by public and admin API's.""" - - # extend in subclass to specify the set of extensions - @property - def extensions(self): - return None - - def get_extensions_info(self, request): - return {'extensions': {'values': list(self.extensions.values())}} - - def get_extension_info(self, request, extension_alias): - try: - return {'extension': self.extensions[extension_alias]} - except KeyError: - raise exception.NotFound(target=extension_alias) - - -class AdminExtensions(Extensions): - @property - def extensions(self): - return extension.ADMIN_EXTENSIONS - - -class PublicExtensions(Extensions): - @property - def extensions(self): - return extension.PUBLIC_EXTENSIONS - - -def register_version(version): - _VERSIONS.append(version) - - -class MimeTypes(object): - JSON = 'application/json' - JSON_HOME = 'application/json-home' - - -def v3_mime_type_best_match(request): - - # accept_header is a WebOb MIMEAccept object so supports best_match. - accept_header = request.accept - - if not accept_header: - return MimeTypes.JSON - - SUPPORTED_TYPES = [MimeTypes.JSON, MimeTypes.JSON_HOME] - return accept_header.best_match(SUPPORTED_TYPES) - - -class Version(wsgi.Application): - - def __init__(self, version_type, routers=None): - self.endpoint_url_type = version_type - self._routers = routers - - super(Version, self).__init__() - - def _get_identity_url(self, context, version): - """Return a URL to keystone's own endpoint.""" - url = self.base_url(context, self.endpoint_url_type) - return '%s/%s/' % (url, version) - - def _get_versions_list(self, context): - """The list of versions is dependent on the context.""" - versions = {} - if 'v2.0' in _VERSIONS: - versions['v2.0'] = { - 'id': 'v2.0', - 'status': 'deprecated', - 'updated': '2016-08-04T00:00:00Z', - 'links': [ - { - 'rel': 'self', - 'href': self._get_identity_url(context, 'v2.0'), - }, { - 'rel': 'describedby', - 'type': 'text/html', - 'href': 'https://docs.openstack.org/' - } - ], - 'media-types': [ - { - 'base': 'application/json', - 'type': MEDIA_TYPE_JSON % 'v2.0' - } - ] - } - - if 'v3' in _VERSIONS: - versions['v3'] = { - 'id': 'v3.10', - 'status': 'stable', - 'updated': '2018-02-28T00:00:00Z', - 'links': [ - { - 'rel': 'self', - 'href': self._get_identity_url(context, 'v3'), - } - ], - 'media-types': [ - { - 'base': 'application/json', - 'type': MEDIA_TYPE_JSON % 'v3' - } - ] - } - - return versions - - def get_versions(self, request): - - req_mime_type = v3_mime_type_best_match(request) - if req_mime_type == MimeTypes.JSON_HOME: - v3_json_home = request_v3_json_home('/v3') - return wsgi.render_response( - body=v3_json_home, - headers=(('Content-Type', MimeTypes.JSON_HOME),)) - - versions = self._get_versions_list(request.context_dict) - return wsgi.render_response( - status=(http_client.MULTIPLE_CHOICES, - http_client.responses[http_client.MULTIPLE_CHOICES]), - body={ - 'versions': { - 'values': list(versions.values()) - } - }) - - def _get_json_home_v3(self): - return json_home.JsonHomeResources.resources() - - def get_version_v3(self, request): - versions = self._get_versions_list(request.context_dict) - if 'v3' in _VERSIONS: - req_mime_type = v3_mime_type_best_match(request) - - if req_mime_type == MimeTypes.JSON_HOME: - return wsgi.render_response( - body=self._get_json_home_v3(), - headers=(('Content-Type', MimeTypes.JSON_HOME),)) - - return wsgi.render_response(body={ - 'version': versions['v3'] - }) - else: - raise exception.VersionNotFound(version='v3') diff --git a/keystone/version/routers.py b/keystone/version/routers.py deleted file mode 100644 index 5da4951c0f..0000000000 --- a/keystone/version/routers.py +++ /dev/null @@ -1,80 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# -# 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. -""" -The only types of routers in this file should be ``ComposingRouters``. - -The routers for the backends should be in the backend-specific router modules. -For example, the ``ComposableRouter`` for ``identity`` belongs in:: - - keystone.identity.routers - -""" - - -from keystone.common import wsgi -from keystone.version import controllers - - -class Extension(wsgi.ComposableRouter): - def __init__(self, is_admin=True): - if is_admin: - self.controller = controllers.AdminExtensions() - else: - self.controller = controllers.PublicExtensions() - - def add_routes(self, mapper): - extensions_controller = self.controller - mapper.connect('/extensions', - controller=extensions_controller, - action='get_extensions_info', - conditions=dict(method=['GET'])) - mapper.connect('/extensions/{extension_alias}', - controller=extensions_controller, - action='get_extension_info', - conditions=dict(method=['GET'])) - - -class VersionV2(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_v2') - - -class VersionV3(wsgi.ComposableRouter): - def __init__(self, description, routers): - self.description = description - self._routers = routers - - def add_routes(self, mapper): - version_controller = controllers.Version(self.description, - routers=self._routers) - mapper.connect('/', - controller=version_controller, - action='get_version_v3') - - -class Versions(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_versions') diff --git a/keystone/version/service.py b/keystone/version/service.py deleted file mode 100644 index 247292a1cc..0000000000 --- a/keystone/version/service.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# -# 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. - -from oslo_log import log - -import keystone.conf -from keystone.server import flask as keystone_flask -from keystone.server.flask import application -from keystone.version import controllers - - -CONF = keystone.conf.CONF -LOG = log.getLogger(__name__) - - -def loadapp(name): - # NOTE(blk-u): Save the application being loaded in the controllers module. - # This is similar to how public_app_factory() and v3_app_factory() - # register the version with the controllers module. - controllers.latest_app = keystone_flask.setup_app_middleware( - application.application_factory(name)) - return controllers.latest_app