Add versioning support
Implements blueprint murano-api-version-support Change-Id: Ib24a737c2817f503b4200117886102c4daab311a
This commit is contained in:
parent
2b0c534a0e
commit
17cfe56728
@ -1,8 +1,5 @@
|
|||||||
[pipeline:muranoapi]
|
[pipeline:muranoapi]
|
||||||
pipeline = authtoken context apiv1app
|
pipeline = versionnegotiation authtoken context rootapp
|
||||||
|
|
||||||
[app:apiv1app]
|
|
||||||
paste.app_factory = muranoapi.api.v1.router:API.factory
|
|
||||||
|
|
||||||
[filter:context]
|
[filter:context]
|
||||||
paste.filter_factory = muranoapi.api.middleware.context:ContextMiddleware.factory
|
paste.filter_factory = muranoapi.api.middleware.context:ContextMiddleware.factory
|
||||||
@ -11,3 +8,17 @@ paste.filter_factory = muranoapi.api.middleware.context:ContextMiddleware.factor
|
|||||||
#http://docs.openstack.org/developer/keystone/configuringservices.html
|
#http://docs.openstack.org/developer/keystone/configuringservices.html
|
||||||
[filter:authtoken]
|
[filter:authtoken]
|
||||||
paste.filter_factory = keystoneclient.middleware.auth_token:filter_factory
|
paste.filter_factory = keystoneclient.middleware.auth_token:filter_factory
|
||||||
|
|
||||||
|
[composite:rootapp]
|
||||||
|
use = egg:Paste#urlmap
|
||||||
|
/: apiversions
|
||||||
|
/v1: apiv1app
|
||||||
|
|
||||||
|
[app:apiversions]
|
||||||
|
paste.app_factory = muranoapi.api.versions:create_resource
|
||||||
|
|
||||||
|
[app:apiv1app]
|
||||||
|
paste.app_factory = muranoapi.api.v1.router:API.factory
|
||||||
|
|
||||||
|
[filter:versionnegotiation]
|
||||||
|
paste.filter_factory = muranoapi.api.middleware.version_negotiation:VersionNegotiationFilter.factory
|
||||||
|
96
muranoapi/api/middleware/version_negotiation.py
Normal file
96
muranoapi/api/middleware/version_negotiation.py
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
# Copyright (c) 2014 Mirantis, 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.
|
||||||
|
|
||||||
|
"""
|
||||||
|
A filter middleware that inspects the requested URI for a version string
|
||||||
|
and/or Accept headers and attempts to negotiate an API controller to
|
||||||
|
return
|
||||||
|
"""
|
||||||
|
|
||||||
|
from oslo.config import cfg
|
||||||
|
|
||||||
|
from muranoapi.api import versions
|
||||||
|
from muranoapi.openstack.common.gettextutils import _ # noqa
|
||||||
|
import muranoapi.openstack.common.log as logging
|
||||||
|
from muranoapi.openstack.common import wsgi
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class VersionNegotiationFilter(wsgi.Middleware):
|
||||||
|
@classmethod
|
||||||
|
def factory(cls, global_conf, **local_conf):
|
||||||
|
def filter(app):
|
||||||
|
return cls(app)
|
||||||
|
return filter
|
||||||
|
|
||||||
|
def __init__(self, app):
|
||||||
|
self.versions_app = versions.Controller()
|
||||||
|
super(VersionNegotiationFilter, self).__init__(app)
|
||||||
|
|
||||||
|
def process_request(self, req):
|
||||||
|
"""Try to find a version first in the accept header, then the URL"""
|
||||||
|
msg = _("Determining version of request: %(method)s %(path)s"
|
||||||
|
" Accept: %(accept)s")
|
||||||
|
args = {'method': req.method, 'path': req.path, 'accept': req.accept}
|
||||||
|
LOG.debug(msg % args)
|
||||||
|
|
||||||
|
LOG.debug(_("Using url versioning"))
|
||||||
|
# Remove version in url so it doesn't conflict later
|
||||||
|
req_version = self._pop_path_info(req)
|
||||||
|
|
||||||
|
try:
|
||||||
|
version = self._match_version_string(req_version)
|
||||||
|
except ValueError:
|
||||||
|
LOG.debug(_("Unknown version. Returning version choices."))
|
||||||
|
return self.versions_app
|
||||||
|
|
||||||
|
req.environ['api.version'] = version
|
||||||
|
req.path_info = ''.join(('/v', str(version), req.path_info))
|
||||||
|
LOG.debug(_("Matched version: v%d"), version)
|
||||||
|
LOG.debug('new path %s' % req.path_info)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _match_version_string(self, subject):
|
||||||
|
"""
|
||||||
|
Given a string, tries to match a major and/or
|
||||||
|
minor version number.
|
||||||
|
|
||||||
|
:param subject: The string to check
|
||||||
|
:returns version found in the subject
|
||||||
|
:raises ValueError if no acceptable version could be found
|
||||||
|
"""
|
||||||
|
if subject in ('v1',):
|
||||||
|
major_version = 1
|
||||||
|
else:
|
||||||
|
raise ValueError()
|
||||||
|
return major_version
|
||||||
|
|
||||||
|
def _pop_path_info(self, req):
|
||||||
|
"""
|
||||||
|
'Pops' off the next segment of PATH_INFO, returns the popped
|
||||||
|
segment. Do NOT push it onto SCRIPT_NAME.
|
||||||
|
"""
|
||||||
|
path = req.path_info
|
||||||
|
if not path:
|
||||||
|
return None
|
||||||
|
while path.startswith('/'):
|
||||||
|
path = path[1:]
|
||||||
|
idx = path.find('/')
|
||||||
|
if idx == -1:
|
||||||
|
idx = len(path)
|
||||||
|
r = path[:idx]
|
||||||
|
req.path_info = path[idx:]
|
||||||
|
return r
|
@ -19,7 +19,6 @@ from muranoapi.common import utils
|
|||||||
from muranoapi.db import models
|
from muranoapi.db import models
|
||||||
from muranoapi.db import session as db_session
|
from muranoapi.db import session as db_session
|
||||||
|
|
||||||
|
|
||||||
from muranoapi.openstack.common.gettextutils import _ # noqa
|
from muranoapi.openstack.common.gettextutils import _ # noqa
|
||||||
from muranoapi.openstack.common import log as logging
|
from muranoapi.openstack.common import log as logging
|
||||||
from muranoapi.openstack.common import wsgi
|
from muranoapi.openstack.common import wsgi
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
from sqlalchemy import desc
|
from sqlalchemy import desc
|
||||||
from webob import exc
|
from webob import exc
|
||||||
|
|
||||||
|
|
||||||
from muranoapi.api.v1 import statistics
|
from muranoapi.api.v1 import statistics
|
||||||
from muranoapi.common import utils
|
from muranoapi.common import utils
|
||||||
from muranoapi.db import models
|
from muranoapi.db import models
|
||||||
@ -23,7 +22,6 @@ from muranoapi.db.services import core_services
|
|||||||
from muranoapi.db.services import environments as envs
|
from muranoapi.db.services import environments as envs
|
||||||
from muranoapi.db import session as db_session
|
from muranoapi.db import session as db_session
|
||||||
|
|
||||||
|
|
||||||
from muranoapi.openstack.common.gettextutils import _ # noqa
|
from muranoapi.openstack.common.gettextutils import _ # noqa
|
||||||
from muranoapi.openstack.common import log as logging
|
from muranoapi.openstack.common import log as logging
|
||||||
from muranoapi.openstack.common import wsgi
|
from muranoapi.openstack.common import wsgi
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import routes
|
import routes
|
||||||
|
|
||||||
from muranoapi.api.v1 import deployments
|
from muranoapi.api.v1 import deployments
|
||||||
|
62
muranoapi/api/versions.py
Normal file
62
muranoapi/api/versions.py
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
# Copyright (c) 2014 Mirantis, 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.
|
||||||
|
|
||||||
|
import httplib
|
||||||
|
|
||||||
|
from oslo.config import cfg
|
||||||
|
import webob.dec
|
||||||
|
|
||||||
|
from muranoapi.openstack.common import jsonutils
|
||||||
|
from muranoapi.openstack.common import wsgi
|
||||||
|
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
|
|
||||||
|
|
||||||
|
class Controller(object):
|
||||||
|
|
||||||
|
"""A wsgi controller that reports which API versions are supported."""
|
||||||
|
|
||||||
|
def index(self, req):
|
||||||
|
"""Respond to a request for all OpenStack API versions."""
|
||||||
|
def build_version_object(version, path, status):
|
||||||
|
return {
|
||||||
|
'id': 'v%s' % version,
|
||||||
|
'status': status,
|
||||||
|
'links': [
|
||||||
|
{
|
||||||
|
'rel': 'self',
|
||||||
|
'href': '%s/%s/' % (req.host_url, path),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
version_objs = []
|
||||||
|
version_objs.extend([
|
||||||
|
build_version_object(1.0, 'v1', 'CURRENT'),
|
||||||
|
])
|
||||||
|
|
||||||
|
response = webob.Response(request=req,
|
||||||
|
status=httplib.MULTIPLE_CHOICES,
|
||||||
|
content_type='application/json')
|
||||||
|
response.body = jsonutils.dumps(dict(versions=version_objs))
|
||||||
|
return response
|
||||||
|
|
||||||
|
@webob.dec.wsgify(RequestClass=wsgi.Request)
|
||||||
|
def __call__(self, req):
|
||||||
|
return self.index(req)
|
||||||
|
|
||||||
|
|
||||||
|
def create_resource(conf):
|
||||||
|
return wsgi.Resource(Controller())
|
Loading…
Reference in New Issue
Block a user