summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--keystone/catalog/backends/kvs.py43
-rw-r--r--keystone/catalog/backends/sql.py61
-rw-r--r--keystone/catalog/core.py30
-rw-r--r--tests/test_backend.py35
-rw-r--r--tests/test_backend_kvs.py16
-rw-r--r--tests/test_backend_templated.py17
-rw-r--r--tests/test_keystoneclient.py5
-rw-r--r--tests/test_v3_catalog.py143
8 files changed, 263 insertions, 87 deletions
diff --git a/keystone/catalog/backends/kvs.py b/keystone/catalog/backends/kvs.py
index e63a75ff..82a30351 100644
--- a/keystone/catalog/backends/kvs.py
+++ b/keystone/catalog/backends/kvs.py
@@ -24,11 +24,7 @@ class Catalog(kvs.Base, catalog.Driver):
def get_catalog(self, user_id, tenant_id, metadata=None):
return self.db.get('catalog-%s-%s' % (tenant_id, user_id))
- def get_service(self, service_id):
- return self.db.get('service-%s' % service_id)
-
- def list_services(self):
- return self.db.get('service_list', [])
+ # service crud
def create_service(self, service_id, service):
self.db.set('service-%s' % service_id, service)
@@ -37,16 +33,53 @@ class Catalog(kvs.Base, catalog.Driver):
self.db.set('service_list', list(service_list))
return service
+ def list_services(self):
+ return [self.get_service(x) for x in self.db.get('service_list', [])]
+
+ def get_service(self, service_id):
+ return self.db.get('service-%s' % service_id)
+
def update_service(self, service_id, service):
self.db.set('service-%s' % service_id, service)
return service
def delete_service(self, service_id):
+ # delete referencing endpoints
+ for endpoint_id in self.db.get('endpoint_list', []):
+ if self.get_endpoint(endpoint_id)['service_id'] == service_id:
+ self.delete_endpoint(endpoint_id)
+
self.db.delete('service-%s' % service_id)
service_list = set(self.db.get('service_list', []))
service_list.remove(service_id)
self.db.set('service_list', list(service_list))
+ # endpoint crud
+
+ def create_endpoint(self, endpoint_id, endpoint):
+ self.get_service(endpoint['service_id'])
+ self.db.set('endpoint-%s' % endpoint_id, endpoint)
+ endpoint_list = set(self.db.get('endpoint_list', []))
+ endpoint_list.add(endpoint_id)
+ self.db.set('endpoint_list', list(endpoint_list))
+ return endpoint
+
+ def list_endpoints(self):
+ return [self.get_endpoint(x) for x in self.db.get('endpoint_list', [])]
+
+ def get_endpoint(self, endpoint_id):
+ return self.db.get('endpoint-%s' % endpoint_id)
+
+ def update_endpoint(self, endpoint_id, endpoint):
+ self.db.set('endpoint-%s' % endpoint_id, endpoint)
+ return endpoint
+
+ def delete_endpoint(self, endpoint_id):
+ self.db.delete('endpoint-%s' % endpoint_id)
+ endpoint_list = set(self.db.get('endpoint_list', []))
+ endpoint_list.remove(endpoint_id)
+ self.db.set('endpoint_list', list(endpoint_list))
+
# Private interface
def _create_catalog(self, user_id, tenant_id, data):
self.db.set('catalog-%s-%s' % (tenant_id, user_id), data)
diff --git a/keystone/catalog/backends/sql.py b/keystone/catalog/backends/sql.py
index b2012c69..df128c7b 100644
--- a/keystone/catalog/backends/sql.py
+++ b/keystone/catalog/backends/sql.py
@@ -52,22 +52,25 @@ class Catalog(sql.Base, catalog.Driver):
# Services
def list_services(self):
session = self.get_session()
- services = session.query(Service)
- return [s['id'] for s in list(services)]
+ services = session.query(Service).all()
+ return [s.to_dict() for s in list(services)]
+
+ def _get_service(self, session, service_id):
+ try:
+ return session.query(Service).filter_by(id=service_id).one()
+ except sql.NotFound:
+ raise exception.ServiceNotFound(service_id=service_id)
def get_service(self, service_id):
session = self.get_session()
- 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 self._get_service(session, service_id).to_dict()
def delete_service(self, service_id):
session = self.get_session()
with session.begin():
+ ref = self._get_service(session, service_id)
session.query(Endpoint).filter_by(service_id=service_id).delete()
- if not session.query(Service).filter_by(id=service_id).delete():
- raise exception.ServiceNotFound(service_id=service_id)
+ session.delete(ref)
session.flush()
def create_service(self, service_id, service_ref):
@@ -78,6 +81,18 @@ class Catalog(sql.Base, catalog.Driver):
session.flush()
return service.to_dict()
+ def update_service(self, service_id, service_ref):
+ session = self.get_session()
+ with session.begin():
+ ref = self._get_service(session, service_id)
+ old_dict = ref.to_dict()
+ old_dict.update(service_ref)
+ new_service = Service.from_dict(old_dict)
+ ref.type = new_service.type
+ ref.extra = new_service.extra
+ session.flush()
+ return ref.to_dict()
+
# Endpoints
def create_endpoint(self, endpoint_id, endpoint_ref):
session = self.get_session()
@@ -95,18 +110,33 @@ class Catalog(sql.Base, catalog.Driver):
raise exception.EndpointNotFound(endpoint_id=endpoint_id)
session.flush()
+ def _get_endpoint(self, session, endpoint_id):
+ try:
+ return session.query(Endpoint).filter_by(id=endpoint_id).one()
+ except sql.NotFound:
+ raise exception.EndpointNotFound(endpoint_id=endpoint_id)
+
def get_endpoint(self, endpoint_id):
session = self.get_session()
- endpoint_ref = session.query(Endpoint)
- endpoint_ref = endpoint_ref.filter_by(id=endpoint_id).first()
- if not endpoint_ref:
- raise exception.EndpointNotFound(endpoint_id=endpoint_id)
- return endpoint_ref.to_dict()
+ return self._get_endpoint(session, endpoint_id).to_dict()
def list_endpoints(self):
session = self.get_session()
endpoints = session.query(Endpoint)
- return [e['id'] for e in list(endpoints)]
+ return [e.to_dict() for e in list(endpoints)]
+
+ def update_endpoint(self, endpoint_id, endpoint_ref):
+ session = self.get_session()
+ with session.begin():
+ ref = self._get_endpoint(session, endpoint_id)
+ old_dict = ref.to_dict()
+ old_dict.update(endpoint_ref)
+ new_endpoint = Endpoint.from_dict(old_dict)
+ ref.service_id = new_endpoint.service_id
+ ref.region = new_endpoint.region
+ ref.extra = new_endpoint.extra
+ session.flush()
+ return ref.to_dict()
def get_catalog(self, user_id, tenant_id, metadata=None):
d = dict(CONF.iteritems())
@@ -114,8 +144,7 @@ class Catalog(sql.Base, catalog.Driver):
'user_id': user_id})
catalog = {}
- endpoints = [self.get_endpoint(e)
- for e in self.list_endpoints()]
+ endpoints = self.list_endpoints()
for ep in endpoints:
service = self.get_service(ep['service_id'])
srv_type = service['type']
diff --git a/keystone/catalog/core.py b/keystone/catalog/core.py
index e5b8fe66..2c396b5d 100644
--- a/keystone/catalog/core.py
+++ b/keystone/catalog/core.py
@@ -116,14 +116,6 @@ class Driver(object):
raise exception.NotImplemented()
def list_services(self):
- """List all service ids in catalog.
-
- :returns: list of service_ids or an empty list.
-
- """
- raise exception.NotImplemented()
-
- def get_all_services(self):
"""List all services.
:returns: list of service_refs or an empty list.
@@ -176,14 +168,6 @@ class Driver(object):
raise exception.NotImplemented()
def list_endpoints(self):
- """List all endpoint ids in catalog.
-
- :returns: list of endpoint_ids or an empty list.
-
- """
- raise exception.NotImplemented()
-
- def get_all_endpoints(self):
"""List all endpoints.
:returns: list of endpoint_refs or an empty list.
@@ -242,14 +226,10 @@ class ServiceController(wsgi.Application):
self.token_api = token.Manager()
super(ServiceController, self).__init__()
- # CRUD extensions
- # NOTE(termie): this OS-KSADM stuff is not very consistent
def get_services(self, context):
self.assert_admin(context)
service_list = self.catalog_api.list_services(context)
- service_refs = [self.catalog_api.get_service(context, x)
- for x in service_list]
- return {'OS-KSADM:services': service_refs}
+ return {'OS-KSADM:services': service_list}
def get_service(self, context, service_id):
self.assert_admin(context)
@@ -281,9 +261,7 @@ class EndpointController(wsgi.Application):
def get_endpoints(self, context):
self.assert_admin(context)
endpoint_list = self.catalog_api.list_endpoints(context)
- endpoint_refs = [self.catalog_api.get_endpoint(context, e)
- for e in endpoint_list]
- return {'endpoints': endpoint_refs}
+ return {'endpoints': endpoint_list}
def create_endpoint(self, context, endpoint):
self.assert_admin(context)
@@ -312,7 +290,7 @@ class ServiceControllerV3(controller.V3Controller):
def list_services(self, context):
self.assert_admin(context)
- refs = self.catalog_api.get_all_services(context)
+ refs = self.catalog_api.list_services(context)
refs = self._filter_by_attribute(context, refs, 'type')
return {'services': self._paginate(context, refs)}
@@ -351,7 +329,7 @@ class EndpointControllerV3(controller.V3Controller):
def list_endpoints(self, context):
self.assert_admin(context)
- refs = self.catalog_api.get_all_endpoints(context)
+ refs = self.catalog_api.list_endpoints(context)
refs = self._filter_by_attribute(context, refs, 'service_id')
refs = self._filter_by_attribute(context, refs, 'interface')
return {'endpoints': self._paginate(context, refs)}
diff --git a/tests/test_backend.py b/tests/test_backend.py
index b1b0da8a..5fd8d3cc 100644
--- a/tests/test_backend.py
+++ b/tests/test_backend.py
@@ -863,7 +863,7 @@ class CatalogTests(object):
# list
services = self.catalog_api.list_services()
- self.assertIn(service_id, services)
+ self.assertIn(service_id, [x['id'] for x in services])
# delete
self.catalog_api.delete_service(service_id)
@@ -872,6 +872,30 @@ class CatalogTests(object):
self.assertRaises(exception.ServiceNotFound,
self.catalog_man.get_service, {}, service_id)
+ def test_delete_service_with_endpoint(self):
+ # create a service
+ service = {
+ 'id': uuid.uuid4().hex,
+ 'type': uuid.uuid4().hex,
+ 'name': uuid.uuid4().hex,
+ 'description': uuid.uuid4().hex,
+ }
+ self.catalog_api.create_service(service['id'], service)
+
+ # create an endpoint attached to the service
+ endpoint = {
+ 'id': uuid.uuid4().hex,
+ 'service_id': service['id'],
+ }
+ self.catalog_api.create_endpoint(endpoint['id'], endpoint)
+
+ # deleting the service should also delete the endpoint
+ self.catalog_api.delete_service(service['id'])
+ self.assertRaises(exception.EndpointNotFound,
+ self.catalog_man.get_endpoint, {}, endpoint['id'])
+ self.assertRaises(exception.EndpointNotFound,
+ self.catalog_man.delete_endpoint, {}, endpoint['id'])
+
def test_get_service_404(self):
self.assertRaises(exception.ServiceNotFound,
self.catalog_man.get_service,
@@ -890,18 +914,21 @@ class CatalogTests(object):
'service_id': uuid.uuid4().hex,
}
self.assertRaises(exception.ServiceNotFound,
- self.catalog_api.create_endpoint,
+ self.catalog_man.create_endpoint,
+ {},
endpoint['id'],
endpoint)
def test_get_endpoint_404(self):
self.assertRaises(exception.EndpointNotFound,
- self.catalog_api.get_endpoint,
+ self.catalog_man.get_endpoint,
+ {},
uuid.uuid4().hex)
def test_delete_endpoint_404(self):
self.assertRaises(exception.EndpointNotFound,
- self.catalog_api.delete_endpoint,
+ self.catalog_man.delete_endpoint,
+ {},
uuid.uuid4().hex)
diff --git a/tests/test_backend_kvs.py b/tests/test_backend_kvs.py
index 76bf9bd0..d3c79e70 100644
--- a/tests/test_backend_kvs.py
+++ b/tests/test_backend_kvs.py
@@ -68,19 +68,3 @@ class KvsCatalog(test.TestCase, test_backend.CatalogTests):
def test_get_catalog(self):
catalog_ref = self.catalog_api.get_catalog('foo', 'bar')
self.assertDictEqual(catalog_ref, self.catalog_foobar)
-
- def test_create_endpoint_404(self):
- self.assertRaises(exception.NotImplemented,
- self.catalog_api.create_endpoint,
- uuid.uuid4().hex,
- {})
-
- def test_get_endpoint_404(self):
- self.assertRaises(exception.NotImplemented,
- self.catalog_api.get_endpoint,
- uuid.uuid4().hex)
-
- def test_delete_endpoint_404(self):
- self.assertRaises(exception.NotImplemented,
- self.catalog_api.delete_endpoint,
- uuid.uuid4().hex)
diff --git a/tests/test_backend_templated.py b/tests/test_backend_templated.py
index 55cede2e..31d77acd 100644
--- a/tests/test_backend_templated.py
+++ b/tests/test_backend_templated.py
@@ -15,7 +15,6 @@
# under the License.
import os
-import uuid
from keystone import catalog
from keystone.catalog.backends import templated as catalog_templated
@@ -67,19 +66,3 @@ class TestTemplatedCatalog(test.TestCase, test_backend.CatalogTests):
'http://localhost:$(compute_port)s/v1.1/$(tenant)s'
with self.assertRaises(exception.MalformedEndpoint):
self.catalog_api.get_catalog('fake-user', 'fake-tenant')
-
- def test_create_endpoint_404(self):
- self.assertRaises(exception.NotImplemented,
- self.catalog_api.create_endpoint,
- uuid.uuid4().hex,
- {})
-
- def test_get_endpoint_404(self):
- self.assertRaises(exception.NotImplemented,
- self.catalog_api.get_endpoint,
- uuid.uuid4().hex)
-
- def test_delete_endpoint_404(self):
- self.assertRaises(exception.NotImplemented,
- self.catalog_api.delete_endpoint,
- uuid.uuid4().hex)
diff --git a/tests/test_keystoneclient.py b/tests/test_keystoneclient.py
index 7531556b..0b82685e 100644
--- a/tests/test_keystoneclient.py
+++ b/tests/test_keystoneclient.py
@@ -711,7 +711,7 @@ class KeystoneClientTests(object):
self.assertEquals(service_type, service.type)
self.assertEquals(service_desc, service.description)
- # update is not supported...
+ # update is not supported in API v2...
# delete & read
client.services.delete(id=service.id)
@@ -736,10 +736,9 @@ class KeystoneClientTests(object):
id=uuid.uuid4().hex)
def test_endpoint_delete_404(self):
- # the catalog backend is expected to return Not Implemented
from keystoneclient import exceptions as client_exceptions
client = self.get_client(admin=True)
- self.assertRaises(client_exceptions.HTTPNotImplemented,
+ self.assertRaises(client_exceptions.NotFound,
client.endpoints.delete,
id=uuid.uuid4().hex)
diff --git a/tests/test_v3_catalog.py b/tests/test_v3_catalog.py
new file mode 100644
index 00000000..9f5bf913
--- /dev/null
+++ b/tests/test_v3_catalog.py
@@ -0,0 +1,143 @@
+import uuid
+
+import test_v3
+
+
+class CatalogTestCase(test_v3.RestfulTestCase):
+ """Test service & endpoint CRUD"""
+
+ def setUp(self):
+ super(CatalogTestCase, self).setUp()
+
+ self.service_id = uuid.uuid4().hex
+ self.service = self.new_service_ref()
+ self.service['id'] = self.service_id
+ self.catalog_api.create_service(
+ self.service_id,
+ self.service.copy())
+
+ self.endpoint_id = uuid.uuid4().hex
+ self.endpoint = self.new_endpoint_ref(service_id=self.service_id)
+ self.endpoint['id'] = self.endpoint_id
+ self.catalog_api.create_endpoint(
+ self.endpoint_id,
+ self.endpoint.copy())
+
+ # service validation
+
+ def assertValidServiceListResponse(self, resp, ref):
+ return self.assertValidListResponse(
+ resp,
+ 'services',
+ self.assertValidService,
+ ref)
+
+ def assertValidServiceResponse(self, resp, ref):
+ return self.assertValidResponse(
+ resp,
+ 'service',
+ self.assertValidService,
+ ref)
+
+ def assertValidService(self, entity, ref=None):
+ self.assertIsNotNone(entity.get('type'))
+ if ref:
+ self.assertEqual(ref['type'], entity['type'])
+ return entity
+
+ # endpoint validation
+
+ def assertValidEndpointListResponse(self, resp, ref):
+ return self.assertValidListResponse(
+ resp,
+ 'endpoints',
+ self.assertValidEndpoint,
+ ref)
+
+ def assertValidEndpointResponse(self, resp, ref):
+ return self.assertValidResponse(
+ resp,
+ 'endpoint',
+ self.assertValidEndpoint,
+ ref)
+
+ def assertValidEndpoint(self, entity, ref=None):
+ self.assertIsNotNone(entity.get('interface'))
+ self.assertIsNotNone(entity.get('service_id'))
+ if ref:
+ self.assertEqual(ref['interface'], entity['interface'])
+ self.assertEqual(ref['service_id'], entity['service_id'])
+ return entity
+
+ # service crud tests
+
+ def test_create_service(self):
+ """POST /services"""
+ ref = self.new_service_ref()
+ r = self.post(
+ '/services',
+ body={'service': ref})
+ return self.assertValidServiceResponse(r, ref)
+
+ def test_list_services(self):
+ """GET /services"""
+ r = self.get('/services')
+ self.assertValidServiceListResponse(r, self.service)
+
+ def test_get_service(self):
+ """GET /services/{service_id}"""
+ r = self.get('/services/%(service_id)s' % {
+ 'service_id': self.service_id})
+ self.assertValidServiceResponse(r, self.service)
+
+ def test_update_service(self):
+ """PATCH /services/{service_id}"""
+ service = self.new_service_ref()
+ del service['id']
+ r = self.patch('/services/%(service_id)s' % {
+ 'service_id': self.service_id},
+ body={'service': service})
+ self.assertValidServiceResponse(r, service)
+
+ def test_delete_service(self):
+ """DELETE /services/{service_id}"""
+ self.delete('/services/%(service_id)s' % {
+ 'service_id': self.service_id})
+
+ # endpoint crud tests
+
+ def test_list_endpoints(self):
+ """GET /endpoints"""
+ r = self.get('/endpoints')
+ self.assertValidEndpointListResponse(r, self.endpoint)
+
+ def test_create_endpoint(self):
+ """POST /endpoints"""
+ ref = self.new_endpoint_ref(service_id=self.service_id)
+ r = self.post(
+ '/endpoints',
+ body={'endpoint': ref})
+ self.assertValidEndpointResponse(r, ref)
+
+ def test_get_endpoint(self):
+ """GET /endpoints/{endpoint_id}"""
+ r = self.get(
+ '/endpoints/%(endpoint_id)s' % {
+ 'endpoint_id': self.endpoint_id})
+ self.assertValidEndpointResponse(r, self.endpoint)
+
+ def test_update_endpoint(self):
+ """PATCH /endpoints/{endpoint_id}"""
+ ref = self.new_endpoint_ref(service_id=self.service_id)
+ del ref['id']
+ r = self.patch(
+ '/endpoints/%(endpoint_id)s' % {
+ 'endpoint_id': self.endpoint_id},
+ body={'endpoint': ref})
+ self.assertValidEndpointResponse(r, ref)
+
+ def test_delete_endpoint(self):
+ """DELETE /endpoints/{endpoint_id}"""
+ self.delete(
+ '/endpoints/%(endpoint_id)s' % {
+ 'endpoint_id': self.endpoint_id})