Merge "Updates microversion root and error messages"

This commit is contained in:
Jenkins 2016-07-12 09:06:10 +00:00 committed by Gerrit Code Review
commit bbb0f6c2b2
5 changed files with 141 additions and 46 deletions

View File

@ -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

View File

@ -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
View 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)

View File

@ -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

View File

@ -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':