diff --git a/keystone/common/json_home.py b/keystone/common/json_home.py index fbc6259928..d092eea3ad 100644 --- a/keystone/common/json_home.py +++ b/keystone/common/json_home.py @@ -12,6 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. +from oslo_serialization import jsonutils from keystone import exception from keystone.i18n import _ @@ -80,6 +81,36 @@ class Status(object): 'Unexpected status requested for JSON Home response, %s') % status) +class JsonHomeResources(object): + """JSON Home resource data.""" + + __resources = {} + __serialized_resource_data = None + + @classmethod + def _reset(cls): + # NOTE(morgan): this will reset all json home resource definitions. + # This is only used for testing. + cls.__resources.clear() + cls.__serialized_resource_data = None + + @classmethod + def append_resource(cls, rel, data): + cls.__resources[rel] = data + cls.__serialized_resource_data = None + + @classmethod + def resources(cls): + # NOTE(morgan): We use a serialized form of the resource data to + # ensure that the raw data is not changed by processing, this method + # simply populates the serialized store if it is not already populated. + # Any changes to this class storage object will result in clearing + # the serialized data value. + if cls.__serialized_resource_data is None: + cls.__serialized_resource_data = jsonutils.dumps(cls.__resources) + return {'resources': jsonutils.loads(cls.__serialized_resource_data)} + + def translate_urls(json_home, new_prefix): """Given a JSON Home document, sticks new_prefix on each of the urls.""" for dummy_rel, resource in json_home['resources'].items(): diff --git a/keystone/common/router.py b/keystone/common/router.py index 113c2b8b7e..fe50568042 100644 --- a/keystone/common/router.py +++ b/keystone/common/router.py @@ -68,6 +68,7 @@ class Router(wsgi.ComposableRouter): self.collection_key) rel_data = {'href': collection_path, } self._resource_descriptions.append((collection_rel, rel_data)) + json_home.JsonHomeResources.append_resource(collection_rel, rel_data) if self._is_entity_implemented: entity_rel = json_home.build_v3_resource_relation(self.key) @@ -80,3 +81,5 @@ class Router(wsgi.ComposableRouter): }, } self._resource_descriptions.append((entity_rel, entity_rel_data)) + json_home.JsonHomeResources.append_resource( + entity_rel, entity_rel_data) diff --git a/keystone/common/wsgi.py b/keystone/common/wsgi.py index 2e1a5cf125..e0ee39f1d0 100644 --- a/keystone/common/wsgi.py +++ b/keystone/common/wsgi.py @@ -623,6 +623,7 @@ class RoutersBase(object): json_home.Status.update_resource_data(resource_data, status) self.v3_resources.append((rel, resource_data)) + json_home.JsonHomeResources.append_resource(rel, resource_data) class V3ExtensionRouter(ExtensionRouter, RoutersBase): diff --git a/keystone/tests/unit/core.py b/keystone/tests/unit/core.py index f1aa4f666f..0d6b260ad1 100644 --- a/keystone/tests/unit/core.py +++ b/keystone/tests/unit/core.py @@ -40,6 +40,7 @@ import testtools from testtools import testcase from keystone.common import context +from keystone.common import json_home from keystone.common import provider_api from keystone.common import request from keystone.common import sql @@ -685,6 +686,9 @@ class TestCase(BaseTestCase): # tests aren't used. self.addCleanup(provider_api.ProviderAPIs._clear_registry_instances) + # Clear the registry of JSON Home Resources + self.addCleanup(json_home.JsonHomeResources._reset) + # Ensure Notification subscriptions and resource types are empty self.addCleanup(notifications.clear_subscribers) self.addCleanup(notifications.reset_notifier) diff --git a/keystone/version/controllers.py b/keystone/version/controllers.py index 5e8910e45c..073140986d 100644 --- a/keystone/version/controllers.py +++ b/keystone/version/controllers.py @@ -12,9 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. -from oslo_serialization import jsonutils from six.moves import http_client -import webob from keystone.common import extension from keystone.common import json_home @@ -39,11 +37,7 @@ def request_v3_json_home(new_prefix): if 'v3' not in _VERSIONS: # No V3 support, so return an empty JSON Home document. return {'resources': {}} - - req = webob.Request.blank( - '/v3', headers={'Accept': 'application/json-home'}) - v3_json_home_str = req.get_response(latest_app).body - v3_json_home = jsonutils.loads(v3_json_home_str) + v3_json_home = json_home.JsonHomeResources.resources() json_home.translate_urls(v3_json_home, new_prefix) return v3_json_home @@ -180,15 +174,7 @@ class Version(wsgi.Application): }) def _get_json_home_v3(self): - - def all_resources(): - for router in self._routers: - for resource in router.v3_resources: - yield resource - - return { - 'resources': dict(all_resources()) - } + return json_home.JsonHomeResources.resources() def get_version_v3(self, request): versions = self._get_versions_list(request.context_dict)