Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 8 additions & 15 deletions coriolis/tests/integration/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import oslo_messaging as messaging
from coriolisclient import client as coriolis_client
from keystoneauth1 import session as ks_session
from keystoneauth1 import token_endpoint
from oslo_config import cfg
from oslo_db.sqlalchemy import models
from oslo_log import log as logging
Expand Down Expand Up @@ -82,8 +81,6 @@ def setUp(self):
# Prevent the test runner from being killed by Coriolis sending
# SIGINT to in-process workers.
"psutil.Process.send_signal",
# We're not using keystone for tests. This is called in a few places.
"coriolis.keystone.delete_trust",
]
for thing in to_patch:
patcher = mock.patch(thing)
Expand Down Expand Up @@ -113,13 +110,12 @@ def _call(self, ctxt, method, **kwargs):
@classmethod
def get_client(cls):
"""Return a coriolisclient.Client pointed at the in-process API."""
auth = token_endpoint.Token(
endpoint='http://127.0.0.1:%d' % cls._api_port,
token='integration-dummy-token',
)
auth = cls._harness.keystone.get_auth()
return coriolis_client.Client(
session=ks_session.Session(auth=auth),
project_id=harness._TEST_PROJECT_ID,
project_id=cls._harness.keystone.project_id,
# Not "endpoint": that appends "/v1", which the test router doesn't use.
endpoint_override='http://127.0.0.1:%d' % cls._api_port,
)

@classmethod
Expand Down Expand Up @@ -220,10 +216,7 @@ def _safe_delete_pool(cls, pool_id):
cls._client.minion_pools.deallocate_minion_pool(pool_id, force=True)
cls._wait_for_pool(pool_id, MINION_DEALLOCATED_TERMINAL)

with mock.patch("coriolis.keystone.delete_trust", autospec=False):
# When removing minion pools, this is also called.
# There is no Keystone, so it needs to be mocked.
cls._client.minion_pools.delete(pool_id)
cls._client.minion_pools.delete(pool_id)

@classmethod
def _wait_for_pool(cls, pool_id, terminal_statuses, timeout=600):
Expand All @@ -246,11 +239,11 @@ def _wait_for_pool(cls, pool_id, terminal_statuses, timeout=600):
% (pool_id, terminal_statuses, timeout, last)
)

@staticmethod
def _get_db_context():
@classmethod
def _get_db_context(cls):
return context.RequestContext(
user='int-test',
project_id=harness._TEST_PROJECT_ID,
project_id=cls._harness.keystone.project_id,
is_admin=True,
)

Expand Down
68 changes: 42 additions & 26 deletions coriolis/tests/integration/harness.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@

Starts conductor, scheduler, and worker services in-process using
oslo.messaging's fake:// transport and a temporary SQLite database. Serves
the Coriolis REST API via cheroot on a random local port. No RabbitMQ,
Keystone, or Barbican are required.
the Coriolis REST API via cheroot on a random local port, a keystonemiddleware
auth_token + CoriolisKeystoneContext pipeline. No RabbitMQ or Barbican are required.

Tasks are executed in-process as greenlets rather than subprocesses. The
fake:// oslo.messaging transport is in-memory and process-local; subprocess
Expand All @@ -28,22 +28,22 @@
import uuid
from unittest import mock

import webob.dec
import yaml
from cheroot import wsgi as cheroot_wsgi
from cheroot.workers import threadpool as cheroot_threadpool
from keystonemiddleware import auth_token as ks_auth_token
from oslo_config import cfg
from oslo_log import log as logging
from oslo_middleware import request_id as request_id_middleware
from oslo_service import wsgi as base_wsgi

from coriolis import api as api_module
from coriolis import conf as coriolis_conf
from coriolis import constants, context, exception, service
from coriolis import constants, exception, service
from coriolis import policy as policy_module
from coriolis import rpc as rpc_module
from coriolis import utils as coriolis_utils
from coriolis.api import wsgi as api_wsgi
from coriolis.api.middleware import auth as keystone_context_middleware
from coriolis.api.middleware import fault as fault_middleware
from coriolis.api.v1 import router as api_v1_router
from coriolis.conductor.rpc import server as conductor_rpc_server
Expand All @@ -56,7 +56,7 @@
from coriolis.scheduler.rpc import server as scheduler_rpc_server
from coriolis.taskflow import runner as taskflow_runner
from coriolis.tasks import factory as task_runners_factory
from coriolis.tests.integration import provider_test_base
from coriolis.tests.integration import keystone_utils, provider_test_base
from coriolis.tests.integration import utils as test_utils
from coriolis.tests.integration.test_provider import exp as test_provider_exp
from coriolis.tests.integration.test_provider import imp as test_provider_imp
Expand All @@ -75,9 +75,6 @@
"coriolis.tests.integration.test_provider.imp.TestImportProvider"
)

