-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[DPE-4106] Tests legacy and modern endpoints simultaneously (#396)
* Legacy endpoints: relate legacy+modern endpoints simultaneously (fool-protection check) * mv func * test legacy + modern endpoints * test legacy + modern endpoints * test relate legacy+modern endpoints simultaneously * Update src/relations/postgresql_provider.py Co-authored-by: Marcelo Henrique Neppel <[email protected]> * Update tests/integration/relations/helpers.py Co-authored-by: Marcelo Henrique Neppel <[email protected]> * Update tests/integration/relations/test_relations.py Co-authored-by: Marcelo Henrique Neppel <[email protected]> * Update tests/integration/relations/test_relations.py Co-authored-by: Marcelo Henrique Neppel <[email protected]> * check deploy postgresql * refactoring * update status on remove multiple relations endpoint * update status on remove multiple relations endpoint * fix unit test. change db endpoint(mailman3_core) to db endpoint(postgresql_test_app) * add pytest.mark.group to test_modern_endpoint_with_multiple_related_endpoints * fix check connect to legacy endpoint * test_self_healing: deploying postgresql-test-app revision 101 * deploy postgresql-test-app with latest version --------- Co-authored-by: BalabaDmintri <[email protected]> Co-authored-by: Marcelo Henrique Neppel <[email protected]>
- Loading branch information
1 parent
89f6d01
commit 0f2c8c2
Showing
7 changed files
with
279 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
# Copyright 2024 Canonical Ltd. | ||
# See LICENSE file for licensing details. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
#!/usr/bin/env python3 | ||
# Copyright 2024 Canonical Ltd. | ||
# See LICENSE file for licensing details. | ||
from typing import Optional | ||
|
||
import yaml | ||
from pytest_operator.plugin import OpsTest | ||
|
||
|
||
async def get_legacy_db_connection_str( | ||
ops_test: OpsTest, | ||
application_name: str, | ||
relation_name: str, | ||
read_only_endpoint: bool = False, | ||
remote_unit_name: str = None, | ||
) -> Optional[str]: | ||
"""Returns a PostgreSQL connection string. | ||
Args: | ||
ops_test: The ops test framework instance | ||
application_name: The name of the application | ||
relation_name: name of the relation to get connection data from | ||
read_only_endpoint: whether to choose the read-only endpoint | ||
instead of the read/write endpoint | ||
remote_unit_name: Optional remote unit name used to retrieve | ||
unit data instead of application data | ||
Returns: | ||
a PostgreSQL connection string | ||
""" | ||
unit_name = f"{application_name}/0" | ||
raw_data = (await ops_test.juju("show-unit", unit_name))[1] | ||
if not raw_data: | ||
raise ValueError(f"no unit info could be grabbed for {unit_name}") | ||
data = yaml.safe_load(raw_data) | ||
# Filter the data based on the relation name. | ||
relation_data = [ | ||
v for v in data[unit_name]["relation-info"] if v["related-endpoint"] == relation_name | ||
] | ||
if len(relation_data) == 0: | ||
raise ValueError( | ||
f"no relation data could be grabbed on relation with endpoint {relation_name}" | ||
) | ||
if remote_unit_name: | ||
data = relation_data[0]["related-units"][remote_unit_name]["data"] | ||
else: | ||
data = relation_data[0]["application-data"] | ||
if read_only_endpoint: | ||
if data.get("standbys") is None: | ||
return None | ||
return data.get("standbys").split(",")[0] | ||
else: | ||
return data.get("master") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
#!/usr/bin/env python3 | ||
# Copyright 2024 Canonical Ltd. | ||
# See LICENSE file for licensing details. | ||
import asyncio | ||
import logging | ||
|
||
import psycopg2 | ||
import pytest | ||
from pytest_operator.plugin import OpsTest | ||
from tenacity import Retrying, stop_after_delay, wait_fixed | ||
|
||
from ..helpers import CHARM_SERIES, METADATA | ||
from ..new_relations.test_new_relations import APPLICATION_APP_NAME, build_connection_string | ||
from ..relations.helpers import get_legacy_db_connection_str | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
APP_NAME = METADATA["name"] | ||
# MAILMAN3_CORE_APP_NAME = "mailman3-core" | ||
DB_RELATION = "db" | ||
DATABASE_RELATION = "database" | ||
FIRST_DATABASE_RELATION = "first-database" | ||
DATABASE_APP_NAME = "database-app" | ||
DB_APP_NAME = "db-app" | ||
APP_NAMES = [APP_NAME, DATABASE_APP_NAME, DB_APP_NAME] | ||
|
||
|
||
@pytest.mark.group(1) | ||
@pytest.mark.abort_on_fail | ||
async def test_deploy_charms(ops_test: OpsTest, charm): | ||
"""Deploy both charms (application and database) to use in the tests.""" | ||
# Deploy both charms (multiple units for each application to test that later they correctly | ||
# set data in the relation application databag using only the leader unit). | ||
async with ops_test.fast_forward(): | ||
await asyncio.gather( | ||
ops_test.model.deploy( | ||
APPLICATION_APP_NAME, | ||
application_name=DATABASE_APP_NAME, | ||
num_units=1, | ||
series=CHARM_SERIES, | ||
channel="edge", | ||
), | ||
ops_test.model.deploy( | ||
charm, | ||
application_name=APP_NAME, | ||
num_units=1, | ||
series=CHARM_SERIES, | ||
config={ | ||
"profile": "testing", | ||
"plugin_unaccent_enable": "True", | ||
"plugin_pg_trgm_enable": "True", | ||
}, | ||
), | ||
ops_test.model.deploy( | ||
APPLICATION_APP_NAME, | ||
application_name=DB_APP_NAME, | ||
num_units=1, | ||
series=CHARM_SERIES, | ||
channel="edge", | ||
), | ||
) | ||
|
||
await ops_test.model.wait_for_idle(apps=APP_NAMES, status="active", timeout=3000) | ||
|
||
|
||
@pytest.mark.group(1) | ||
async def test_legacy_endpoint_with_multiple_related_endpoints(ops_test: OpsTest): | ||
await ops_test.model.relate(f"{DB_APP_NAME}:{DB_RELATION}", f"{APP_NAME}:{DB_RELATION}") | ||
await ops_test.model.relate(APP_NAME, f"{DATABASE_APP_NAME}:{FIRST_DATABASE_RELATION}") | ||
|
||
app = ops_test.model.applications[APP_NAME] | ||
await ops_test.model.block_until( | ||
lambda: "blocked" in {unit.workload_status for unit in app.units}, | ||
timeout=1500, | ||
) | ||
|
||
logger.info(" remove relation with modern endpoints") | ||
await ops_test.model.applications[APP_NAME].remove_relation( | ||
f"{APP_NAME}:{DATABASE_RELATION}", f"{DATABASE_APP_NAME}:{FIRST_DATABASE_RELATION}" | ||
) | ||
async with ops_test.fast_forward(): | ||
await ops_test.model.wait_for_idle( | ||
status="active", | ||
timeout=1500, | ||
raise_on_error=False, | ||
) | ||
|
||
legacy_interface_connect = await get_legacy_db_connection_str( | ||
ops_test, DB_APP_NAME, DB_RELATION, remote_unit_name=f"{APP_NAME}/0" | ||
) | ||
logger.info(f" check connect to = {legacy_interface_connect}") | ||
for attempt in Retrying(stop=stop_after_delay(60 * 3), wait=wait_fixed(10)): | ||
with attempt: | ||
with psycopg2.connect(legacy_interface_connect) as connection: | ||
assert connection.status == psycopg2.extensions.STATUS_READY | ||
|
||
logger.info(f" remove relation {DB_APP_NAME}:{DB_RELATION}") | ||
async with ops_test.fast_forward(): | ||
await ops_test.model.applications[APP_NAME].remove_relation( | ||
f"{APP_NAME}:{DB_RELATION}", f"{DB_APP_NAME}:{DB_RELATION}" | ||
) | ||
await ops_test.model.wait_for_idle(apps=[APP_NAME], status="active", timeout=1000) | ||
for attempt in Retrying(stop=stop_after_delay(60 * 5), wait=wait_fixed(10)): | ||
with attempt: | ||
with pytest.raises(psycopg2.OperationalError): | ||
psycopg2.connect(legacy_interface_connect) | ||
|
||
|
||
@pytest.mark.group(1) | ||
async def test_modern_endpoint_with_multiple_related_endpoints(ops_test: OpsTest): | ||
await ops_test.model.relate(f"{DB_APP_NAME}:{DB_RELATION}", f"{APP_NAME}:{DB_RELATION}") | ||
await ops_test.model.relate(APP_NAME, f"{DATABASE_APP_NAME}:{FIRST_DATABASE_RELATION}") | ||
|
||
app = ops_test.model.applications[APP_NAME] | ||
await ops_test.model.block_until( | ||
lambda: "blocked" in {unit.workload_status for unit in app.units}, | ||
timeout=1500, | ||
) | ||
|
||
logger.info(" remove relation with legacy endpoints") | ||
await ops_test.model.applications[APP_NAME].remove_relation( | ||
f"{DB_APP_NAME}:{DB_RELATION}", f"{APP_NAME}:{DB_RELATION}" | ||
) | ||
async with ops_test.fast_forward(): | ||
await ops_test.model.wait_for_idle(status="active", timeout=3000, raise_on_error=False) | ||
|
||
modern_interface_connect = await build_connection_string( | ||
ops_test, DATABASE_APP_NAME, FIRST_DATABASE_RELATION | ||
) | ||
logger.info(f"check connect to = {modern_interface_connect}") | ||
for attempt in Retrying(stop=stop_after_delay(60 * 3), wait=wait_fixed(10)): | ||
with attempt: | ||
with psycopg2.connect(modern_interface_connect) as connection: | ||
assert connection.status == psycopg2.extensions.STATUS_READY | ||
|
||
logger.info(f"remove relation {DATABASE_APP_NAME}:{FIRST_DATABASE_RELATION}") | ||
async with ops_test.fast_forward(): | ||
await ops_test.model.applications[APP_NAME].remove_relation( | ||
f"{APP_NAME}:{DATABASE_RELATION}", f"{DATABASE_APP_NAME}:{FIRST_DATABASE_RELATION}" | ||
) | ||
await ops_test.model.wait_for_idle(apps=[APP_NAME], status="active", timeout=1000) | ||
for attempt in Retrying(stop=stop_after_delay(60 * 5), wait=wait_fixed(10)): | ||
with attempt: | ||
with pytest.raises(psycopg2.OperationalError): | ||
psycopg2.connect(modern_interface_connect) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters