From 3a87d0e99b30e847ee313a071d7672b0087784ec Mon Sep 17 00:00:00 2001 From: Alexander Kukushkin Date: Wed, 16 Dec 2020 18:44:58 +0100 Subject: [PATCH] Implement missing validators for etcd3 and raft (#1798) Close https://github.com/zalando/patroni/issues/1771 --- patroni/validator.py | 34 ++++++++++++++++++++++++---------- tests/test_validator.py | 30 +++++++++++++++++++++--------- 2 files changed, 45 insertions(+), 19 deletions(-) diff --git a/patroni/validator.py b/patroni/validator.py index 7e777da0..117b40d6 100644 --- a/patroni/validator.py +++ b/patroni/validator.py @@ -53,11 +53,15 @@ def validate_host_port(host_port, listen=False, multiple_hosts=False): return True -def comma_separated_host_port(string): - assert all([validate_host_port(s.strip()) for s in string.split(",")]), "didn't pass the validation" +def validate_host_port_list(value): + assert all([validate_host_port(v) for v in value]), "didn't pass the validation" return True +def comma_separated_host_port(string): + return validate_host_port_list([s.strip() for s in string.split(",")]) + + def validate_host_port_listen(host_port): return validate_host_port(host_port, listen=True) @@ -291,11 +295,20 @@ def assert_(condition, message="Wrong value"): userattributes = {"username": "", Optional("password"): ""} available_dcs = [m.split(".")[-1] for m in dcs_modules()] +validate_host_port_list.expected_type = list comma_separated_host_port.expected_type = string_types validate_connect_address.expected_type = string_types validate_host_port_listen.expected_type = string_types validate_host_port_listen_multiple_hosts.expected_type = string_types validate_data_dir.expected_type = string_types +validate_etcd = { + Or("host", "hosts", "srv", "url", "proxy"): Case({ + "host": validate_host_port, + "hosts": Or(comma_separated_host_port, [validate_host_port]), + "srv": str, + "url": str, + "proxy": str}) +} schema = Schema({ "name": str, @@ -320,19 +333,20 @@ schema = Schema({ "host": validate_host_port, "url": str}) }, - "etcd": { - Or("host", "hosts", "srv", "url", "proxy"): Case({ - "host": validate_host_port, - "hosts": Or(comma_separated_host_port, [validate_host_port]), - "srv": str, - "url": str, - "proxy": str}) - }, + "etcd": validate_etcd, + "etcd3": validate_etcd, "exhibitor": { "hosts": [str], "port": lambda i: assert_(int(i) <= 65535), Optional("pool_interval"): int }, + "raft": { + "self_addr": validate_connect_address, + Optional("bind_addr"): validate_host_port_listen, + "partner_addrs": validate_host_port_list, + Optional("data_dir"): str, + Optional("password"): str + }, "zookeeper": { "hosts": Or(comma_separated_host_port, [validate_host_port]), }, diff --git a/tests/test_validator.py b/tests/test_validator.py index 1a27514a..a04403ce 100644 --- a/tests/test_validator.py +++ b/tests/test_validator.py @@ -33,11 +33,21 @@ config = { "etcd": { "hosts": "127.0.0.1:2379,127.0.0.1:2380" }, + "etcd3": { + "url": "https://127.0.0.1:2379" + }, "exhibitor": { "hosts": ["string"], "port": 4000, "pool_interval": 1000 }, + "raft": { + "self_addr": "127.0.0.1:2222", + "bind_addr": "0.0.0.0:2222", + "partner_addrs": ["127.0.0.1:2223", "127.0.0.1:2224"], + "data_dir": "/", + "password": "12345" + }, "zookeeper": { "hosts": "127.0.0.1:3379,127.0.0.1:3380" }, @@ -139,7 +149,7 @@ class TestValidator(unittest.TestCase): def test_complete_config(self, mock_out, mock_err): schema(config) output = mock_out.getvalue() - self.assertEqual(['postgresql.bin_dir'], parse_output(output)) + self.assertEqual(['postgresql.bin_dir', 'raft.bind_addr', 'raft.self_addr'], parse_output(output)) def test_bin_dir_is_file(self, mock_out, mock_err): files.append(config["postgresql"]["data_dir"]) @@ -151,7 +161,8 @@ class TestValidator(unittest.TestCase): schema(c) output = mock_out.getvalue() self.assertEqual(['etcd.hosts.1', 'etcd.hosts.2', 'kubernetes.pod_ip', 'postgresql.bin_dir', - 'postgresql.data_dir', 'restapi.connect_address'], parse_output(output)) + 'postgresql.data_dir', 'raft.bind_addr', 'raft.self_addr', + 'restapi.connect_address'], parse_output(output)) @patch('socket.inet_pton', Mock(), create=True) def test_bin_dir_is_empty(self, mock_out, mock_err): @@ -167,8 +178,8 @@ class TestValidator(unittest.TestCase): with patch('patroni.validator.open', mock_open(read_data='9')): schema(c) output = mock_out.getvalue() - self.assertEqual(['consul.host', 'etcd.host', 'postgresql.bin_dir', 'postgresql.data_dir', - 'postgresql.listen', 'restapi.connect_address'], parse_output(output)) + self.assertEqual(['consul.host', 'etcd.host', 'postgresql.bin_dir', 'postgresql.data_dir', 'postgresql.listen', + 'raft.bind_addr', 'raft.self_addr', 'restapi.connect_address'], parse_output(output)) @patch('subprocess.check_output', Mock(return_value=b"postgres (PostgreSQL) 12.1")) def test_data_dir_contains_pg_version(self, mock_out, mock_err): @@ -186,7 +197,7 @@ class TestValidator(unittest.TestCase): with patch('patroni.validator.open', mock_open(read_data='12')): schema(config) output = mock_out.getvalue() - self.assertEqual([], parse_output(output)) + self.assertEqual(['raft.bind_addr', 'raft.self_addr'], parse_output(output)) @patch('subprocess.check_output', Mock(return_value=b"postgres (PostgreSQL) 12.1")) def test_pg_version_missmatch(self, mock_out, mock_err): @@ -201,7 +212,8 @@ class TestValidator(unittest.TestCase): with patch('patroni.validator.open', mock_open(read_data='11')): schema(c) output = mock_out.getvalue() - self.assertEqual(['etcd.hosts', 'postgresql.data_dir'], parse_output(output)) + self.assertEqual(['etcd.hosts', 'postgresql.data_dir', + 'raft.bind_addr', 'raft.self_addr'], parse_output(output)) @patch('subprocess.check_output', Mock(return_value=b"postgres (PostgreSQL) 12.1")) def test_pg_wal_doesnt_exist(self, mock_out, mock_err): @@ -214,7 +226,7 @@ class TestValidator(unittest.TestCase): with patch('patroni.validator.open', mock_open(read_data='11')): schema(c) output = mock_out.getvalue() - self.assertEqual(['postgresql.data_dir'], parse_output(output)) + self.assertEqual(['postgresql.data_dir', 'raft.bind_addr', 'raft.self_addr'], parse_output(output)) def test_data_dir_is_empty_string(self, mock_out, mock_err): directories.append(config["postgresql"]["data_dir"]) @@ -226,5 +238,5 @@ class TestValidator(unittest.TestCase): c["postgresql"]["bin_dir"] = "" schema(c) output = mock_out.getvalue() - self.assertEqual(['kubernetes', 'postgresql.bin_dir', - 'postgresql.data_dir', 'postgresql.pg_hba'], parse_output(output)) + self.assertEqual(['kubernetes', 'postgresql.bin_dir', 'postgresql.data_dir', + 'postgresql.pg_hba', 'raft.bind_addr', 'raft.self_addr'], parse_output(output))