Fix api wsgi entrypoint
This patch fixes a bunch of issues with the wsgi entrypoint and the api implementation in general. * Fixed issue with versions and mod_wsgi. * Fixed href not properly redirecting under mod_wsgi. * Fixed Webhook middleware not handling /cluster/v1 uri. * Register objects when starting api_wsgi. Closes-Bug: #1849174 Closes-Bug: #1849175 Change-Id: Idebab562d1cd4b6b498d90a93577f34b6f56cd78
This commit is contained in:
parent
427acad1e2
commit
ba74c64771
|
@ -46,11 +46,14 @@ class VersionNegotiationFilter(wsgi.Middleware):
|
|||
If there is a version identifier in the URI, simply return the correct
|
||||
API controller, otherwise, if we find an Accept: header, process it
|
||||
"""
|
||||
LOG.debug("Processing request: %(m)s %(p)s Accept: %(a)s",
|
||||
{'m': req.method, 'p': req.path, 'a': req.accept})
|
||||
LOG.debug(
|
||||
"Processing request: %(method)s %(path)s Accept: %(accept)s",
|
||||
{'method': req.method, 'path': req.path, 'accept': req.accept}
|
||||
)
|
||||
|
||||
# If the request is for /versions, just return the versions container
|
||||
if req.path_info_peek() in ("versions", ""):
|
||||
path_info_peak = req.path_info_peek()
|
||||
if path_info_peak in ('versions', ''):
|
||||
return self.versions_app
|
||||
|
||||
accept = str(req.accept)
|
||||
|
@ -69,11 +72,7 @@ class VersionNegotiationFilter(wsgi.Middleware):
|
|||
if path is None or path == '/':
|
||||
return controller(self.conf)
|
||||
return None
|
||||
else:
|
||||
LOG.debug("Unknown version in URI")
|
||||
|
||||
# Try another path
|
||||
if accept.startswith('application/vnd.openstack.clustering-'):
|
||||
elif accept.startswith('application/vnd.openstack.clustering-'):
|
||||
token_loc = len('application/vnd.openstack.clustering-')
|
||||
accept_version = accept[token_loc:]
|
||||
controller = self._get_controller(accept_version, req)
|
||||
|
@ -88,10 +87,10 @@ class VersionNegotiationFilter(wsgi.Middleware):
|
|||
if path is None or path == '/':
|
||||
return controller(self.conf)
|
||||
return None
|
||||
else:
|
||||
LOG.debug("Unknown version in request header")
|
||||
else:
|
||||
LOG.debug("Unknown version in request")
|
||||
|
||||
if accept not in ('*/*', ''):
|
||||
if accept not in ('*/*', '') and path_info_peak is not None:
|
||||
LOG.debug("Returning HTTP 404 due to unknown Accept header: %s ",
|
||||
accept)
|
||||
return webob.exc.HTTPNotFound()
|
||||
|
|
|
@ -69,18 +69,19 @@ class WebhookMiddleware(wsgi.Middleware):
|
|||
def _parse_url(self, url):
|
||||
"""Extract receiver ID from the request URL.
|
||||
|
||||
Parse a URL of format: http://host:port/webhooks/id/trigger?V=1&k=v
|
||||
Parse a URL of format: http://host:port/v1/webhooks/id/trigger?V=1&k=v
|
||||
:param url: The URL from which the request is received.
|
||||
"""
|
||||
parts = urlparse.urlparse(url)
|
||||
p = parts.path.split('/')
|
||||
|
||||
# check if URL is a webhook trigger request
|
||||
# expected: ['', 'v1', 'webhooks', 'webhook-id', 'trigger']
|
||||
if len(p) != 5:
|
||||
return None
|
||||
try:
|
||||
index = p.index('v1')
|
||||
p = p[(index + 1):]
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if any((p[0] != '', p[2] != 'webhooks', p[4] != 'trigger')):
|
||||
if len(p) != 3 or p[0] != 'webhooks' or p[2] != 'trigger':
|
||||
return None
|
||||
|
||||
# at this point it has been determined that the URL is a webhook
|
||||
|
@ -94,7 +95,7 @@ class WebhookMiddleware(wsgi.Middleware):
|
|||
'trigger URL'))
|
||||
|
||||
params = dict((k, v[0]) for k, v in qs.items())
|
||||
return p[3], params
|
||||
return p[1], params
|
||||
|
||||
def _get_token(self, **kwargs):
|
||||
"""Get a valid token based on the credential provided.
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
# 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 oslo_serialization import jsonutils
|
||||
from oslo_utils import encodeutils
|
||||
import webob.dec
|
||||
|
@ -41,7 +40,7 @@ class VersionController(object):
|
|||
return response
|
||||
|
||||
@classmethod
|
||||
def version_info(cls):
|
||||
def version_info(cls, req):
|
||||
return {
|
||||
"id": "1.0",
|
||||
"status": "CURRENT",
|
||||
|
@ -53,7 +52,7 @@ class VersionController(object):
|
|||
}
|
||||
],
|
||||
"links": [{
|
||||
"href": "/v1",
|
||||
"href": req.application_url.rstrip('/') + '/v1',
|
||||
"rel": "self"}, {
|
||||
"rel": "help",
|
||||
"href": "https://docs.openstack.org/api-ref/clustering"
|
||||
|
@ -63,7 +62,7 @@ class VersionController(object):
|
|||
}
|
||||
|
||||
def version(self, req):
|
||||
return {"version": self.version_info()}
|
||||
return {"version": self.version_info(req)}
|
||||
|
||||
@classmethod
|
||||
def min_api_version(cls):
|
||||
|
|
|
@ -39,7 +39,7 @@ class Controller(object):
|
|||
|
||||
versions = []
|
||||
for ver, vc in self.Controllers.items():
|
||||
versions.append(vc.version_info())
|
||||
versions.append(vc.version_info(req))
|
||||
|
||||
body = jsonutils.dumps(dict(versions=versions))
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ from senlin.api.common import wsgi
|
|||
from senlin.common import config
|
||||
from senlin.common import messaging
|
||||
from senlin.common import profiler
|
||||
from senlin import objects
|
||||
from senlin import version
|
||||
|
||||
|
||||
|
@ -34,6 +35,7 @@ def init_app():
|
|||
version=version.version_info.version_string())
|
||||
logging.setup(cfg.CONF, 'senlin-api')
|
||||
config.set_config_defaults()
|
||||
objects.register_all()
|
||||
messaging.setup()
|
||||
|
||||
profiler.setup('senlin-api', cfg.CONF.host)
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
import mock
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import uuidutils
|
||||
import six
|
||||
import webob
|
||||
|
||||
|
@ -153,7 +154,8 @@ class TestWebhookMiddleware(base.SenlinTestCase):
|
|||
|
||||
req = mock.Mock()
|
||||
req.method = 'POST'
|
||||
req.url = 'http://url1'
|
||||
req.url = 'http://url1/v1'
|
||||
req.script_name = '/v1'
|
||||
req.params = {'key': 'FAKE_KEY'}
|
||||
req.headers = {}
|
||||
req.version_request = vr.APIVersionRequest('1.0')
|
||||
|
@ -181,7 +183,7 @@ class TestWebhookMiddleware(base.SenlinTestCase):
|
|||
self.assertIsNone(res)
|
||||
|
||||
self.assertEqual('FAKE_TOKEN', req.headers['X-Auth-Token'])
|
||||
mock_extract.assert_called_once_with('http://url1')
|
||||
mock_extract.assert_called_once_with('http://url1/v1')
|
||||
mock_token.assert_called_once_with(
|
||||
auth_url='AUTH_URL', password='PASSWORD', username='USERNAME',
|
||||
user_domain_name='DOMAIN', foo='bar')
|
||||
|
@ -202,7 +204,8 @@ class TestWebhookMiddleware(base.SenlinTestCase):
|
|||
# no webhook_id extracted
|
||||
req = mock.Mock()
|
||||
req.method = 'POST'
|
||||
req.url = 'http://url1'
|
||||
req.url = 'http://url1/v1'
|
||||
req.script_name = '/v1'
|
||||
mock_extract = self.patchobject(self.middleware, '_parse_url',
|
||||
return_value=None)
|
||||
|
||||
|
@ -210,3 +213,50 @@ class TestWebhookMiddleware(base.SenlinTestCase):
|
|||
self.assertIsNone(res)
|
||||
mock_extract.assert_called_once_with(req.url)
|
||||
self.assertNotIn('X-Auth-Token', req.headers)
|
||||
|
||||
def test_parse_url_valid(self):
|
||||
uid = uuidutils.generate_uuid()
|
||||
|
||||
result = self.middleware._parse_url(
|
||||
'https://url1/cluster/v1/webhooks/%s/trigger?V=2&k=v' % uid
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
(uid, {'k': 'v'}), result
|
||||
)
|
||||
|
||||
def test_parse_url_valid_with_port(self):
|
||||
uid = uuidutils.generate_uuid()
|
||||
|
||||
result = self.middleware._parse_url(
|
||||
'http://url1:5000/v1/webhooks/%s/trigger?V=2&k=v' % uid
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
(uid, {'k': 'v'}), result
|
||||
)
|
||||
|
||||
def test_parse_url_invalid(self):
|
||||
result = self.middleware._parse_url(
|
||||
'http://url1'
|
||||
)
|
||||
|
||||
self.assertIsNone(result)
|
||||
|
||||
def test_parse_url_missing_version(self):
|
||||
uid = uuidutils.generate_uuid()
|
||||
|
||||
result = self.middleware._parse_url(
|
||||
'https://url1/cluster/webhooks/%s/trigger?V=2&k=v' % uid
|
||||
)
|
||||
|
||||
self.assertIsNone(result)
|
||||
|
||||
def test_parse_url_missing_webhooks(self):
|
||||
uid = uuidutils.generate_uuid()
|
||||
|
||||
result = self.middleware._parse_url(
|
||||
'https://url1/cluster/v1/%s/trigger?V=2&k=v' % uid
|
||||
)
|
||||
|
||||
self.assertIsNone(result)
|
||||
|
|
|
@ -51,7 +51,7 @@ class VersionControllerTest(shared.ControllerTest, base.SenlinTestCase):
|
|||
}]
|
||||
self.assertEqual(expected, response['media-types'])
|
||||
expected = [{
|
||||
'href': '/v1',
|
||||
'href': 'http://server.test:8004/v1',
|
||||
'rel': 'self'}, {
|
||||
'href': 'https://docs.openstack.org/api-ref/clustering',
|
||||
'rel': 'help',
|
||||
|
|
|
@ -58,7 +58,7 @@ class ControllerTest(object):
|
|||
return {
|
||||
'SERVER_NAME': 'server.test',
|
||||
'SERVER_PORT': 8004,
|
||||
'SCRIPT_NAME': '/v1',
|
||||
'SCRIPT_NAME': '',
|
||||
'PATH_INFO': '/%s' % self.project + path,
|
||||
'wsgi.url_scheme': 'http',
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue