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."""
from ceilometer.openstack.common import policy
from keystoneclient.middleware import auth_token
from oslo.config import cfg
_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):

View File

@ -23,9 +23,9 @@ from wsgiref import simple_server
import netaddr
from oslo.config import cfg
from paste import deploy
import pecan
from ceilometer.api import acl
from ceilometer.api import config as api_config
from ceilometer.api import hooks
from ceilometer.api import middleware
@ -35,12 +35,13 @@ from ceilometer import storage
LOG = log.getLogger(__name__)
auth_opts = [
cfg.StrOpt('auth_strategy',
default='keystone',
help='The strategy to use for auth: noauth or keystone.'),
cfg.BoolOpt('enable_v1_api',
default=True,
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
@ -80,9 +81,6 @@ def setup_app(pecan_config=None, extra_hooks=None):
guess_content_type_from_ext=False
)
if getattr(pecan_config.app, 'enable_acl', True):
return acl.install(app, cfg.CONF)
return app
@ -90,10 +88,9 @@ class VersionSelectorApplication(object):
def __init__(self):
pc = get_pecan_config()
pc.app.debug = CONF.debug
pc.app.enable_acl = (CONF.auth_strategy == 'keystone')
if cfg.CONF.enable_v1_api:
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:
def not_found(environ, start_response):
start_response('404 Not Found', [])
@ -136,14 +133,36 @@ def get_handler_cls():
return CeilometerHandler
def build_server():
def load_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
host, port = cfg.CONF.api.host, cfg.CONF.api.port
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())
LOG.info(_('Starting server in PID %s') % os.getpid())
@ -157,4 +176,9 @@ def build_server():
else:
LOG.info(_("serving on http://%(host)s:%(port)s") % (
{'host': host, 'port': port}))
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.
"""
from ceilometer import service
from ceilometer.api import app
# Initialize the oslo configuration library and logging
service.prepare_service([])
application = app.VersionSelectorApplication()
application = app.load_app()

View File

@ -20,7 +20,6 @@
import flask
from oslo.config import cfg
from ceilometer.api import acl
from ceilometer.api.v1 import blueprint as v1_blueprint
from ceilometer.openstack.common import jsonutils
from ceilometer import storage
@ -32,7 +31,7 @@ class JSONEncoder(flask.json.JSONEncoder):
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'):
app = flask.Flask('ceilometer.api')
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 = \
storage.get_connection(conf)
# Install the middleware wrapper
if enable_acl:
app.wsgi_app = acl.install(app.wsgi_app, conf)
return app
# 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
from six.moves import urllib
from ceilometer.api import acl
from ceilometer.api.v1 import app as v1_app
from ceilometer.api.v1 import blueprint as v1_blueprint
from ceilometer import messaging
@ -31,6 +30,8 @@ from ceilometer.openstack.common import jsonutils
from ceilometer import service
from ceilometer.tests import db as db_test_base
OPT_GROUP_NAME = 'keystone_authtoken'
class TestBase(db_test_base.TestBase):
"""Use only for v1 API tests.
@ -42,12 +43,11 @@ class TestBase(db_test_base.TestBase):
self.addCleanup(messaging.cleanup)
service.prepare_service([])
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.path_get('etc/ceilometer/policy.json'))
sources_file = self.path_get('ceilometer/tests/sources.json')
self.app = v1_app.make_app(self.CONF,
enable_acl=False,
attach_storage=False,
sources_file=sources_file)
@ -90,7 +90,7 @@ class FunctionalTest(db_test_base.TestBase):
self.addCleanup(messaging.cleanup)
super(FunctionalTest, self).setUp()
self.CONF.set_override("auth_version", "v2.0",
group=acl.OPT_GROUP_NAME)
group=OPT_GROUP_NAME)
self.CONF.set_override("policy_file",
self.path_get('etc/ceilometer/policy.json'))
self.app = self._make_app()

View File

@ -19,13 +19,15 @@
"""
import os
from ceilometer.api import acl
from keystoneclient.middleware import auth_token
from ceilometer.api.v1 import app
from ceilometer import messaging
from ceilometer.openstack.common import fileutils
from ceilometer.openstack.common.fixture import config
from ceilometer.openstack.common import test
from ceilometer import service
from ceilometer.tests import api as acl
class TestApp(test.BaseTestCase):
@ -44,7 +46,10 @@ class TestApp(test.BaseTestCase):
self.CONF.set_override("auth_uri", None,
group=acl.OPT_GROUP_NAME)
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):
content = "[{0}]\nauth_protocol = file"\
@ -55,5 +60,8 @@ class TestApp(test.BaseTestCase):
service.prepare_service(['ceilometer-api',
'--config-file=%s' % tmpfile])
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)

View File

@ -20,11 +20,14 @@
import datetime
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.openstack.common import timeutils
from ceilometer.publisher import utils
from ceilometer import sample
from ceilometer.tests import api as acl
from ceilometer.tests.api.v2 import FunctionalTest
from ceilometer.tests import db as tests_db
@ -115,7 +118,9 @@ class TestAPIACL(FunctionalTest,
def _make_app(self):
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):
response = self.get_json('/meters', expect_errors=True)

View File

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

View File

@ -105,6 +105,17 @@ class BinApiTestCase(base.BaseTestCase):
def setUp(self):
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.http = httplib2.Http()
pipeline_cfg_file = self.path_get('etc/ceilometer/pipeline.yaml')
@ -115,11 +126,13 @@ class BinApiTestCase(base.BaseTestCase):
"debug=true\n"\
"pipeline_cfg_file={0}\n"\
"policy_file={1}\n"\
"api_paste_config={2}\n"\
"[api]\n"\
"port={2}\n"\
"port={3}\n"\
"[database]\n"\
"connection=log://localhost\n".format(pipeline_cfg_file,
policy_file,
self.paste,
self.api_port)
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
oslo.config>=1.2.0
oslo.vmware>=0.2 # Apache-2.0
PasteDeploy>=1.5.0
pbr>=0.6,<1.0
pecan>=0.4.5
posix_ipc