From a0d3948d6603a3b2aeb7015cf6af819c594aa936 Mon Sep 17 00:00:00 2001 From: Paulo Machado Date: Thu, 12 Sep 2024 09:45:01 -0300 Subject: [PATCH] DPE-4613 Test for multi-relation scale in/out (#489) * test for multiple relations being scaled in/out together * fallback for juju 2.x tests * increased scale * use different dbs * fix block until to account for units count * avoid pod label update for every relation * bigger timeouts required --- src/relations/mysql_provider.py | 10 ++- tests/integration/test_multi_relations.py | 99 +++++++++++++++++++++++ 2 files changed, 105 insertions(+), 4 deletions(-) create mode 100644 tests/integration/test_multi_relations.py diff --git a/src/relations/mysql_provider.py b/src/relations/mysql_provider.py index 219afe8db..0eedfd88b 100644 --- a/src/relations/mysql_provider.py +++ b/src/relations/mysql_provider.py @@ -222,11 +222,13 @@ def _configure_endpoints(self, _) -> None: relation_data = self.database.fetch_relation_data() for relation in relations: - # only update endpoints if on_database_requested has executed - if relation.id not in relation_data: - continue + # only update endpoints if on_database_requested on any + # relation + if relation.id in relation_data: + break + return - self.charm._mysql.update_endpoints() + self.charm._mysql.update_endpoints() def _on_update_status(self, _) -> None: """Handle the update status event. diff --git a/tests/integration/test_multi_relations.py b/tests/integration/test_multi_relations.py new file mode 100644 index 000000000..271e96605 --- /dev/null +++ b/tests/integration/test_multi_relations.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python3 +# Copyright 2024 Canonical Ltd. +# See LICENSE file for licensing details. + + +from pathlib import Path + +import pytest +import yaml +from pytest_operator.plugin import OpsTest + +DB_METADATA = yaml.safe_load(Path("./metadata.yaml").read_text()) +SCALE_APPS = 7 +SCALE_UNITS = 3 + + +@pytest.mark.group(1) +@pytest.mark.abort_on_fail +async def test_build_and_deploy(ops_test: OpsTest): + """Build the charm and deploy 1 units to ensure a cluster is formed.""" + # Build and deploy charm from local source folder + db_charm = await ops_test.build_charm(".") + + config = {"profile": "testing"} + resources = {"mysql-image": DB_METADATA["resources"]["mysql-image"]["upstream-source"]} + + await ops_test.model.deploy( + db_charm, + application_name="mysql", + config=config, + num_units=1, + resources=resources, + series="jammy", + trust=True, + ) + + for i in range(SCALE_APPS): + config = {"database_name": f"database{i}", "sleep_interval": "2000"} + await ops_test.model.deploy( + "mysql-test-app", + application_name=f"app{i}", + num_units=1, + channel="latest/edge", + config=config, + ) + await ops_test.model.deploy( + "mysql-router-k8s", + application_name=f"router{i}", + num_units=1, + channel="8.0/edge", + trust=True, + ) + + +@pytest.mark.group(1) +@pytest.mark.abort_on_fail +async def test_relate_all(ops_test: OpsTest): + """Relate all the applications to the database.""" + for i in range(SCALE_APPS): + await ops_test.model.relate("mysql:database", f"router{i}:backend-database") + await ops_test.model.relate(f"app{i}:database", f"router{i}:database") + + await ops_test.model.block_until( + lambda: all(unit.workload_status == "active" for unit in ops_test.model.units.values()), + timeout=60 * 25, + wait_period=5, + ) + + +@pytest.mark.group(1) +@pytest.mark.abort_on_fail +async def test_scale_out(ops_test: OpsTest): + """Scale database and routers.""" + await ops_test.model.applications["mysql"].scale(SCALE_UNITS) + for i in range(SCALE_APPS): + await ops_test.model.applications[f"router{i}"].scale(SCALE_UNITS) + expected_unit_sum = SCALE_UNITS + 4 * SCALE_APPS + await ops_test.model.block_until( + lambda: all(unit.workload_status == "active" for unit in ops_test.model.units.values()) + and len(ops_test.model.units) == expected_unit_sum, + timeout=60 * 30, + wait_period=5, + ) + + +@pytest.mark.group(1) +@pytest.mark.abort_on_fail +async def test_scale_in(ops_test: OpsTest): + """Scale database and routers.""" + await ops_test.model.applications["mysql"].scale(1) + for i in range(SCALE_APPS): + await ops_test.model.applications[f"router{i}"].scale(1) + expected_unit_sum = 1 + 2 * SCALE_APPS + await ops_test.model.block_until( + lambda: all(unit.workload_status == "active" for unit in ops_test.model.units.values()) + and len(ops_test.model.units) == expected_unit_sum, + timeout=60 * 15, + wait_period=5, + )