Well-formed exception types for 413 & 503.
Fixes bug 952618 Raise well-formed exception types when 413 or 503 status returned to client. Change-Id: I26118ff7ec7ba968b303435287d0eb3ff4bd443f
This commit is contained in:
parent
50e5d3544d
commit
c5937221b5
|
@ -521,6 +521,10 @@ class BaseClient(object):
|
|||
raise TypeError('Unsupported image type: %s' % body.__class__)
|
||||
|
||||
res = c.getresponse()
|
||||
|
||||
def _retry(res):
|
||||
return res.getheader('Retry-After')
|
||||
|
||||
status_code = self.get_status_code(res)
|
||||
if status_code in self.OK_RESPONSE_CODES:
|
||||
return res
|
||||
|
@ -538,8 +542,13 @@ class BaseClient(object):
|
|||
raise exception.Invalid(res.read())
|
||||
elif status_code == httplib.MULTIPLE_CHOICES:
|
||||
raise exception.MultipleChoices(body=res.read())
|
||||
elif status_code == httplib.REQUEST_ENTITY_TOO_LARGE:
|
||||
raise exception.LimitExceeded(retry=_retry(res),
|
||||
body=res.read())
|
||||
elif status_code == httplib.INTERNAL_SERVER_ERROR:
|
||||
raise Exception("Internal Server error: %s" % res.read())
|
||||
elif status_code == httplib.SERVICE_UNAVAILABLE:
|
||||
raise exception.ServiceUnavailable(retry=_retry(res))
|
||||
else:
|
||||
raise Exception("Unknown error occurred! %s" % res.read())
|
||||
|
||||
|
|
|
@ -143,6 +143,28 @@ class MultipleChoices(GlanceException):
|
|||
"request URI.\n\nThe body of response returned:\n%(body)s")
|
||||
|
||||
|
||||
class LimitExceeded(GlanceException):
|
||||
message = _("The request returned a 413 Request Entity Too Large. This "
|
||||
"generally means that rate limiting or a quota threshold was "
|
||||
"breached.\n\nThe response body:\n%(body)s")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.retry_after = (int(kwargs['retry']) if kwargs.get('retry')
|
||||
else None)
|
||||
super(LimitExceeded, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class ServiceUnavailable(GlanceException):
|
||||
message = _("The request returned a 503 ServiceUnavilable. This "
|
||||
"generally occurs on service overload or other transient "
|
||||
"outage.")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.retry_after = (int(kwargs['retry']) if kwargs.get('retry')
|
||||
else None)
|
||||
super(ServiceUnavailable, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class InvalidContentType(GlanceException):
|
||||
message = _("Invalid content type %(content_type)s")
|
||||
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2011 OpenStack, LLC
|
||||
# Copyright 2012 Red Hat, Inc
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""Functional test asserting strongly typed exceptions from glance client"""
|
||||
|
||||
import eventlet.patcher
|
||||
import webob.dec
|
||||
import webob.exc
|
||||
|
||||
from glance.common import client
|
||||
from glance.common import exception
|
||||
from glance.common import wsgi
|
||||
from glance.tests import functional
|
||||
from glance.tests import utils
|
||||
|
||||
|
||||
eventlet.patcher.monkey_patch(socket=True)
|
||||
|
||||
|
||||
class ExceptionTestApp(object):
|
||||
"""
|
||||
Test WSGI application which can respond with multiple kinds of HTTP
|
||||
status codes
|
||||
"""
|
||||
|
||||
@webob.dec.wsgify
|
||||
def __call__(self, request):
|
||||
path = request.path_qs
|
||||
|
||||
if path == "/rate-limit":
|
||||
request.response = webob.exc.HTTPRequestEntityTooLarge()
|
||||
|
||||
elif path == "/rate-limit-retry":
|
||||
request.response.retry_after = 10
|
||||
request.response.status = 413
|
||||
|
||||
if path == "/service-unavailable":
|
||||
request.response = webob.exc.HTTPServiceUnavailable()
|
||||
|
||||
elif path == "/service-unavailable-retry":
|
||||
request.response.retry_after = 10
|
||||
request.response.status = 503
|
||||
|
||||
|
||||
class TestClientExceptions(functional.FunctionalTest):
|
||||
|
||||
def setUp(self):
|
||||
super(TestClientExceptions, self).setUp()
|
||||
self.port = utils.get_unused_port()
|
||||
server = wsgi.Server()
|
||||
conf = utils.TestConfigOpts({'bind_host': '127.0.0.1'})
|
||||
server.start(ExceptionTestApp(), conf, self.port)
|
||||
self.client = client.BaseClient("127.0.0.1", self.port)
|
||||
|
||||
def _do_test_exception(self, path, exc_type):
|
||||
try:
|
||||
self.client.do_request("GET", path)
|
||||
self.fail('expected %s' % exc_type)
|
||||
except exc_type as e:
|
||||
self.assertEquals('retry' in path, e.retry_after == 10)
|
||||
|
||||
def test_rate_limited(self):
|
||||
"""
|
||||
Test rate limited response
|
||||
"""
|
||||
self._do_test_exception('/rate-limit', exception.LimitExceeded)
|
||||
|
||||
def test_rate_limited_retry(self):
|
||||
"""
|
||||
Test rate limited response with retry
|
||||
"""
|
||||
self._do_test_exception('/rate-limit-retry', exception.LimitExceeded)
|
||||
|
||||
def test_service_unavailable(self):
|
||||
"""
|
||||
Test service unavailable response
|
||||
"""
|
||||
self._do_test_exception('/service-unavailable',
|
||||
exception.ServiceUnavailable)
|
||||
|
||||
def test_service_unavailable_retry(self):
|
||||
"""
|
||||
Test service unavailable response with retry
|
||||
"""
|
||||
self._do_test_exception('/service-unavailable-retry',
|
||||
exception.ServiceUnavailable)
|
Loading…
Reference in New Issue