Reorganize pipelines for multiple api versions

* Partially implements bp api-2

Change-Id: Ie942c54e0ba294da661de870a95d685a9b8966de
This commit is contained in:
Brian Waldon 2012-03-27 19:18:50 -07:00
parent 8e9e0cadaf
commit 35ed310555
9 changed files with 53 additions and 34 deletions

@ -1,6 +1,6 @@
# Default minimal pipeline
[pipeline:glance-api]
pipeline = versionnegotiation context apiv1app
pipeline = versionnegotiation context rootapp
# Use the following pipeline for keystone auth
# i.e. in glance-api.conf:
@ -8,7 +8,7 @@ pipeline = versionnegotiation context apiv1app
# flavor = keystone
#
[pipeline:glance-api-keystone]
pipeline = versionnegotiation authtoken context apiv1app
pipeline = versionnegotiation authtoken context rootapp
# Use the following pipeline to enable transparent caching of image files
# i.e. in glance-api.conf:
@ -16,7 +16,7 @@ pipeline = versionnegotiation authtoken context apiv1app
# flavor = caching
#
[pipeline:glance-api-caching]
pipeline = versionnegotiation context cache apiv1app
pipeline = versionnegotiation context cache rootapp
# Use the following pipeline for keystone auth with caching
# i.e. in glance-api.conf:
@ -24,7 +24,7 @@ pipeline = versionnegotiation context cache apiv1app
# flavor = keystone+caching
#
[pipeline:glance-api-keystone+caching]
pipeline = versionnegotiation authtoken context cache apiv1app
pipeline = versionnegotiation authtoken context cache rootapp
# Use the following pipeline to enable the Image Cache Management API
# i.e. in glance-api.conf:
@ -32,7 +32,7 @@ pipeline = versionnegotiation authtoken context cache apiv1app
# flavor = cachemanagement
#
[pipeline:glance-api-cachemanagement]
pipeline = versionnegotiation context cache cachemanage apiv1app
pipeline = versionnegotiation context cache cachemanage rootapp
# Use the following pipeline for keystone auth with cache management
# i.e. in glance-api.conf:
@ -40,7 +40,15 @@ pipeline = versionnegotiation context cache cachemanage apiv1app
# flavor = keystone+cachemanagement
#
[pipeline:glance-api-keystone+cachemanagement]
pipeline = versionnegotiation authtoken context cache cachemanage apiv1app
pipeline = versionnegotiation authtoken context cache cachemanage rootapp
[composite:rootapp]
use = egg:Paste#urlmap
/: apiversions
/v1: apiv1app
[app:apiversions]
paste.app_factory = glance.api.versions:create_resource
[app:apiv1app]
paste.app_factory = glance.common.wsgi:app_factory

@ -77,7 +77,7 @@ class CacheManageFilter(wsgi.Middleware):
def process_request(self, request):
# Map request to our resource object if we can handle it
match = self._mapper.match(request.path, request.environ)
match = self._mapper.match(request.path_info, request.environ)
if match:
request.environ['wsgiorg.routing_args'] = (None, match)
return self._resource(request)