# Fixed project used for all test requests.
_TEST_PROJECT_ID = 'integration-project'

# Path to an optional YAML file that configures the destination provider,
# its connection_info, environment, and storage mappings.
_PROVIDERS_YAML = os.environ.get("CORIOLIS_PROVIDERS_YAML")
Expand Down Expand Up @@ -161,19 +158,20 @@ def __init__(self, *args, **kwargs):
cheroot_threadpool.WorkerThread = DaemonCherootWorker


class _NoAuthMiddleware(api_wsgi.Middleware):
"""Injects a fixed admin RequestContext; replaces keystonecontext."""
class _AdminRoleMiddleware:
"""Marks contexts holding the admin role as is_admin.

@webob.dec.wsgify(RequestClass=api_wsgi.Request)
def __call__(self, req):
req.environ['coriolis.context'] = context.RequestContext(
user='integration-test',
project_id=_TEST_PROJECT_ID,
is_admin=True,
# Skip Keystone trust creation / deletion.
trust_id='integration-dummy-trust',
)
return self.application
Sits after CoriolisKeystoneContext, which builds the contexts.
"""

def __init__(self, app):
self._app = app

def __call__(self, environ, start_response):
ctx = environ.get('coriolis.context')
if ctx is not None and keystone_utils.ROLE_NAME in ctx.roles:
ctx.is_admin = True
return self._app(environ, start_response)


class _TestAPIRouter(api_v1_router.APIRouter):
Expand Down Expand Up @@ -321,8 +319,8 @@ def __init__(self):
self.lock_path = os.path.join(self.workdir, "locks")
os.makedirs(self.lock_path)

session_tag = str(uuid.uuid4()).split("-")[0]
self._mysql_container_name = "coriolis-test-mysql-%s" % session_tag
self._session_tag = str(uuid.uuid4()).split("-")[0]
self._mysql_container_name = "coriolis-test-mysql-%s" % self._session_tag
self._mysql_username = "root"
self._mysql_password = "coriolis"
self._mysql_database = "coriolis"
Expand Down Expand Up @@ -443,6 +441,16 @@ def __init__(self):
engine = db_api.get_engine()
db_migration.db_sync(engine)

self.keystone = keystone_utils.KeystoneContainer(
self.workdir,
self._session_tag,
self._mysql_container_name,
self._mysql_ip,
self._mysql_username,
self._mysql_password,
)
self.keystone.setup()

self._start_coriolis_services()

def _start_db_container(self):
Expand Down Expand Up @@ -563,10 +571,13 @@ def _start_coriolis_services(self):
)
self._worker_host_svc.start()

# API: build the WSGI stack without keystonemiddleware and serve it
# on a random local port.
# API: build the same pipeline as api-paste.ini (request_id, faultwrap,
# authtoken, keystonecontext, apiv1) and serve it on a random local port.
# authtoken validates tokens against the Keystone container started above.
wsgi_app = _TestAPIRouter()
wsgi_app = _NoAuthMiddleware(wsgi_app)
wsgi_app = _AdminRoleMiddleware(wsgi_app)
wsgi_app = keystone_context_middleware.CoriolisKeystoneContext(wsgi_app)
wsgi_app = ks_auth_token.AuthProtocol(wsgi_app, {})
wsgi_app = fault_middleware.FaultWrapper(wsgi_app)
wsgi_app = request_id_middleware.RequestId(wsgi_app)

Expand Down Expand Up @@ -622,6 +633,11 @@ def _teardown(self):
self._mysql_container_name,
)

try:
self.keystone.teardown()
except Exception:
LOG.exception("Failed to stop / remove the Keystone container.")

shutil.rmtree(self.workdir, True)
try:
test_utils.destroy_leaked_loop_devices()
Expand Down
Loading
Loading