Skip to content

Commit

Permalink
[DPE-1926] Remove fallback_application_name field from relation data (#…
Browse files Browse the repository at this point in the history
…133)

* Remove fallback_application_name field from relation data

* Add integration test for livepatch onprem bundle

* Minor improvements in the helper function

* Fix relation with livepatch bundle when having multiple units

* Improved db relation fields

* Uncommented tests

* Fixed unit tests
  • Loading branch information
marceloneppel authored Jun 14, 2023
1 parent 5de53a0 commit a07b31f
Show file tree
Hide file tree
Showing 5 changed files with 296 additions and 246 deletions.
39 changes: 16 additions & 23 deletions src/relations/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,32 +172,13 @@ def set_up_relation(self, relation: Relation) -> bool:

# Enable/disable extensions in the new database.
self.charm.enable_disable_extensions(database)

postgresql_version = self.charm.postgresql.get_postgresql_version()
except (
PostgreSQLCreateDatabaseError,
PostgreSQLCreateUserError,
PostgreSQLGetPostgreSQLVersionError,
) as e:
except (PostgreSQLCreateDatabaseError, PostgreSQLCreateUserError) as e:
logger.exception(e)
self.charm.unit.status = BlockedStatus(
f"Failed to initialize {self.relation_name} relation"
)
return False

# Set the data in the unit data bag. It's needed to run this logic on every
# relation changed event setting the data again in the databag, otherwise the
# application charm that is connecting to this database will receive a
# "database gone" event from the old PostgreSQL library (ops-lib-pgsql)
# and the connection between the application and this charm will not work.
unit_relation_databag.update(
{
"version": postgresql_version,
"password": password,
"database": database,
"extensions": ",".join(required_extensions),
}
)
self.update_endpoints(relation)

self._update_unit_status(relation)
Expand Down Expand Up @@ -286,6 +267,15 @@ def update_endpoints(self, relation: Relation = None) -> None:
if len(relations) == 0:
return

try:
postgresql_version = self.charm.postgresql.get_postgresql_version()
except PostgreSQLGetPostgreSQLVersionError as e:
logger.exception(e)
self.charm.unit.status = BlockedStatus(
f"Failed to retrieve the PostgreSQL version to initialise/update {self.relation_name} relation"
)
return

# List the replicas endpoints.
replicas_endpoint = self.charm.members_ips - {self.charm.primary_endpoint}

Expand All @@ -308,7 +298,6 @@ def update_endpoints(self, relation: Relation = None) -> None:
port=DATABASE_PORT,
user=user,
password=password,
fallback_application_name=relation.app.name,
)
)

Expand All @@ -322,7 +311,6 @@ def update_endpoints(self, relation: Relation = None) -> None:
port=DATABASE_PORT,
user=user,
password=password,
fallback_application_name=relation.app.name,
)
)
for replica_endpoint in replicas_endpoint
Expand All @@ -340,6 +328,11 @@ def update_endpoints(self, relation: Relation = None) -> None:
"host": self.charm.primary_endpoint,
"port": DATABASE_PORT,
"user": user,
"schema_user": user,
"version": postgresql_version,
"password": password,
"schema_password": password,
"database": database,
"master": primary_endpoint,
"standbys": read_only_endpoints,
"state": self._get_state(),
Expand Down Expand Up @@ -386,4 +379,4 @@ def _get_state(self) -> str:
if self.charm._patroni.get_primary(unit_name_pattern=True) == self.charm.unit.name:
return "master"
else:
return "standby"
return "hot standby"
78 changes: 66 additions & 12 deletions tests/integration/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import zipfile
from datetime import datetime
from pathlib import Path
from typing import List, Optional
from typing import Dict, List, Optional

