Add versioning support

Implements blueprint murano-api-version-support
Change-Id: Ib24a737c2817f503b4200117886102c4daab311a
This commit is contained in:
Ekaterina Fedorova 2014-03-11 21:38:27 +04:00
parent 2b0c534a0e
commit 17cfe56728
6 changed files with 173 additions and 8 deletions

View File

@ -1,8 +1,5 @@
[pipeline:muranoapi]
pipeline = authtoken context apiv1app
[app:apiv1app]
paste.app_factory = muranoapi.api.v1.router:API.factory
pipeline = versionnegotiation authtoken context rootapp
[filter:context]
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
[filter:authtoken]
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

View 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

View File

@ -19,7 +19,6 @@ from muranoapi.common import utils
from muranoapi.db import models
from muranoapi.db import session as db_session
from muranoapi.openstack.common.gettextutils import _ # noqa
from muranoapi.openstack.common import log as logging
from muranoapi.openstack.common import wsgi

View File

@ -15,7 +15,6 @@
from sqlalchemy import desc
from webob import exc
from muranoapi.api.v1 import statistics
from muranoapi.common import utils
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 import session as db_session
from muranoapi.openstack.common.gettextutils import _ # noqa
from muranoapi.openstack.common import log as logging
from muranoapi.openstack.common import wsgi

View File

@ -11,7 +11,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.
import routes
from muranoapi.api.v1 import deployments

62
muranoapi/api/versions.py Normal file
View 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())