@ -65,6 +65,8 @@ class VersionNegotiationFilter(wsgi.Middleware):
req.environ['api.minor_version'])
# Strip the version from the path
req.path_info_pop()
args = (req.environ['api.major_version'], req.path_info)
req.path_info = '/v%s%s' % args
return None
else:
logger.debug(_("Unknown version in versioned URI: %d.%d. "
@ -85,6 +87,8 @@ class VersionNegotiationFilter(wsgi.Middleware):
"Version: %d.%d"),
req.environ['api.major_version'],
req.environ['api.minor_version'])
args = (req.environ['api.major_version'], req.path_info)
req.path_info = '/v%s%s' % args
return None
else:
logger.debug(_("Unknown version in accept header: %d.%d..."

@ -20,6 +20,8 @@ import json
import webob.dec
from glance.common import wsgi
class Controller(object):
@ -28,8 +30,7 @@ class Controller(object):
def __init__(self, conf):
self.conf = conf
@webob.dec.wsgify
def __call__(self, req):
def index(self, req):
"""Respond to a request for all OpenStack API versions."""
def build_version_object(version, path, status):
return {
@ -55,5 +56,10 @@ class Controller(object):
response.body = json.dumps(dict(versions=version_objs))
return response
def get_href(self, req, version):
return "%s/v%s/" % (req.host_url, version)
@webob.dec.wsgify(RequestClass=wsgi.Request)
def __call__(self, req):
return self.index(req)
def create_resource(conf):
return wsgi.Resource(Controller(conf))

@ -451,7 +451,7 @@ class Resource(object):
may raise a webob.exc exception or return a dict, which will be
serialized by requested content type.
"""
def __init__(self, controller, deserializer, serializer):
def __init__(self, controller, deserializer=None, serializer=None):
"""
:param controller: object that implement methods created by routes lib
:param deserializer: object that supports webob request deserialization
@ -460,8 +460,8 @@ class Resource(object):
through controller-like actions
"""
self.controller = controller
self.serializer = serializer
self.deserializer = deserializer
self.serializer = serializer or JSONResponseSerializer()
self.deserializer = deserializer or JSONRequestDeserializer()
@webob.dec.wsgify(RequestClass=Request)
def __call__(self, request):

@ -252,16 +252,24 @@ policy_default_rule = %(policy_default_rule)s
flavor = %(deployment_flavor)s
"""
self.paste_conf_base = """[pipeline:glance-api]
pipeline = versionnegotiation context apiv1app
pipeline = versionnegotiation context rootapp
[pipeline:glance-api-caching]
pipeline = versionnegotiation context cache apiv1app
pipeline = versionnegotiation context cache rootapp
[pipeline:glance-api-cachemanagement]
pipeline = versionnegotiation context cache cache_manage apiv1app
pipeline = versionnegotiation context cache cache_manage rootapp
[pipeline:glance-api-fakeauth]
pipeline = versionnegotiation fakeauth context apiv1app
pipeline = versionnegotiation fakeauth context rootapp
[composite:rootapp]
use = egg:Paste#urlmap
/: apiversions
/v1: apiv1app
[app:apiversions]
paste.app_factory = glance.api.versions:create_resource
[app:apiv1app]
paste.app_factory = glance.common.wsgi:app_factory

@ -56,7 +56,7 @@ class TestRootApi(functional.FunctionalTest):
# 0. GET / with no Accept: header
# Verify version choices returned.
# Bug lp:803260 no Accept header causes a 500 in glance-api
path = 'http://%s:%d/' % ('0.0.0.0', self.api_port)
path = 'http://%s:%d' % ('0.0.0.0', self.api_port)
http = httplib2.Http()
response, content = http.request(path, 'GET')
self.assertEqual(response.status, 300)

@ -18,7 +18,6 @@
"""Stubouts, mocks and fixtures for the test suite"""
import os
import shutil
try:
import sendfile
@ -28,11 +27,9 @@ except ImportError:
import webob
from glance.api.middleware import version_negotiation
from glance.api.v1 import router
import glance.common.client
from glance.common import context
from glance.common import exception
from glance.registry.api import v1 as rserver
from glance.tests import utils
@ -118,8 +115,12 @@ def stub_out_registry_and_store_server(stubs, base_dir):
def close(self):
return True
def _clean_url(self, url):
#TODO(bcwaldon): Fix the hack that strips off v1
return url.replace('/v1', '', 1) if url.startswith('/v1') else url
def putrequest(self, method, url):
self.req = webob.Request.blank("/" + url.lstrip("/"))
self.req = webob.Request.blank(self._clean_url(url))
if SENDFILE_SUPPORTED:
fake_sendfile = FakeSendFile(self.req)
stubs.Set(sendfile, 'sendfile', fake_sendfile.sendfile)
@ -138,7 +139,7 @@ def stub_out_registry_and_store_server(stubs, base_dir):
self.req.body += data.split("\r\n")[1]
def request(self, method, url, body=None, headers={}):
self.req = webob.Request.blank("/" + url.lstrip("/"))
self.req = webob.Request.blank(self._clean_url(url))
self.req.method = method
if headers:
self.req.headers = headers
@ -157,9 +158,7 @@ def stub_out_registry_and_store_server(stubs, base_dir):
'filesystem_store_datadir': base_dir,
'policy_file': os.path.join(base_dir, 'policy.json'),
})
api = version_negotiation.VersionNegotiationFilter(
context.ContextMiddleware(router.API(conf), conf),
conf)
api = context.ContextMiddleware(router.API(conf), conf)
res = self.req.get_response(api)
# httplib.Response has a read() method...fake it out

@ -20,8 +20,6 @@ import json
import webob
from glance.api import versions
from glance import client
from glance.common import exception
from glance.tests.unit import base
from glance.tests import utils
@ -35,7 +33,7 @@ class VersionsTest(base.IsolatedUnitTest):
req.accept = 'application/json'
config_opts = {'bind_host': '0.0.0.0', 'bind_port': 9292}
conf = utils.TestConfigOpts(config_opts)
res = req.get_response(versions.Controller(conf))
res = versions.Controller(conf).index(req)
self.assertEqual(res.status_int, 300)
self.assertEqual(res.content_type, 'application/json')
results = json.loads(res.body)['versions']
@ -57,7 +55,3 @@ class VersionsTest(base.IsolatedUnitTest):
},
]
self.assertEqual(results, expected)
def test_client_handles_versions(self):
api_client = client.Client('0.0.0.0', doc_root='')
self.assertRaises(exception.MultipleChoices, api_client.get_images)