Skip to content

Commit 706e9b9

Browse files
authored
Add test_config_parameters test (#374)
* add `test_config_parameters` test * updated * updated * updated
1 parent 388f01a commit 706e9b9

File tree

3 files changed

+123
-11
lines changed

3 files changed

+123
-11
lines changed

src/charm.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,10 @@ def _on_peer_relation_changed(self, event: HookEvent):
480480
except RetryError:
481481
self.unit.status = BlockedStatus("failed to update cluster members on member")
482482
return
483+
except ValueError as e:
484+
self.unit.status = BlockedStatus("Configuration Error. Please check the logs")
485+
logger.error("Invalid configuration: %s", str(e))
486+
return
483487

484488
# Start can be called here multiple times as it's idempotent.
485489
# At this moment, it starts Patroni at the first time the data is received
@@ -505,6 +509,12 @@ def _on_peer_relation_changed(self, event: HookEvent):
505509
event.defer()
506510
return
507511

512+
self._start_stop_pgbackrest_service(event)
513+
514+
self._update_new_unit_status()
515+
516+
# Split off into separate function, because of complexity _on_peer_relation_changed
517+
def _start_stop_pgbackrest_service(self, event: HookEvent) -> None:
508518
# Start or stop the pgBackRest TLS server service when TLS certificate change.
509519
if not self.backup.start_stop_pgbackrest_service():
510520
logger.debug(
@@ -520,8 +530,6 @@ def _on_peer_relation_changed(self, event: HookEvent):
520530
if "exporter-started" not in self.unit_peer_data:
521531
self._setup_exporter()
522532

523-
self._update_new_unit_status()
524-
525533
def _update_new_unit_status(self) -> None:
526534
"""Update the status of a new unit that recently joined the cluster."""
527535
# Only update the connection endpoints if there is a primary.
@@ -862,8 +870,17 @@ def _on_config_changed(self, _) -> None:
862870
if not self.upgrade.idle:
863871
logger.debug("Early exit on_config_changed: upgrade in progress")
864872
return
865-
# update config on every run
866-
self.update_config()
873+
874+
try:
875+
# update config on every run
876+
self.update_config()
877+
except ValueError as e:
878+
self.unit.status = BlockedStatus("Configuration Error. Please check the logs")
879+
logger.error("Invalid configuration: %s", str(e))
880+
return
881+
882+
if self.is_blocked and "Configuration Error" in self.unit.status.message:
883+
self.unit.status = ActiveStatus()
867884

868885
if not self.unit.is_leader():
869886
return

src/config.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -198,8 +198,7 @@ def optimizer_default_statistics_target_values(cls, value: int) -> Optional[int]
198198

199199
return value
200200

201-
@validator("optimizer_from_collapse_limit", allow_reuse=True)
202-
@validator("optimizer_join_collapse_limit", allow_reuse=True)
201+
@validator("optimizer_from_collapse_limit", "optimizer_join_collapse_limit")
203202
@classmethod
204203
def optimizer_collapse_limit_values(cls, value: int) -> Optional[int]:
205204
"""Check optimizer collapse_limit config option is between 1 and 2147483647."""
@@ -237,9 +236,7 @@ def response_bytea_output_values(cls, value: str) -> Optional[str]:
237236

238237
return value
239238

240-
@validator("response_lc_monetary", allow_reuse=True)
241-
@validator("response_lc_numeric", allow_reuse=True)
242-
@validator("response_lc_time", allow_reuse=True)
239+
@validator("response_lc_monetary", "response_lc_numeric", "response_lc_time")
243240
@classmethod
244241
def response_lc_values(cls, value: str) -> Optional[str]:
245242
"""Check if the requested locale is available in the system."""
@@ -251,8 +248,7 @@ def response_lc_values(cls, value: str) -> Optional[str]:
251248

252249
return value
253250

254-
@validator("vacuum_autovacuum_analyze_scale_factor", allow_reuse=True)
255-
@validator("vacuum_autovacuum_vacuum_scale_factor", allow_reuse=True)
251+
@validator("vacuum_autovacuum_analyze_scale_factor", "vacuum_autovacuum_vacuum_scale_factor")
256252
@classmethod
257253
def vacuum_autovacuum_vacuum_scale_factor_values(cls, value: float) -> Optional[float]:
258254
"""Check autovacuum scale_factor config option is between 0 and 100."""

tests/integration/test_config.py

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
#!/usr/bin/env python3
2+
# Copyright 2023 Canonical Ltd.
3+
# See LICENSE file for licensing details.
4+
import logging
5+
6+
import pytest as pytest
7+
from pytest_operator.plugin import OpsTest
8+
9+
from .helpers import (
10+
CHARM_SERIES,
11+
DATABASE_APP_NAME,
12+
get_leader_unit,
13+
)
14+
15+
logger = logging.getLogger(__name__)
16+
17+
18+
@pytest.mark.group(1)
19+
@pytest.mark.abort_on_fail
20+
async def test_config_parameters(ops_test: OpsTest) -> None:
21+
"""Build and deploy one unit of PostgreSQL and then test config with wrong parameters."""
22+
# Build and deploy the PostgreSQL charm.
23+
async with ops_test.fast_forward():
24+
charm = await ops_test.build_charm(".")
25+
await ops_test.model.deploy(
26+
charm,
27+
num_units=1,
28+
series=CHARM_SERIES,
29+
config={"profile": "testing"},
30+
)
31+
await ops_test.model.wait_for_idle(apps=[DATABASE_APP_NAME], status="active", timeout=1500)
32+
33+
leader_unit = await get_leader_unit(ops_test, DATABASE_APP_NAME)
34+
test_string = "abcXYZ123"
35+
36+
configs = [
37+
{
38+
"durability_synchronous_commit": [test_string, "on"]
39+
}, # config option is one of `on`, `remote_apply` or `remote_write`
40+
{
41+
"instance_password_encryption": [test_string, "scram-sha-256"]
42+
}, # config option is one of `md5` or `scram-sha-256`
43+
{
44+
"logging_log_min_duration_statement": ["-2", "-1"]
45+
}, # config option is between -1 and 2147483647
46+
{
47+
"memory_maintenance_work_mem": ["1023", "65536"]
48+
}, # config option is between 1024 and 2147483647
49+
{"memory_max_prepared_transactions": ["-1", "0"]}, # config option is between 0 and 262143
50+
{"memory_shared_buffers": ["15", "1024"]}, # config option is greater or equal than 16
51+
{"memory_temp_buffers": ["99", "1024"]}, # config option is between 100 and 1073741823
52+
{"memory_work_mem": ["63", "4096"]}, # config option is between 64 and 2147483647
53+
{
54+
"optimizer_constraint_exclusion": [test_string, "partition"]
55+
}, # config option is one of `on`, `off` or `partition`
56+
{
57+
"optimizer_default_statistics_target": ["0", "100"]
58+
}, # config option is between 1 and 10000
59+
{"optimizer_from_collapse_limit": ["0", "8"]}, # config option is between 1 and 2147483647
60+
{"optimizer_join_collapse_limit": ["0", "8"]}, # config option is between 1 and 2147483647
61+
{"profile": [test_string, "testing"]}, # config option is one of `testing` or `production`
62+
# {"profile_limit_memory": {"127", "128"}}, # config option is between 128 and 9999999
63+
{
64+
"response_bytea_output": [test_string, "hex"]
65+
}, # config option is one of `escape` or `hex`
66+
{
67+
"vacuum_autovacuum_analyze_scale_factor": ["-1", "0.1"]
68+
}, # config option is between 0 and 100
69+
{
70+
"vacuum_autovacuum_vacuum_scale_factor": ["-1", "0.2"]
71+
}, # config option is between 0 and 100
72+
{
73+
"vacuum_autovacuum_analyze_threshold": ["-1", "50"]
74+
}, # config option is between 0 and 2147483647
75+
{
76+
"vacuum_autovacuum_freeze_max_age": ["99999", "200000000"]
77+
}, # config option is between 100000 and 2000000000
78+
{
79+
"vacuum_autovacuum_vacuum_cost_delay": ["-2", "2.0"]
80+
}, # config option is between -1 and 100
81+
{
82+
"vacuum_vacuum_freeze_table_age": ["-1", "150000000"]
83+
}, # config option is between 0 and 2000000000
84+
]
85+
86+
charm_config = {}
87+
for config in configs:
88+
for k, v in config.items():
89+
logger.info(k)
90+
charm_config[k] = v[0]
91+
await ops_test.model.applications[DATABASE_APP_NAME].set_config(charm_config)
92+
await ops_test.model.wait_for_idle(
93+
apps=[DATABASE_APP_NAME], status="blocked", timeout=100
94+
)
95+
assert "Configuration Error" in leader_unit.workload_status_message
96+
charm_config[k] = v[1]
97+
98+
await ops_test.model.applications[DATABASE_APP_NAME].set_config(charm_config)
99+
await ops_test.model.wait_for_idle(apps=[DATABASE_APP_NAME], status="active", timeout=100)

0 commit comments

Comments
 (0)