Merge "Replace hard coded WSGI application creation"

This commit is contained in:
Jenkins 2014-04-27 23:22:20 +00:00 committed by Gerrit Code Review
commit ad942bbdd7
11 changed files with 100 additions and 57 deletions

View File

@ -19,29 +19,8 @@
"""Access Control Lists (ACL's) control access the API server.""" """Access Control Lists (ACL's) control access the API server."""
from ceilometer.openstack.common import policy from ceilometer.openstack.common import policy
from keystoneclient.middleware import auth_token
from oslo.config import cfg
_ENFORCER = None _ENFORCER = None
OPT_GROUP_NAME = 'keystone_authtoken'
def register_opts(conf):
"""Register keystoneclient middleware options
"""
conf.register_opts(auth_token.opts,
group=OPT_GROUP_NAME)
auth_token.CONF = conf
register_opts(cfg.CONF)
def install(app, conf):
"""Install ACL check on application."""
return auth_token.AuthProtocol(app,
conf=dict(conf.get(OPT_GROUP_NAME)))
def get_limited_to(headers): def get_limited_to(headers):

View File

@ -23,9 +23,9 @@ from wsgiref import simple_server
import netaddr import netaddr
from oslo.config import cfg from oslo.config import cfg
from paste import deploy
import pecan import pecan
from ceilometer.api import acl
from ceilometer.api import config as api_config from ceilometer.api import config as api_config
from ceilometer.api import hooks from ceilometer.api import hooks
from ceilometer.api import middleware from ceilometer.api import middleware
@ -35,12 +35,13 @@ from ceilometer import storage
LOG = log.getLogger(__name__) LOG = log.getLogger(__name__)
auth_opts = [ auth_opts = [
cfg.StrOpt('auth_strategy',
default='keystone',
help='The strategy to use for auth: noauth or keystone.'),
cfg.BoolOpt('enable_v1_api', cfg.BoolOpt('enable_v1_api',
default=True, default=True,
help='Deploy the deprecated v1 API.'), help='Deploy the deprecated v1 API.'),
cfg.StrOpt('api_paste_config',
default="api_paste.ini",
help="Configuration file for WSGI definition of API."
),
] ]
CONF = cfg.CONF CONF = cfg.CONF
@ -80,9 +81,6 @@ def setup_app(pecan_config=None, extra_hooks=None):
guess_content_type_from_ext=False guess_content_type_from_ext=False
) )
if getattr(pecan_config.app, 'enable_acl', True):
return acl.install(app, cfg.CONF)
return app return app
@ -90,10 +88,9 @@ class VersionSelectorApplication(object):
def __init__(self): def __init__(self):
pc = get_pecan_config() pc = get_pecan_config()
pc.app.debug = CONF.debug pc.app.debug = CONF.debug
pc.app.enable_acl = (CONF.auth_strategy == 'keystone')
if cfg.CONF.enable_v1_api: if cfg.CONF.enable_v1_api:
from ceilometer.api.v1 import app as v1app from ceilometer.api.v1 import app as v1app
self.v1 = v1app.make_app(cfg.CONF, enable_acl=pc.app.enable_acl) self.v1 = v1app.make_app(cfg.CONF)
else: else:
def not_found(environ, start_response): def not_found(environ, start_response):
start_response('404 Not Found', []) start_response('404 Not Found', [])
@ -136,14 +133,36 @@ def get_handler_cls():
return CeilometerHandler return CeilometerHandler
def build_server(): def load_app():
# Build the WSGI app # Build the WSGI app
root = VersionSelectorApplication() cfg_file = cfg.CONF.api_paste_config
LOG.info("WSGI config requested: %s" % cfg_file)
if not os.path.exists(cfg_file):
# this code is to work around chicken-egg dependency between
# ceilometer gate jobs use of devstack and this change.
# The gate job uses devstack to run tempest.
# devstack does not copy api_paste.ini into /etc/ceilometer because it
# is introduced in this change. Once this is merged, we will change
# devstack to copy api_paste.ini and once that is merged will remove
# this code.
root = os.path.abspath(os.path.join(os.path.dirname(__file__),
'..', '..', 'etc', 'ceilometer'
)
)
cfg_file = os.path.join(root, cfg_file)
if not os.path.exists(cfg_file):
raise Exception('api_paste_config file not found')
LOG.info("Full WSGI config used: %s" % cfg_file)
return deploy.loadapp("config:" + cfg_file)
def build_server():
app = load_app()
# Create the WSGI server and start it # Create the WSGI server and start it
host, port = cfg.CONF.api.host, cfg.CONF.api.port host, port = cfg.CONF.api.host, cfg.CONF.api.port
server_cls = get_server_cls(host) server_cls = get_server_cls(host)
srv = simple_server.make_server(host, port, root,
srv = simple_server.make_server(host, port, app,
server_cls, get_handler_cls()) server_cls, get_handler_cls())
LOG.info(_('Starting server in PID %s') % os.getpid()) LOG.info(_('Starting server in PID %s') % os.getpid())
@ -157,4 +176,9 @@ def build_server():
else: else:
LOG.info(_("serving on http://%(host)s:%(port)s") % ( LOG.info(_("serving on http://%(host)s:%(port)s") % (
{'host': host, 'port': port})) {'host': host, 'port': port}))
return srv return srv
def app_factory(global_config, **local_conf):
return VersionSelectorApplication()

