From ad17b2070a4a48c62d72f4090f309620b749e9a4 Mon Sep 17 00:00:00 2001 From: Alexander Kukushkin Date: Wed, 10 Feb 2016 12:37:34 +0100 Subject: [PATCH 1/2] Create superuser with the name specified in config file And later use this name to connect to the cluster --- patroni/postgresql.py | 35 ++++++++++++++++++++--------------- tests/test_postgresql.py | 8 +------- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/patroni/postgresql.py b/patroni/postgresql.py index 5a6cb390..9d46952f 100644 --- a/patroni/postgresql.py +++ b/patroni/postgresql.py @@ -131,9 +131,10 @@ class Postgresql: def connection(self): if not self._connection or self._connection.closed != 0: r = parseurl('postgres://{}/postgres'.format(self.local_address)) - r.update(self.superuser) - if r.get('username'): - r['user'] = r['username'] + if 'username' in self.superuser: + r['user'] = self.superuser['username'] + if 'password' in self.superuser: + r['password'] = self.superuser['password'] self._connection = psycopg2.connect(**r) self._connection.autocommit = True self.server_version = self._connection.server_version @@ -196,11 +197,15 @@ class Postgresql: self.set_state('initalizing new cluster') options = self.get_initdb_options() pwfile = None - if self.superuser and 'username' not in self.superuser and 'password' in self.superuser: - (fd, pwfile) = tempfile.mkstemp() - os.write(fd, self.superuser['password'].encode()) - os.close(fd) - options.append('--pwfile={}'.format(pwfile)) + + if self.superuser: + if 'username' in self.superuser: + options.append('--username={}'.format(self.superuser['username'])) + if 'password' in self.superuser: + (fd, pwfile) = tempfile.mkstemp() + os.write(fd, self.superuser['password'].encode()) + os.close(fd) + options.append('--pwfile={}'.format(pwfile)) ret = subprocess.call(self._pg_ctl + ['initdb'] + (['-o', ' '.join(options)] if options else [])) == 0 if pwfile: @@ -351,7 +356,10 @@ class Postgresql: if not block_callbacks: self.set_state('starting') - ret = subprocess.call(self._pg_ctl + ['start', '-o', self.server_options()]) == 0 + env = os.environ.copy() + if 'username' in self.superuser: + env['PGUSER'] = self.superuser['username'] + ret = subprocess.call(self._pg_ctl + ['start', '-o', self.server_options()], env=env) == 0 self.set_state('running' if ret else 'start failed') @@ -633,11 +641,8 @@ $$""".format(name, options), name, password, password) def create_replication_user(self): self.create_or_update_role(self.replication['username'], self.replication['password'], 'REPLICATION') - def create_connection_users(self): - if 'username' in self.superuser: - self.create_or_update_role(self.superuser['username'], self.superuser['password'], 'SUPERUSER') - if self.admin: - self.create_or_update_role(self.admin['username'], self.admin['password'], 'CREATEDB CREATEROLE') + def create_connection_user(self): + self.admin and self.create_or_update_role(self.admin['username'], self.admin['password'], 'CREATEDB CREATEROLE') def xlog_position(self): return self.query("""SELECT pg_xlog_location_diff(CASE WHEN pg_is_in_recovery() @@ -711,7 +716,7 @@ $$""".format(name, options), name, password, password) ret = self.initialize() and self.start() if ret: self.create_replication_user() - self.create_connection_users() + self.create_connection_user() else: raise PostgresException("Could not bootstrap master PostgreSQL") else: diff --git a/tests/test_postgresql.py b/tests/test_postgresql.py index a88f150c..3c93be44 100644 --- a/tests/test_postgresql.py +++ b/tests/test_postgresql.py @@ -165,7 +165,7 @@ class TestPostgresql(unittest.TestCase): self.p = Postgresql({'name': 'test0', 'scope': 'batman', 'data_dir': 'data/test0', 'listen': '127.0.0.1, *:5432', 'connect_address': '127.0.0.2:5432', 'pg_hba': ['hostssl all all 0.0.0.0/0 md5', 'host all all 0.0.0.0/0 md5'], - 'superuser': {'password': 'test'}, + 'superuser': {'username': 'test', 'password': 'test'}, 'admin': {'username': 'admin', 'password': 'admin'}, 'pg_rewind': {'username': 'admin', 'password': 'admin'}, 'replication': {'username': 'replicator', @@ -295,12 +295,6 @@ class TestPostgresql(unittest.TestCase): with patch('subprocess.call', Mock(side_effect=Exception("foo"))): self.assertEquals(self.p.create_replica(self.leader, ''), 1) - def test_create_connection_users(self): - cfg = self.p.config - cfg['superuser']['username'] = 'test' - p = Postgresql(cfg) - p.create_connection_users() - def test_sync_replication_slots(self): self.p.start() cluster = Cluster(True, self.leader, 0, [self.me, self.other, self.leadermem], None) From b4af126bc3f3de31b2aeaa789910c23435ff4bbd Mon Sep 17 00:00:00 2001 From: Alexander Kukushkin Date: Wed, 10 Feb 2016 13:50:05 +0100 Subject: [PATCH 2/2] Apply superuser name and password when doing checkpoint --- patroni/postgresql.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/patroni/postgresql.py b/patroni/postgresql.py index 9d46952f..4e60aa70 100644 --- a/patroni/postgresql.py +++ b/patroni/postgresql.py @@ -128,14 +128,18 @@ class Postgresql: break return local_address + ':' + self.port + @property + def _connect_kwargs(self): + r = parseurl('postgres://{}/postgres'.format(self.local_address)) + if 'username' in self.superuser: + r['user'] = self.superuser['username'] + if 'password' in self.superuser: + r['password'] = self.superuser['password'] + return r + def connection(self): if not self._connection or self._connection.closed != 0: - r = parseurl('postgres://{}/postgres'.format(self.local_address)) - if 'username' in self.superuser: - r['user'] = self.superuser['username'] - if 'password' in self.superuser: - r['password'] = self.superuser['password'] - self._connection = psycopg2.connect(**r) + self._connection = psycopg2.connect(**self._connect_kwargs) self._connection.autocommit = True self.server_version = self._connection.server_version return self._connection @@ -370,10 +374,12 @@ class Postgresql: ret and not block_callbacks and self.call_nowait(ACTION_ON_START) return ret - def checkpoint(self, connstring=None): + def checkpoint(self, connect_kwargs=None): + connect_kwargs = connect_kwargs or self._connect_kwargs + for p in ['connect_timeout', 'options']: + connect_kwargs.pop(p, None) try: - connstring = connstring or 'postgres://{}/postgres'.format(self.local_address) - with psycopg2.connect(connstring) as conn: + with psycopg2.connect(**connect_kwargs) as conn: conn.autocommit = True with conn.cursor() as cur: cur.execute("SET statement_timeout = 0") @@ -480,12 +486,12 @@ recovery_target_timeline = 'latest' # prepare pg_rewind connection r = parseurl(leader.conn_url) r.update(self.pg_rewind) - r['user'] = r['username'] + r['user'] = r.pop('username') env = self.write_pgpass(r) pc = "user={user} host={host} port={port} dbname=postgres sslmode=prefer sslcompression=1".format(**r) # first run a checkpoint on a promoted master in order # to make it store the new timeline (5540277D.8020309@iki.fi) - self.checkpoint(pc) + self.checkpoint(r) logger.info("running pg_rewind from {}".format(pc)) pg_rewind = ['pg_rewind', '-D', self.data_dir, '--source-server', pc] try: