[placement] set accept to application/json if accept not set

If an incoming request to placement does not set the accept header
force it to 'application/json'. This not only ensures that error
responses are in JSON but also that errors early in the middleware
stack do not cause a key error (see the #1724065 bug).

This fix is done by checking headers early in the middleware stack
using the requestlog middleware as a convenient place to do the
check. This overloads requestlog's purpose, but avoids adding yet
more middleware (doing so has some small impact per request).

Fixing bug 1724065 fixes a 500. Fixing bug 1674694 changes (for some
requests) the content type of the bodies of 400-499 responses. This
creates a bit of quandry for microversion handling. If a microversion
is considered required here then it's not clear the global fix is
worth doing and the 500 fix should be limited the microversion
middleware. The intent all along has been that responses should
strive to align with the API-WG errors guideline [1], which assumes
application/json.

[1] http://specs.openstack.org/openstack/api-wg/guidelines/errors.html

Change-Id: Ice27c7080fc2df097cb387f7438c0aaf32b4c63d
Closes-Bug: #1724065
Closes-Bug: #1674694
This commit is contained in:
Chris Dent 2017-11-07 15:25:31 +11:00
parent 760eb4643a
commit f269023c49
4 changed files with 68 additions and 4 deletions

View File

@ -36,6 +36,10 @@ class RequestLog(object):
LOG.debug('Starting request: %s "%s %s"',
environ['REMOTE_ADDR'], environ['REQUEST_METHOD'],
self._get_uri(environ))
# Set the accept header if it is not otherwise set. This
# ensures that error responses will be in JSON.
if not environ.get('HTTP_ACCEPT'):
environ['HTTP_ACCEPT'] = 'application/json'
if LOG.isEnabledFor(logging.INFO):
return self._log_app(environ, start_response)
else:

View File

@ -0,0 +1,38 @@
# Test launchpad bug https://bugs.launchpad.net/nova/+bug/1674694
fixtures:
- APIFixture
defaults:
request_headers:
x-auth-token: admin
tests:
- name: 404 with application/json
GET: /bc8d9d50-7b0d-45ef-839c-e7b5e1c4e8fd
request_headers:
accept: application/json
status: 404
response_headers:
content-type: application/json
response_json_paths:
$.errors[0].status: 404
- name: 404 with no accept
GET: /bc8d9d50-7b0d-45ef-839c-e7b5e1c4e8fd
status: 404
response_headers:
content-type: application/json
response_json_paths:
$.errors[0].status: 404
- name: 404 with other accept
GET: /bc8d9d50-7b0d-45ef-839c-e7b5e1c4e8fd
status: 404
request_headers:
accept: text/html
response_headers:
content-type: /text/html/
response_strings:
- The resource could not be found

View File

@ -0,0 +1,22 @@
# Test launchpad bug https://bugs.launchpad.net/nova/+bug/1724065
fixtures:
- APIFixture
defaults:
request_headers:
x-auth-token: user
tests:
# min version from start of placement time is 1.0
# Without the fix, this results in a 500 with an 'HTTP_ACCEPT'
# KeyError.
- name: no accept header and out of range microversion
GET: /resource_providers
request_headers:
openstack-api-version: placement 0.9
status: 406
response_strings:
- Unacceptable version header

View File

@ -13,13 +13,13 @@ tests:
PUT: /traits/TRAIT_X
status: 400
response_strings:
- 'The trait is invalid. A valid trait must include prefix "CUSTOM_" and use following characters: "A"-"Z", "0"-"9" and "_"'
- 'The trait is invalid. A valid trait must include prefix \"CUSTOM_\" and use following characters: \"A\"-\"Z\", \"0\"-\"9\" and \"_\"'
- name: create a trait with invalid characters
PUT: /traits/CUSTOM_ABC:1
status: 400
response_strings:
- 'The trait is invalid. A valid trait must include prefix "CUSTOM_" and use following characters: "A"-"Z", "0"-"9" and "_"'
- 'The trait is invalid. A valid trait must include prefix \"CUSTOM_\" and use following characters: \"A\"-\"Z\", \"0\"-\"9\" and \"_\"'
- name: create a trait
PUT: /traits/CUSTOM_TRAIT_1
@ -93,7 +93,7 @@ tests:
GET: /traits?name=in_abc
status: 400
response_strings:
- 'Badly formatted name parameter. Expected name query string parameter in form: ?name=[in|startswith]:[name1,name2|prefix]. Got: "in_abc"'
- 'Badly formatted name parameter. Expected name query string parameter in form: ?name=[in|startswith]:[name1,name2|prefix]. Got: \"in_abc\"'
- name: list traits with name=in filter
GET: /traits?name=in:CUSTOM_TRAIT_1,CUSTOM_TRAIT_2
@ -177,7 +177,7 @@ tests:
GET: /traits?associated=xyz
status: 400
response_strings:
- 'The query parameter "associated" only accepts "true" or "false"'
- 'The query parameter \"associated\" only accepts \"true\" or \"false\"'
- name: set traits for resource provider without resource provider generation
PUT: /resource_providers/$ENVIRON['RP_UUID']/traits