View File

@ -20,11 +20,9 @@
See http://pecan.readthedocs.org/en/latest/deployment.html for details. See http://pecan.readthedocs.org/en/latest/deployment.html for details.
""" """
from ceilometer import service from ceilometer import service
from ceilometer.api import app from ceilometer.api import app
# Initialize the oslo configuration library and logging # Initialize the oslo configuration library and logging
service.prepare_service([]) service.prepare_service([])
application = app.load_app()
application = app.VersionSelectorApplication()

View File

@ -20,7 +20,6 @@
import flask import flask
from oslo.config import cfg from oslo.config import cfg
from ceilometer.api import acl
from ceilometer.api.v1 import blueprint as v1_blueprint from ceilometer.api.v1 import blueprint as v1_blueprint
from ceilometer.openstack.common import jsonutils from ceilometer.openstack.common import jsonutils
from ceilometer import storage from ceilometer import storage
@ -32,7 +31,7 @@ class JSONEncoder(flask.json.JSONEncoder):
return jsonutils.to_primitive(o) return jsonutils.to_primitive(o)
def make_app(conf, enable_acl=True, attach_storage=True, def make_app(conf, attach_storage=True,
sources_file='sources.json'): sources_file='sources.json'):
app = flask.Flask('ceilometer.api') app = flask.Flask('ceilometer.api')
app.register_blueprint(v1_blueprint.blueprint, url_prefix='/v1') app.register_blueprint(v1_blueprint.blueprint, url_prefix='/v1')
@ -56,11 +55,7 @@ def make_app(conf, enable_acl=True, attach_storage=True,
flask.request.storage_conn = \ flask.request.storage_conn = \
storage.get_connection(conf) storage.get_connection(conf)
# Install the middleware wrapper
if enable_acl:
app.wsgi_app = acl.install(app.wsgi_app, conf)
return app return app
# For documentation # For documentation
app = make_app(cfg.CONF, enable_acl=False, attach_storage=False) app = make_app(cfg.CONF, attach_storage=False)

View File

@ -23,7 +23,6 @@ import pecan
import pecan.testing import pecan.testing
from six.moves import urllib from six.moves import urllib
from ceilometer.api import acl
from ceilometer.api.v1 import app as v1_app from ceilometer.api.v1 import app as v1_app
from ceilometer.api.v1 import blueprint as v1_blueprint from ceilometer.api.v1 import blueprint as v1_blueprint
from ceilometer import messaging from ceilometer import messaging
@ -31,6 +30,8 @@ from ceilometer.openstack.common import jsonutils
from ceilometer import service from ceilometer import service
from ceilometer.tests import db as db_test_base from ceilometer.tests import db as db_test_base
OPT_GROUP_NAME = 'keystone_authtoken'
class TestBase(db_test_base.TestBase): class TestBase(db_test_base.TestBase):
"""Use only for v1 API tests. """Use only for v1 API tests.
@ -42,12 +43,11 @@ class TestBase(db_test_base.TestBase):
self.addCleanup(messaging.cleanup) self.addCleanup(messaging.cleanup)
service.prepare_service([]) service.prepare_service([])
self.CONF.set_override("auth_version", self.CONF.set_override("auth_version",
"v2.0", group=acl.OPT_GROUP_NAME) "v2.0", group=OPT_GROUP_NAME)
self.CONF.set_override("policy_file", self.CONF.set_override("policy_file",
self.path_get('etc/ceilometer/policy.json')) self.path_get('etc/ceilometer/policy.json'))
sources_file = self.path_get('ceilometer/tests/sources.json') sources_file = self.path_get('ceilometer/tests/sources.json')
self.app = v1_app.make_app(self.CONF, self.app = v1_app.make_app(self.CONF,
enable_acl=False,
attach_storage=False, attach_storage=False,
sources_file=sources_file) sources_file=sources_file)
@ -90,7 +90,7 @@ class FunctionalTest(db_test_base.TestBase):
self.addCleanup(messaging.cleanup) self.addCleanup(messaging.cleanup)
super(FunctionalTest, self).setUp() super(FunctionalTest, self).setUp()
self.CONF.set_override("auth_version", "v2.0", self.CONF.set_override("auth_version", "v2.0",
group=acl.OPT_GROUP_NAME) group=OPT_GROUP_NAME)
self.CONF.set_override("policy_file", self.CONF.set_override("policy_file",
self.path_get('etc/ceilometer/policy.json')) self.path_get('etc/ceilometer/policy.json'))
self.app = self._make_app() self.app = self._make_app()

View File

@ -19,13 +19,15 @@
""" """
import os import os
from ceilometer.api import acl from keystoneclient.middleware import auth_token
from ceilometer.api.v1 import app from ceilometer.api.v1 import app
from ceilometer import messaging from ceilometer import messaging
from ceilometer.openstack.common import fileutils from ceilometer.openstack.common import fileutils
from ceilometer.openstack.common.fixture import config from ceilometer.openstack.common.fixture import config
from ceilometer.openstack.common import test from ceilometer.openstack.common import test
from ceilometer import service from ceilometer import service
from ceilometer.tests import api as acl
class TestApp(test.BaseTestCase): class TestApp(test.BaseTestCase):
@ -44,7 +46,10 @@ class TestApp(test.BaseTestCase):
self.CONF.set_override("auth_uri", None, self.CONF.set_override("auth_uri", None,
group=acl.OPT_GROUP_NAME) group=acl.OPT_GROUP_NAME)
api_app = app.make_app(self.CONF, attach_storage=False) api_app = app.make_app(self.CONF, attach_storage=False)
self.assertTrue(api_app.wsgi_app.auth_uri.startswith('file')) conf = dict(self.CONF.get(acl.OPT_GROUP_NAME))
api_app = auth_token.AuthProtocol(api_app,
conf=conf)
self.assertTrue(api_app.auth_uri.startswith('file'))
def test_keystone_middleware_parse_conffile(self): def test_keystone_middleware_parse_conffile(self):
content = "[{0}]\nauth_protocol = file"\ content = "[{0}]\nauth_protocol = file"\
@ -55,5 +60,8 @@ class TestApp(test.BaseTestCase):
service.prepare_service(['ceilometer-api', service.prepare_service(['ceilometer-api',
'--config-file=%s' % tmpfile]) '--config-file=%s' % tmpfile])
api_app = app.make_app(self.CONF, attach_storage=False) api_app = app.make_app(self.CONF, attach_storage=False)
self.assertTrue(api_app.wsgi_app.auth_uri.startswith('file')) conf = dict(self.CONF.get(acl.OPT_GROUP_NAME))
api_app = auth_token.AuthProtocol(api_app,
conf=conf)
self.assertTrue(api_app.auth_uri.startswith('file'))
os.unlink(tmpfile) os.unlink(tmpfile)

View File

@ -20,11 +20,14 @@
import datetime import datetime
import json import json
from ceilometer.api import acl import webtest
from ceilometer.api import app
from ceilometer.api.controllers import v2 as v2_api from ceilometer.api.controllers import v2 as v2_api
from ceilometer.openstack.common import timeutils from ceilometer.openstack.common import timeutils
from ceilometer.publisher import utils from ceilometer.publisher import utils
from ceilometer import sample from ceilometer import sample
from ceilometer.tests import api as acl
from ceilometer.tests.api.v2 import FunctionalTest from ceilometer.tests.api.v2 import FunctionalTest
from ceilometer.tests import db as tests_db from ceilometer.tests import db as tests_db
@ -115,7 +118,9 @@ class TestAPIACL(FunctionalTest,
def _make_app(self): def _make_app(self):
self.CONF.set_override("cache", "fake.cache", group=acl.OPT_GROUP_NAME) self.CONF.set_override("cache", "fake.cache", group=acl.OPT_GROUP_NAME)
return super(TestAPIACL, self)._make_app(enable_acl=True) file_name = self.path_get('etc/ceilometer/api_paste.ini')
self.CONF.set_override("api_paste_config", file_name)
return webtest.TestApp(app.load_app())
def test_non_authenticated(self): def test_non_authenticated(self):
response = self.get_json('/meters', expect_errors=True) response = self.get_json('/meters', expect_errors=True)

View File

@ -24,12 +24,12 @@ import os
import mock import mock
import wsme import wsme
from ceilometer.api import acl
from ceilometer.api import app from ceilometer.api import app
from ceilometer.openstack.common import fileutils from ceilometer.openstack.common import fileutils
from ceilometer.openstack.common.fixture import config from ceilometer.openstack.common.fixture import config
from ceilometer.openstack.common import gettextutils from ceilometer.openstack.common import gettextutils
from ceilometer import service from ceilometer import service
from ceilometer.tests import api as acl
from ceilometer.tests.api.v2 import FunctionalTest from ceilometer.tests.api.v2 import FunctionalTest
from ceilometer.tests import base from ceilometer.tests import base
from ceilometer.tests import db as tests_db from ceilometer.tests import db as tests_db
@ -50,18 +50,23 @@ class TestApp(base.BaseTestCase):
self.path_get("etc/ceilometer/pipeline.yaml")) self.path_get("etc/ceilometer/pipeline.yaml"))
self.CONF.set_override('connection', "log://", group="database") self.CONF.set_override('connection', "log://", group="database")
self.CONF.set_override("auth_uri", None, group=acl.OPT_GROUP_NAME) self.CONF.set_override("auth_uri", None, group=acl.OPT_GROUP_NAME)
file_name = self.path_get('etc/ceilometer/api_paste.ini')
self.CONF.set_override("api_paste_config", file_name)
api_app = app.setup_app() api_app = app.load_app()
self.assertTrue(api_app.auth_uri.startswith('file')) self.assertTrue(api_app.auth_uri.startswith('file'))
def test_keystone_middleware_parse_conffile(self): def test_keystone_middleware_parse_conffile(self):
pipeline_conf = self.path_get("etc/ceilometer/pipeline.yaml") pipeline_conf = self.path_get("etc/ceilometer/pipeline.yaml")
api_conf = self.path_get('etc/ceilometer/api_paste.ini')
content = "[DEFAULT]\n"\ content = "[DEFAULT]\n"\
"rpc_backend = fake\n"\ "rpc_backend = fake\n"\
"pipeline_cfg_file = {0}\n"\ "pipeline_cfg_file = {0}\n"\
"[{1}]\n"\ "api_paste_config = {1}\n"\
"[{2}]\n"\
"auth_protocol = file\n"\ "auth_protocol = file\n"\
"auth_version = v2.0\n".format(pipeline_conf, "auth_version = v2.0\n".format(pipeline_conf,
api_conf,
acl.OPT_GROUP_NAME) acl.OPT_GROUP_NAME)
tmpfile = fileutils.write_to_tempfile(content=content, tmpfile = fileutils.write_to_tempfile(content=content,
@ -70,7 +75,7 @@ class TestApp(base.BaseTestCase):
service.prepare_service(['ceilometer-api', service.prepare_service(['ceilometer-api',
'--config-file=%s' % tmpfile]) '--config-file=%s' % tmpfile])
self.CONF.set_override('connection', "log://", group="database") self.CONF.set_override('connection', "log://", group="database")
api_app = app.setup_app() api_app = app.load_app()
self.assertTrue(api_app.auth_uri.startswith('file')) self.assertTrue(api_app.auth_uri.startswith('file'))
os.unlink(tmpfile) os.unlink(tmpfile)

View File

@ -105,6 +105,17 @@ class BinApiTestCase(base.BaseTestCase):
def setUp(self): def setUp(self):
super(BinApiTestCase, self).setUp() super(BinApiTestCase, self).setUp()
# create api_paste.ini file without authentication
content = \
"[pipeline:main]\n"\
"pipeline = api-server\n"\
"[app:api-server]\n"\
"paste.app_factory = ceilometer.api.app:app_factory\n"
self.paste = fileutils.write_to_tempfile(content=content,
prefix='api_paste',
suffix='.ini')
# create ceilometer.conf file
self.api_port = random.randint(10000, 11000) self.api_port = random.randint(10000, 11000)
self.http = httplib2.Http() self.http = httplib2.Http()
pipeline_cfg_file = self.path_get('etc/ceilometer/pipeline.yaml') pipeline_cfg_file = self.path_get('etc/ceilometer/pipeline.yaml')
@ -115,11 +126,13 @@ class BinApiTestCase(base.BaseTestCase):
"debug=true\n"\ "debug=true\n"\
"pipeline_cfg_file={0}\n"\ "pipeline_cfg_file={0}\n"\
"policy_file={1}\n"\ "policy_file={1}\n"\
"api_paste_config={2}\n"\
"[api]\n"\ "[api]\n"\
"port={2}\n"\ "port={3}\n"\
"[database]\n"\ "[database]\n"\
"connection=log://localhost\n".format(pipeline_cfg_file, "connection=log://localhost\n".format(pipeline_cfg_file,
policy_file, policy_file,
self.paste,
self.api_port) self.api_port)
self.tempfile = fileutils.write_to_tempfile(content=content, self.tempfile = fileutils.write_to_tempfile(content=content,

View File

@ -0,0 +1,15 @@
# Ceilometer API WSGI Pipeline
# Define the filters that make up the pipeline for processing WSGI requests
# Note: This pipeline is PasteDeploy's term rather than Ceilometer's pipeline
# used for processing samples
# Remove authtoken from the pipeline if you don't want to use keystone authentication
[pipeline:main]
pipeline = authtoken api-server
[app:api-server]
paste.app_factory = ceilometer.api.app:app_factory
[filter:authtoken]
paste.filter_factory = keystoneclient.middleware.auth_token:filter_factory

View File

@ -14,6 +14,7 @@ msgpack-python
netaddr>=0.7.6 netaddr>=0.7.6
oslo.config>=1.2.0 oslo.config>=1.2.0
oslo.vmware>=0.2 # Apache-2.0 oslo.vmware>=0.2 # Apache-2.0
PasteDeploy>=1.5.0
pbr>=0.6,<1.0 pbr>=0.6,<1.0
pecan>=0.4.5 pecan>=0.4.5
posix_ipc posix_ipc