Skip to content

Commit 8e652ce

Browse files
Rachel Heatonjchampio
andauthored
Monitor config set postgresql.pg_ctl bug fix (#818)
- removes double-reload of config file during `config set` - errors out when attempting to update to an invalid pg_ctl - allows update to an invalid pg_ctl, in case the binary moved - add new config test (to be backfilled as more changes come up) - fix code comment in related code Co-authored-by: Jacob Champion <pchampion@vmware.com>
1 parent c1dfd70 commit 8e652ce

4 files changed

Lines changed: 99 additions & 14 deletions

File tree

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ TESTS_SINGLE += test_create_run
2626
TESTS_SINGLE += test_create_standby_with_pgdata
2727
TESTS_SINGLE += test_ensure
2828
TESTS_SINGLE += test_skip_pg_hba
29+
TESTS_SINGLE += test_config_get_set
2930

3031
# Tests for SSL
3132
TESTS_SSL = test_enable_ssl

src/bin/pg_autoctl/cli_config.c

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -583,7 +583,7 @@ cli_monitor_config_get(int argc, char **argv)
583583

584584

585585
/*
586-
* cli_keeper_config_get retrieves the value of a given configuration value,
586+
* cli_config_set sets the value of a given configuration value,
587587
* supporting either a Keeper or a Monitor configuration file.
588588
*/
589589
static void
@@ -686,8 +686,6 @@ cli_keeper_config_set(int argc, char **argv)
686686
static void
687687
cli_monitor_config_set(int argc, char **argv)
688688
{
689-
KeeperConfig kconfig = keeperOptions;
690-
691689
if (argc != 2)
692690
{
693691
log_error("Two arguments are expected, found %d", argc);
@@ -698,15 +696,13 @@ cli_monitor_config_set(int argc, char **argv)
698696
/* we print out the value that we parsed, as a double-check */
699697
char value[BUFSIZE];
700698
MonitorConfig mconfig = { 0 };
701-
bool missing_pgdata_is_ok = true;
702-
bool pg_is_not_running_is_ok = true;
703699

704-
if (!monitor_config_init_from_pgsetup(&mconfig,
705-
&kconfig.pgSetup,
706-
missing_pgdata_is_ok,
707-
pg_is_not_running_is_ok))
700+
mconfig.pgSetup = keeperOptions.pgSetup;
701+
702+
if (!monitor_config_set_pathnames_from_pgdata(&mconfig))
708703
{
709-
exit(EXIT_CODE_PGCTL);
704+
/* errors have already been logged */
705+
exit(EXIT_CODE_INTERNAL_ERROR);
710706
}
711707

712708
/* first write the new configuration settings to file */

tests/pgautofailover_utils.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@
2121

2222
NodeState = namedtuple("NodeState", "reported assigned")
2323

24+
# Append stderr output to default CalledProcessError message
25+
class CalledProcessError(subprocess.CalledProcessError):
26+
def __str__(self):
27+
return super().__str__() + "\n\t" + self.stderr
28+
2429

2530
class Role(Enum):
2631
Monitor = 1
@@ -1952,10 +1957,7 @@ def execute(self, name, *args, timeout=COMMAND_TIMEOUT):
19521957

19531958
self.last_returncode = proc.returncode
19541959
if proc.returncode > 0:
1955-
raise Exception(
1956-
"%s failed\n%s\n%s\n%s"
1957-
% (name, " ".join(self.command), out, err)
1958-
)
1960+
raise CalledProcessError(proc.returncode, self.cmd, out, err)
19591961

19601962
return out, err, proc.returncode
19611963

tests/test_config_get_set.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import pgautofailover_utils as pgautofailover
2+
from nose.tools import assert_raises, raises, eq_
3+
4+
import os
5+
import shutil
6+
import subprocess
7+
import time
8+
9+
cluster = None
10+
monitor = None
11+
node1 = None
12+
13+
14+
def setup_module():
15+
global cluster
16+
cluster = pgautofailover.Cluster()
17+
18+
19+
def teardown_module():
20+
cluster.destroy()
21+
22+
23+
def test_000_create_monitor():
24+
global monitor
25+
monitor = cluster.create_monitor("/tmp/config_test/monitor")
26+
monitor.run()
27+
28+
29+
def test_001_init_primary():
30+
global node1
31+
node1 = cluster.create_datanode("/tmp/config_test/node1")
32+
node1.create()
33+
34+
# the name of the node should be "%s_%d" % ("node", node1.nodeid)
35+
eq_(node1.get_nodename(), "node_%d" % node1.get_nodeid())
36+
37+
# we can change the name on the monitor with pg_autoctl set node metadata
38+
node1.set_metadata(name="node a")
39+
eq_(node1.get_nodename(), "node a")
40+
41+
node1.run()
42+
assert node1.wait_until_state(target_state="single")
43+
44+
# we can also change the name directly in the configuration file
45+
node1.config_set("pg_autoctl.name", "a")
46+
47+
# wait until the reload signal has been processed before checking
48+
time.sleep(2)
49+
eq_(node1.get_nodename(), "a")
50+
51+
52+
def test_002_config_set_monitor():
53+
pg_ctl = monitor.config_get("postgresql.pg_ctl")
54+
55+
# set something non-default to assert no side-effects later
56+
sslmode = "prefer"
57+
monitor.config_set("ssl.sslmode", sslmode)
58+
59+
# set monitor config postgresql.pg_ctl to something invalid
60+
with assert_raises(subprocess.CalledProcessError):
61+
monitor.config_set("postgresql.pg_ctl", "invalid")
62+
63+
# it should not get changed
64+
eq_(monitor.config_get("postgresql.pg_ctl"), pg_ctl)
65+
66+
# try again with a keeper
67+
pg_ctl = node1.config_get("postgresql.pg_ctl")
68+
69+
# set the keeper to something invalid
70+
with assert_raises(subprocess.CalledProcessError):
71+
node1.config_set("postgresql.pg_ctl", "invalid")
72+
73+
# it should not get changed
74+
eq_(node1.config_get("postgresql.pg_ctl"), pg_ctl)
75+
76+
# pg_ctl can be moved and `config set` will still operate.
77+
shutil.copy(pg_ctl, "/tmp/pg_ctl")
78+
monitor.config_set("postgresql.pg_ctl", "/tmp/pg_ctl")
79+
# "move" pg_ctl
80+
os.remove("/tmp/pg_ctl")
81+
monitor.config_set("postgresql.pg_ctl", pg_ctl)
82+
83+
eq_(monitor.config_get("postgresql.pg_ctl"), pg_ctl)
84+
85+
# no side effects
86+
eq_(monitor.config_get("ssl.sslmode"), sslmode)

0 commit comments

Comments
 (0)