Skip to content
Open
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
367 changes: 264 additions & 103 deletions coriolis/tests/integration/base.py

Large diffs are not rendered by default.

6 changes: 0 additions & 6 deletions coriolis/tests/integration/deployments/test_deployment.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,3 @@ def test_cancel_deployment(self):
constants.EXECUTION_STATUS_CANCELED_FOR_DEBUGGING,
],
)


class MinionPoolReplicaDeploymentTests(
base.MinionPoolReplicaTestBase, ReplicaDeploymentIntegrationTest
):
"""Replica deployment that uses a pre-allocated destination minion pool."""
13 changes: 13 additions & 0 deletions coriolis/tests/integration/deployments/test_luks_osmorphing.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ class _LUKSOSMorphingMixin:
_SRC_DEVICE_SIZE_MB = 512
_CONTAINER_IMAGE = "ubuntu:24.04"

# Exercises the temporary OS Morphing minions.
_CREATE_DST_MINION_POOL = False

@classmethod
def setUpClass(cls):
harness = integration_harness._IntegrationHarness.get()
Expand Down Expand Up @@ -214,11 +217,21 @@ def _assert_firstboot_setup(self):
)


class LUKSOSMorphingMinionPoolDeploymentTest(
integration_base.DestinationMinionPoolTestBase, LUKSOSMorphingDeploymentTest
):
"""Same as LUKSOSMorphingDeploymentTest, OS Morphing in a pool minion."""

_CREATE_DST_MINION_POOL = True


class LUKSRockyLinuxOSMorphingDeploymentTest(
_LUKSOSMorphingMixin, integration_base.ReplicaIntegrationTestBase
):
"""LUKS + dracut OS morphing test using Rocky Linux 9."""

_CREATE_DST_MINION_POOL = True

# kernel-core (~150 MB installed) needs extra room on top of the base
# container image and the other morphing packages.
_SRC_DEVICE_SIZE_MB = 777
Expand Down
58 changes: 37 additions & 21 deletions coriolis/tests/integration/deployments/test_osmorphing.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ def setUp(self):


class OsMorphingDeploymentTest(OsMorphingDeploymentTestBase):
# Exercises the temporary workers.
_CREATE_DST_MINION_POOL = False

def test_deployment_with_os_morphing(self):
self.assertFalse(
osmorphing_utils.path_exists_on_device(self._src_device, "usr/bin/jq"),
Expand All @@ -53,6 +56,8 @@ def test_deployment_with_os_morphing(self):
"jq was not found on the destination device after OS morphing",
)


class _OsMorphingScriptTestsMixin:
def test_os_morphing_global_script_basic_format(self):
expected_string = str(uuid.uuid4())
user_scripts = {
Expand Down Expand Up @@ -194,44 +199,33 @@ def test_os_morphing_global_script_first_boot(self):


class OsMorphingMinionPoolDeploymentTest(
integration_base.DestinationMinionPoolTestBase, OsMorphingDeploymentTestBase
integration_base.DestinationMinionPoolTestBase,
_OsMorphingScriptTestsMixin,
OsMorphingDeploymentTestBase,
):
"""OS morphing deployment using a minion pool for the OS morphing phase."""
"""OS morphing deployment using the shared destination pool.

@classmethod
def setUpClass(cls):
super().setUpClass()
OsMorphingDeploymentTest tests cover the temporary OS Morphing minion, while these
ones covers OS Morphing in a (reused) pool minion, including the user scripts.
"""

pool = cls._create_pool(
cls._dst_endpoint.id,
"osmorph-pool",
skip_allocation=False,
wait_for_allocation=True,
)
cls._osmorph_pool_id = pool.id
_CREATE_DST_MINION_POOL = True

def test_deployment_with_os_morphing(self):
self.assertFalse(
osmorphing_utils.path_exists_on_device(self._src_device, "usr/bin/jq"),
"jq was found on the source device before OS morphing",
)

deployment_kwargs = {
"instance_osmorphing_minion_pool_mappings": {
self._instance_name: self._osmorph_pool_id,
},
}
self._execute_transfer_and_deployment(deployment_kwargs)
self._execute_transfer_and_deployment()

self.assertTrue(
osmorphing_utils.path_exists_on_device(self._dst_device, "usr/bin/jq"),
"jq was not found on the destination device after OS morphing",
)

ctxt = self._get_db_context()
pool = db_api.get_minion_pool(
ctxt, self._osmorph_pool_id, include_machines=True
)
pool = db_api.get_minion_pool(ctxt, self._dst_pool_id, include_machines=True)
self.assertTrue(pool.minion_machines, "OS morphing pool has no minion machines")

for machine in pool.minion_machines:
Expand All @@ -240,6 +234,28 @@ def test_deployment_with_os_morphing(self):
"OS morphing minion machine %s was never used" % machine.id,
)


class OsMorphingMinionPoolAllocationFailureTest(
integration_base.DestinationMinionPoolTestBase, OsMorphingDeploymentTestBase
):
"""OS morphing minion pool allocation failure test.

Deliberately breaks the pool's only machine, so it needs its own dedicated pool,
rather than the shared one.
"""

@classmethod
def setUpClass(cls):
super().setUpClass()

pool = cls._create_pool(
cls._dst_endpoint.id,
"osmorph-pool",
skip_allocation=False,
wait_for_allocation=True,
)
cls._osmorph_pool_id = pool.id

def test_osmorphing_minion_allocation_failure_cleans_up(self):
"""OS morphing minion pool allocation fail test.

Expand Down
27 changes: 27 additions & 0 deletions coriolis/tests/integration/management/test_region.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,33 @@ def test_region_crud(self):


class RegionSchedulingTests(base.ReplicaIntegrationTestBase):
"""Tests scheduling for endpoints with mapped regions.

Mutates the endpoints' mapped_regions, so it can't use the shared ones. Since it's
a new endpoint, we can't reuse the existing minion pool.
"""

_CREATE_DST_MINION_POOL = False
_CREATE_SRC_MINION_POOL = False

@classmethod
def _get_src_endpoint(cls):
return cls._create_endpoint(
name="test-src",
endpoint_type=cls._exp_platform,
description="integration source endpoint",
connection_info=cls._exp_conn_info,
)

@classmethod
def _get_dst_endpoint(cls):
return cls._create_endpoint(
name="test-dest",
endpoint_type=cls._imp_platform,
description="integration destination endpoint",
connection_info=cls._imp_conn_info,
)

@classmethod
def setUpClass(cls):
super().setUpClass()
Expand Down
19 changes: 17 additions & 2 deletions coriolis/tests/integration/test_failure_recovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@
class TransferFailureIntegrationTest(base.ReplicaIntegrationTestBase):
"""Error path and resource cleanup."""

# Patches deploy_replica_{target,source}_resources, which are not used for
# pool-backed transfers.
_CREATE_DST_MINION_POOL = False
_CREATE_SRC_MINION_POOL = False

def _assertResourcesCleaned(self, execution_id, task_type, resource_key):
ctxt = self._get_db_context()
execution = db_api.get_tasks_execution(ctxt, execution_id)
Expand Down Expand Up @@ -127,8 +132,18 @@ def _slow_then_fail(self_provider, *args, **kwargs):
self.assertTargetResourcesCleaned(execution.id)


class MinionPoolAllocationFailureTest(base.MinionPoolReplicaTestBase):
"""Transfer minion pool allocation failure tests."""
class MinionPoolAllocationFailureTest(
base.DedicatedMinionPoolsMixin,
base.DestinationMinionPoolTestBase,
base.ReplicaIntegrationTestBase,
):
"""Transfer minion pool allocation failure tests.

Deliberately breaks its pool's only machine, so it needs a dedicated pool
rather than the shared one.
"""

_CREATE_SRC_MINION_POOL = False

def test_transfer_minion_allocation_failure_cleans_up(self):
"""Transfer minion pool allocation fail test.
Expand Down
28 changes: 22 additions & 6 deletions coriolis/tests/integration/test_minion_pools.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ def _create_pool(self, endpoint_id, **kwargs):
)


