Merge "Fixes LP #954089 - Service list templated catalog"
This commit is contained in:
commit
3a70a2f928
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
|
|
||||||
from keystone import catalog
|
from keystone import catalog
|
||||||
|
from keystone import exception
|
||||||
from keystone.common import kvs
|
from keystone.common import kvs
|
||||||
|
|
||||||
|
|
||||||
@ -25,7 +26,10 @@ class Catalog(kvs.Base, catalog.Driver):
|
|||||||
return self.db.get('catalog-%s-%s' % (tenant_id, user_id))
|
return self.db.get('catalog-%s-%s' % (tenant_id, user_id))
|
||||||
|
|
||||||
def get_service(self, service_id):
|
def get_service(self, service_id):
|
||||||
return self.db.get('service-%s' % service_id)
|
res = self.db.get('service-%s' % service_id)
|
||||||
|
if not res:
|
||||||
|
raise exception.ServiceNotFound(service_id=service_id)
|
||||||
|
return res
|
||||||
|
|
||||||
def list_services(self):
|
def list_services(self):
|
||||||
return self.db.get('service_list', [])
|
return self.db.get('service_list', [])
|
||||||
@ -42,6 +46,9 @@ class Catalog(kvs.Base, catalog.Driver):
|
|||||||
return service
|
return service
|
||||||
|
|
||||||
def delete_service(self, service_id):
|
def delete_service(self, service_id):
|
||||||
|
if not self.db.get('service-%s' % service_id):
|
||||||
|
raise exception.ServiceNotFound(service_id=service_id)
|
||||||
|
|
||||||
self.db.delete('service-%s' % service_id)
|
self.db.delete('service-%s' % service_id)
|
||||||
service_list = set(self.db.get('service_list', []))
|
service_list = set(self.db.get('service_list', []))
|
||||||
service_list.remove(service_id)
|
service_list.remove(service_id)
|
||||||
|
@ -90,11 +90,15 @@ class Catalog(sql.Base, catalog.Driver):
|
|||||||
def get_service(self, service_id):
|
def get_service(self, service_id):
|
||||||
session = self.get_session()
|
session = self.get_session()
|
||||||
service_ref = session.query(Service).filter_by(id=service_id).first()
|
service_ref = session.query(Service).filter_by(id=service_id).first()
|
||||||
|
if not service_ref:
|
||||||
|
raise exception.ServiceNotFound(service_id=service_id)
|
||||||
return service_ref.to_dict()
|
return service_ref.to_dict()
|
||||||
|
|
||||||
def delete_service(self, service_id):
|
def delete_service(self, service_id):
|
||||||
session = self.get_session()
|
session = self.get_session()
|
||||||
service_ref = session.query(Service).filter_by(id=service_id).first()
|
service_ref = session.query(Service).filter_by(id=service_id).first()
|
||||||
|
if not service_ref:
|
||||||
|
raise exception.ServiceNotFound(service_id=service_id)
|
||||||
with session.begin():
|
with session.begin():
|
||||||
session.delete(service_ref)
|
session.delete(service_ref)
|
||||||
session.flush()
|
session.flush()
|
||||||
@ -107,12 +111,6 @@ class Catalog(sql.Base, catalog.Driver):
|
|||||||
session.flush()
|
session.flush()
|
||||||
return service.to_dict()
|
return service.to_dict()
|
||||||
|
|
||||||
def service_exists(self, service_id):
|
|
||||||
session = self.get_session()
|
|
||||||
if not session.query(Service).filter_by(id=service_id).first():
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
# Endpoints
|
# Endpoints
|
||||||
def create_endpoint(self, endpoint_id, endpoint_ref):
|
def create_endpoint(self, endpoint_id, endpoint_ref):
|
||||||
session = self.get_session()
|
session = self.get_session()
|
||||||
|
@ -52,6 +52,9 @@ def parse_templates(template_lines):
|
|||||||
return o
|
return o
|
||||||
|
|
||||||
|
|
||||||
|
# TODO(jaypipes): should be templated.Catalog,
|
||||||
|
# not templated.TemplatedCatalog to be consistent with
|
||||||
|
# other catalog backends
|
||||||
class TemplatedCatalog(kvs.Catalog):
|
class TemplatedCatalog(kvs.Catalog):
|
||||||
"""A backend that generates endpoints for the Catalog based on templates.
|
"""A backend that generates endpoints for the Catalog based on templates.
|
||||||
|
|
||||||
|
@ -69,14 +69,6 @@ class Driver(object):
|
|||||||
def create_service(self, service_id, service_ref):
|
def create_service(self, service_id, service_ref):
|
||||||
raise exception.NotImplemented()
|
raise exception.NotImplemented()
|
||||||
|
|
||||||
def service_exists(self, service_id):
|
|
||||||
"""Query existence of a service by id.
|
|
||||||
|
|
||||||
Returns: True if the service exists or False.
|
|
||||||
|
|
||||||
"""
|
|
||||||
raise exception.NotImplemented()
|
|
||||||
|
|
||||||
def create_endpoint(self, endpoint_id, endpoint_ref):
|
def create_endpoint(self, endpoint_id, endpoint_ref):
|
||||||
raise exception.NotImplemented()
|
raise exception.NotImplemented()
|
||||||
|
|
||||||
@ -176,7 +168,9 @@ class EndpointController(wsgi.Application):
|
|||||||
endpoint_ref['id'] = endpoint_id
|
endpoint_ref['id'] = endpoint_id
|
||||||
|
|
||||||
service_id = endpoint_ref['service_id']
|
service_id = endpoint_ref['service_id']
|
||||||
if not self.catalog_api.service_exists(context, service_id):
|
try:
|
||||||
|
service = self.catalog_api.get_service(context, service_id)
|
||||||
|
except exception.ServiceNotFound:
|
||||||
msg = 'No service exists with id %s' % service_id
|
msg = 'No service exists with id %s' % service_id
|
||||||
raise webob.exc.HTTPBadRequest(msg)
|
raise webob.exc.HTTPBadRequest(msg)
|
||||||
|
|
||||||
|
@ -77,3 +77,7 @@ class NotImplemented(Error):
|
|||||||
|
|
||||||
class TokenNotFound(NotFound):
|
class TokenNotFound(NotFound):
|
||||||
"""Could not find token: %(token_id)s"""
|
"""Could not find token: %(token_id)s"""
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceNotFound(NotFound):
|
||||||
|
"""Could not find service: %(service_id)s"""
|
||||||
|
@ -122,6 +122,7 @@ class TestCase(unittest.TestCase):
|
|||||||
self._paths = []
|
self._paths = []
|
||||||
self._memo = {}
|
self._memo = {}
|
||||||
self._overrides = []
|
self._overrides = []
|
||||||
|
self._group_overrides = {}
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestCase, self).setUp()
|
super(TestCase, self).setUp()
|
||||||
@ -147,15 +148,26 @@ class TestCase(unittest.TestCase):
|
|||||||
kvs.INMEMDB.clear()
|
kvs.INMEMDB.clear()
|
||||||
self.reset_opts()
|
self.reset_opts()
|
||||||
|
|
||||||
|
def opt_in_group(self, group, **kw):
|
||||||
|
for k, v in kw.iteritems():
|
||||||
|
CONF.set_override(k, v, group)
|
||||||
|
if group not in self._group_overrides:
|
||||||
|
self._group_overrides[group] = []
|
||||||
|
self._group_overrides[group].append(k)
|
||||||
|
|
||||||
def opt(self, **kw):
|
def opt(self, **kw):
|
||||||
for k, v in kw.iteritems():
|
for k, v in kw.iteritems():
|
||||||
CONF.set_override(k, v)
|
CONF.set_override(k, v)
|
||||||
self._overrides.append(k)
|
self._overrides.append(k)
|
||||||
|
|
||||||
def reset_opts(self):
|
def reset_opts(self):
|
||||||
|
for group, opt_list in self._group_overrides.iteritems():
|
||||||
|
for k in opt_list:
|
||||||
|
CONF.set_override(k, None, group)
|
||||||
for k in self._overrides:
|
for k in self._overrides:
|
||||||
CONF.set_override(k, None)
|
CONF.set_override(k, None)
|
||||||
self._overrides = []
|
self._overrides = []
|
||||||
|
self._group_overrides = {}
|
||||||
CONF.reset()
|
CONF.reset()
|
||||||
|
|
||||||
def load_backends(self):
|
def load_backends(self):
|
||||||
@ -173,34 +185,41 @@ class TestCase(unittest.TestCase):
|
|||||||
"""
|
"""
|
||||||
# TODO(termie): doing something from json, probably based on Django's
|
# TODO(termie): doing something from json, probably based on Django's
|
||||||
# loaddata will be much preferred.
|
# loaddata will be much preferred.
|
||||||
for tenant in fixtures.TENANTS:
|
if hasattr(self, 'catalog_api'):
|
||||||
rv = self.identity_api.create_tenant(tenant['id'], tenant)
|
for service in fixtures.SERVICES:
|
||||||
setattr(self, 'tenant_%s' % tenant['id'], rv)
|
rv = self.catalog_api.create_service(service['id'], service)
|
||||||
|
setattr(self, 'service_%s' % service['id'], rv)
|
||||||
|
|
||||||
for user in fixtures.USERS:
|
if hasattr(self, 'identity_api'):
|
||||||
user_copy = user.copy()
|
for tenant in fixtures.TENANTS:
|
||||||
tenants = user_copy.pop('tenants')
|
rv = self.identity_api.create_tenant(tenant['id'], tenant)
|
||||||
rv = self.identity_api.create_user(user['id'], user_copy.copy())
|
setattr(self, 'tenant_%s' % tenant['id'], rv)
|
||||||
for tenant_id in tenants:
|
|
||||||
self.identity_api.add_user_to_tenant(tenant_id, user['id'])
|
|
||||||
setattr(self, 'user_%s' % user['id'], user_copy)
|
|
||||||
|
|
||||||
for role in fixtures.ROLES:
|
for user in fixtures.USERS:
|
||||||
rv = self.identity_api.create_role(role['id'], role)
|
user_copy = user.copy()
|
||||||
setattr(self, 'role_%s' % role['id'], rv)
|
tenants = user_copy.pop('tenants')
|
||||||
|
rv = self.identity_api.create_user(user['id'],
|
||||||
|
user_copy.copy())
|
||||||
|
for tenant_id in tenants:
|
||||||
|
self.identity_api.add_user_to_tenant(tenant_id, user['id'])
|
||||||
|
setattr(self, 'user_%s' % user['id'], user_copy)
|
||||||
|
|
||||||
for metadata in fixtures.METADATA:
|
for role in fixtures.ROLES:
|
||||||
metadata_ref = metadata.copy()
|
rv = self.identity_api.create_role(role['id'], role)
|
||||||
# TODO(termie): these will probably end up in the model anyway,
|
setattr(self, 'role_%s' % role['id'], rv)
|
||||||
# so this may be futile
|
|
||||||
del metadata_ref['user_id']
|
for metadata in fixtures.METADATA:
|
||||||
del metadata_ref['tenant_id']
|
metadata_ref = metadata.copy()
|
||||||
rv = self.identity_api.create_metadata(metadata['user_id'],
|
# TODO(termie): these will probably end up in the model anyway,
|
||||||
metadata['tenant_id'],
|
# so this may be futile
|
||||||
metadata_ref)
|
del metadata_ref['user_id']
|
||||||
setattr(self,
|
del metadata_ref['tenant_id']
|
||||||
'metadata_%s%s' % (metadata['user_id'],
|
rv = self.identity_api.create_metadata(metadata['user_id'],
|
||||||
metadata['tenant_id']), rv)
|
metadata['tenant_id'],
|
||||||
|
metadata_ref)
|
||||||
|
setattr(self,
|
||||||
|
'metadata_%s%s' % (metadata['user_id'],
|
||||||
|
metadata['tenant_id']), rv)
|
||||||
|
|
||||||
def _paste_config(self, config):
|
def _paste_config(self, config):
|
||||||
if not config.startswith('config:'):
|
if not config.startswith('config:'):
|
||||||
|
@ -13,3 +13,6 @@ driver = keystone.token.backends.sql.Token
|
|||||||
|
|
||||||
[ec2]
|
[ec2]
|
||||||
driver = keystone.contrib.ec2.backends.sql.Ec2
|
driver = keystone.contrib.ec2.backends.sql.Ec2
|
||||||
|
|
||||||
|
[catalog]
|
||||||
|
driver = keystone.catalog.backends.sql.Catalog
|
||||||
|
@ -39,3 +39,24 @@ ROLES = [
|
|||||||
{'id': 'keystone_admin', 'name': 'Keystone Admin'},
|
{'id': 'keystone_admin', 'name': 'Keystone Admin'},
|
||||||
{'id': 'useless', 'name': 'Useless'},
|
{'id': 'useless', 'name': 'Useless'},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
SERVICES = [
|
||||||
|
{
|
||||||
|
'id': 'COMPUTE_ID',
|
||||||
|
'type': 'compute',
|
||||||
|
'name': 'Nova',
|
||||||
|
'description': 'OpenStack Compute service'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'id': 'IDENTITY_ID',
|
||||||
|
'type': 'identity',
|
||||||
|
'name': 'Keystone',
|
||||||
|
'description': 'OpenStack Identity service'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'id': 'IMAGE_ID',
|
||||||
|
'type': 'image',
|
||||||
|
'name': 'Glance',
|
||||||
|
'description': 'OpenStack Image service'
|
||||||
|
},
|
||||||
|
]
|
||||||
|
@ -317,3 +317,23 @@ class TokenTests(object):
|
|||||||
self.assertDictEquals(data_ref, data)
|
self.assertDictEquals(data_ref, data)
|
||||||
new_data_ref = self.token_api.get_token(token_id)
|
new_data_ref = self.token_api.get_token(token_id)
|
||||||
self.assertEqual(data_ref, new_data_ref)
|
self.assertEqual(data_ref, new_data_ref)
|
||||||
|
|
||||||
|
|
||||||
|
class CatalogTests(object):
|
||||||
|
|
||||||
|
def test_service_crud(self):
|
||||||
|
new_service = {'id': 'MY_SERVICE', 'type': 'myservice',
|
||||||
|
'name': 'My Service', 'description': 'My description'}
|
||||||
|
res = self.catalog_api.create_service(new_service['id'], new_service)
|
||||||
|
self.assertDictEquals(res, new_service)
|
||||||
|
|
||||||
|
service_id = new_service['id']
|
||||||
|
self.catalog_api.delete_service(service_id)
|
||||||
|
self.assertRaises(exception.ServiceNotFound,
|
||||||
|
self.catalog_api.delete_service, service_id)
|
||||||
|
self.assertRaises(exception.ServiceNotFound,
|
||||||
|
self.catalog_api.get_service, service_id)
|
||||||
|
|
||||||
|
def test_service_list(self):
|
||||||
|
services = self.catalog_api.list_services()
|
||||||
|
self.assertEqual(3, len(services))
|
||||||
|
@ -35,13 +35,14 @@ class KvsToken(test.TestCase, test_backend.TokenTests):
|
|||||||
self.token_api = token_kvs.Token(db={})
|
self.token_api = token_kvs.Token(db={})
|
||||||
|
|
||||||
|
|
||||||
class KvsCatalog(test.TestCase):
|
class KvsCatalog(test.TestCase, test_backend.CatalogTests):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(KvsCatalog, self).setUp()
|
super(KvsCatalog, self).setUp()
|
||||||
self.catalog_api = catalog_kvs.Catalog(db={})
|
self.catalog_api = catalog_kvs.Catalog(db={})
|
||||||
self._load_fixtures()
|
self.load_fixtures(default_fixtures)
|
||||||
|
self._load_fake_catalog()
|
||||||
|
|
||||||
def _load_fixtures(self):
|
def _load_fake_catalog(self):
|
||||||
self.catalog_foobar = self.catalog_api._create_catalog(
|
self.catalog_foobar = self.catalog_api._create_catalog(
|
||||||
'foo', 'bar',
|
'foo', 'bar',
|
||||||
{'RegionFoo': {'service_bar': {'foo': 'bar'}}})
|
{'RegionFoo': {'service_bar': {'foo': 'bar'}}})
|
||||||
|
57
tests/test_backend_templated.py
Normal file
57
tests/test_backend_templated.py
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright 2012 OpenStack LLC
|
||||||
|
#
|
||||||
|
# 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 os
|
||||||
|
|
||||||
|
from keystone import test
|
||||||
|
from keystone.catalog.backends import templated as catalog_templated
|
||||||
|
|
||||||
|
import test_backend
|
||||||
|
import default_fixtures
|
||||||
|
|
||||||
|
DEFAULT_CATALOG_TEMPLATES = os.path.abspath(os.path.join(
|
||||||
|
os.path.dirname(__file__),
|
||||||
|
'default_catalog.templates'))
|
||||||
|
|
||||||
|
|
||||||
|
class TestTemplatedCatalog(test.TestCase, test_backend.CatalogTests):
|
||||||
|
|
||||||
|
DEFAULT_FIXTURE = {
|
||||||
|
'RegionOne': {
|
||||||
|
'compute': {
|
||||||
|
'adminURL': 'http://localhost:8774/v1.1/bar',
|
||||||
|
'publicURL': 'http://localhost:8774/v1.1/bar',
|
||||||
|
'internalURL': 'http://localhost:8774/v1.1/bar',
|
||||||
|
'name': "'Compute Service'"
|
||||||
|
},
|
||||||
|
'identity': {
|
||||||
|
'adminURL': 'http://localhost:35357/v2.0',
|
||||||
|
'publicURL': 'http://localhost:5000/v2.0',
|
||||||
|
'internalURL': 'http://localhost:35357/v2.0',
|
||||||
|
'name': "'Identity Service'"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestTemplatedCatalog, self).setUp()
|
||||||
|
self.opt_in_group('catalog', template_file=DEFAULT_CATALOG_TEMPLATES)
|
||||||
|
self.catalog_api = catalog_templated.TemplatedCatalog()
|
||||||
|
self.load_fixtures(default_fixtures)
|
||||||
|
|
||||||
|
def test_get_catalog(self):
|
||||||
|
catalog_ref = self.catalog_api.get_catalog('foo', 'bar')
|
||||||
|
self.assertDictEquals(catalog_ref, self.DEFAULT_FIXTURE)
|
@ -175,14 +175,6 @@ class KeystoneClientTests(object):
|
|||||||
self.get_client,
|
self.get_client,
|
||||||
user_ref)
|
user_ref)
|
||||||
|
|
||||||
# TODO(termie): I'm not really sure that this is testing much
|
|
||||||
def test_endpoints(self):
|
|
||||||
raise nose.exc.SkipTest('Not implemented due to bug 933555')
|
|
||||||
|
|
||||||
client = self.get_client(admin=True)
|
|
||||||
token = client.auth_token
|
|
||||||
endpoints = client.tokens.endpoints(token=token)
|
|
||||||
|
|
||||||
# FIXME(ja): this test should require the "keystone:admin" roled
|
# FIXME(ja): this test should require the "keystone:admin" roled
|
||||||
# (probably the role set via --keystone_admin_role flag)
|
# (probably the role set via --keystone_admin_role flag)
|
||||||
# FIXME(ja): add a test that admin endpoint is only sent to admin user
|
# FIXME(ja): add a test that admin endpoint is only sent to admin user
|
||||||
|
Loading…
x
Reference in New Issue
Block a user