Skip to content

Commit 15c445b

Browse files
Create Jubilant stop-all test
1 parent 72535fc commit 15c445b

File tree

2 files changed

+150
-0
lines changed

2 files changed

+150
-0
lines changed
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
# Copyright 2025 Canonical Ltd.
2+
# See LICENSE file for licensing details.
3+
4+
import logging
5+
6+
import jubilant_backports
7+
import pytest
8+
from jubilant_backports import Juju
9+
10+
from constants import CLUSTER_ADMIN_USERNAME
11+
12+
from ..helpers import (
13+
generate_random_string,
14+
is_connection_possible,
15+
)
16+
from .high_availability_helpers_new import (
17+
check_mysql_units_writes_increment,
18+
fast_interval,
19+
get_app_units,
20+
get_unit_ip,
21+
insert_mysql_test_data,
22+
remove_mysql_test_data,
23+
start_mysql_process_gracefully,
24+
stop_mysql_process_gracefully,
25+
verify_mysql_test_data,
26+
wait_for_apps_status,
27+
wait_for_unit_status,
28+
)
29+
30+
MYSQL_APP_NAME = "mysql"
31+
MYSQL_TEST_APP_NAME = "mysql-test-app"
32+
33+
MINUTE_SECS = 60
34+
35+
logging.getLogger("jubilant.wait").setLevel(logging.WARNING)
36+
37+
38+
@pytest.mark.abort_on_fail
39+
def test_deploy_highly_available_cluster(juju: Juju, charm: str) -> None:
40+
"""Simple test to ensure that the MySQL and application charms get deployed."""
41+
logging.info("Deploying MySQL cluster")
42+
juju.deploy(
43+
charm=charm,
44+
app=MYSQL_APP_NAME,
45+
46+
config={"profile": "testing"},
47+
num_units=3,
48+
)
49+
juju.deploy(
50+
charm=MYSQL_TEST_APP_NAME,
51+
app=MYSQL_TEST_APP_NAME,
52+
53+
channel="latest/edge",
54+
config={"sleep_interval": 500},
55+
num_units=1,
56+
)
57+
58+
juju.integrate(
59+
f"{MYSQL_APP_NAME}:database",
60+
f"{MYSQL_TEST_APP_NAME}:database",
61+
)
62+
63+
logging.info("Wait for applications to become active")
64+
juju.wait(
65+
ready=wait_for_apps_status(
66+
jubilant_backports.all_active, MYSQL_APP_NAME, MYSQL_TEST_APP_NAME
67+
),
68+
error=jubilant_backports.any_blocked,
69+
timeout=20 * MINUTE_SECS,
70+
)
71+
72+
73+
@pytest.mark.abort_on_fail
74+
async def test_cluster_pause(juju: Juju, continuous_writes_new) -> None:
75+
"""Pause test.
76+
77+
A graceful simultaneous restart of all instances,
78+
check primary election after the start, write and read data
79+
"""
80+
# Ensure continuous writes still incrementing for all units
81+
await check_mysql_units_writes_increment(juju, MYSQL_APP_NAME)
82+
83+
mysql_units = get_app_units(juju, MYSQL_APP_NAME)
84+
85+
# Ensure update status will not run to avoid self-healing
86+
juju.model_config({"update-status-hook-interval": "60m"})
87+
88+
logging.info("Stopping all instances")
89+
for unit_name in mysql_units:
90+
stop_mysql_process_gracefully(juju, unit_name)
91+
92+
logging.info("Checking all instances connectivity")
93+
for unit_name in mysql_units:
94+
credentials_task = juju.run(
95+
unit=unit_name,
96+
action="get-password",
97+
params={"username": CLUSTER_ADMIN_USERNAME},
98+
)
99+
config = {
100+
"username": credentials_task.results["username"],
101+
"password": credentials_task.results["password"],
102+
"host": get_unit_ip(juju, MYSQL_APP_NAME, unit_name),
103+
}
104+
105+
assert not is_connection_possible(config)
106+
107+
logging.info("Starting all instances")
108+
for unit_name in mysql_units:
109+
start_mysql_process_gracefully(juju, unit_name)
110+
111+
with fast_interval(juju):
112+
logging.info("Waiting units to enter maintenance")
113+
juju.wait(
114+
ready=lambda status: all((
115+
wait_for_unit_status(MYSQL_APP_NAME, f"{MYSQL_APP_NAME}/0", "maintenance")(status),
116+
wait_for_unit_status(MYSQL_APP_NAME, f"{MYSQL_APP_NAME}/1", "maintenance")(status),
117+
wait_for_unit_status(MYSQL_APP_NAME, f"{MYSQL_APP_NAME}/2", "maintenance")(status),
118+
)),
119+
timeout=20 * MINUTE_SECS,
120+
)
121+
logging.info("Waiting units to be back online")
122+
juju.wait(
123+
ready=lambda status: all((
124+
wait_for_unit_status(MYSQL_APP_NAME, f"{MYSQL_APP_NAME}/0", "active")(status),
125+
wait_for_unit_status(MYSQL_APP_NAME, f"{MYSQL_APP_NAME}/1", "active")(status),
126+
wait_for_unit_status(MYSQL_APP_NAME, f"{MYSQL_APP_NAME}/2", "active")(status),
127+
)),
128+
timeout=20 * MINUTE_SECS,
129+
)
130+
131+
# Ensure continuous writes still incrementing for all units
132+
await check_mysql_units_writes_increment(juju, MYSQL_APP_NAME)
133+
134+
# Ensure that we are able to insert data into the primary
135+
table_name = "data"
136+
table_value = generate_random_string(255)
137+
138+
await insert_mysql_test_data(juju, MYSQL_APP_NAME, table_name, table_value)
139+
await verify_mysql_test_data(juju, MYSQL_APP_NAME, table_name, table_value)
140+
await remove_mysql_test_data(juju, MYSQL_APP_NAME, table_name)
141+
142+
# Restore standard interval
143+
juju.model_config({"update-status-hook-interval": "5m"})
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
summary: test_self_healing_stop_all_new.py
2+
environment:
3+
TEST_MODULE: high_availability/test_self_healing_stop_all_new.py
4+
execute: |
5+
tox run -e integration -- "tests/integration/$TEST_MODULE" --model testing --alluredir="$SPREAD_TASK/allure-results"
6+
artifacts:
7+
- allure-results

0 commit comments

Comments
 (0)