Abstract API exception handling

Change-Id: I5017dfa60d4a8266908cc09c40b95139f01da0de
This commit is contained in:
Kiall Mac Innes 2013-03-22 15:41:09 +00:00
parent 19087e5ba5
commit f0c49181d9
13 changed files with 289 additions and 396 deletions

View File

@ -8,12 +8,15 @@ paste.app_factory = moniker.api.versions:factory
[composite:osapi_dns_v1]
use = call:moniker.api.auth:pipeline_factory
noauth = noauthcontext osapi_dns_app_v1
keystone = authtoken keystonecontext osapi_dns_app_v1
noauth = noauthcontext faultwrapper_v1 osapi_dns_app_v1
keystone = authtoken keystonecontext faultwrapper_v1 osapi_dns_app_v1
[app:osapi_dns_app_v1]
paste.app_factory = moniker.api.v1:factory
[filter:faultwrapper_v1]
paste.filter_factory = moniker.api.v1:FaultWrapperMiddleware.factory
[filter:noauthcontext]
paste.filter_factory = moniker.api.auth:NoAuthContextMiddleware.factory

View File

@ -14,10 +14,15 @@
# License for the specific language governing permissions and limitations
# under the License.
import flask
import webob.dec
from stevedore import extension
from stevedore import named
from moniker.openstack.common import cfg
from moniker.openstack.common import jsonutils as json
from moniker.openstack.common import log as logging
from moniker.openstack.common.rpc import common as rpc_common
from moniker import exceptions
from moniker import wsgi
LOG = logging.getLogger(__name__)
@ -29,6 +34,9 @@ cfg.CONF.register_opts([
def factory(global_config, **local_conf):
app = flask.Flask('moniker.api.v1')
app.config.update(
PROPAGATE_EXCEPTIONS=True
)
# TODO(kiall): Ideally, we want to make use of the Plugin class here.
# This works for the moment though.
@ -48,3 +56,61 @@ def factory(global_config, **local_conf):
extmgr.map(_register_blueprint)
return app
class FaultWrapperMiddleware(wsgi.Middleware):
@webob.dec.wsgify
def __call__(self, request):
try:
return request.get_response(self.application)
except exceptions.Base, e:
# Handle Moniker Exceptions
status = e.error_code if hasattr(e, 'error_code') else 500
# Start building up a response
response = {
'code': status
}
if e.error_type:
response['type'] = e.error_type
if e.error_message:
response['message'] = e.error_message
if e.errors:
response['errors'] = e.errors
return self._handle_exception(request, e, status, response)
except rpc_common.Timeout, e:
# Special case for RPC timeout's
response = {
'code': 504,
'type': 'timeout',
}
return self._handle_exception(request, e, 504, response)
except Exception, e:
# Handle all other exception types
return self._handle_exception(request, e)
def _handle_exception(self, request, e, status=500, response={}):
# Log the exception ASAP
LOG.exception(e)
headers = [
('Content-Type', 'application/json'),
]
# Set a response code and type, if they are missing.
if 'code' not in response:
response['code'] = status
if 'type' not in response:
response['type'] = 'unknown'
# TODO(kiall): Send a fault notification
# Return the new response
return flask.Response(status=status, headers=headers,
response=json.dumps(response))

View File

@ -15,9 +15,6 @@
# under the License.
import flask
from moniker.openstack.common import log as logging
from moniker.openstack.common import jsonutils as json
from moniker.openstack.common.rpc import common as rpc_common
from moniker import exceptions
from moniker import schema
from moniker.central import api as central_api
@ -43,29 +40,13 @@ def create_domain():
context = flask.request.environ.get('context')
values = flask.request.json
try:
domain_schema.validate(values)
domain = central_api.create_domain(context, values)
except exceptions.InvalidObject, e:
response_body = json.dumps({'errors': e.errors})
return flask.Response(status=400, response=response_body)
except exceptions.BadRequest:
return flask.Response(status=400)
except exceptions.Forbidden:
return flask.Response(status=401)
except exceptions.DuplicateDomain:
return flask.Response(status=409)
except exceptions.NoServersConfigured:
return flask.Response(status=500)
except rpc_common.Timeout:
return flask.Response(status=504)
else:
domain = domain_schema.filter(domain)
response = flask.jsonify(domain)
response = flask.jsonify(domain_schema.filter(domain))
response.status_int = 201
response.location = flask.url_for('.get_domain',
domain_id=domain['id'])
response.location = flask.url_for('.get_domain', domain_id=domain['id'])
return response
@ -73,38 +54,18 @@ def create_domain():
def get_domains():
context = flask.request.environ.get('context')
try:
domains = central_api.get_domains(context)
except exceptions.BadRequest:
return flask.Response(status=400)
except exceptions.Forbidden:
return flask.Response(status=401)
except rpc_common.Timeout:
return flask.Response(status=504)
else:
domains = domains_schema.filter({'domains': domains})
return flask.jsonify(domains)
return flask.jsonify(domains_schema.filter({'domains': domains}))
@blueprint.route('/domains/<domain_id>', methods=['GET'])
def get_domain(domain_id):
context = flask.request.environ.get('context')
try:
domain = central_api.get_domain(context, domain_id)
except exceptions.BadRequest:
return flask.Response(status=400)
except exceptions.Forbidden:
return flask.Response(status=401)
except exceptions.DomainNotFound:
return flask.Response(status=404)
except rpc_common.Timeout:
return flask.Response(status=504)
else:
domain = domain_schema.filter(domain)
return flask.jsonify(domain)
return flask.jsonify(domain_schema.filter(domain))
@blueprint.route('/domains/<domain_id>', methods=['PUT'])
@ -112,46 +73,21 @@ def update_domain(domain_id):
context = flask.request.environ.get('context')
values = flask.request.json
try:
domain = central_api.get_domain(context, domain_id)
domain.update(values)
domain_schema.validate(domain)
domain = central_api.update_domain(context, domain_id, values)
except exceptions.InvalidObject, e:
response_body = json.dumps({'errors': e.errors})
return flask.Response(status=400, response=response_body)
except exceptions.BadRequest:
return flask.Response(status=400)
except exceptions.Forbidden:
return flask.Response(status=401)
except exceptions.DomainNotFound:
return flask.Response(status=404)
except exceptions.DuplicateDomain:
return flask.Response(status=409)
except rpc_common.Timeout:
return flask.Response(status=504)
else:
domain = domain_schema.filter(domain)
return flask.jsonify(domain)
return flask.jsonify(domain_schema.filter(domain))
@blueprint.route('/domains/<domain_id>', methods=['DELETE'])
def delete_domain(domain_id):
context = flask.request.environ.get('context')
try:
central_api.delete_domain(context, domain_id)
except exceptions.BadRequest:
return flask.Response(status=400)
except exceptions.Forbidden:
return flask.Response(status=401)
except exceptions.DomainNotFound:
return flask.Response(status=404)
except rpc_common.Timeout:
return flask.Response(status=504)
else:
return flask.Response(status=200)
@ -159,17 +95,6 @@ def delete_domain(domain_id):
def get_domain_servers(domain_id):
context = flask.request.environ.get('context')
try:
servers = central_api.get_domain_servers(context, domain_id)
except exceptions.BadRequest:
return flask.Response(status=400)
except exceptions.Forbidden:
return flask.Response(status=401)
except exceptions.DomainNotFound:
return flask.Response(status=404)
except rpc_common.Timeout:
return flask.Response(status=504)
else:
servers = servers_schema.filter({'servers': servers})
return flask.jsonify(servers)
return flask.jsonify(servers_schema.filter({'servers': servers}))

View File

@ -15,9 +15,6 @@
# under the License.
import flask
from moniker.openstack.common import log as logging
from moniker.openstack.common import jsonutils as json
from moniker.openstack.common.rpc import common as rpc_common
from moniker import exceptions
from moniker import schema
from moniker.central import api as central_api
@ -42,28 +39,14 @@ def create_record(domain_id):
context = flask.request.environ.get('context')
values = flask.request.json
try:
record_schema.validate(values)
record = central_api.create_record(context, domain_id, values)
except exceptions.InvalidObject, e:
response_body = json.dumps({'errors': e.errors})
return flask.Response(status=400, response=response_body)
except exceptions.BadRequest:
return flask.Response(status=400)
except exceptions.Forbidden:
return flask.Response(status=401)
except exceptions.DuplicateRecord:
return flask.Response(status=409)
except rpc_common.Timeout:
return flask.Response(status=504)
else:
record = record_schema.filter(record)
response = flask.jsonify(record)
response = flask.jsonify(record_schema.filter(record))
response.status_int = 201
response.location = flask.url_for('.get_record',
domain_id=domain_id,
response.location = flask.url_for('.get_record', domain_id=domain_id,
record_id=record['id'])
return response
@ -71,40 +54,18 @@ def create_record(domain_id):
def get_records(domain_id):
context = flask.request.environ.get('context')
try:
records = central_api.get_records(context, domain_id)
except exceptions.BadRequest:
return flask.Response(status=400)
except exceptions.Forbidden:
return flask.Response(status=401)
except exceptions.DomainNotFound:
return flask.Response(status=404)
except rpc_common.Timeout:
return flask.Response(status=504)
else:
records = records_schema.filter({'records': records})
return flask.jsonify(records)
return flask.jsonify(records_schema.filter({'records': records}))
@blueprint.route('/domains/<domain_id>/records/<record_id>', methods=['GET'])
def get_record(domain_id, record_id):
context = flask.request.environ.get('context')
try:
record = central_api.get_record(context, domain_id, record_id)
except exceptions.BadRequest:
return flask.Response(status=400)
except exceptions.Forbidden:
return flask.Response(status=401)
except (exceptions.RecordNotFound, exceptions.DomainNotFound):
return flask.Response(status=404)
except rpc_common.Timeout:
return flask.Response(status=504)
else:
record = record_schema.filter(record)
return flask.jsonify(record)
return flask.jsonify(record_schema.filter(record))
@blueprint.route('/domains/<domain_id>/records/<record_id>', methods=['PUT'])
@ -112,30 +73,13 @@ def update_record(domain_id, record_id):
context = flask.request.environ.get('context')
values = flask.request.json
try:
record = central_api.get_record(context, domain_id, record_id)
record.update(values)
record_schema.validate(record)
record = central_api.update_record(context, domain_id, record_id,
values)
except exceptions.InvalidObject, e:
response_body = json.dumps({'errors': e.errors})
return flask.Response(status=400, response=response_body)
except exceptions.BadRequest:
return flask.Response(status=400)
except exceptions.Forbidden:
return flask.Response(status=401)
except (exceptions.RecordNotFound, exceptions.DomainNotFound):
return flask.Response(status=404)
except exceptions.DuplicateRecord:
return flask.Response(status=409)
except rpc_common.Timeout:
return flask.Response(status=504)
else:
record = record_schema.filter(record)
record = central_api.update_record(context, domain_id, record_id, values)
return flask.jsonify(record)
return flask.jsonify(record_schema.filter(record))
@blueprint.route('/domains/<domain_id>/records/<record_id>',
@ -143,15 +87,6 @@ def update_record(domain_id, record_id):
def delete_record(domain_id, record_id):
context = flask.request.environ.get('context')
try:
central_api.delete_record(context, domain_id, record_id)
except exceptions.BadRequest:
return flask.Response(status=400)
except exceptions.Forbidden:
return flask.Response(status=401)
except (exceptions.RecordNotFound, exceptions.DomainNotFound):
return flask.Response(status=404)
except rpc_common.Timeout:
return flask.Response(status=504)
else:
return flask.Response(status=200)

View File

@ -15,9 +15,6 @@
# under the License.
import flask
from moniker.openstack.common import log as logging
from moniker.openstack.common import jsonutils as json
from moniker.openstack.common.rpc import common as rpc_common
from moniker import exceptions
from moniker import schema
from moniker.central import api as central_api
@ -42,27 +39,13 @@ def create_server():
context = flask.request.environ.get('context')
values = flask.request.json
try:
server_schema.validate(values)
server = central_api.create_server(context, values=flask.request.json)
except exceptions.InvalidObject, e:
response_body = json.dumps({'errors': e.errors})
return flask.Response(status=400, response=response_body)
except exceptions.BadRequest:
return flask.Response(status=400)
except exceptions.Forbidden:
return flask.Response(status=401)
except exceptions.DuplicateServer:
return flask.Response(status=409)
except rpc_common.Timeout:
return flask.Response(status=504)
else:
server = server_schema.filter(server)
response = flask.jsonify(server)
response = flask.jsonify(server_schema.filter(server))
response.status_int = 201
response.location = flask.url_for('.get_server',
server_id=server['id'])
response.location = flask.url_for('.get_server', server_id=server['id'])
return response
@ -70,38 +53,18 @@ def create_server():
def get_servers():
context = flask.request.environ.get('context')
try:
servers = central_api.get_servers(context)
except exceptions.BadRequest:
return flask.Response(status=400)
except exceptions.Forbidden:
return flask.Response(status=401)
except rpc_common.Timeout:
return flask.Response(status=504)
else:
servers = servers_schema.filter({'servers': servers})
return flask.jsonify(servers)
return flask.jsonify(servers_schema.filter({'servers': servers}))
@blueprint.route('/servers/<server_id>', methods=['GET'])
def get_server(server_id):
context = flask.request.environ.get('context')
try:
server = central_api.get_server(context, server_id)
except exceptions.BadRequest:
return flask.Response(status=400)
except exceptions.Forbidden:
return flask.Response(status=401)
except exceptions.ServerNotFound:
return flask.Response(status=404)
except rpc_common.Timeout:
return flask.Response(status=504)
else:
server = server_schema.filter(server)
return flask.jsonify(server)
return flask.jsonify(server_schema.filter(server))
@blueprint.route('/servers/<server_id>', methods=['PUT'])
@ -109,44 +72,19 @@ def update_server(server_id):
context = flask.request.environ.get('context')
values = flask.request.json
try:
server = central_api.get_server(context, server_id)
server.update(values)
server_schema.validate(server)
server = central_api.update_server(context, server_id, values=values)
except exceptions.InvalidObject, e:
response_body = json.dumps({'errors': e.errors})
return flask.Response(status=400, response=response_body)
except exceptions.BadRequest:
return flask.Response(status=400)
except exceptions.Forbidden:
return flask.Response(status=401)
except exceptions.ServerNotFound:
return flask.Response(status=404)
except exceptions.DuplicateServer:
return flask.Response(status=409)
except rpc_common.Timeout:
return flask.Response(status=504)
else:
server = server_schema.filter(server)
return flask.jsonify(server)
return flask.jsonify(server_schema.filter(server))
@blueprint.route('/servers/<server_id>', methods=['DELETE'])
def delete_server(server_id):
context = flask.request.environ.get('context')
try:
central_api.delete_server(context, server_id)
except exceptions.BadRequest:
return flask.Response(status=400)
except exceptions.Forbidden:
return flask.Response(status=401)
except exceptions.ServerNotFound:
return flask.Response(status=404)
except rpc_common.Timeout:
return flask.Response(status=504)
else:
return flask.Response(status=200)

View File

@ -15,9 +15,6 @@
# under the License.
import flask
from moniker.openstack.common import log as logging
from moniker.openstack.common import jsonutils as json
from moniker.openstack.common.rpc import common as rpc_common
from moniker import exceptions
from moniker import schema
from moniker.central import api as central_api
@ -42,28 +39,13 @@ def create_tsigkey():
context = flask.request.environ.get('context')
values = flask.request.json
try:
tsigkey_schema.validate(values)
tsigkey = central_api.create_tsigkey(context,
values=flask.request.json)
except exceptions.InvalidObject, e:
response_body = json.dumps({'errors': e.errors})
return flask.Response(status=400, response=response_body)
except exceptions.BadRequest:
return flask.Response(status=400)
except exceptions.Forbidden:
return flask.Response(status=401)
except exceptions.DuplicateTsigKey:
return flask.Response(status=409)
except rpc_common.Timeout:
return flask.Response(status=504)
else:
tsigkey = tsigkey_schema.filter(tsigkey)
tsigkey = central_api.create_tsigkey(context, values=flask.request.json)
response = flask.jsonify(tsigkey)
response = flask.jsonify(tsigkey_schema.filter(tsigkey))
response.status_int = 201
response.location = flask.url_for('.get_tsigkey',
tsigkey_id=tsigkey['id'])
response.location = flask.url_for('.get_tsigkey', tsigkey_id=tsigkey['id'])
return response
@ -71,38 +53,18 @@ def create_tsigkey():
def get_tsigkeys():
context = flask.request.environ.get('context')
try:
tsigkeys = central_api.get_tsigkeys(context)
except exceptions.BadRequest:
return flask.Response(status=400)
except exceptions.Forbidden:
return flask.Response(status=401)
except rpc_common.Timeout:
return flask.Response(status=504)
else:
tsigkeys = tsigkeys_schema.filter({'tsigkeys': tsigkeys})
return flask.jsonify(tsigkeys)
return flask.jsonify(tsigkeys_schema.filter({'tsigkeys': tsigkeys}))
@blueprint.route('/tsigkeys/<tsigkey_id>', methods=['GET'])
def get_tsigkey(tsigkey_id):
context = flask.request.environ.get('context')
try:
tsigkey = central_api.get_tsigkey(context, tsigkey_id)
except exceptions.BadRequest:
return flask.Response(status=400)
except exceptions.Forbidden:
return flask.Response(status=401)
except exceptions.TsigKeyNotFound:
return flask.Response(status=404)
except rpc_common.Timeout:
return flask.Response(status=504)
else:
tsigkey = tsigkey_schema.filter(tsigkey)
return flask.jsonify(tsigkey)
return flask.jsonify(tsigkey_schema.filter(tsigkey))
@blueprint.route('/tsigkeys/<tsigkey_id>', methods=['PUT'])
@ -110,45 +72,19 @@ def update_tsigkey(tsigkey_id):
context = flask.request.environ.get('context')
values = flask.request.json
try:
tsigkey = central_api.get_tsigkey(context, tsigkey_id)
tsigkey.update(values)
tsigkey_schema.validate(tsigkey)
tsigkey = central_api.update_tsigkey(context, tsigkey_id,
values=values)
except exceptions.InvalidObject, e:
response_body = json.dumps({'errors': e.errors})
return flask.Response(status=400, response=response_body)
except exceptions.BadRequest:
return flask.Response(status=400)
except exceptions.Forbidden:
return flask.Response(status=401)
except exceptions.TsigKeyNotFound:
return flask.Response(status=404)
except exceptions.DuplicateTsigKey:
return flask.Response(status=409)
except rpc_common.Timeout:
return flask.Response(status=504)
else:
tsigkey = tsigkey_schema.filter(tsigkey)
tsigkey = central_api.update_tsigkey(context, tsigkey_id, values=values)
return flask.jsonify(tsigkey)
return flask.jsonify(tsigkey_schema.filter(tsigkey))
@blueprint.route('/tsigkeys/<tsigkey_id>', methods=['DELETE'])
def delete_tsigkey(tsigkey_id):
context = flask.request.environ.get('context')
try:
central_api.delete_tsigkey(context, tsigkey_id)
except exceptions.BadRequest:
return flask.Response(status=400)
except exceptions.Forbidden:
return flask.Response(status=401)
except exceptions.TsigKeyNotFound:
return flask.Response(status=404)
except rpc_common.Timeout:
return flask.Response(status=504)
else:
return flask.Response(status=200)

View File

@ -72,27 +72,28 @@ class Service(rpc_service.Service):
def _is_valid_domain_name(self, context, domain_name):
# Validate domain name length
if len(domain_name) > cfg.CONF['service:central'].max_domain_name_len:
raise exceptions.BadRequest('Domain name too long')
raise exceptions.InvalidDomainName('Name too long')
# Break the domain name up into its component labels
domain_labels = domain_name.strip('.').split('.')
# We need more than 1 label.
if len(domain_labels) <= 1:
raise exceptions.BadRequest('Invalid Domain Name')
raise exceptions.InvalidDomainName('More than one label is '
'required')
# Check the TLD for validity
if self.accepted_tld_list:
domain_tld = domain_labels[-1].lower()
if domain_tld not in self.accepted_tld_list:
raise exceptions.BadRequest('Invalid TLD')
raise exceptions.InvalidDomainName('Unknown or invalid TLD')
# Check domain name blacklist
if self._is_blacklisted_domain_name(context, domain_name):
# Some users are allowed bypass the blacklist.. Is this one?
if not policy.check('use_blacklisted_domain', context, exc=None):
raise exceptions.BadRequest('Blacklisted domain name')
raise exceptions.InvalidDomainName('Blacklisted domain name')
return True
@ -102,17 +103,18 @@ class Service(rpc_service.Service):
# Validate record name length
if len(record_name) > cfg.CONF['service:central'].max_record_name_len:
raise exceptions.BadRequest('Record name too long')
raise exceptions.InvalidRecordName('Name too long')
# Record must be contained in the parent zone.
if not record_name.endswith(domain['name']):
raise exceptions.BadRequest('Records must be contained within '
'their parent zone.')
raise exceptions.InvalidRecordLocation('Record is not contained '
'within it\'s parent '
'domain')
# CNAME's must not be created at the zone apex.
if record_type == 'CNAME' and record_name == domain['name']:
raise exceptions.BadRequest('%s records may not be created at '
'the zone apex' % record_type)
raise exceptions.InvalidRecordLocation('CNAME records may not be '
'created at the zone apex')
# CNAME's must not share a name with other records
criterion = {'name': record_name}
@ -123,8 +125,9 @@ class Service(rpc_service.Service):
records = self.storage.get_records(context, domain['id'],
criterion=criterion)
if len(records) > 0:
raise exceptions.BadRequest('CNAME records must not share a name'
'with any other records')
raise exceptions.InvalidRecordLocation('CNAME records may not '
'share a name with any '
'other records')
if record_type == 'CNAME':
# CNAME's may not have children. Ever.
@ -133,15 +136,17 @@ class Service(rpc_service.Service):
criterion=criterion)
if len(records) > 0:
raise exceptions.BadRequest('CNAME records must not have any '
'child records')
raise exceptions.InvalidRecordLocation('CNAME records may not '
'have any child '
'records')
else:
# No record may have a CNAME as a parent
if self._is_subrecord(context, domain, record_name,
{'type': 'CNAME'}):
raise exceptions.BadRequest('Records must not be children of '
'a CNAME record')
raise exceptions.InvalidRecordLocation('CNAME records may not '
'have any child '
'records')
return True

View File

@ -16,7 +16,18 @@
class Base(Exception):
pass
error_code = 500
error_type = None
error_message = None
errors = None
def __init__(self, *args, **kwargs):
self.errors = kwargs.pop('errors', None)
super(Base, self).__init__(*args, **kwargs)
if len(args) > 0 and isinstance(args[0], basestring):
self.error_message = args[0]
class Backend(Exception):
@ -28,7 +39,7 @@ class NotImplemented(Base, NotImplementedError):
class ConfigurationError(Base):
pass
error_type = 'configuration_error'
class NoServersConfigured(ConfigurationError):
@ -36,17 +47,33 @@ class NoServersConfigured(ConfigurationError):
class InvalidObject(Base):
def __init__(self, *args, **kwargs):
self.errors = kwargs.pop('errors', None)
super(InvalidObject, self).__init__(*args, **kwargs)
error_code = 400
error_type = 'invalid_object'
class BadRequest(Base):
pass
error_code = 400
error_type = 'bad_request'
class InvalidDomainName(Base):
error_code = 400
error_type = 'invalid_domain_name'
class InvalidRecordName(Base):
error_code = 400
error_type = 'invalid_record_name'
class InvalidRecordLocation(Base):
error_code = 400
error_type = 'invalid_record_location'
class Forbidden(Base):
pass
error_code = 401
error_type = 'forbidden'
class InvalidSortKey(Base):
@ -54,44 +81,47 @@ class InvalidSortKey(Base):
class Duplicate(Base):
pass
error_code = 409
error_type = 'duplicate'
class DuplicateServer(Duplicate):
pass
error_type = 'duplicate_server'
class DuplicateTsigKey(Duplicate):
pass
error_type = 'duplicate_tsigkey'
class DuplicateDomain(Duplicate):
pass
error_type = 'duplicate_domain'
class DuplicateRecord(Duplicate):
pass
error_type = 'duplicate_record'
class NotFound(Base):
pass
error_code = 404
error_type = 'not_found'
class ServerNotFound(NotFound):
pass
error_type = 'server_not_found'
class TsigKeyNotFound(NotFound):
pass
error_type = 'tsigkey_not_found'
class DomainNotFound(NotFound):
pass
error_type = 'domain_not_found'
class RecordNotFound(NotFound):
pass
error_type = 'record_not_found'
class ResourceNotFound(NotFound):
# TODO(kiall): Should this be extending NotFound??
pass

View File

@ -313,7 +313,7 @@ def deserialize_remote_exception(conf, data):
if not issubclass(klass, Exception):
raise TypeError("Can only deserialize Exceptions")
failure = klass(**failure.get('kwargs', {}))
failure = klass(*failure.get('args', []), **failure.get('kwargs', {}))
except (AttributeError, TypeError, ImportError):
return RemoteError(name, failure.get('message'), trace)

View File

@ -0,0 +1,48 @@
{
"id": "/schemas/fault",
"$schema": "http://json-schema.org/draft-03/hyper-schema",
"title": "fault",
"description": "Fault",
"properties": {
"code": {
"type": "integer",
"description": "Fault Code",
"required": true,
"readonly": true
},
"type": {
"type": "string",
"description": "Fault Type",
"required": true,
"readonly": true
},
"message": {
"type": "string",
"description": "Fault Message",
"readonly": true
},
"errors": {
"type": "array",
"description": "List of Errors",
"readonly": true,
"items": {
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "Error Path",
"readonly": true
},
"type": {
"type": "string",
"description": "Error Type",
"readonly": true
}
}
}
}
}
}

View File

@ -21,6 +21,10 @@ class ApiServiceTest(ApiTestCase):
def setUp(self):
super(ApiServiceTest, self).setUp()
# Use a random port for the API
self.config(api_port=0, group='service:api')
self.service = self.get_api_service()
def test_start_and_stop(self):

View File

@ -14,8 +14,8 @@
# License for the specific language governing permissions and limitations
# under the License.
from moniker.openstack.common import log as logging
from moniker.api.v1 import factory
from moniker.api.auth import NoAuthContextMiddleware
from moniker.api import v1 as api_v1
from moniker.api import auth
from moniker.tests.test_api import ApiTestCase
@ -29,10 +29,13 @@ class ApiV1Test(ApiTestCase):
super(ApiV1Test, self).setUp()
# Create a Flask application
self.app = factory({})
self.app = api_v1.factory({})
# Inject the FaultWrapper middleware
self.app.wsgi_app = api_v1.FaultWrapperMiddleware(self.app.wsgi_app)
# Inject the NoAuth middleware
self.app.wsgi_app = NoAuthContextMiddleware(self.app.wsgi_app)
self.app.wsgi_app = auth.NoAuthContextMiddleware(self.app.wsgi_app)
# Obtain a test client
self.client = self.app.test_client()

View File

@ -43,10 +43,10 @@ class CentralServiceTest(CentralTestCase):
self.central_service._is_valid_domain_name(context, 'valid.org.')
with self.assertRaises(exceptions.BadRequest):
with self.assertRaises(exceptions.InvalidDomainName):
self.central_service._is_valid_domain_name(context, 'example.org.')
with self.assertRaises(exceptions.BadRequest):
with self.assertRaises(exceptions.InvalidDomainName):
self.central_service._is_valid_domain_name(context, 'example.tld.')
def test_is_valid_record_name(self):
@ -62,15 +62,15 @@ class CentralServiceTest(CentralTestCase):
'valid.example.org.',
'A')
with self.assertRaises(exceptions.BadRequest):
with self.assertRaises(exceptions.InvalidRecordName):
self.central_service._is_valid_record_name(
context, domain, 'toolong.example.org.', 'A')
with self.assertRaises(exceptions.BadRequest):
with self.assertRaises(exceptions.InvalidRecordLocation):
self.central_service._is_valid_record_name(
context, domain, 'a.example.COM.', 'A')
with self.assertRaises(exceptions.BadRequest):
with self.assertRaises(exceptions.InvalidRecordLocation):
self.central_service._is_valid_record_name(
context, domain, 'example.org.', 'CNAME')
@ -386,7 +386,7 @@ class CentralServiceTest(CentralTestCase):
email='info@blacklisted.com'
)
with self.assertRaises(exceptions.BadRequest):
with self.assertRaises(exceptions.InvalidDomainName):
# Create a domain
self.central_service.create_domain(context, values=values)
@ -412,7 +412,7 @@ class CentralServiceTest(CentralTestCase):
email='info@invalid.com'
)
with self.assertRaises(exceptions.BadRequest):
with self.assertRaises(exceptions.InvalidDomainName):
# Create an invalid domain
self.central_service.create_domain(context, values=values)
@ -630,7 +630,7 @@ class CentralServiceTest(CentralTestCase):
)
# Attempt to create a CNAME record at the apex
with self.assertRaises(exceptions.BadRequest):
with self.assertRaises(exceptions.InvalidRecordLocation):
self.central_service.create_record(context, domain['id'],
values=values)
@ -648,7 +648,7 @@ class CentralServiceTest(CentralTestCase):
values=values)
# Attempt to create a CNAME record alongside an A record
with self.assertRaises(exceptions.BadRequest):
with self.assertRaises(exceptions.InvalidRecordLocation):
values = dict(
name='www.%s' % domain['name'],
type='CNAME',
@ -672,7 +672,7 @@ class CentralServiceTest(CentralTestCase):
values=values)
# Attempt to create a CNAME record alongside an A record
with self.assertRaises(exceptions.BadRequest):
with self.assertRaises(exceptions.InvalidRecordLocation):
values = dict(
name='www.%s' % domain['name'],
type='CNAME',
@ -696,7 +696,7 @@ class CentralServiceTest(CentralTestCase):
values=values)
# Attempt to create a CNAME record alongside an A record
with self.assertRaises(exceptions.BadRequest):
with self.assertRaises(exceptions.InvalidRecordLocation):
values = dict(
name='www.%s' % domain['name'],
type='A',
@ -720,7 +720,7 @@ class CentralServiceTest(CentralTestCase):
values=values)
# Attempt to create a CNAME record alongside an A record
with self.assertRaises(exceptions.BadRequest):
with self.assertRaises(exceptions.InvalidRecordLocation):
values = dict(
name='t.www.%s' % domain['name'],
type='A',