Merge "Updates microversion root and error messages"
This commit is contained in:
commit
bbb0f6c2b2
@ -31,12 +31,24 @@ class Version(base.APIBase):
|
||||
links = [link.Link]
|
||||
"""A Link that point to a specific version of the API"""
|
||||
|
||||
status = wtypes.text
|
||||
"""The current status of the version: CURRENT, SUPPORTED, UNSUPPORTED"""
|
||||
|
||||
max_version = wtypes.text
|
||||
"""The max microversion supported by this version"""
|
||||
|
||||
min_version = wtypes.text
|
||||
"""The min microversion supported by this version"""
|
||||
|
||||
@staticmethod
|
||||
def convert(id):
|
||||
def convert(id, status, max, min):
|
||||
version = Version()
|
||||
version.id = id
|
||||
version.links = [link.Link.make_link('self', pecan.request.host_url,
|
||||
id, '', bookmark=True)]
|
||||
version.status = status
|
||||
version.max_version = max
|
||||
version.min_version = min
|
||||
return version
|
||||
|
||||
|
||||
@ -51,17 +63,13 @@ class Root(base.APIBase):
|
||||
versions = [Version]
|
||||
"""Links to all the versions available in this API"""
|
||||
|
||||
default_version = Version
|
||||
"""A link to the default version of the API"""
|
||||
|
||||
@staticmethod
|
||||
def convert():
|
||||
root = Root()
|
||||
root.name = "OpenStack Magnum API"
|
||||
root.description = ("Magnum is an OpenStack project which aims to "
|
||||
"provide container management.")
|
||||
root.versions = [Version.convert('v1')]
|
||||
root.default_version = Version.convert('v1')
|
||||
root.versions = [Version.convert('v1', "CURRENT", "1.1", "1.1")]
|
||||
return root
|
||||
|
||||
|
||||
|
@ -21,7 +21,6 @@ NOTE: IN PROGRESS AND NOT FULLY IMPLEMENTED.
|
||||
from oslo_log import log as logging
|
||||
import pecan
|
||||
from pecan import rest
|
||||
from webob import exc
|
||||
from wsme import types as wtypes
|
||||
|
||||
from magnum.api.controllers import base as controllers_base
|
||||
@ -31,6 +30,7 @@ from magnum.api.controllers.v1 import baymodel
|
||||
from magnum.api.controllers.v1 import certificate
|
||||
from magnum.api.controllers.v1 import magnum_services
|
||||
from magnum.api import expose
|
||||
from magnum.api import http_error
|
||||
from magnum.i18n import _
|
||||
|
||||
|
||||
@ -157,32 +157,41 @@ class Controller(rest.RestController):
|
||||
headers = {}
|
||||
# ensure that major version in the URL matches the header
|
||||
if version.major != BASE_VERSION:
|
||||
raise exc.HTTPNotAcceptable(_(
|
||||
raise http_error.HTTPNotAcceptableAPIVersion(_(
|
||||
"Mutually exclusive versions requested. Version %(ver)s "
|
||||
"requested but not supported by this service."
|
||||
"The supported version range is: "
|
||||
"[%(min)s, %(max)s].") % {'ver': version,
|
||||
'min': MIN_VER_STR,
|
||||
'max': MAX_VER_STR},
|
||||
headers=headers)
|
||||
headers=headers,
|
||||
max_version=str(MAX_VER),
|
||||
min_version=str(MIN_VER))
|
||||
# ensure the minor version is within the supported range
|
||||
if version < MIN_VER or version > MAX_VER:
|
||||
raise exc.HTTPNotAcceptable(_(
|
||||
raise http_error.HTTPNotAcceptableAPIVersion(_(
|
||||
"Version %(ver)s was requested but the minor version is not "
|
||||
"supported by this service. The supported version range is: "
|
||||
"[%(min)s, %(max)s].") % {'ver': version, 'min': MIN_VER_STR,
|
||||
'max': MAX_VER_STR}, headers=headers)
|
||||
'max': MAX_VER_STR},
|
||||
headers=headers,
|
||||
max_version=str(MAX_VER),
|
||||
min_version=str(MIN_VER))
|
||||
|
||||
@pecan.expose()
|
||||
def _route(self, args):
|
||||
version = controllers_base.Version(
|
||||
pecan.request.headers, MIN_VER_STR, MAX_VER_STR)
|
||||
|
||||
# Always set the min and max headers
|
||||
# Always set the basic version headers
|
||||
pecan.response.headers[
|
||||
controllers_base.Version.min_string] = MIN_VER_STR
|
||||
pecan.response.headers[
|
||||
controllers_base.Version.max_string] = MAX_VER_STR
|
||||
pecan.response.headers[
|
||||
controllers_base.Version.string] = " ".join(
|
||||
[controllers_base.Version.service_string, str(version)])
|
||||
pecan.response.headers["vary"] = controllers_base.Version.string
|
||||
|
||||
# assert that requested version is supported
|
||||
self._check_version(version, pecan.response.headers)
|
||||
|
70
magnum/api/http_error.py
Normal file
70
magnum/api/http_error.py
Normal file
@ -0,0 +1,70 @@
|
||||
# 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.
|
||||
|
||||
import json
|
||||
import six
|
||||
from webob import exc
|
||||
|
||||
|
||||
class HTTPNotAcceptableAPIVersion(exc.HTTPNotAcceptable):
|
||||
# subclass of :class:`~HTTPNotAcceptable`
|
||||
#
|
||||
# This indicates the resource identified by the request is only
|
||||
# capable of generating response entities which have content
|
||||
# characteristics not acceptable according to the accept headers
|
||||
# sent in the request.
|
||||
#
|
||||
# code: 406, title: Not Acceptable
|
||||
#
|
||||
# differences from webob.exc.HTTPNotAcceptable:
|
||||
#
|
||||
# - additional max and min version paramters
|
||||
# - additional error info for code, title, and links
|
||||
code = 406
|
||||
title = 'Not Acceptable'
|
||||
max_version = ''
|
||||
min_version = ''
|
||||
|
||||
def __init__(self, detail=None, headers=None, comment=None,
|
||||
body_template=None, max_version='', min_version='', **kw):
|
||||
|
||||
super(HTTPNotAcceptableAPIVersion, self).__init__(
|
||||
detail=detail, headers=headers, comment=comment,
|
||||
body_template=body_template, **kw)
|
||||
|
||||
self.max_version = max_version
|
||||
self.min_version = min_version
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
for err_str in self.app_iter:
|
||||
err = {}
|
||||
try:
|
||||
err = json.loads(err_str.decode('utf-8'))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
links = {'rel': 'help', 'href': 'http://developer.openstack.org'
|
||||
'/api-guide/compute/microversions.html'}
|
||||
|
||||
err['max_version'] = self.max_version
|
||||
err['min_version'] = self.min_version
|
||||
err['code'] = "magnum.microversion-unsupported"
|
||||
err['links'] = [links]
|
||||
err['title'] = "Requested microversion is unsupported"
|
||||
|
||||
self.app_iter = [six.b(json.dumps(err))]
|
||||
self.headers['Content-Length'] = str(len(self.app_iter[0]))
|
||||
|
||||
return super(HTTPNotAcceptableAPIVersion, self).__call__(
|
||||
environ, start_response)
|
@ -29,6 +29,41 @@ class ParsableErrorMiddleware(object):
|
||||
def __init__(self, app):
|
||||
self.app = app
|
||||
|
||||
def _update_errors(self, app_iter, status_code):
|
||||
errs = []
|
||||
for err_str in app_iter:
|
||||
err = {}
|
||||
try:
|
||||
err = json.loads(err_str.decode('utf-8'))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if 'title' in err and 'description' in err:
|
||||
title = err['title']
|
||||
desc = err['description']
|
||||
elif 'faultstring' in err:
|
||||
title = err['faultstring'].split('.', 1)[0]
|
||||
desc = err['faultstring']
|
||||
else:
|
||||
title = ''
|
||||
desc = ''
|
||||
|
||||
code = err['faultcode'].lower() if 'faultcode' in err else ''
|
||||
|
||||
# if already formatted by custom exception, don't update
|
||||
if 'min_version' in err:
|
||||
errs.append(err)
|
||||
else:
|
||||
errs.append({
|
||||
'request_id': '',
|
||||
'code': code,
|
||||
'status': status_code,
|
||||
'title': title,
|
||||
'detail': desc,
|
||||
'links': []})
|
||||
|
||||
return errs
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
# Request for this state, modified by replace_start_response()
|
||||
# and used when an error is being reported.
|
||||
@ -54,44 +89,17 @@ class ParsableErrorMiddleware(object):
|
||||
]
|
||||
# Save the headers in case we need to modify them.
|
||||
state['headers'] = headers
|
||||
|
||||
return start_response(status, headers, exc_info)
|
||||
|
||||
app_iter = self.app(environ, replacement_start_response)
|
||||
|
||||
if (state['status_code'] // 100) not in (2, 3):
|
||||
errs = []
|
||||
for err_str in app_iter:
|
||||
err = {}
|
||||
try:
|
||||
err = json.loads(err_str.decode('utf-8'))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if 'title' in err and 'description' in err:
|
||||
title = err['title']
|
||||
desc = err['description']
|
||||
elif 'faultstring' in err:
|
||||
title = err['faultstring'].split('.', 1)[0]
|
||||
desc = err['faultstring']
|
||||
else:
|
||||
title = ''
|
||||
desc = ''
|
||||
|
||||
code = err['faultcode'].lower() if 'faultcode' in err else ''
|
||||
|
||||
errs.append({
|
||||
'request_id': '',
|
||||
'code': code,
|
||||
'status': state['status_code'],
|
||||
'title': title,
|
||||
'detail': desc,
|
||||
'links': []
|
||||
})
|
||||
|
||||
errs = self._update_errors(app_iter, state['status_code'])
|
||||
body = [six.b(json.dumps({'errors': errs}))]
|
||||
|
||||
state['headers'].append(('Content-Type', 'application/json'))
|
||||
state['headers'].append(('Content-Length', str(len(body[0]))))
|
||||
|
||||
else:
|
||||
body = app_iter
|
||||
return body
|
||||
|
@ -32,16 +32,16 @@ class TestRootController(api_base.FunctionalTest):
|
||||
def setUp(self):
|
||||
super(TestRootController, self).setUp()
|
||||
self.root_expected = {
|
||||
u'default_version':
|
||||
{u'id': u'v1', u'links':
|
||||
[{u'href': u'http://localhost/v1/', u'rel': u'self'}]},
|
||||
u'description': u'Magnum is an OpenStack project which '
|
||||
'aims to provide container management.',
|
||||
u'name': u'OpenStack Magnum API',
|
||||
u'versions': [{u'id': u'v1',
|
||||
u'links':
|
||||
[{u'href': u'http://localhost/v1/',
|
||||
u'rel': u'self'}]}]}
|
||||
u'rel': u'self'}],
|
||||
u'status': u'CURRENT',
|
||||
u'max_version': u'1.1',
|
||||
u'min_version': u'1.1'}]}
|
||||
|
||||
self.v1_expected = {
|
||||
u'media_types':
|
||||
|
Loading…
Reference in New Issue
Block a user