import botocore
import psycopg2
Expand Down Expand Up @@ -308,48 +308,102 @@ async def deploy_and_relate_application_with_postgresql(
return relation.id


async def deploy_and_relate_landscape_bundle_with_postgresql(
async def deploy_and_relate_bundle_with_postgresql(
ops_test: OpsTest,
bundle_name: str,
main_application_name: str,
relation_name: str = "db",
status: str = "active",
status_message: str = None,
overlay: Dict = None,
) -> str:
"""Helper function to deploy and relate the Landscape bundle with PostgreSQL.
"""Helper function to deploy and relate a bundle with PostgreSQL.
Args:
ops_test: The ops test framework.
bundle_name: The name of the bundle to deploy.
main_application_name: The name of the application that should be
related to PostgreSQL.
relation_name: The name of the relation to use in PostgreSQL
(db or db-admin).
status: Status to wait for in the application after relating
it to PostgreSQL.
status_message: Status message to wait for in the application after
relating it to PostgreSQL.
overlay: Optional overlay to be used when deploying the bundle.
"""
# Deploy the bundle.
with tempfile.NamedTemporaryFile() as original:
# Download the original bundle.
await ops_test.juju("download", "ch:landscape-scalable", "--filepath", original.name)
await ops_test.juju("download", bundle_name, "--filepath", original.name)

# Open the bundle compressed file and update the contents
# of the bundle.yaml file to deploy it.
with zipfile.ZipFile(original.name, "r") as archive:
bundle_yaml = archive.read("bundle.yaml")
data = yaml.load(bundle_yaml, Loader=yaml.FullLoader)

# Save the list of relations other than `db` and `db-admin`,
# so we can add them back later.
other_relations = [
relation for relation in data["relations"] if "postgresql" in relation
]

# Remove PostgreSQL and relations with it from the bundle.yaml file.
del data["applications"]["postgresql"]
data["relations"] = [
relation
for relation in data["relations"]
if "postgresql:db" not in relation and "postgresql:db-admin" not in relation
if "postgresql" not in relation
and "postgresql:db" not in relation
and "postgresql:db-admin" not in relation
]

# Write the new bundle content to a temporary file and deploy it.
with tempfile.NamedTemporaryFile() as patched:
patched.write(yaml.dump(data).encode("utf_8"))
patched.seek(0)
await ops_test.juju("deploy", patched.name)
if overlay is not None:
with tempfile.NamedTemporaryFile() as overlay_file:
overlay_file.write(yaml.dump(overlay).encode("utf_8"))
overlay_file.seek(0)
await ops_test.juju("deploy", patched.name, "--overlay", overlay_file.name)
else:
await ops_test.juju("deploy", patched.name)

# Relate application to PostgreSQL.
async with ops_test.fast_forward(fast_interval="30s"):
relation = await ops_test.model.relate("landscape-server", f"{DATABASE_APP_NAME}:db-admin")
await ops_test.model.wait_for_idle(
apps=["landscape-server", DATABASE_APP_NAME],
status="active",
timeout=1500,
# Relate application to PostgreSQL.
relation = await ops_test.model.relate(
main_application_name, f"{DATABASE_APP_NAME}:{relation_name}"
)

# Restore previous existing relations.
for other_relation in other_relations:
await ops_test.model.relate(other_relation[0], other_relation[1])

# Wait for the deployment to complete.
unit = ops_test.model.units.get(f"{main_application_name}/0")
awaits = [
ops_test.model.wait_for_idle(
apps=[DATABASE_APP_NAME],
status="active",
timeout=1500,
),
ops_test.model.wait_for_idle(
apps=[main_application_name],
raise_on_blocked=False,
status=status,
timeout=1500,
),
]
if status_message:
awaits.append(
ops_test.model.block_until(
lambda: unit.workload_status_message == status_message, timeout=1500
)
)
await asyncio.gather(*awaits)

return relation.id


Expand Down
28 changes: 28 additions & 0 deletions tests/integration/test_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@
check_database_users_existence,
check_databases_creation,
deploy_and_relate_application_with_postgresql,
deploy_and_relate_bundle_with_postgresql,
find_unit,
)

logger = logging.getLogger(__name__)

LIVEPATCH_APP_NAME = "livepatch"
MAILMAN3_CORE_APP_NAME = "mailman3-core"
APPLICATION_UNITS = 1
DATABASE_UNITS = 2
Expand Down Expand Up @@ -307,3 +309,29 @@ async def test_weebl_db(ops_test: OpsTest, charm: str) -> None:
raise_on_blocked=False,
timeout=1000,
)

await ops_test.model.remove_application("weebl", block_until_done=True)


async def test_canonical_livepatch_onprem_bundle_db(ops_test: OpsTest) -> None:
# Deploy and test the Livepatch onprem bundle (using this PostgreSQL charm
# and an overlay to make the Ubuntu Advantage charm work with PostgreSQL).
# We intentionally wait for the `✘ sync_token not set` status message as we
# aren't providing an Ubuntu Pro token (as this is just a test to ensure
# the database works in the context of the relation with the Livepatch charm).
overlay = {
"applications": {"ubuntu-advantage": {"charm": "ubuntu-advantage", "series": CHARM_SERIES}}
}
await deploy_and_relate_bundle_with_postgresql(
ops_test,
"canonical-livepatch-onprem",
LIVEPATCH_APP_NAME,
"db",
"blocked",
"✘ sync_token not set",
overlay,
)

action = await ops_test.model.units.get(f"{LIVEPATCH_APP_NAME}/0").run_action("schema-upgrade")
await action.wait()
assert action.results.get("Code") == "0", "schema-upgrade action hasn't succeeded"
6 changes: 4 additions & 2 deletions tests/integration/test_db_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
DATABASE_APP_NAME,
check_database_users_existence,
check_databases_creation,
deploy_and_relate_landscape_bundle_with_postgresql,
deploy_and_relate_bundle_with_postgresql,
ensure_correct_relation_data,
get_landscape_api_credentials,
get_machine_from_unit,
Expand Down Expand Up @@ -42,7 +42,9 @@ async def test_landscape_scalable_bundle_db(ops_test: OpsTest, charm: str) -> No
)

# Deploy and test the Landscape Scalable bundle (using this PostgreSQL charm).
relation_id = await deploy_and_relate_landscape_bundle_with_postgresql(ops_test)
relation_id = await deploy_and_relate_bundle_with_postgresql(
ops_test, "ch:landscape-scalable", LANDSCAPE_APP_NAME, RELATION_NAME
)
await check_databases_creation(
ops_test,
[
Expand Down
Loading

0 comments on commit a07b31f

Please sign in to comment.