Skip to content

Commit

Permalink
Initial code for test about upgrade from stable
Browse files Browse the repository at this point in the history
  • Loading branch information
marceloneppel committed Oct 11, 2023
1 parent 89c803e commit c1488e7
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 1 deletion.
1 change: 1 addition & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ jobs:
- password-rotation-integration
- plugins-integration
- tls-integration
- upgrade-from-stable-integration
agent-versions:
- "2.9.44" # renovate: latest juju 2
- "3.1.5" # renovate: latest juju 3
Expand Down
2 changes: 1 addition & 1 deletion src/dependency.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@
"dependencies": {},
"name": "charmed-postgresql",
"upgrade_supported": "^14",
"version": "14.8"
"version": "14.9"
}
}
115 changes: 115 additions & 0 deletions tests/integration/ha_tests/test_upgrade_from_stable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# Copyright 2023 Canonical Ltd.
# See LICENSE file for licensing details.

import logging

import pytest
from pytest_operator.plugin import OpsTest

from tests.integration.ha_tests.helpers import (
APPLICATION_NAME,
are_writes_increasing,
check_writes,
start_continuous_writes,
)
from tests.integration.helpers import (
DATABASE_APP_NAME,
count_switchovers,
get_leader_unit,
get_primary,
)

logger = logging.getLogger(__name__)

TIMEOUT = 5 * 60


@pytest.mark.abort_on_fail
async def test_deploy_stable(ops_test: OpsTest) -> None:
"""Simple test to ensure that the PostgreSQL and application charms get deployed."""
await ops_test.model.deploy(
DATABASE_APP_NAME,
num_units=2,
channel="14/edge",
trust=True,
),
await ops_test.model.deploy(
APPLICATION_NAME,
num_units=1,
channel="latest/edge",
)
logger.info("Wait for applications to become active")
async with ops_test.fast_forward():
await ops_test.model.wait_for_idle(
apps=[DATABASE_APP_NAME, APPLICATION_NAME], status="active", timeout=(20 * 60)
)
# assert len(ops_test.model.applications[DATABASE_APP_NAME].units) == 3


@pytest.mark.abort_on_fail
async def test_pre_upgrade_check(ops_test: OpsTest) -> None:
"""Test that the pre-upgrade-check action runs successfully."""
application = ops_test.model.applications[DATABASE_APP_NAME]
if "pre-upgrade-check" not in await application.get_actions():
logger.info("skipping the test because the charm from 14/stable doesn't support upgrade")
return

logger.info("Get leader unit")
leader_unit = await get_leader_unit(ops_test, DATABASE_APP_NAME)
assert leader_unit is not None, "No leader unit found"

logger.info("Run pre-upgrade-check action")
action = await leader_unit.run_action("pre-upgrade-check")
await action.wait()


@pytest.mark.abort_on_fail
async def test_upgrade_from_stable(ops_test: OpsTest):
"""Test updating from stable channel."""
# Start an application that continuously writes data to the database.
logger.info("starting continuous writes to the database")
await start_continuous_writes(ops_test, DATABASE_APP_NAME)

# Check whether writes are increasing.
logger.info("checking whether writes are increasing")
await are_writes_increasing(ops_test)

primary_name = await get_primary(ops_test, f"{DATABASE_APP_NAME}/0")
initial_number_of_switchovers = count_switchovers(ops_test, primary_name)

application = ops_test.model.applications[DATABASE_APP_NAME]
actions = await application.get_actions()

logger.info("Build charm locally")
charm = await ops_test.build_charm(".")

logger.info("Refresh the charm")
await application.refresh(path=charm)

logger.info("Wait for upgrade to complete")
async with ops_test.fast_forward("60s"):
await ops_test.model.block_until(
lambda: ops_test.model.applications[DATABASE_APP_NAME].units[0].workload_status_message
== "upgrade completed",
timeout=TIMEOUT,
)
await ops_test.model.wait_for_idle(
apps=[DATABASE_APP_NAME], status="active", idle_period=30, timeout=TIMEOUT
)

# Check whether writes are increasing.
logger.info("checking whether writes are increasing")
await are_writes_increasing(ops_test)

# Verify that no writes to the database were missed after stopping the writes
# (check that all the units have all the writes).
logger.info("checking whether no writes were lost")
await check_writes(ops_test)

# Check the number of switchovers.
if "pre-upgrade-check" in actions:
logger.info("checking the number of switchovers")
final_number_of_switchovers = count_switchovers(ops_test, primary_name)
assert (
final_number_of_switchovers - initial_number_of_switchovers
) <= 2, "Number of switchovers is greater than 2"
17 changes: 17 additions & 0 deletions tests/integration/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,13 @@ def convert_records_to_dict(records: List[tuple]) -> dict:
return records_dict


def count_switchovers(ops_test: OpsTest, unit_name: str) -> int:
"""Return the number of performed switchovers."""
unit_address = get_unit_address(ops_test, unit_name)
switchover_history_info = requests.get(f"http://{unit_address}:8008/history")
return len(switchover_history_info.json())


def db_connect(host: str, password: str) -> psycopg2.extensions.connection:
"""Returns psycopg2 connection object linked to postgres db in the given host.
Expand Down Expand Up @@ -559,6 +566,16 @@ async def get_landscape_api_credentials(ops_test: OpsTest) -> List[str]:
return output


async def get_leader_unit(ops_test: OpsTest, app: str) -> Optional[Unit]:
leader_unit = None
for unit in ops_test.model.applications[app].units:
if await unit.is_leader_from_status():
leader_unit = unit
break

return leader_unit


async def get_machine_from_unit(ops_test: OpsTest, unit_name: str) -> str:
"""Get the name of the machine from a specific unit.
Expand Down
11 changes: 11 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,17 @@ commands =
pip install juju=={env:LIBJUJU}
poetry run pytest -v --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/integration/test_tls.py

[testenv:upgrade-from-stable-integration-{juju2, juju3}]
description = Run upgrade from stable integration tests
pass_env =
{[testenv]pass_env}
CI
CI_PACKED_CHARMS
commands =
poetry install --with integration
pip install juju=={env:LIBJUJU}
poetry run pytest -v --tb native --log-cli-level=INFO -s {posargs} {[vars]tests_path}/integration/ha_tests/test_upgrade_from_stable.py

[testenv:integration-{juju2, juju3}]
description = Run all integration tests
pass_env =
Expand Down

0 comments on commit c1488e7

Please sign in to comment.