Stop using global oslo_config
This change was driven out of trying to get nova functional tests working with an extracted placement, starting with getting the database fixture cleaner. Perhaps not surprisingly, trying to share the same 'cfg.CONF' between two services is rather fraught. Rather than trying to tease out all the individual issues, which is a very time consuming effort for not much gain, a different time consuming effort with great gain was tried instead. This patch removes the use of the default global cfg.CONF that oslo_config (optionally) provides and instead ensures that at the various ways in which one might enter placement: wsgi, cli, tests, the config is generated and managed in a more explicit fashion. Unfortunately this is a large change, but there's no easy way to do it in incremental chunks without getting very confused and having tests pass. There are a few classes of changes here, surrounded by various cleanups to address their addition. Quite a few holes were found in how config is managed, especially in tests where often we were getting what we wanted pretty much by accident. The big changes: * Importing placement.conf does not automatically register options with the global conf. Instead there is a now a register_opts method to which a ConfigOpts() is required. * Because of policy enforcement wanting access to conf, a convenient way of having the config pass through context.can() was needed. At the start of PlacementHandler (the main dispatch routine) the current config (provided to the PlacementHandler at application configuration time) is set as an attribute on the RequestContext. This is also used where CONF is required in the objects, such as randomizing the limited allocation canidates. * Passing in config to PlacementHandler changes the way the gabbi fixture loads the WSGI application. To work around a shortcoming in gabbi the fixture needs to CONF global. This is _not_ an oslo_config.cfg.CONF global, but something used locally in the fixture to set a different config per gabbi test suite. * The --sql command for alembic commands has been disabled. We don't really need that and it would require some messing about with config. The command lets you dump raw sql intead of migration files. * PlacementFixture, for use by nova, has been expanded to create and manage its config, database and policy requirements using non-global config. It can also accept a previously prepared config. * The Database fixtures calls 'reset()' in both setUp and cleanUp to be certain we are both starting and ending in a known state that will not disturb or be disturbed by other tests. This adds confidence (but not a guarantee) that in tests that run with eventlet (as in nova) things are in more consistent state. * Configuring the db in the Database fixture is moved into setUp where it should have been all along, but is important to be there _after_ 'reset()'. These of course cascade other changes all over the place. Especially the need to manually register_opts. There are probably refactorings that can be done or base classes that can be removed. Command line tools (e.g. status) which are mostly based on external libraries continue to use config in the pre-existing way. A lock fixture for the opportunistic migration tests has been added. There was a lock fixture previously, provided by oslo_concurrency, but it, as far as I can tell, requires global config. We don't want that. Things that will need to be changed as a result of these changes: * The goals doc at https://review.openstack.org/#/c/618811/ will need to be changed to say "keep it this way" rather than "get it this way". Change-Id: Icd629d7cd6d68ca08f9f3b4f0465c3d9a1efeb22
This commit is contained in:
parent
324e4f44da
commit
6fa9eabb79
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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()
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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:
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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.
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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}
|
||||
|
@ -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
|
||||
|
@ -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(
|
||||
|
@ -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()
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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."""
|
||||
|
@ -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)
|
||||
|
@ -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'
|
||||
|
@ -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(
|
||||
|
@ -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."""
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user