From 92fe6a1de9c05dfc9946ec66691529a55af1571f Mon Sep 17 00:00:00 2001 From: Oleksii Kliukin Date: Tue, 20 Oct 2015 11:28:26 +0200 Subject: [PATCH 1/3] Make pgpass location configurable. One can use pgpass configuration parameter in the postgres subsection of Patroni. By default pgpass is written in ~/. Mock actual writes to pgpass in the tests. --- patroni/postgresql.py | 9 ++++++--- tests/test_postgresql.py | 8 ++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/patroni/postgresql.py b/patroni/postgresql.py index f75324e7..81758909 100644 --- a/patroni/postgresql.py +++ b/patroni/postgresql.py @@ -48,6 +48,7 @@ class Postgresql: self.replication = config['replication'] self.superuser = config['superuser'] self.admin = config['admin'] + self.pgpass = config.get('pgpass', None) self.pg_rewind = config.get('pg_rewind', {}) self.callback = config.get('callbacks', {}) self.use_slots = config.get('use_slots', True) @@ -171,12 +172,14 @@ class Postgresql: os.path.exists(self.trigger_file) and os.unlink(self.trigger_file) def write_pgpass(self, record): - pgpass = 'pgpass' - with open(pgpass, 'w') as f: + self.pgpass = self.pgpass or os.path.join(os.path.expanduser('~'), 'pgpass') + + with open(self.pgpass, 'w') as f: os.fchmod(f.fileno(), 0o600) f.write('{host}:{port}:*:{user}:{password}\n'.format(**record)) + env = os.environ.copy() - env['PGPASSFILE'] = pgpass + env['PGPASSFILE'] = self.pgpass return env def sync_from_leader(self, leader): diff --git a/tests/test_postgresql.py b/tests/test_postgresql.py index 9a02ece6..5bd09e13 100644 --- a/tests/test_postgresql.py +++ b/tests/test_postgresql.py @@ -210,10 +210,16 @@ class TestPostgresql(unittest.TestCase): self.assertFalse(self.p.restart()) self.assertEquals(self.p.state, 'restart failed (restarting)') + @patch.object(builtins, 'open', MagicMock()) + def test_write_pgpass(self): + self.p.write_pgpass({'host': 'localhost', 'port': '5432', 'user': 'foo', 'password': 'bar'}) + + @patch('patroni.postgresql.Postgresql.write_pgpass', MagicMock(return_value=dict())) def test_sync_from_leader(self): self.assertTrue(self.p.sync_from_leader(self.leader)) @patch('subprocess.call', side_effect=Exception("Test")) + @patch('patroni.postgresql.Postgresql.write_pgpass', MagicMock(return_value=dict())) def test_pg_rewind(self, mock_call): self.assertTrue(self.p.rewind(self.leader)) subprocess.call = mock_call @@ -222,6 +228,7 @@ class TestPostgresql(unittest.TestCase): @patch('patroni.postgresql.Postgresql.rewind', return_value=False) @patch('patroni.postgresql.Postgresql.remove_data_directory', MagicMock(return_value=True)) @patch('patroni.postgresql.Postgresql.single_user_mode', MagicMock(return_value=1)) + @patch('patroni.postgresql.Postgresql.write_pgpass', MagicMock(return_value=dict())) def test_follow_the_leader(self, mock_pg_rewind): self.p.demote() self.p.follow_the_leader(None) @@ -327,6 +334,7 @@ class TestPostgresql(unittest.TestCase): with patch('os.rename', Mock(side_effect=OSError())): self.p.move_data_directory() + @patch('patroni.postgresql.Postgresql.write_pgpass', MagicMock(return_value=dict())) def test_bootstrap(self): with patch('subprocess.call', Mock(return_value=1)): self.assertRaises(PostgresException, self.p.bootstrap) From 35641ac0727f04f9527333a45a76e7ded1f55734 Mon Sep 17 00:00:00 2001 From: Oleksii Kliukin Date: Tue, 20 Oct 2015 11:40:52 +0200 Subject: [PATCH 2/3] Use distinct paths for pgpass from test nodes. --- postgres0.yml | 1 + postgres1.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/postgres0.yml b/postgres0.yml index a155b1cd..8747a3af 100644 --- a/postgres0.yml +++ b/postgres0.yml @@ -34,6 +34,7 @@ postgresql: data_dir: data/postgresql0 maximum_lag_on_failover: 1048576 # 1 megabyte in bytes use_slots: True + pgpass: /tmp/pgpass0 pg_rewind: username: postgres password: zalando diff --git a/postgres1.yml b/postgres1.yml index 94e33a42..dcf2f0cf 100644 --- a/postgres1.yml +++ b/postgres1.yml @@ -34,6 +34,7 @@ postgresql: data_dir: data/postgresql1 maximum_lag_on_failover: 1048576 # 1 megabyte in bytes use_slots: True + pgpass: /tmp/pgpass1 pg_rewind: username: postgres password: zalando From 9130891029076f3bf0aadb3e54bdf829bcc4deec Mon Sep 17 00:00:00 2001 From: Oleksii Kliukin Date: Wed, 21 Oct 2015 13:06:54 +0200 Subject: [PATCH 3/3] Move calculation of pgpass to the class constructor: better to fail fast in case of issues. --- patroni/postgresql.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/patroni/postgresql.py b/patroni/postgresql.py index 81758909..44406fe2 100644 --- a/patroni/postgresql.py +++ b/patroni/postgresql.py @@ -48,7 +48,7 @@ class Postgresql: self.replication = config['replication'] self.superuser = config['superuser'] self.admin = config['admin'] - self.pgpass = config.get('pgpass', None) + self.pgpass = config.get('pgpass', None) or os.path.join(os.path.expanduser('~'), 'pgpass') self.pg_rewind = config.get('pg_rewind', {}) self.callback = config.get('callbacks', {}) self.use_slots = config.get('use_slots', True) @@ -172,8 +172,6 @@ class Postgresql: os.path.exists(self.trigger_file) and os.unlink(self.trigger_file) def write_pgpass(self, record): - self.pgpass = self.pgpass or os.path.join(os.path.expanduser('~'), 'pgpass') - with open(self.pgpass, 'w') as f: os.fchmod(f.fileno(), 0o600) f.write('{host}:{port}:*:{user}:{password}\n'.format(**record))