Skip to content

Commit

Permalink
Upgrade to jammy (#67)
Browse files Browse the repository at this point in the history
* Upgrade to jammy

* Disable db-admin relation integration test
  • Loading branch information
marceloneppel authored Mar 2, 2023
1 parent 9a79cb1 commit e427c2f
Show file tree
Hide file tree
Showing 16 changed files with 80 additions and 85 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ jobs:
- charm-integration
- database-relation-integration
- db-relation-integration
- db-admin-relation-integration
# - db-admin-relation-integration
- ha-self-healing-integration
- password-rotation-integration
- tls-integration
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
needs:
- lib-check
- ci-tests
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- name: Checkout
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,5 @@ juju add-model dev
# Enable DEBUG logging
juju model-config logging-config="<root>=INFO;unit=DEBUG"
# Deploy the charm
juju deploy ./postgresql_ubuntu-20.04-amd64.charm
juju deploy ./postgresql_ubuntu-22.04-amd64.charm
```
4 changes: 2 additions & 2 deletions charmcraft.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ type: charm
bases:
- build-on:
- name: "ubuntu"
channel: "20.04"
channel: "22.04"
run-on:
- name: "ubuntu"
channel: "20.04"
channel: "22.04"
parts:
charm:
build-packages:
Expand Down
5 changes: 0 additions & 5 deletions metadata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,6 @@ description: |
summary: |
Charm to operate the PostgreSQL database on machines
series:
# TODO: add jammy when it's released
- focal
- bionic

peers:
database-peers:
interface: postgresql_peers
Expand Down
4 changes: 2 additions & 2 deletions tests/integration/ha_tests/application-charm/charmcraft.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ type: charm
bases:
- build-on:
- name: "ubuntu"
channel: "20.04"
channel: "22.04"
run-on:
- name: "ubuntu"
channel: "20.04"
channel: "22.04"
parts:
charm:
charm-binary-python-packages: [psycopg2-binary==2.9.3]
6 changes: 4 additions & 2 deletions tests/integration/ha_tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
get_postgresql_parameter,
update_restart_delay,
)
from tests.integration.helpers import run_command_on_unit
from tests.integration.helpers import CHARM_SERIES, run_command_on_unit

APPLICATION_NAME = "application"

Expand All @@ -25,7 +25,9 @@ async def continuous_writes(ops_test: OpsTest) -> None:
async with ops_test.fast_forward():
if await app_name(ops_test, APPLICATION_NAME) is None:
charm = await ops_test.build_charm("tests/integration/ha_tests/application-charm")
await ops_test.model.deploy(charm, application_name=APPLICATION_NAME)
await ops_test.model.deploy(
charm, application_name=APPLICATION_NAME, series=CHARM_SERIES
)
await ops_test.model.wait_for_idle(status="active", timeout=1000)
yield
# Clear the written data at the end.
Expand Down
5 changes: 4 additions & 1 deletion tests/integration/ha_tests/test_self_healing.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
update_restart_delay,
)
from tests.integration.helpers import (
CHARM_SERIES,
db_connect,
get_password,
get_unit_address,
Expand All @@ -50,7 +51,9 @@ async def test_build_and_deploy(ops_test: OpsTest) -> None:

charm = await ops_test.build_charm(".")
async with ops_test.fast_forward():
await ops_test.model.deploy(charm, resources={"patroni": "patroni.tar.gz"}, num_units=3)
await ops_test.model.deploy(
charm, resources={"patroni": "patroni.tar.gz"}, num_units=3, series=CHARM_SERIES
)
await ops_test.juju("attach-resource", APP_NAME, "patroni=patroni.tar.gz")
await ops_test.model.wait_for_idle(status="active", timeout=1000)

Expand Down
6 changes: 1 addition & 5 deletions tests/integration/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
wait_fixed,
)

CHARM_SERIES = "jammy"
METADATA = yaml.safe_load(Path("./metadata.yaml").read_text())
DATABASE_APP_NAME = METADATA["name"]

Expand Down Expand Up @@ -183,11 +184,6 @@ def check_patroni(ops_test: OpsTest, unit_name: str, restart_time: float) -> boo
return postmaster_start_time > restart_time and health_info["state"] == "running"


def build_application_name(series: str) -> str:
"""Return a composite application name combining application name and series."""
return f"{DATABASE_APP_NAME}-{series}"


async def check_cluster_members(ops_test: OpsTest, application_name: str) -> None:
"""Check that the correct members are part of the cluster.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ type: charm
bases:
- build-on:
- name: "ubuntu"
channel: "20.04"
channel: "22.04"
run-on:
- name: "ubuntu"
channel: "20.04"
channel: "22.04"
7 changes: 4 additions & 3 deletions tests/integration/new_relations/test_new_relations.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import yaml
from pytest_operator.plugin import OpsTest

from tests.integration.helpers import scale_application
from tests.integration.helpers import CHARM_SERIES, scale_application
from tests.integration.new_relations.helpers import (
build_connection_string,
check_relation_data_existence,
Expand Down Expand Up @@ -42,20 +42,21 @@ async def test_deploy_charms(ops_test: OpsTest, application_charm, database_char
application_charm,
application_name=APPLICATION_APP_NAME,
num_units=2,
series=CHARM_SERIES,
),
ops_test.model.deploy(
database_charm,
resources={"patroni": "patroni.tar.gz"},
application_name=DATABASE_APP_NAME,
num_units=1,
trust=True,
series=CHARM_SERIES,
),
ops_test.model.deploy(
database_charm,
resources={"patroni": "patroni.tar.gz"},
application_name=ANOTHER_DATABASE_APP_NAME,
num_units=2,
trust=True,
series=CHARM_SERIES,
),
)

Expand Down
95 changes: 40 additions & 55 deletions tests/integration/test_charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@

from tests.helpers import STORAGE_PATH
from tests.integration.helpers import (
CHARM_SERIES,
DATABASE_APP_NAME,
build_application_name,
check_cluster_members,
convert_records_to_dict,
db_connect,
Expand All @@ -29,63 +29,54 @@

logger = logging.getLogger(__name__)

SERIES = ["focal"]
UNIT_IDS = [0, 1, 2]


@pytest.mark.abort_on_fail
@pytest.mark.parametrize("series", SERIES)
@pytest.mark.skip_if_deployed
async def test_deploy(ops_test: OpsTest, charm: str, series: str):
async def test_deploy(ops_test: OpsTest, charm: str):
"""Deploy the charm-under-test.
Assert on the unit status before any relations/configurations take place.
"""
# Set a composite application name in order to test in more than one series at the same time.
application_name = build_application_name(series)

# Deploy the charm with Patroni resource.
resources = {"patroni": "patroni.tar.gz"}
await ops_test.model.deploy(
charm, resources=resources, application_name=application_name, series=series, num_units=3
charm,
resources=resources,
application_name=DATABASE_APP_NAME,
num_units=3,
series=CHARM_SERIES,
)
# Attach the resource to the controller.
await ops_test.juju("attach-resource", application_name, "patroni=patroni.tar.gz")
await ops_test.juju("attach-resource", DATABASE_APP_NAME, "patroni=patroni.tar.gz")

# Reducing the update status frequency to speed up the triggering of deferred events.
await ops_test.model.set_config({"update-status-hook-interval": "10s"})

await ops_test.model.wait_for_idle(apps=[application_name], status="active", timeout=1000)
assert ops_test.model.applications[application_name].units[0].workload_status == "active"
await ops_test.model.wait_for_idle(apps=[DATABASE_APP_NAME], status="active", timeout=1000)
assert ops_test.model.applications[DATABASE_APP_NAME].units[0].workload_status == "active"


@pytest.mark.abort_on_fail
@pytest.mark.parametrize("series", SERIES)
@pytest.mark.parametrize("unit_id", UNIT_IDS)
async def test_database_is_up(ops_test: OpsTest, series: str, unit_id: int):
# Set a composite application name in order to test in more than one series at the same time.
application_name = build_application_name(series)

async def test_database_is_up(ops_test: OpsTest, unit_id: int):
# Query Patroni REST API and check the status that indicates
# both Patroni and PostgreSQL are up and running.
host = get_unit_address(ops_test, f"{application_name}/{unit_id}")
host = get_unit_address(ops_test, f"{DATABASE_APP_NAME}/{unit_id}")
result = requests.get(f"http://{host}:8008/health")
assert result.status_code == 200


@pytest.mark.parametrize("series", SERIES)
@pytest.mark.parametrize("unit_id", UNIT_IDS)
async def test_settings_are_correct(ops_test: OpsTest, series: str, unit_id: int):
async def test_settings_are_correct(ops_test: OpsTest, unit_id: int):
# Connect to the PostgreSQL instance.
# Set a composite application name in order to test in more than one series at the same time.
application_name = build_application_name(series)

# Retrieving the operator user password using the action.
any_unit_name = ops_test.model.applications[application_name].units[0].name
any_unit_name = ops_test.model.applications[DATABASE_APP_NAME].units[0].name
password = await get_password(ops_test, any_unit_name)

# Connect to PostgreSQL.
host = get_unit_address(ops_test, f"{application_name}/{unit_id}")
host = get_unit_address(ops_test, f"{DATABASE_APP_NAME}/{unit_id}")
logger.info("connecting to the database host: %s", host)
with db_connect(host, password) as connection:
assert connection.status == psycopg2.extensions.STATUS_READY
Expand Down Expand Up @@ -117,7 +108,7 @@ async def test_settings_are_correct(ops_test: OpsTest, series: str, unit_id: int
# Validate each configuration set by Patroni on PostgreSQL.
assert settings["archive_command"] == "/bin/true"
assert settings["archive_mode"] == "on"
assert settings["cluster_name"] == f"{DATABASE_APP_NAME}-{series}"
assert settings["cluster_name"] == DATABASE_APP_NAME
assert settings["data_directory"] == f"{STORAGE_PATH}/pgdata"
assert settings["data_checksums"] == "on"
assert settings["listen_addresses"] == host
Expand All @@ -136,33 +127,29 @@ async def test_settings_are_correct(ops_test: OpsTest, series: str, unit_id: int
assert settings["maximum_lag_on_failover"] == 1048576


@pytest.mark.parametrize("series", SERIES)
async def test_scale_down_and_up(ops_test: OpsTest, series: str):
async def test_scale_down_and_up(ops_test: OpsTest):
"""Test data is replicated to new units after a scale up."""
# Set a composite application name in order to test in more than one series at the same time.
application_name = build_application_name(series)

# Ensure the initial number of units in the application.
initial_scale = len(UNIT_IDS)
await scale_application(ops_test, application_name, initial_scale)
await scale_application(ops_test, DATABASE_APP_NAME, initial_scale)

# Scale down the application.
await scale_application(ops_test, application_name, initial_scale - 1)
await scale_application(ops_test, DATABASE_APP_NAME, initial_scale - 1)

# Ensure the member was correctly removed from the cluster
# (by comparing the cluster members and the current units).
await check_cluster_members(ops_test, application_name)
await check_cluster_members(ops_test, DATABASE_APP_NAME)

# Scale up the application (2 more units than the current scale).
await scale_application(ops_test, application_name, initial_scale + 1)
await scale_application(ops_test, DATABASE_APP_NAME, initial_scale + 1)

# Assert the correct members are part of the cluster.
await check_cluster_members(ops_test, application_name)
await check_cluster_members(ops_test, DATABASE_APP_NAME)

# Test the deletion of the unit that is both the leader and the primary.
any_unit_name = ops_test.model.applications[application_name].units[0].name
any_unit_name = ops_test.model.applications[DATABASE_APP_NAME].units[0].name
primary = await get_primary(ops_test, any_unit_name)
leader_unit = await find_unit(ops_test, leader=True, application=application_name)
leader_unit = await find_unit(ops_test, leader=True, application=DATABASE_APP_NAME)

# Trigger a switchover if the primary and the leader are not the same unit.
if primary != leader_unit.name:
Expand All @@ -177,21 +164,21 @@ async def test_scale_down_and_up(ops_test: OpsTest, series: str):
with attempt:
assert primary == leader_unit.name

await ops_test.model.applications[application_name].destroy_units(leader_unit.name)
await ops_test.model.applications[DATABASE_APP_NAME].destroy_units(leader_unit.name)
await ops_test.model.wait_for_idle(
apps=[application_name], status="active", timeout=1000, wait_for_exact_units=initial_scale
apps=[DATABASE_APP_NAME], status="active", timeout=1000, wait_for_exact_units=initial_scale
)

# Assert the correct members are part of the cluster.
await check_cluster_members(ops_test, application_name)
await check_cluster_members(ops_test, DATABASE_APP_NAME)

# Scale up the application (2 more units than the current scale).
await scale_application(ops_test, application_name, initial_scale + 2)
await scale_application(ops_test, DATABASE_APP_NAME, initial_scale + 2)

# Test the deletion of both the unit that is the leader and the unit that is the primary.
any_unit_name = ops_test.model.applications[application_name].units[0].name
any_unit_name = ops_test.model.applications[DATABASE_APP_NAME].units[0].name
primary = await get_primary(ops_test, any_unit_name)
leader_unit = await find_unit(ops_test, application_name, True)
leader_unit = await find_unit(ops_test, DATABASE_APP_NAME, True)

# Trigger a switchover if the primary and the leader are the same unit.
if primary == leader_unit.name:
Expand All @@ -206,27 +193,25 @@ async def test_scale_down_and_up(ops_test: OpsTest, series: str):
with attempt:
assert primary != leader_unit.name

await ops_test.model.applications[application_name].destroy_units(primary, leader_unit.name)
await ops_test.model.applications[DATABASE_APP_NAME].destroy_units(primary, leader_unit.name)
await ops_test.model.wait_for_idle(
apps=[application_name],
apps=[DATABASE_APP_NAME],
status="active",
timeout=1000,
wait_for_exact_units=initial_scale,
)

# Assert the correct members are part of the cluster.
await check_cluster_members(ops_test, application_name)
await check_cluster_members(ops_test, DATABASE_APP_NAME)

# End with the cluster having the initial number of units.
await scale_application(ops_test, application_name, initial_scale)
await scale_application(ops_test, DATABASE_APP_NAME, initial_scale)


@pytest.mark.parametrize("series", SERIES)
async def test_persist_data_through_primary_deletion(ops_test: OpsTest, series: str):
async def test_persist_data_through_primary_deletion(ops_test: OpsTest):
"""Test data persists through a primary deletion."""
# Set a composite application name in order to test in more than one series at the same time.
application_name = build_application_name(series)
any_unit_name = ops_test.model.applications[application_name].units[0].name
any_unit_name = ops_test.model.applications[DATABASE_APP_NAME].units[0].name
primary = await get_primary(ops_test, any_unit_name)
password = await get_password(ops_test, primary)

Expand All @@ -243,14 +228,14 @@ async def test_persist_data_through_primary_deletion(ops_test: OpsTest, series:
await ops_test.model.destroy_units(
primary,
)
await ops_test.model.wait_for_idle(apps=[application_name], status="active", timeout=1000)
await ops_test.model.wait_for_idle(apps=[DATABASE_APP_NAME], status="active", timeout=1000)

# Add the unit again.
await ops_test.model.applications[application_name].add_unit(count=1)
await ops_test.model.wait_for_idle(apps=[application_name], status="active", timeout=1000)
await ops_test.model.applications[DATABASE_APP_NAME].add_unit(count=1)
await ops_test.model.wait_for_idle(apps=[DATABASE_APP_NAME], status="active", timeout=1000)

# Testing write occurred to every postgres instance by reading from them
for unit in ops_test.model.applications[application_name].units:
for unit in ops_test.model.applications[DATABASE_APP_NAME].units:
host = unit.public_address
logger.info("connecting to the database host: %s", host)
with db_connect(host, password) as connection:
Expand Down
Loading

0 comments on commit e427c2f

Please sign in to comment.