mirror of
https://github.com/outbackdingo/patroni.git
synced 2026-08-25 14:53:37 +00:00
Add config_dir option for configuration files location (#466)
On debian, the configuration files (postgresql.conf, pg_hba.conf, etc) are not stored in the data directory. It would be great to be able to configure the location of this separate directory. Patroni could override existing configuration files where they are used to be. The default is to store configuration files in the data directory. This setting is targeting custom installations like debian and any others moving configuration files out of the data directory. Fixes #465
This commit is contained in:
committed by
Alexander Kukushkin
parent
b60f65a2ca
commit
4ca94a5dab
@@ -46,6 +46,7 @@ PostgreSQL
|
|||||||
- **PATRONI\_POSTGRESQL\_LISTEN**: IP address + port that Postgres listens to. Multiple comma-separated addresses are permitted, as long as the port component is appended after to the last one with a colon, i.e. ``listen: 127.0.0.1,127.0.0.2:5432``. Patroni will use the first address from this list to establish local connections to the PostgreSQL node.
|
- **PATRONI\_POSTGRESQL\_LISTEN**: IP address + port that Postgres listens to. Multiple comma-separated addresses are permitted, as long as the port component is appended after to the last one with a colon, i.e. ``listen: 127.0.0.1,127.0.0.2:5432``. Patroni will use the first address from this list to establish local connections to the PostgreSQL node.
|
||||||
- **PATRONI\_POSTGRESQL\_CONNECT\_ADDRESS**: IP address + port through which Postgres is accessible from other nodes and applications.
|
- **PATRONI\_POSTGRESQL\_CONNECT\_ADDRESS**: IP address + port through which Postgres is accessible from other nodes and applications.
|
||||||
- **PATRONI\_POSTGRESQL\_DATA\_DIR**: The location of the Postgres data directory, either existing or to be initialized by Patroni.
|
- **PATRONI\_POSTGRESQL\_DATA\_DIR**: The location of the Postgres data directory, either existing or to be initialized by Patroni.
|
||||||
|
- **PATRONI\_POSTGRESQL\_CONFIG\_DIR**: The location of the Postgres configuration directory, defaults to the data directory. Must be writable by Patroni.
|
||||||
- **PATRONI\_POSTGRESQL\_BIN_DIR**: Path to PostgreSQL binaries. (pg_ctl, pg_rewind, pg_basebackup, postgres) The default value is an empty string meaning that PATH environment variable will be used to find the executables.
|
- **PATRONI\_POSTGRESQL\_BIN_DIR**: Path to PostgreSQL binaries. (pg_ctl, pg_rewind, pg_basebackup, postgres) The default value is an empty string meaning that PATH environment variable will be used to find the executables.
|
||||||
- **PATRONI\_POSTGRESQL\_PGPASS**: path to the `.pgpass <https://www.postgresql.org/docs/current/static/libpq-pgpass.html>`__ password file. Patroni creates this file before executing pg\_basebackup and under some other circumstances. The location must be writable by Patroni.
|
- **PATRONI\_POSTGRESQL\_PGPASS**: path to the `.pgpass <https://www.postgresql.org/docs/current/static/libpq-pgpass.html>`__ password file. Patroni creates this file before executing pg\_basebackup and under some other circumstances. The location must be writable by Patroni.
|
||||||
- **PATRONI\_REPLICATION\_USERNAME**: replication username; the user will be created during initialization. Replicas will use this user to access master via streaming replication
|
- **PATRONI\_REPLICATION\_USERNAME**: replication username; the user will be created during initialization. Replicas will use this user to access master via streaming replication
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ PostgreSQL
|
|||||||
- **connect\_address**: IP address + port through which Postgres is accessible from other nodes and applications.
|
- **connect\_address**: IP address + port through which Postgres is accessible from other nodes and applications.
|
||||||
- **create\_replica\_methods**: an ordered list of the create methods for turning a Patroni node into a new replica. "basebackup" is the default method; other methods are assumed to refer to scripts, each of which is configured as its own config item.
|
- **create\_replica\_methods**: an ordered list of the create methods for turning a Patroni node into a new replica. "basebackup" is the default method; other methods are assumed to refer to scripts, each of which is configured as its own config item.
|
||||||
- **data\_dir**: The location of the Postgres data directory, either existing or to be initialized by Patroni.
|
- **data\_dir**: The location of the Postgres data directory, either existing or to be initialized by Patroni.
|
||||||
|
- **config\_dir**: The location of the Postgres configuration directory, defaults to the data directory. Must be writable by Patroni.
|
||||||
- **bin\_dir**: Path to PostgreSQL binaries. (pg_ctl, pg_rewind, pg_basebackup, postgres) The default value is an empty string meaning that PATH environment variable will be used to find the executables.
|
- **bin\_dir**: Path to PostgreSQL binaries. (pg_ctl, pg_rewind, pg_basebackup, postgres) The default value is an empty string meaning that PATH environment variable will be used to find the executables.
|
||||||
- **listen**: IP address + port that Postgres listens to; must be accessible from other nodes in the cluster, if you're using streaming replication. Multiple comma-separated addresses are permitted, as long as the port component is appended after to the last one with a colon, i.e. ``listen: 127.0.0.1,127.0.0.2:5432``. Patroni will use the first address from this list to establish local connections to the PostgreSQL node.
|
- **listen**: IP address + port that Postgres listens to; must be accessible from other nodes in the cluster, if you're using streaming replication. Multiple comma-separated addresses are permitted, as long as the port component is appended after to the last one with a colon, i.e. ``listen: 127.0.0.1,127.0.0.2:5432``. Patroni will use the first address from this list to establish local connections to the PostgreSQL node.
|
||||||
- **use\_unix\_socket**: specifies that Patroni should prefer to use unix sockets to connect to the cluster. Default value is ``false``. If ``unix_socket_directories`` is definded, Patroni will use first suitable value from it to connect to the cluster and fallback to tcp if nothing is suitable. If ``unix_socket_directories`` is not specified in ``postgresql.parameters``, Patroni will assume that default value should be used and omit ``host`` from connection parameters.
|
- **use\_unix\_socket**: specifies that Patroni should prefer to use unix sockets to connect to the cluster. Default value is ``false``. If ``unix_socket_directories`` is definded, Patroni will use first suitable value from it to connect to the cluster and fallback to tcp if nothing is suitable. If ``unix_socket_directories`` is not specified in ``postgresql.parameters``, Patroni will assume that default value should be used and omit ``host`` from connection parameters.
|
||||||
|
|||||||
+22
-13
@@ -99,6 +99,7 @@ class Postgresql(object):
|
|||||||
self._bin_dir = config.get('bin_dir') or ''
|
self._bin_dir = config.get('bin_dir') or ''
|
||||||
self._database = config.get('database', 'postgres')
|
self._database = config.get('database', 'postgres')
|
||||||
self._data_dir = config['data_dir']
|
self._data_dir = config['data_dir']
|
||||||
|
self._config_dir = config.get('config_dir') or self._data_dir
|
||||||
self._pending_restart = False
|
self._pending_restart = False
|
||||||
self.__thread_ident = current_thread().ident
|
self.__thread_ident = current_thread().ident
|
||||||
|
|
||||||
@@ -120,9 +121,9 @@ class Postgresql(object):
|
|||||||
self.__cb_called = False
|
self.__cb_called = False
|
||||||
self.__cb_pending = None
|
self.__cb_pending = None
|
||||||
config_base_name = config.get('config_base_name', 'postgresql')
|
config_base_name = config.get('config_base_name', 'postgresql')
|
||||||
self._postgresql_conf = os.path.join(self._data_dir, config_base_name + '.conf')
|
self._postgresql_conf = os.path.join(self._config_dir, config_base_name + '.conf')
|
||||||
self._postgresql_base_conf_name = config_base_name + '.base.conf'
|
self._postgresql_base_conf_name = config_base_name + '.base.conf'
|
||||||
self._postgresql_base_conf = os.path.join(self._data_dir, self._postgresql_base_conf_name)
|
self._postgresql_base_conf = os.path.join(self._config_dir, self._postgresql_base_conf_name)
|
||||||
self._recovery_conf = os.path.join(self._data_dir, 'recovery.conf')
|
self._recovery_conf = os.path.join(self._data_dir, 'recovery.conf')
|
||||||
self._postmaster_pid = os.path.join(self._data_dir, 'postmaster.pid')
|
self._postmaster_pid = os.path.join(self._data_dir, 'postmaster.pid')
|
||||||
self._trigger_file = config.get('recovery_conf', {}).get('trigger_file') or 'promote'
|
self._trigger_file = config.get('recovery_conf', {}).get('trigger_file') or 'promote'
|
||||||
@@ -162,11 +163,11 @@ class Postgresql(object):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def _configuration_to_save(self):
|
def _configuration_to_save(self):
|
||||||
configuration = [self._postgresql_conf]
|
configuration = [os.path.basename(self._postgresql_conf)]
|
||||||
if 'custom_conf' not in self.config:
|
if 'custom_conf' not in self.config:
|
||||||
configuration.append(self._postgresql_base_conf)
|
configuration.append(os.path.basename(self._postgresql_base_conf))
|
||||||
if not self.config['parameters'].get('hba_file'):
|
if not self.config['parameters'].get('hba_file'):
|
||||||
configuration.append(os.path.join(self._data_dir, 'pg_hba.conf'))
|
configuration.append('pg_hba.conf')
|
||||||
return configuration
|
return configuration
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -826,8 +827,9 @@ class Postgresql(object):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
start_initiated = time.time()
|
start_initiated = time.time()
|
||||||
proc = call_self(['pg_ctl_start', self._pgcommand('postgres'), '-D', self._data_dir] + options,
|
proc = call_self(['pg_ctl_start', self._pgcommand('postgres'), '-D', self._data_dir,
|
||||||
close_fds=True, preexec_fn=os.setsid, stdout=subprocess.PIPE,
|
'--config-file={}'.format(self._postgresql_conf)] + options, close_fds=True,
|
||||||
|
preexec_fn=os.setsid, stdout=subprocess.PIPE,
|
||||||
env={p: os.environ[p] for p in ('PATH', 'LC_ALL', 'LANG') if p in os.environ})
|
env={p: os.environ[p] for p in ('PATH', 'LC_ALL', 'LANG') if p in os.environ})
|
||||||
pid = int(proc.stdout.readline().strip())
|
pid = int(proc.stdout.readline().strip())
|
||||||
proc.wait()
|
proc.wait()
|
||||||
@@ -1060,6 +1062,9 @@ class Postgresql(object):
|
|||||||
with open(self._postgresql_conf, 'w') as f:
|
with open(self._postgresql_conf, 'w') as f:
|
||||||
f.write('# Do not edit this file manually!\n# It will be overwritten by Patroni!\n')
|
f.write('# Do not edit this file manually!\n# It will be overwritten by Patroni!\n')
|
||||||
f.write("include '{0}'\n\n".format(self.config.get('custom_conf') or self._postgresql_base_conf_name))
|
f.write("include '{0}'\n\n".format(self.config.get('custom_conf') or self._postgresql_base_conf_name))
|
||||||
|
f.write("data_directory = '{}'\n".format(self._data_dir))
|
||||||
|
f.write("hba_file = '{}'\n".format(os.path.join(self._config_dir, 'pg_hba.conf')))
|
||||||
|
f.write("ident_file = '{}'\n".format(os.path.join(self._config_dir, 'pg_ident.conf')))
|
||||||
for name, value in sorted(self._server_parameters.items()):
|
for name, value in sorted(self._server_parameters.items()):
|
||||||
f.write("{0} = '{1}'\n".format(name, value))
|
f.write("{0} = '{1}'\n".format(name, value))
|
||||||
|
|
||||||
@@ -1070,7 +1075,7 @@ class Postgresql(object):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def write_pg_hba(self, config):
|
def write_pg_hba(self, config):
|
||||||
with open(os.path.join(self._data_dir, 'pg_hba.conf'), 'a') as f:
|
with open(os.path.join(self._config_dir, 'pg_hba.conf'), 'a') as f:
|
||||||
f.write('\n{}\n'.format('\n'.join(config)))
|
f.write('\n{}\n'.format('\n'.join(config)))
|
||||||
|
|
||||||
def _replace_pg_hba(self):
|
def _replace_pg_hba(self):
|
||||||
@@ -1081,7 +1086,7 @@ class Postgresql(object):
|
|||||||
:returns: True if pg_hba.conf was rewritten.
|
:returns: True if pg_hba.conf was rewritten.
|
||||||
"""
|
"""
|
||||||
if not self.config['parameters'].get('hba_file') and self.config.get('pg_hba'):
|
if not self.config['parameters'].get('hba_file') and self.config.get('pg_hba'):
|
||||||
with open(os.path.join(self._data_dir, 'pg_hba.conf'), 'w') as f:
|
with open(os.path.join(self._config_dir, 'pg_hba.conf'), 'w') as f:
|
||||||
f.write('# Do not edit this file manually!\n# It will be overwritten by Patroni!\n')
|
f.write('# Do not edit this file manually!\n# It will be overwritten by Patroni!\n')
|
||||||
for line in self.config['pg_hba']:
|
for line in self.config['pg_hba']:
|
||||||
f.write('{0}\n'.format(line))
|
f.write('{0}\n'.format(line))
|
||||||
@@ -1366,8 +1371,10 @@ class Postgresql(object):
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
for f in self._configuration_to_save:
|
for f in self._configuration_to_save:
|
||||||
if os.path.isfile(f):
|
config_file = os.path.join(self._config_dir, f)
|
||||||
shutil.copy(f, f + '.backup')
|
backup_file = os.path.join(self._data_dir, f + '.backup')
|
||||||
|
if os.path.isfile(config_file):
|
||||||
|
shutil.copy(config_file, backup_file)
|
||||||
except IOError:
|
except IOError:
|
||||||
logger.exception('unable to create backup copies of configuration files')
|
logger.exception('unable to create backup copies of configuration files')
|
||||||
|
|
||||||
@@ -1375,8 +1382,10 @@ class Postgresql(object):
|
|||||||
""" restore a previously saved postgresql.conf """
|
""" restore a previously saved postgresql.conf """
|
||||||
try:
|
try:
|
||||||
for f in self._configuration_to_save:
|
for f in self._configuration_to_save:
|
||||||
if not os.path.isfile(f) and os.path.isfile(f + '.backup'):
|
config_file = os.path.join(self._config_dir, f)
|
||||||
shutil.copy(f + '.backup', f)
|
backup_file = os.path.join(self._data_dir, f + '.backup')
|
||||||
|
if not os.path.isfile(config_file) and os.path.isfile(backup_file):
|
||||||
|
shutil.copy(backup_file, config_file)
|
||||||
except IOError:
|
except IOError:
|
||||||
logger.exception('unable to restore configuration files from backup')
|
logger.exception('unable to restore configuration files from backup')
|
||||||
|
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ postgresql:
|
|||||||
connect_address: 127.0.0.1:5432
|
connect_address: 127.0.0.1:5432
|
||||||
data_dir: data/postgresql0
|
data_dir: data/postgresql0
|
||||||
# bin_dir:
|
# bin_dir:
|
||||||
|
# config_dir:
|
||||||
pgpass: /tmp/pgpass0
|
pgpass: /tmp/pgpass0
|
||||||
authentication:
|
authentication:
|
||||||
replication:
|
replication:
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ postgresql:
|
|||||||
connect_address: 127.0.0.1:5433
|
connect_address: 127.0.0.1:5433
|
||||||
data_dir: data/postgresql1
|
data_dir: data/postgresql1
|
||||||
# bin_dir:
|
# bin_dir:
|
||||||
|
# config_dir:
|
||||||
pgpass: /tmp/pgpass1
|
pgpass: /tmp/pgpass1
|
||||||
authentication:
|
authentication:
|
||||||
replication:
|
replication:
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ postgresql:
|
|||||||
connect_address: 127.0.0.1:5434
|
connect_address: 127.0.0.1:5434
|
||||||
data_dir: data/postgresql2
|
data_dir: data/postgresql2
|
||||||
# bin_dir:
|
# bin_dir:
|
||||||
|
# config_dir:
|
||||||
pgpass: /tmp/pgpass2
|
pgpass: /tmp/pgpass2
|
||||||
authentication:
|
authentication:
|
||||||
replication:
|
replication:
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ class TestConfig(unittest.TestCase):
|
|||||||
'PATRONI_POSTGRESQL_LISTEN': '0.0.0.0:5432',
|
'PATRONI_POSTGRESQL_LISTEN': '0.0.0.0:5432',
|
||||||
'PATRONI_POSTGRESQL_CONNECT_ADDRESS': '127.0.0.1:5432',
|
'PATRONI_POSTGRESQL_CONNECT_ADDRESS': '127.0.0.1:5432',
|
||||||
'PATRONI_POSTGRESQL_DATA_DIR': 'data/postgres0',
|
'PATRONI_POSTGRESQL_DATA_DIR': 'data/postgres0',
|
||||||
|
'PATRONI_POSTGRESQL_CONFIG_DIR': 'data/postgres0',
|
||||||
'PATRONI_POSTGRESQL_PGPASS': '/tmp/pgpass0',
|
'PATRONI_POSTGRESQL_PGPASS': '/tmp/pgpass0',
|
||||||
'PATRONI_ETCD_HOST': '127.0.0.1:2379',
|
'PATRONI_ETCD_HOST': '127.0.0.1:2379',
|
||||||
'PATRONI_ETCD_URL': 'https://127.0.0.1:2379',
|
'PATRONI_ETCD_URL': 'https://127.0.0.1:2379',
|
||||||
|
|||||||
@@ -168,9 +168,11 @@ class TestPostgresql(unittest.TestCase):
|
|||||||
@patch.object(Postgresql, 'is_running', Mock(return_value=True))
|
@patch.object(Postgresql, 'is_running', Mock(return_value=True))
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.data_dir = 'data/test0'
|
self.data_dir = 'data/test0'
|
||||||
|
self.config_dir = self.data_dir
|
||||||
if not os.path.exists(self.data_dir):
|
if not os.path.exists(self.data_dir):
|
||||||
os.makedirs(self.data_dir)
|
os.makedirs(self.data_dir)
|
||||||
self.p = Postgresql({'name': 'test0', 'scope': 'batman', 'data_dir': self.data_dir, 'retry_timeout': 10,
|
self.p = Postgresql({'name': 'test0', 'scope': 'batman', 'data_dir': self.data_dir,
|
||||||
|
'config_dir': self.config_dir, 'retry_timeout': 10,
|
||||||
'listen': '127.0.0.2, 127.0.0.3:5432', 'connect_address': '127.0.0.2:5432',
|
'listen': '127.0.0.2, 127.0.0.3:5432', 'connect_address': '127.0.0.2:5432',
|
||||||
'authentication': {'superuser': {'username': 'test', 'password': 'test'},
|
'authentication': {'superuser': {'username': 'test', 'password': 'test'},
|
||||||
'replication': {'username': 'replicator', 'password': 'rep-pass'}},
|
'replication': {'username': 'replicator', 'password': 'rep-pass'}},
|
||||||
@@ -502,7 +504,7 @@ class TestPostgresql(unittest.TestCase):
|
|||||||
config = {'users': {'replicator': {'password': 'rep-pass', 'options': ['replication']}}}
|
config = {'users': {'replicator': {'password': 'rep-pass', 'options': ['replication']}}}
|
||||||
|
|
||||||
self.p.bootstrap(config)
|
self.p.bootstrap(config)
|
||||||
with open(os.path.join(self.data_dir, 'pg_hba.conf')) as f:
|
with open(os.path.join(self.config_dir, 'pg_hba.conf')) as f:
|
||||||
lines = f.readlines()
|
lines = f.readlines()
|
||||||
self.assertTrue('host all all 0.0.0.0/0 md5\n' in lines)
|
self.assertTrue('host all all 0.0.0.0/0 md5\n' in lines)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user