Use oslo_middleware to support CORS configuration
Currently there is no way in Qinling configuration file to configure the CORS. oslo_middleware should be implemented to be able to set [cors] section in qinling.conf. Change-Id: Ib4664ba7cbda28a9dfda1c1dd9f0e7457c0ee7de Story: 2005788 Task: 33515
This commit is contained in:
parent
02b957f92e
commit
e18f80345a
@ -1,7 +1,10 @@
|
||||
# The order of packages is significant, because pip processes them in the order
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
|
||||
sphinx!=1.6.6,!=1.6.7,>=1.6.2 # BSD
|
||||
openstackdocstheme>=1.24.0 # Apache-2.0
|
||||
reno>=2.5.0 # Apache-2.0
|
||||
# this is required for the docs build jobs
|
||||
sphinx>=1.6.2,<2.0.0;python_version=='2.7' # BSD
|
||||
sphinx>=1.6.2;python_version>='3.4' # BSD
|
||||
sphinxcontrib-apidoc>=0.2.0 # BSD
|
||||
openstackdocstheme>=1.11.0 # Apache-2.0
|
||||
reno>=1.8.0 # Apache-2.0
|
||||
os-api-ref>=1.0.0 # Apache-2.0
|
||||
|
@ -67,6 +67,7 @@ oslo.serialization==2.18.0
|
||||
oslo.service==1.24.0
|
||||
oslo.upgradecheck==0.1.0
|
||||
oslo.utils==3.33.0
|
||||
oslosphinx==4.7.0
|
||||
oslotest==3.2.0
|
||||
paramiko==2.4.1
|
||||
Paste==2.0.3
|
||||
@ -106,6 +107,10 @@ setproctitle==1.1.10
|
||||
setuptools==21.0.0
|
||||
simplegeneric==0.8.1
|
||||
six==1.10.0
|
||||
Sphinx==1.6.2
|
||||
sphinxcontrib-httpdomain==1.3.0
|
||||
sphinxcontrib-pecanwsme==0.10.0
|
||||
sphinxcontrib-websupport==1.0.1
|
||||
SQLAlchemy==1.0.10
|
||||
sqlalchemy-migrate==0.11.0
|
||||
sqlparse==0.2.4
|
||||
|
@ -14,9 +14,12 @@
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
import oslo_middleware.cors as cors_middleware
|
||||
import oslo_middleware.http_proxy_to_wsgi as http_proxy_to_wsgi_middleware
|
||||
import pecan
|
||||
|
||||
from qinling.api import access_control
|
||||
from qinling import config as q_config
|
||||
from qinling import context as ctx
|
||||
from qinling.db import api as db_api
|
||||
from qinling.services import periodics
|
||||
@ -43,6 +46,9 @@ def get_pecan_config():
|
||||
def setup_app(config=None):
|
||||
if not config:
|
||||
config = get_pecan_config()
|
||||
|
||||
q_config.set_config_defaults()
|
||||
|
||||
app_conf = dict(config.app)
|
||||
|
||||
db_api.setup_db()
|
||||
@ -61,4 +67,18 @@ def setup_app(config=None):
|
||||
# Set up access control.
|
||||
app = access_control.setup(app)
|
||||
|
||||
return app
|
||||
# Create HTTPProxyToWSGI wrapper
|
||||
app = http_proxy_to_wsgi_middleware.HTTPProxyToWSGI(app, cfg.CONF)
|
||||
|
||||
# Create a CORS wrapper, and attach mistral-specific defaults that must be
|
||||
# included in all CORS responses.
|
||||
return cors_middleware.CORS(app, cfg.CONF)
|
||||
|
||||
|
||||
def init_wsgi():
|
||||
# By default, oslo.config parses the CLI args if no args is provided.
|
||||
# As a result, invoking this wsgi script from gunicorn leads to the error
|
||||
# with argparse complaining that the CLI options have already been parsed.
|
||||
q_config.parse_args(args=[])
|
||||
|
||||
return setup_app()
|
||||
|
@ -67,7 +67,7 @@ class RootController(object):
|
||||
def index(self):
|
||||
LOG.info("Fetching API versions.")
|
||||
|
||||
host_url_v1 = '%s/%s' % (pecan.request.host_url, 'v1')
|
||||
host_url_v1 = '%s/%s' % (pecan.request.application_url, 'v1')
|
||||
api_v1 = APIVersion(
|
||||
id='v1.0',
|
||||
status='CURRENT',
|
||||
|
@ -43,4 +43,5 @@ class Controller(object):
|
||||
|
||||
@wsme_pecan.wsexpose(RootResource)
|
||||
def index(self):
|
||||
return RootResource(uri='%s/%s' % (pecan.request.host_url, 'v1'))
|
||||
return RootResource(uri='%s/%s' % (pecan.request.application_url,
|
||||
'v1'))
|
||||
|
17
qinling/api/wsgi.py
Normal file
17
qinling/api/wsgi.py
Normal file
@ -0,0 +1,17 @@
|
||||
# Copyright 2019 - Ormuco, Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
from qinling.api import app
|
||||
|
||||
application = app.init_wsgi()
|
@ -16,6 +16,7 @@ from keystonemiddleware import auth_token
|
||||
from oslo_concurrency import processutils
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log
|
||||
from oslo_middleware import cors
|
||||
|
||||
from qinling import version
|
||||
|
||||
@ -335,3 +336,34 @@ def parse_args(args=None, usage=None, default_config_files=None):
|
||||
usage=usage,
|
||||
default_config_files=default_config_files
|
||||
)
|
||||
|
||||
|
||||
def set_config_defaults():
|
||||
"""This method updates all configuration default values."""
|
||||
set_cors_middleware_defaults()
|
||||
|
||||
|
||||
def set_cors_middleware_defaults():
|
||||
"""Update default configuration options for oslo.middleware."""
|
||||
cors.set_defaults(
|
||||
allow_headers=['X-Auth-Token',
|
||||
'X-Identity-Status',
|
||||
'X-Roles',
|
||||
'X-Service-Catalog',
|
||||
'X-User-Id',
|
||||
'X-Tenant-Id',
|
||||
'X-Project-Id',
|
||||
'X-User-Name',
|
||||
'X-Project-Name'],
|
||||
allow_methods=['GET',
|
||||
'PUT',
|
||||
'POST',
|
||||
'DELETE',
|
||||
'PATCH'],
|
||||
expose_headers=['X-Auth-Token',
|
||||
'X-Subject-Token',
|
||||
'X-Service-Token',
|
||||
'X-Project-Id',
|
||||
'X-User-Name',
|
||||
'X-Project-Name']
|
||||
)
|
||||
|
96
qinling/tests/unit/api/test_cors_middleware.py
Normal file
96
qinling/tests/unit/api/test_cors_middleware.py
Normal file
@ -0,0 +1,96 @@
|
||||
# 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.
|
||||
|
||||
"""Tests cors middleware."""
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_middleware import cors as cors_middleware
|
||||
from qinling.tests.unit.api import base
|
||||
|
||||
|
||||
class TestCORSMiddleware(base.APITest):
|
||||
"""Provide a basic smoke test to ensure CORS middleware is active.
|
||||
|
||||
The tests below provide minimal confirmation that the CORS middleware
|
||||
is active, and may be configured. For comprehensive tests, please consult
|
||||
the test suite in oslo_middleware.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
# Make sure the CORS options are registered
|
||||
cfg.CONF.register_opts(cors_middleware.CORS_OPTS, 'cors')
|
||||
|
||||
# Load up our valid domain values before the application is created.
|
||||
self.override_config(
|
||||
"allowed_origin",
|
||||
"http://valid.example.com",
|
||||
group='cors'
|
||||
)
|
||||
|
||||
# Create the application.
|
||||
super(TestCORSMiddleware, self).setUp()
|
||||
|
||||
def test_valid_cors_options_request(self):
|
||||
response = self.app.options(
|
||||
'/',
|
||||
headers={
|
||||
'Origin': 'http://valid.example.com',
|
||||
'Access-Control-Request-Method': 'GET'
|
||||
}
|
||||
)
|
||||
|
||||
self.assertEqual(200, response.status_code)
|
||||
self.assertIn('access-control-allow-origin', response.headers)
|
||||
self.assertEqual(
|
||||
'http://valid.example.com',
|
||||
response.headers['access-control-allow-origin']
|
||||
)
|
||||
|
||||
def test_invalid_cors_options_request(self):
|
||||
response = self.app.options(
|
||||
'/',
|
||||
headers={
|
||||
'Origin': 'http://invalid.example.com',
|
||||
'Access-Control-Request-Method': 'GET'
|
||||
}
|
||||
)
|
||||
|
||||
self.assertEqual(200, response.status_code)
|
||||
self.assertNotIn('access-control-allow-origin', response.headers)
|
||||
|
||||
def test_valid_cors_get_request(self):
|
||||
response = self.app.get(
|
||||
'/',
|
||||
headers={
|
||||
'Origin': 'http://valid.example.com'
|
||||
}
|
||||
)
|
||||
|
||||
self.assertEqual(200, response.status_code)
|
||||
self.assertIn('access-control-allow-origin', response.headers)
|
||||
self.assertEqual(
|
||||
'http://valid.example.com',
|
||||
response.headers['access-control-allow-origin']
|
||||
)
|
||||
|
||||
def test_invalid_cors_get_request(self):
|
||||
response = self.app.get(
|
||||
'/',
|
||||
headers={
|
||||
'Origin': 'http://invalid.example.com'
|
||||
}
|
||||
)
|
||||
|
||||
self.assertEqual(200, response.status_code)
|
||||
self.assertNotIn('access-control-allow-origin', response.headers)
|
42
qinling/tests/unit/api/test_oslo_middleware.py
Normal file
42
qinling/tests/unit/api/test_oslo_middleware.py
Normal file
@ -0,0 +1,42 @@
|
||||
# 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.
|
||||
|
||||
"""Tests http_proxy_to_wsgi middleware."""
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_middleware import http_proxy_to_wsgi as http_proxy_to_wsgi_middleware
|
||||
from qinling.tests.unit.api import base
|
||||
|
||||
|
||||
class TestHTTPProxyToWSGIMiddleware(base.APITest):
|
||||
"""Test oslo_middleware HTTPProxyToWSGI.
|
||||
|
||||
It checks that oslo_middleware middleware HTTPProxyToWSGI is executed
|
||||
when enabled.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
# Make sure the HTTPProxyToWSGI options are registered
|
||||
cfg.CONF.register_opts(http_proxy_to_wsgi_middleware.OPTS,
|
||||
'oslo_middleware')
|
||||
|
||||
# Enable proxy headers parsing in HTTPProxyToWSGI middleware.
|
||||
self.override_config(
|
||||
"enable_proxy_headers_parsing",
|
||||
"True",
|
||||
group='oslo_middleware'
|
||||
)
|
||||
|
||||
# Create the application.
|
||||
super(TestHTTPProxyToWSGIMiddleware, self).setUp()
|
@ -0,0 +1,9 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Add CORS and HTTPProxyToWSGI support based on oslo_middleware in front
|
||||
of the Qinling API. The purpose of this middleware is to set up the
|
||||
request URL correctly in the case there is a proxy (for instance, a
|
||||
loadbalancer such as HAProxy) in front of the Qinling API.
|
||||
The HTTPProxyToWSGI is off by default and needs to be enabled via a
|
||||
configuration value.
|
@ -10,6 +10,7 @@ oslo.concurrency>=3.26.0 # Apache-2.0
|
||||
oslo.config>=5.2.0 # Apache-2.0
|
||||
oslo.db>=4.27.0 # Apache-2.0
|
||||
oslo.messaging>=5.29.0 # Apache-2.0
|
||||
oslo.middleware>=3.35.0 # Apache-2.0
|
||||
oslo.policy>=1.30.0 # Apache-2.0
|
||||
oslo.upgradecheck>=0.1.0 # Apache-2.0
|
||||
oslo.utils>=3.33.0 # Apache-2.0
|
||||
|
@ -30,8 +30,11 @@ console_scripts =
|
||||
qinling-db-manage = qinling.db.sqlalchemy.migration.cli:main
|
||||
qinling-status = qinling.cmd.status:main
|
||||
|
||||
wsgi_scripts =
|
||||
qinling-wsgi-api = qinling.api.app:init_wsgi
|
||||
|
||||
qinling.storage.provider:
|
||||
local = qinling.storage.file_system:FileSystemStorage
|
||||
local = qinling.storage.file_system:FileSystemStorage
|
||||
|
||||
qinling.orchestrator =
|
||||
kubernetes = qinling.orchestrator.kubernetes.manager:KubernetesManager
|
||||
@ -39,6 +42,9 @@ qinling.orchestrator =
|
||||
oslo.config.opts =
|
||||
qinling.config = qinling.config:list_opts
|
||||
|
||||
oslo.config.opts.defaults =
|
||||
qinling.config = qinling.config:set_cors_middleware_defaults
|
||||
|
||||
tempest.test_plugins =
|
||||
qinling_test = qinling_tempest_plugin.plugin:QinlingTempestPlugin
|
||||
|
||||
|
@ -2,6 +2,8 @@
|
||||
namespace = qinling.config
|
||||
namespace = keystonemiddleware.auth_token
|
||||
namespace = oslo.messaging
|
||||
namespace = oslo.middleware.cors
|
||||
namespace = oslo.middleware.http_proxy_to_wsgi
|
||||
namespace = oslo.log
|
||||
namespace = oslo.policy
|
||||
namespace = oslo.db
|
||||
|
Loading…
x
Reference in New Issue
Block a user