Merge "Stop using global oslo_config"

This commit is contained in:
Zuul 2018-11-30 18:44:32 +00:00 committed by Gerrit Code Review
commit 764d0e255d
32 changed files with 278 additions and 265 deletions

View File

@ -69,19 +69,20 @@ def setup_commands():
def main():
CONF = conf.CONF
config = cfg.ConfigOpts()
conf.register_opts(config)
command_opts = setup_commands()
CONF.register_cli_opts(command_opts)
CONF(sys.argv[1:], project='placement',
version=version_info.version_string(),
default_config_files=None)
db_api.configure(CONF)
config.register_cli_opts(command_opts)
config(sys.argv[1:], project='placement',
version=version_info.version_string(),
default_config_files=None)
db_api.configure(config)
try:
func = CONF.command.func
func = config.command.func
return_code = func()
# If return_code ends up None we assume 0.
sys.exit(return_code or 0)
except cfg.NoSuchOptError:
CONF.print_help()
config.print_help()
sys.exit(1)

View File

@ -14,18 +14,19 @@
# under the License.
from __future__ import absolute_import
from oslo_config import cfg
from placement.conf import api
from placement.conf import base
from placement.conf import database
from placement.conf import paths
from placement.conf import placement
CONF = cfg.CONF
api.register_opts(CONF)
base.register_opts(CONF)
database.register_opts(CONF)
paths.register_opts(CONF)
placement.register_opts(CONF)
# To avoid global config, we require an existing ConfigOpts is passed
# to register_opts. Then the caller can have some assurance that the
# config they are using will maintain some independence.
def register_opts(conf):
api.register_opts(conf)
base.register_opts(conf)
database.register_opts(conf)
paths.register_opts(conf)
placement.register_opts(conf)

View File

@ -20,6 +20,10 @@ from placement import policy
@enginefacade.transaction_context_provider
class RequestContext(context.RequestContext):
def __init__(self, *args, **kwargs):
self.config = None
super(RequestContext, self).__init__(*args, **kwargs)
def can(self, action, target=None, fatal=True):
"""Verifies that the given action is valid on the target in this
context.

View File

@ -15,15 +15,14 @@ from __future__ import with_statement
from logging.config import fileConfig
from alembic import context
from oslo_config import cfg
from oslo_db import exception as db_exc
from placement import conf
from placement.db.sqlalchemy import models
from placement import db_api as placement_db
CONF = conf.CONF
# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config
@ -45,26 +44,6 @@ target_metadata = models.BASE.metadata
# ... etc.
def run_migrations_offline():
"""Run migrations in 'offline' mode.
This configures the context with just a URL
and not an Engine, though an Engine is acceptable
here as well. By skipping the Engine creation
we don't even need a DBAPI to be available.
Calls to context.execute() here emit the given string to the
script output.
"""
url = CONF.placement_database.connection
context.configure(
url=url, target_metadata=target_metadata, literal_binds=True)
with context.begin_transaction():
context.run_migrations()
def run_migrations_online():
"""Run migrations in 'online' mode.
@ -72,12 +51,17 @@ def run_migrations_online():
and associate a connection with the context.
"""
# If CONF and the database are not already configured, set them up. This
# can happen when using the alembic command line tool.
if not CONF.placement_database.connection:
CONF([], project="placement", default_config_files=None)
placement_db.configure(CONF)
connectable = placement_db.get_placement_engine()
try:
connectable = placement_db.get_placement_engine()
except db_exc.CantStartEngineError:
# We are being called from a context where the database hasn't been
# configured so we need to set up Config and config the database.
# This is usually the alembic command line.
config = cfg.ConfigOpts()
conf.register_opts(config)
config([], project="placement", default_config_files=None)
placement_db.configure(config)
connectable = placement_db.get_placement_engine()
with connectable.connect() as connection:
context.configure(
@ -87,7 +71,8 @@ def run_migrations_online():
with context.begin_transaction():
context.run_migrations()
if context.is_offline_mode():
run_migrations_offline()
raise Exception('offline mode disabled')
else:
run_migrations_online()

View File

@ -57,7 +57,7 @@ def deploy(conf):
fault_middleware = fault_wrap.FaultWrapper
request_log = requestlog.RequestLog
application = handler.PlacementHandler()
application = handler.PlacementHandler(config=conf)
# configure microversion middleware in the old school way
application = microversion_middleware(
application, microversion.SERVICE_TYPE, microversion.VERSIONS,

View File

@ -192,10 +192,13 @@ class PlacementHandler(object):
"""
def __init__(self, **local_config):
# NOTE(cdent): Local config currently unused.
self._map = make_map(ROUTE_DECLARATIONS)
self.config = local_config['config']
def __call__(self, environ, start_response):
# set a reference to the oslo.config ConfigOpts on the RequestContext
context = environ['placement.context']
context.config = self.config
# Check that an incoming request with a content-length header
# that is an integer > 0 and not empty, also has a content-type
# header that is not empty. If not raise a 400.

View File

