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: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

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 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

View File

@ -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

View File

@ -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
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())