class _MinionPoolPowerCycleTestMixin:
class _MinionPoolPowerCycleTestMixin(base.DedicatedMinionPoolsMixin):
"""Transfer that reuses pool machines across a power cycle.

The pool allows up to 2 machines (minimum 1) with a tiny idle time and the
Expand Down Expand Up @@ -305,26 +305,34 @@ def test_transfer_after_pool_machine_power_cycle(self):


class MinionPoolPowerCycleTransferTest(
_MinionPoolPowerCycleTestMixin, base.MinionPoolReplicaTestBase
_MinionPoolPowerCycleTestMixin,
base.DestinationMinionPoolTestBase,
base.ReplicaIntegrationTestBase,
):
"""Power-cycle test exercising a destination minion pool."""

_CREATE_SRC_MINION_POOL = False

@property
def _pool_id(self):
return self._dst_pool_id


class SourceMinionPoolPowerCycleTransferTest(
_MinionPoolPowerCycleTestMixin, base.SourceMinionPoolReplicaTestBase
_MinionPoolPowerCycleTestMixin,
base.SourceMinionPoolTestBase,
base.ReplicaIntegrationTestBase,
):
"""Power-cycle test exercising a source minion pool."""

_CREATE_DST_MINION_POOL = False

@property
def _pool_id(self):
return self._src_pool_id


class _MinionPoolRefreshDeallocationTestMixin:
class _MinionPoolRefreshDeallocationTestMixin(base.DedicatedMinionPoolsMixin):
"""Excess pool machine gets deleted on refresh.

Mirrors _MinionPoolPowerCycleTestMixin but with the default "delete" retention
Expand Down Expand Up @@ -395,20 +403,28 @@ def test_excess_pool_machine_deleted_on_refresh(self):


class MinionPoolRefreshDeallocationTransferTest(
_MinionPoolRefreshDeallocationTestMixin, base.MinionPoolReplicaTestBase
_MinionPoolRefreshDeallocationTestMixin,
base.DestinationMinionPoolTestBase,
base.ReplicaIntegrationTestBase,
):
"""Deletion-on-refresh test exercising a destination minion pool."""

_CREATE_SRC_MINION_POOL = False

@property
def _pool_id(self):
return self._dst_pool_id


class SourceMinionPoolRefreshDeallocationTransferTest(
_MinionPoolRefreshDeallocationTestMixin, base.SourceMinionPoolReplicaTestBase
_MinionPoolRefreshDeallocationTestMixin,
base.SourceMinionPoolTestBase,
base.ReplicaIntegrationTestBase,
):
"""Deletion-on-refresh test exercising a source minion pool."""

_CREATE_DST_MINION_POOL = False

@property
def _pool_id(self):
return self._src_pool_id
Expand Down
12 changes: 11 additions & 1 deletion coriolis/tests/integration/test_provider/imp.py
Original file line number Diff line number Diff line change
Expand Up @@ -445,11 +445,18 @@ def create_minion(
#
# Mount the host's /lib/modules tree so that modprobe can
# resolve built-in modules.
#
# A pool minion is created once and reused for arbitrary future
# transfers / deployments, so unlike deploy_os_morphing_resources (which
# only adds /dev/mapper/control when it already knows the deployment
# is LUKS-encrypted), this must always include it: luksOpen needs it,
# and there's no way to add it retroactively to an already-running
# container if a later LUKS-encrypted instance gets mapped to it.
volumes = ["/lib/modules:/lib/modules:ro"]
result = self._create_minion(
"coriolis-pool-minion",
connection_info,
[],
["/dev/mapper/control"],
volumes,
device_cgroup_rules=["b *:* rwm"],
)
Expand Down Expand Up @@ -484,5 +491,8 @@ def get_additional_os_morphing_info(
"os_type": instance_deployment_info.get("os_type", "linux"),
"ignore_devices": ignore_devices,
"_include_loop_devices": True,
constants.ENCRYPTED_DISKS_PASS: instance_deployment_info.get(
constants.ENCRYPTED_DISKS_PASS
),
}
}
Loading
Loading