@ -11,7 +11,6 @@
# under the License.
"""DB Utility methods for placement."""
from oslo_config import cfg
from oslo_log import log as logging
import webob
@ -23,7 +22,6 @@ from placement.objects import project as project_obj
from placement.objects import user as user_obj
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
@ -54,8 +52,8 @@ def ensure_consumer(ctx, consumer_uuid, project_id, user_id,
created_new_consumer = False
requires_consumer_generation = want_version.matches((1, 28))
if project_id is None:
project_id = CONF.placement.incomplete_consumer_project_id
user_id = CONF.placement.incomplete_consumer_user_id
project_id = ctx.config.placement.incomplete_consumer_project_id
user_id = ctx.config.placement.incomplete_consumer_user_id
try:
proj = project_obj.Project.get_by_external_id(ctx, project_id)
except exception.NotFound:

View File

@ -10,7 +10,6 @@
# License for the specific language governing permissions and limitations
# under the License.
from oslo_config import cfg
from oslo_db import exception as db_exc
from oslo_versionedobjects import base
from oslo_versionedobjects import fields
@ -20,7 +19,6 @@ from placement.db.sqlalchemy import models
from placement import db_api
from placement import exception
CONF = cfg.CONF
PROJECT_TBL = models.Project.__table__
@ -29,7 +27,7 @@ def ensure_incomplete_project(ctx):
"""Ensures that a project record is created for the "incomplete consumer
project". Returns the internal ID of that record.
"""
incomplete_id = CONF.placement.incomplete_consumer_project_id
incomplete_id = ctx.config.placement.incomplete_consumer_project_id
sel = sa.select([PROJECT_TBL.c.id]).where(
PROJECT_TBL.c.external_id == incomplete_id)
res = ctx.session.execute(sel).fetchone()

View File

@ -23,7 +23,6 @@ import random
import os_traits
from oslo_concurrency import lockutils
from oslo_config import cfg
from oslo_db import api as oslo_db_api
from oslo_db import exception as db_exc
from oslo_log import log as logging
@ -63,7 +62,6 @@ _RC_CACHE = None
_TRAIT_LOCK = 'trait_sync'
_TRAITS_SYNCED = False
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
@ -4014,9 +4012,9 @@ class AllocationCandidates(base.VersionedObject):
"""Returns an AllocationCandidates object containing all resource
providers matching a set of supplied resource constraints, with a set
of allocation requests constructed from that list of resource
providers. If CONF.placement.randomize_allocation_candidates is True
(default is False) then the order of the allocation requests will
be randomized.
providers. If CONF.placement.randomize_allocation_candidates (on
contex.config) is True (default is False) then the order of the
allocation requests will be randomized.
:param context: Nova RequestContext.
:param requests: Dict, keyed by suffix, of placement.lib.RequestGroup
@ -4159,16 +4157,17 @@ class AllocationCandidates(base.VersionedObject):
alloc_request_objs, summary_objs = _merge_candidates(
candidates, group_policy=group_policy)
return cls._limit_results(alloc_request_objs, summary_objs, limit)
return cls._limit_results(context, alloc_request_objs, summary_objs,
limit)
@staticmethod
def _limit_results(alloc_request_objs, summary_objs, limit):
def _limit_results(context, alloc_request_objs, summary_objs, limit):
# Limit the number of allocation request objects. We do this after
# creating all of them so that we can do a random slice without
# needing to mess with the complex sql above or add additional
# columns to the DB.
if limit and limit < len(alloc_request_objs):
if CONF.placement.randomize_allocation_candidates:
if context.config.placement.randomize_allocation_candidates:
alloc_request_objs = random.sample(alloc_request_objs, limit)
else:
alloc_request_objs = alloc_request_objs[:limit]
@ -4187,7 +4186,7 @@ class AllocationCandidates(base.VersionedObject):
continue
kept_summary_objs.append(summary)
summary_objs = kept_summary_objs
elif CONF.placement.randomize_allocation_candidates:
elif context.config.placement.randomize_allocation_candidates:
random.shuffle(alloc_request_objs)
return alloc_request_objs, summary_objs

View File

@ -10,7 +10,6 @@
# License for the specific language governing permissions and limitations
# under the License.
from oslo_config import cfg
from oslo_db import exception as db_exc
from oslo_versionedobjects import base
from oslo_versionedobjects import fields
@ -20,7 +19,6 @@ from placement.db.sqlalchemy import models
from placement import db_api
from placement import exception
CONF = cfg.CONF
USER_TBL = models.User.__table__
@ -29,7 +27,7 @@ def ensure_incomplete_user(ctx):
"""Ensures that a user record is created for the "incomplete consumer
user". Returns the internal ID of that record.
"""
incomplete_id = CONF.placement.incomplete_consumer_user_id
incomplete_id = ctx.config.placement.incomplete_consumer_user_id
sel = sa.select([USER_TBL.c.id]).where(
USER_TBL.c.external_id == incomplete_id)
res = ctx.session.execute(sel).fetchone()

View File

@ -20,7 +20,6 @@ from placement import exception
from placement import policies
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
_ENFORCER_PLACEMENT = None
@ -33,7 +32,7 @@ def reset():
_ENFORCER_PLACEMENT = None
def init():
def init(conf):
"""Init an Enforcer class. Sets the _ENFORCER_PLACEMENT global."""
global _ENFORCER_PLACEMENT
if not _ENFORCER_PLACEMENT:
@ -43,7 +42,7 @@ def init():
# which is used by nova. In other words, to have separate policy files
# for placement and nova, we have to use separate policy_file options.
_ENFORCER_PLACEMENT = policy.Enforcer(
CONF, policy_file=CONF.placement.policy_file)
conf, policy_file=conf.placement.policy_file)
_ENFORCER_PLACEMENT.register_defaults(policies.list_rules())
_ENFORCER_PLACEMENT.load_rules()
@ -53,7 +52,7 @@ def get_enforcer():
# files from overrides on disk and defaults in code. We can just pass an
# empty list and let oslo do the config lifting for us.
cfg.CONF([], project='placement')
init()
init(cfg.CONF)
return _ENFORCER_PLACEMENT
@ -74,7 +73,7 @@ def authorize(context, action, target, do_raise=True):
:returns: non-False value (not necessarily "True") if authorized, and the
exact value False if not authorized and do_raise is False.
"""
init()
init(context.config)
credentials = context.to_policy_values()
try:
# NOTE(mriedem): The "action" kwarg is for the PolicyNotAuthorized exc.

View File

@ -14,11 +14,14 @@
# License for the specific language governing permissions and limitations
# under the License.
"""Fixtures for Nova tests."""
"""Fixtures for Placement tests."""
from __future__ import absolute_import
import tempfile
import fixtures
from oslo_concurrency.fixture import lockutils as lock_fixture
from oslo_concurrency import lockutils
from oslo_config import cfg
from placement.db.sqlalchemy import migration
@ -27,16 +30,10 @@ from placement import deploy
from placement.objects import resource_provider
CONF = cfg.CONF
session_configured = False
def reset():
"""Call this to allow the placement db fixture to be reconfigured
in the same process.
"""
global session_configured
session_configured = False
placement_db.placement_context_manager.dispose_pool()
# TODO(cdent): Future handling in sqlalchemy may allow doing this
# in a less hacky way.
@ -46,26 +43,25 @@ def reset():
class Database(fixtures.Fixture):
def __init__(self, set_config=False):
def __init__(self, conf_fixture, set_config=False):
"""Create a database fixture."""
super(Database, self).__init__()
global session_configured
if not session_configured:
if set_config:
try:
CONF.register_opt(cfg.StrOpt('connection'),
group='placement_database')
except cfg.DuplicateOptError:
# already registered
pass
CONF.set_override('connection', 'sqlite://',
group='placement_database')
placement_db.configure(CONF)
session_configured = True
if set_config:
try:
conf_fixture.register_opt(
cfg.StrOpt('connection'), group='placement_database')
except cfg.DuplicateOptError:
# already registered
pass
conf_fixture.config(connection='sqlite://',
group='placement_database')
self.conf_fixture = conf_fixture
self.get_engine = placement_db.get_placement_engine
def setUp(self):
super(Database, self).setUp()
reset()
placement_db.configure(self.conf_fixture.conf)
migration.create_schema()
resource_provider._TRAITS_SYNCED = False
resource_provider._RC_CACHE = None
@ -76,3 +72,15 @@ class Database(fixtures.Fixture):
reset()
resource_provider._TRAITS_SYNCED = False
resource_provider._RC_CACHE = None
class ExternalLockFixture(lock_fixture.LockFixture):
"""Provide a predictable inter-process file-based lock that doesn't
require oslo.config, by setting its own lock_path.
This is used to prevent live database test from conflicting with
one another in a concurrent enviornment.
"""
def __init__(self, name):
lock_path = tempfile.gettempdir()
self.mgr = lockutils.lock(name, external=True, lock_path=lock_path)

View File

@ -16,15 +16,13 @@ from oslo_log.fixture import logging_error
from oslotest import output
import testtools
from placement import conf
from placement import context
from placement.tests import fixtures
from placement.tests.functional.fixtures import capture
from placement.tests.unit import policy_fixture
CONF = cfg.CONF
class TestCase(testtools.TestCase):
"""A base test case for placement functional tests.
@ -36,14 +34,14 @@ class TestCase(testtools.TestCase):
super(TestCase, self).setUp()
# Manage required configuration
conf_fixture = self.useFixture(config_fixture.Config(CONF))
conf_fixture.config(
group='placement_database',
connection='sqlite://',
sqlite_synchronous=False)
CONF([], default_config_files=[])
self.conf_fixture = self.useFixture(
config_fixture.Config(cfg.ConfigOpts()))
conf.register_opts(self.conf_fixture.conf)
self.placement_db = self.useFixture(fixtures.Database(
self.conf_fixture, set_config=True))
self.conf_fixture.conf([], default_config_files=[])
self.useFixture(policy_fixture.PolicyFixture())
self.useFixture(policy_fixture.PolicyFixture(self.conf_fixture))
self.useFixture(capture.Logging())
self.useFixture(output.CaptureOutput())
@ -51,5 +49,5 @@ class TestCase(testtools.TestCase):
self.useFixture(capture.WarningsFixture())
self.useFixture(logging_error.get_logging_handle_error_fixture())
self.placement_db = self.useFixture(fixtures.Database())
self.context = context.RequestContext()
self.context.config = self.conf_fixture.conf

View File

@ -10,7 +10,6 @@
# License for the specific language governing permissions and limitations
# under the License.
import os_traits
from oslo_config import cfg
from oslo_utils.fixture import uuidsentinel as uuids
import six
import sqlalchemy as sa
@ -22,9 +21,6 @@ from placement import rc_fields as fields
from placement.tests.functional.db import test_base as tb
CONF = cfg.CONF
class ProviderDBHelperTestCase(tb.PlacementDbBaseTestCase):
def test_get_provider_ids_matching(self):
@ -705,8 +701,8 @@ class AllocationCandidatesTestCase(tb.PlacementDbBaseTestCase):
# Do it again, with conf set to randomize. We can't confirm the
# random-ness but we can be sure the code path doesn't explode.
CONF.set_override('randomize_allocation_candidates', True,
group='placement')
self.conf_fixture.config(randomize_allocation_candidates=True,
group='placement')
# Ask for two candidates.
limit = 2

View File

@ -10,7 +10,6 @@
# License for the specific language governing permissions and limitations
# under the License.
from oslo_config import cfg
from oslo_utils.fixture import uuidsentinel as uuids
import sqlalchemy as sa
@ -25,7 +24,6 @@ from placement.tests.functional import base
from placement.tests.functional.db import test_base as tb
CONF = cfg.CONF
CONSUMER_TBL = consumer_obj.CONSUMER_TBL
PROJECT_TBL = project_obj.PROJECT_TBL
USER_TBL = user_obj.USER_TBL
@ -137,7 +135,8 @@ class CreateIncompleteConsumersTestCase(base.TestCase):
@db_api.placement_context_manager.reader
def _check_incomplete_consumers(self, ctx):
incomplete_project_id = CONF.placement.incomplete_consumer_project_id
config = ctx.config
incomplete_project_id = config.placement.incomplete_consumer_project_id
# Verify we have a record in projects for the missing sentinel
sel = PROJECT_TBL.select(
@ -147,7 +146,7 @@ class CreateIncompleteConsumersTestCase(base.TestCase):
incomplete_proj_id = rec['id']
# Verify we have a record in users for the missing sentinel
incomplete_user_id = CONF.placement.incomplete_consumer_user_id
incomplete_user_id = config.placement.incomplete_consumer_user_id
sel = user_obj.USER_TBL.select(
USER_TBL.c.external_id == incomplete_user_id)
rec = ctx.session.execute(sel).first()

View File

@ -25,11 +25,10 @@ the tests.
import contextlib
import functools
import tempfile
from alembic import script
import mock
from oslo_concurrency.fixture import lockutils as concurrency
from oslo_config import cfg
from oslo_config import fixture as config_fixture
from oslo_db import exception as db_exc
from oslo_db.sqlalchemy import enginefacade
@ -46,7 +45,6 @@ from placement import db_api
from placement.tests import fixtures as db_fixture
CONF = conf.CONF
DB_NAME = 'openstack_citest'
LOG = logging.getLogger(__name__)
@ -64,8 +62,7 @@ def configure(conf_fixture, db_url):
here, not done as a base class as the mess of mixins makes that
inscrutable. So instead we create a nice simple function.
"""
conf_fixture.config(lock_path=tempfile.gettempdir(),
group='oslo_concurrency')
conf.register_opts(conf_fixture.conf)
conf_fixture.config(group='placement_database', connection=db_url)
# We need to retry at least once (and quickly) otherwise the connection
# test routines in oslo_db do not run, and the exception handling for
@ -192,11 +189,11 @@ class MigrationCheckersMixin(object):
def setUp(self):
self.addCleanup(db_fixture.reset)
db_url = generate_url(self.DRIVER)
conf_fixture = self.useFixture(config_fixture.Config(CONF))
conf_fixture = self.useFixture(config_fixture.Config(cfg.ConfigOpts()))
configure(conf_fixture, db_url)
self.useFixture(concurrency.LockFixture('test_mig'))
self.useFixture(db_fixture.ExternalLockFixture('test_mig'))
db_fixture.reset()
db_api.configure(CONF)
db_api.configure(conf_fixture.conf)
try:
self.engine = db_api.get_placement_engine()
except (db_exc.DBNonExistentDatabase, db_exc.DBConnectionError):
@ -261,11 +258,11 @@ class TestMigrationsPostgresql(MigrationCheckersMixin,
class ModelsMigrationSyncMixin(object):
def setUp(self):
url = generate_url(self.DRIVER)
conf_fixture = self.useFixture(config_fixture.Config(CONF))
conf_fixture = self.useFixture(config_fixture.Config(cfg.ConfigOpts()))
configure(conf_fixture, url)
self.useFixture(concurrency.LockFixture('test_mig'))
self.useFixture(db_fixture.ExternalLockFixture('test_mig'))
db_fixture.reset()
db_api.configure(CONF)
db_api.configure(conf_fixture.conf)
super(ModelsMigrationSyncMixin, self).setUp()
# This is required to prevent the global opportunistic db settings
# leaking into other tests.

View File

@ -23,6 +23,7 @@ from oslo_utils.fixture import uuidsentinel as uuids
from oslo_utils import uuidutils
from oslotest import output
from placement import conf
from placement import context
from placement import deploy
from placement.objects import project as project_obj
@ -36,10 +37,14 @@ from placement.tests.functional.fixtures import capture
from placement.tests.unit import policy_fixture
CONF = cfg.CONF
# This global conf is not a global olso_config.cfg.CONF. It's a global
# used locally to work around a limitation in the way that gabbi instantiates
# the WSGI application being tested.
CONF = None
def setup_app():
global CONF
return deploy.loadapp(CONF)
@ -47,6 +52,7 @@ class APIFixture(fixture.GabbiFixture):
"""Setup the required backend fixtures for a basic placement service."""
def start_fixture(self):
global CONF
# Set up stderr and stdout captures by directly driving the
# existing nova fixtures that do that. This captures the
# output that happens outside individual tests (for
@ -62,31 +68,35 @@ class APIFixture(fixture.GabbiFixture):
self.warnings_fixture = capture.WarningsFixture()
self.warnings_fixture.setUp()
self.conf_fixture = config_fixture.Config(CONF)
# Do not use global CONF
self.conf_fixture = config_fixture.Config(cfg.ConfigOpts())
self.conf_fixture.setUp()
self.conf_fixture.config(
group="placement_database",
connection='sqlite://',
sqlite_synchronous=False)
conf.register_opts(self.conf_fixture.conf)
self.conf_fixture.config(group='api', auth_strategy='noauth2')
self.placement_db_fixture = fixtures.Database(
self.conf_fixture, set_config=True)
self.placement_db_fixture.setUp()
self.context = context.RequestContext()
# Register CORS opts, but do not set config. This has the
# effect of exercising the "don't use cors" path in
# deploy.py. Without setting some config the group will not
# be present.
CONF.register_opts(cors.CORS_OPTS, 'cors')
self.conf_fixture.register_opts(cors.CORS_OPTS, 'cors')
# Set default policy opts, otherwise the deploy module can
# NoSuchOptError.
policy_opts.set_defaults(CONF)
policy_opts.set_defaults(self.conf_fixture.conf)
# Make sure default_config_files is an empty list, not None.
# If None /etc/nova/nova.conf is read and confuses results.
CONF([], default_config_files=[])
# If None /etc/placement/placement.conf is read and confuses results.
self.conf_fixture.conf([], default_config_files=[])
self.placement_db_fixture = fixtures.Database()
self.placement_db_fixture.setUp()
# Turn on a policy fixture.
self.policy_fixture = policy_fixture.PolicyFixture(
self.conf_fixture)
self.policy_fixture.setUp()
os.environ['RP_UUID'] = uuidutils.generate_uuid()
os.environ['RP_NAME'] = uuidutils.generate_uuid()
@ -100,14 +110,18 @@ class APIFixture(fixture.GabbiFixture):
os.environ['CONSUMER_UUID'] = uuidutils.generate_uuid()
os.environ['PARENT_PROVIDER_UUID'] = uuidutils.generate_uuid()
os.environ['ALT_PARENT_PROVIDER_UUID'] = uuidutils.generate_uuid()
CONF = self.conf_fixture.conf
def stop_fixture(self):
global CONF
self.placement_db_fixture.cleanUp()
self.warnings_fixture.cleanUp()
self.output_stream_fixture.cleanUp()
self.standard_logging_fixture.cleanUp()
self.logging_error_fixture.cleanUp()
self.policy_fixture.cleanUp()
self.conf_fixture.cleanUp()
CONF = None
class AllocationFixture(APIFixture):
@ -476,8 +490,6 @@ class OpenPolicyFixture(APIFixture):
def start_fixture(self):
super(OpenPolicyFixture, self).start_fixture()
self.placement_policy_fixture = policy_fixture.PolicyFixture()
self.placement_policy_fixture.setUp()
# Get all of the registered rules and set them to '@' to allow any
# user to have access. The nova policy "admin_or_owner" concept does
# not really apply to most of placement resources since they do not
@ -489,8 +501,7 @@ class OpenPolicyFixture(APIFixture):
if name in ['placement', 'admin_api']:
continue
rules[name] = '@'
self.placement_policy_fixture.set_rules(rules)
self.policy_fixture.set_rules(rules)
def stop_fixture(self):
super(OpenPolicyFixture, self).stop_fixture()
self.placement_policy_fixture.cleanUp()

View File

@ -14,14 +14,14 @@ from __future__ import absolute_import
import fixtures
from oslo_config import cfg
from oslo_config import fixture as config_fixture
from oslo_policy import opts as policy_opts
from oslo_utils import uuidutils
from wsgi_intercept import interceptor
from placement import conf
from placement import deploy
from placement.tests import fixtures as db_fixture
CONF = cfg.CONF
from placement.tests.unit import policy_fixture
class PlacementFixture(fixtures.Fixture):
@ -34,22 +34,33 @@ class PlacementFixture(fixtures.Fixture):
all calls would be passing this token.
This fixture takes care of starting a fixture for an in-RAM placement
database, unless the db kwargs is False.
database, unless the db kwarg is False.
Used by other services, including nova, for functional tests.
"""
def __init__(self, token='admin', db=True):
def __init__(self, token='admin', conf_fixture=None, db=True):
self.token = token
self.db = db
self.conf_fixture = conf_fixture
def setUp(self):
super(PlacementFixture, self).setUp()
if self.db:
self.useFixture(db_fixture.Database(set_config=True))
if not self.conf_fixture:
config = cfg.ConfigOpts()
self.conf_fixture = self.useFixture(config_fixture.Config(config))
conf.register_opts(self.conf_fixture.conf)
conf_fixture = config_fixture.Config(CONF)
conf_fixture.config(group='api', auth_strategy='noauth2')
loader = deploy.loadapp(CONF)
if self.db:
self.useFixture(db_fixture.Database(self.conf_fixture,
set_config=True))
policy_opts.set_defaults(self.conf_fixture.conf)
self.conf_fixture.config(group='api', auth_strategy='noauth2')
self.conf_fixture.conf([], default_config_files=[])
self.useFixture(policy_fixture.PolicyFixture(self.conf_fixture))
loader = deploy.loadapp(self.conf_fixture.conf)
app = lambda: loader
self.endpoint = 'http://%s/placement' % uuidutils.generate_uuid()
intercept = interceptor.RequestsInterceptor(app, url=self.endpoint)

View File

@ -11,26 +11,31 @@
# under the License.
from oslo_config import cfg
from oslo_policy import opts as policy_opts
from oslo_utils.fixture import uuidsentinel
from placement import conf
from placement import direct
from placement.tests.functional import base
CONF = cfg.CONF
class TestDirect(base.TestCase):
def setUp(self):
super(TestDirect, self).setUp()
self.conf = cfg.ConfigOpts()
conf.register_opts(self.conf)
policy_opts.set_defaults(self.conf)
def test_direct_is_there(self):
with direct.PlacementDirect(CONF) as client:
with direct.PlacementDirect(self.conf) as client:
resp = client.get('/')
self.assertTrue(resp)
data = resp.json()
self.assertEqual('v1.0', data['versions'][0]['id'])
def test_get_resource_providers(self):
with direct.PlacementDirect(CONF) as client:
with direct.PlacementDirect(self.conf) as client:
resp = client.get('/resource_providers')
self.assertTrue(resp)
data = resp.json()
@ -38,7 +43,7 @@ class TestDirect(base.TestCase):
def test_create_resource_provider(self):
data = {'name': 'fake'}
with direct.PlacementDirect(CONF) as client:
with direct.PlacementDirect(self.conf) as client:
resp = client.post('/resource_providers', json=data)
self.assertTrue(resp)
resp = client.get('/resource_providers')
@ -48,13 +53,13 @@ class TestDirect(base.TestCase):
def test_json_validation_happens(self):
data = {'name': 'fake', 'cowsay': 'moo'}
with direct.PlacementDirect(CONF) as client:
with direct.PlacementDirect(self.conf) as client:
resp = client.post('/resource_providers', json=data)
self.assertFalse(resp)
self.assertEqual(400, resp.status_code)
def test_microversion_handling(self):
with direct.PlacementDirect(CONF) as client:
with direct.PlacementDirect(self.conf) as client:
# create parent
parent_data = {'name': uuidsentinel.p_rp,
'uuid': uuidsentinel.p_rp}

View File

@ -10,16 +10,11 @@
# License for the specific language governing permissions and limitations
# under the License.
from oslo_config import cfg
from placement import direct
from placement import handler
from placement.tests.functional import base
CONF = cfg.CONF
class TestVerifyPolicy(base.TestCase):
"""Verify that all defined placement routes have a policy."""
@ -42,7 +37,8 @@ class TestVerifyPolicy(base.TestCase):
(method, route, response.status_code))
def test_verify_policy(self):
with direct.PlacementDirect(CONF, latest_microversion=True) as client:
conf = self.conf_fixture.conf
with direct.PlacementDirect(conf, latest_microversion=True) as client:
for route, methods in handler.ROUTE_DECLARATIONS.items():
if route in self.EXCEPTIONS:
continue

View File

@ -19,15 +19,17 @@ import six
import testtools
from placement.cmd import manage
from placement import conf
class TestCommandParsers(testtools.TestCase):
def setUp(self):
super(TestCommandParsers, self).setUp()
self.conf = cfg.CONF
self.conf = cfg.ConfigOpts()
conf_fixture = config_fixture.Config(self.conf)
self.useFixture(conf_fixture)
conf.register_opts(conf_fixture.conf)
# Quiet output from argparse (used within oslo_config).
# If you are debugging, commenting this out might be useful.
self.output = self.useFixture(

View File

@ -14,12 +14,14 @@
import fixtures
import microversion_parse
import mock
from oslo_config import cfg
from oslo_config import fixture as config_fixture
from oslo_utils.fixture import uuidsentinel
import testtools
import webob
from placement import conf
from placement import context
from placement import exception
from placement.handlers import util
from placement import microversion
@ -28,12 +30,12 @@ from placement.objects import project as project_obj
from placement.objects import user as user_obj
CONF = conf.CONF
class TestEnsureConsumer(testtools.TestCase):
def setUp(self):
super(TestEnsureConsumer, self).setUp()
self.conf = cfg.ConfigOpts()
self.useFixture(config_fixture.Config(self.conf))
conf.register_opts(self.conf)
self.mock_project_get = self.useFixture(fixtures.MockPatch(
'placement.objects.project.'
'Project.get_by_external_id')).mock
@ -52,7 +54,8 @@ class TestEnsureConsumer(testtools.TestCase):
self.mock_consumer_create = self.useFixture(fixtures.MockPatch(
'placement.objects.consumer.'
'Consumer.create')).mock
self.ctx = mock.sentinel.ctx
self.ctx = context.RequestContext(user_id='fake', project_id='fake')
self.ctx.config = self.conf
self.consumer_id = uuidsentinel.consumer
self.project_id = uuidsentinel.project
self.user_id = uuidsentinel.user
@ -144,9 +147,9 @@ class TestEnsureConsumer(testtools.TestCase):
consumer_gen, self.before_version)
self.mock_project_get.assert_called_once_with(
self.ctx, CONF.placement.incomplete_consumer_project_id)
self.ctx, self.conf.placement.incomplete_consumer_project_id)
self.mock_user_get.assert_called_once_with(
self.ctx, CONF.placement.incomplete_consumer_user_id)
self.ctx, self.conf.placement.incomplete_consumer_user_id)
self.mock_consumer_get.assert_called_once_with(
self.ctx, self.consumer_id)
self.mock_project_create.assert_called_once()

View File

@ -11,11 +11,14 @@
# under the License.
import mock
from oslo_config import cfg
from oslo_config import fixture as config_fixture
from oslo_utils.fixture import uuidsentinel as uuids
from oslo_utils import timeutils
import six
import testtools
from placement import conf
from placement import context
from placement import exception
from placement.objects import resource_provider
@ -103,6 +106,10 @@ class _TestCase(testtools.TestCase):
self.user_id = 'fake-user'
self.project_id = 'fake-project'
self.context = context.RequestContext(self.user_id, self.project_id)
config = cfg.ConfigOpts()
self.conf_fixture = self.useFixture(config_fixture.Config(config))
conf.register_opts(config)
self.context.config = config
class TestResourceProviderNoDB(_TestCase):
@ -356,6 +363,6 @@ class TestAllocationCandidatesNoDB(_TestCase):
sum6 = mock.Mock(resource_provider=mock.Mock(uuid=6))
sum_in = [sum1, sum0, sum4, sum8, sum5, sum7, sum6]
aro, sum = resource_provider.AllocationCandidates._limit_results(
aro_in, sum_in, 2)
self.context, aro_in, sum_in, 2)
self.assertEqual(aro_in[:2], aro)
self.assertEqual(set([sum1, sum0, sum4, sum8, sum5]), set(sum))

View File

@ -14,8 +14,6 @@
import fixtures
from oslo_config import cfg
from oslo_config import fixture as config_fixture
from oslo_policy import policy as oslo_policy
from placement.conf import paths
@ -23,15 +21,19 @@ from placement import policy as placement_policy
class PolicyFixture(fixtures.Fixture):
def __init__(self, conf_fixture):
self.conf_fixture = conf_fixture
super(PolicyFixture, self).__init__()
"""Load the default placement policy for tests."""
def setUp(self):
super(PolicyFixture, self).setUp()
self.conf_fixture = self.useFixture(config_fixture.Config(cfg.CONF))
policy_file = paths.state_path_def(
'etc/placement/placement-policy.yaml')
self.conf_fixture.config(group='placement', policy_file=policy_file)
placement_policy.reset()
placement_policy.init()
placement_policy.init(self.conf_fixture.conf)
self.addCleanup(placement_policy.reset)
@staticmethod

View File

@ -17,17 +17,17 @@ import testtools
from oslo_config import cfg
from oslo_config import fixture as config_fixture
from placement import conf
from placement import db_api
CONF = cfg.CONF
class DbApiTests(testtools.TestCase):
def setUp(self):
super(DbApiTests, self).setUp()
self.conf_fixture = self.useFixture(config_fixture.Config(CONF))
config = cfg.ConfigOpts()
self.conf_fixture = self.useFixture(config_fixture.Config(config))
conf.register_opts(self.conf_fixture.conf)
db_api.configure.reset()
@mock.patch.object(db_api.placement_context_manager, "configure")

View File

@ -16,8 +16,7 @@ import testtools
from oslo_config import cfg
from oslo_config import fixture as config_fixture
CONF = cfg.CONF
from placement import conf
class TestPlacementDBConf(testtools.TestCase):
@ -25,7 +24,9 @@ class TestPlacementDBConf(testtools.TestCase):
def setUp(self):
super(TestPlacementDBConf, self).setUp()
self.conf_fixture = self.useFixture(config_fixture.Config(CONF))
config = cfg.ConfigOpts()
self.conf_fixture = self.useFixture(config_fixture.Config(config))
conf.register_opts(config)
def test_missing_config_raises(self):
"""Not setting [placement_database]/connection is an error."""

View File

@ -13,31 +13,39 @@
# under the License.
"""Unit tests for the deply function used to build the Placement service."""
from keystonemiddleware import auth_token
from oslo_config import cfg
from oslo_config import fixture as config_fixture
from oslo_policy import opts as policy_opts
import testtools
import webob
from placement import conf
from placement import deploy
CONF = cfg.CONF
class DeployTest(testtools.TestCase):
def test_auth_middleware_factory(self):
"""Make sure that configuration settings make their way to
the keystone middleware correctly.
"""
config = cfg.ConfigOpts()
conf_fixture = self.useFixture(config_fixture.Config(config))
conf.register_opts(conf_fixture.conf)
# NOTE(cdent): There appears to be no simple way to get the list of
# options used by the auth_token middleware. So we pull from an
# existing data structure.
auth_token_opts = auth_token.AUTH_TOKEN_OPTS[0][1]
conf_fixture.register_opts(auth_token_opts, group='keystone_authtoken')
www_authenticate_uri = 'http://example.com/identity'
CONF.set_override('www_authenticate_uri', www_authenticate_uri,
conf_fixture.config(www_authenticate_uri=www_authenticate_uri,
group='keystone_authtoken')
# ensure that the auth_token middleware is chosen
CONF.set_override('auth_strategy', 'keystone', group='api')
conf_fixture.config(auth_strategy='keystone', group='api')
# register and default policy opts (referenced by deploy)
policy_opts.set_defaults(CONF)
app = deploy.deploy(CONF)
policy_opts.set_defaults(conf_fixture.conf)
app = deploy.deploy(conf_fixture.conf)
req = webob.Request.blank('/resource_providers', method="GET")
response = req.get_response(app)

View File

@ -128,10 +128,11 @@ class PlacementLoggingTest(testtools.TestCase):
@mock.patch("placement.handler.LOG")
def test_404_no_error_log(self, mocked_log):
environ = _environ(path='/hello', method='GET')
config = mock.MagicMock()
context_mock = mock.Mock()
context_mock.to_policy_values.return_value = {'roles': ['admin']}
environ['placement.context'] = context_mock
app = handler.PlacementHandler()
app = handler.PlacementHandler(config=config)
self.assertRaises(webob.exc.HTTPNotFound,
app, environ, start_response)
mocked_log.error.assert_not_called()
@ -160,7 +161,9 @@ class ContentHeadersTest(testtools.TestCase):
def setUp(self):
super(ContentHeadersTest, self).setUp()
self.environ = _environ(path='/')
self.app = handler.PlacementHandler()
config = mock.MagicMock()
self.environ['placement.context'] = mock.MagicMock()
self.app = handler.PlacementHandler(config=config)
def test_no_content_type(self):
self.environ['CONTENT_LENGTH'] = '10'

View File

@ -12,32 +12,33 @@
import os
import fixtures
from oslo_config import cfg
from oslo_config import fixture as config_fixture
from oslo_policy import policy as oslo_policy
import testtools
from placement import conf
from placement import context
from placement import exception
from placement import policy
from placement.tests.unit import policy_fixture
from placement import util
CONF = cfg.CONF
class PlacementPolicyTestCase(testtools.TestCase):
"""Tests interactions with placement policy."""
def setUp(self):
super(PlacementPolicyTestCase, self).setUp()
self.conf_fixture = self.useFixture(config_fixture.Config(CONF))
config = cfg.ConfigOpts()
self.conf_fixture = self.useFixture(config_fixture.Config(config))
conf.register_opts(config)
self.ctxt = context.RequestContext(user_id='fake', project_id='fake')
self.target = {'user_id': 'fake', 'project_id': 'fake'}
# A value is required in the database connection opt for CONF to
# A value is required in the database connection opt for conf to
# parse.
CONF.set_default('connection', 'stub', group='placement_database')
CONF([], default_config_files=[])
self.conf_fixture.config(connection='stub', group='placement_database')
config([], default_config_files=[])
self.ctxt.config = config
policy.reset()
self.addCleanup(policy.reset)
@ -46,39 +47,40 @@ class PlacementPolicyTestCase(testtools.TestCase):
authorizations against a fake rule between updates to the physical
policy file.
"""
with util.tempdir() as tmpdir:
tmpfilename = os.path.join(tmpdir, 'placement-policy.yaml')
tempdir = self.useFixture(fixtures.TempDir())
tmpfilename = os.path.join(tempdir.path, 'placement-policy.yaml')
self.conf_fixture.config(
group='placement', policy_file=tmpfilename)
self.conf_fixture.config(
group='placement', policy_file=tmpfilename)
action = 'placement:test'
# Expect PolicyNotRegistered since defaults are not yet loaded.
self.assertRaises(oslo_policy.PolicyNotRegistered,
policy.authorize, self.ctxt, action, self.target)
action = 'placement:test'
# Expect PolicyNotRegistered since defaults are not yet loaded.
self.assertRaises(oslo_policy.PolicyNotRegistered,
policy.authorize, self.ctxt, action, self.target)
# Load the default action and rule (defaults to "any").
enforcer = policy.get_enforcer()
rule = oslo_policy.RuleDefault(action, '')
enforcer.register_default(rule)
# Load the default action and rule (defaults to "any").
enforcer = policy.get_enforcer()
rule = oslo_policy.RuleDefault(action, '')
enforcer.register_default(rule)
# Now auth should work because the action is registered and anyone
# can perform the action.
policy.authorize(self.ctxt, action, self.target)
# Now auth should work because the action is registered and anyone
# can perform the action.
policy.authorize(self.ctxt, action, self.target)
# Now update the policy file and reload it to disable the action
# from all users.
with open(tmpfilename, "w") as policyfile:
policyfile.write('"%s": "!"' % action)
enforcer.load_rules(force_reload=True)
self.assertRaises(exception.PolicyNotAuthorized, policy.authorize,
self.ctxt, action, self.target)
# Now update the policy file and reload it to disable the action
# from all users.
with open(tmpfilename, "w") as policyfile:
policyfile.write('"%s": "!"' % action)
enforcer.load_rules(force_reload=True)
self.assertRaises(exception.PolicyNotAuthorized, policy.authorize,
self.ctxt, action, self.target)
def test_authorize_do_raise_false(self):
"""Tests that authorize does not raise an exception when the check
fails.
"""
fixture = self.useFixture(policy_fixture.PolicyFixture())
fixture = self.useFixture(
policy_fixture.PolicyFixture(self.conf_fixture))
fixture.set_rules({'placement': '!'})
self.assertFalse(
policy.authorize(

View File

@ -18,7 +18,6 @@ import datetime
import fixtures
import microversion_parse
import mock
from oslo_config import cfg
from oslo_middleware import request_id
from oslo_utils.fixture import uuidsentinel
from oslo_utils import timeutils
@ -33,9 +32,6 @@ from placement.objects import resource_provider as rp_obj
from placement import util
CONF = cfg.CONF
class TestCheckAccept(testtools.TestCase):
"""Confirm behavior of util.check_accept."""

View File

@ -11,13 +11,9 @@
# under the License.
"""Utility methods for placement API."""
import contextlib
import functools
import shutil
import tempfile
import jsonschema
from oslo_config import cfg
from oslo_log import log as logging
from oslo_middleware import request_id
from oslo_serialization import jsonutils
@ -31,7 +27,6 @@ from placement.i18n import _
# microversion
import placement.microversion
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
# Error code handling constants
@ -398,21 +393,6 @@ def normalize_member_of_qs_param(value):
return value
@contextlib.contextmanager
def tempdir(**kwargs):
argdict = kwargs.copy()
if 'dir' not in argdict:
argdict['dir'] = CONF.tempdir
tmpdir = tempfile.mkdtemp(**argdict)
try:
yield tmpdir
finally:
try:
shutil.rmtree(tmpdir)
except OSError as e:
LOG.error('Could not remove tmpdir: %s', e)
def run_once(message, logger, cleanup=None):
"""This is a utility function decorator to ensure a function
is run once and only once in an interpreter instance.

View File

@ -18,6 +18,7 @@ import logging as py_logging
import os
import os.path
from oslo_config import cfg
from oslo_log import log as logging
from oslo_middleware import cors
from oslo_policy import opts as policy_opts
@ -67,21 +68,23 @@ def _get_config_files(env=None):
return None
def _parse_args(argv, default_config_files):
logging.register_options(conf.CONF)
def _parse_args(config, argv, default_config_files):
# register placement's config options
conf.register_opts(config)
logging.register_options(config)
if profiler:
profiler.set_defaults(conf.CONF)
profiler.set_defaults(config)
_set_middleware_defaults()
# This is needed so we can check [oslo_policy]/enforce_scope in the
# deploy module.
policy_opts.set_defaults(conf.CONF)
policy_opts.set_defaults(config)
conf.CONF(argv[1:], project='placement',
version=version_info.version_string(),
default_config_files=default_config_files)
config(argv[1:], project='placement',
version=version_info.version_string(),
default_config_files=default_config_files)
def _set_middleware_defaults():
@ -110,26 +113,25 @@ def init_application():
# initialize the config system
conffiles = _get_config_files()
# NOTE(lyarwood): Call reset to ensure the ConfigOpts object doesn't
# already contain registered options if the app is reloaded.
conf.CONF.reset()
config = cfg.ConfigOpts()
conf.register_opts(config)
# This will raise cfg.RequiredOptError when a required option is not set
# (notably the database connection string). We want this to be a hard fail
# that prevents the application from starting. The error will show up in
# the wsgi server's logs.
_parse_args([], default_config_files=conffiles)
_parse_args(config, [], default_config_files=conffiles)
# initialize the logging system
setup_logging(conf.CONF)
setup_logging(config)
# configure database
db_api.configure(conf.CONF)
db_api.configure(config)
# dump conf at debug if log_options
if conf.CONF.log_options:
conf.CONF.log_opt_values(
if config.log_options:
config.log_opt_values(
logging.getLogger(__name__),
logging.DEBUG)
# build and return our WSGI app
return deploy.loadapp(conf.CONF)
return deploy.loadapp(config)