Call initdb directly (#2633)

Previously it was called via `pg_ctl`, what required a special quoting of parameters passed to `initdb`.
Co-authored-by: Israel <[email protected]>
This commit is contained in:
Matt Baker
2023-04-24 09:35:39 +02:00
committed by GitHub
parent 4f458baa0e
commit ff6d728f07
5 changed files with 168 additions and 9 deletions
+57 -8
View File
@@ -3,10 +3,11 @@ import os
import shlex
import tempfile
import time
from typing import List, Dict, Union, Callable, Tuple
from ..dcs import RemoteMember
from ..psycopg import quote_ident, quote_literal
from ..utils import deep_compare
from ..utils import deep_compare, unquote
logger = logging.getLogger(__name__)
@@ -26,7 +27,56 @@ class Bootstrap(object):
return self._running_custom_bootstrap and self._keep_existing_recovery_conf
@staticmethod
def process_user_options(tool, options, not_allowed_options, error_handler):
def process_user_options(tool: str,
options: Union[Dict[str, str], List[Union[str, Dict[str, str]]]],
not_allowed_options: Tuple[str, ...],
error_handler: Callable[[str], None]) -> List:
"""Format *options* in a list or dictionary format into command line long form arguments.
.. note::
The format of the output of this method is to prepare arguments for use in the ``initdb``
method of `self._postgres`.
:Example:
The *options* can be defined as a dictionary of key, values to be converted into arguments:
>>> Bootstrap.process_user_options('foo', {'foo': 'bar'}, (), print)
['--foo=bar']
Or as a list of single string arguments
>>> Bootstrap.process_user_options('foo', ['yes'], (), print)
['--yes']
Or as a list of key, value options
>>> Bootstrap.process_user_options('foo', [{'foo': 'bar'}], (), print)
['--foo=bar']
Or a combination of single and key, values
>>> Bootstrap.process_user_options('foo', ['yes', {'foo': 'bar'}], (), print)
['--yes', '--foo=bar']
Options that contain spaces are passed as is to ``subprocess.call``
>>> Bootstrap.process_user_options('foo', [{'foo': 'bar baz'}], (), print)
['--foo=bar baz']
Options that are quoted will be unquoted, so the quotes aren't interpreted
literally by the postgres command
>>> Bootstrap.process_user_options('foo', [{'foo': '"bar baz"'}], (), print)
['--foo=bar baz']
.. note::
The *error_handler* is called when any of these conditions are met:
* Key, value dictionaries in the list form contains multiple keys.
* If a key is listed in *not_allowed_options*.
* If the options list is not in the required structure.
:param tool: The name of the tool used in error reports to *error_handler*
:param options: Options to parse as a list of key, values or single values, or a dictionary
:param not_allowed_options: List of keys that cannot be used in the list of key, value formatted options
:param error_handler: A function which will be called when an error condition is encountered
:returns: List of long form arguments to pass to the named tool
"""
user_options = []
def option_is_allowed(name):
@@ -36,9 +86,9 @@ class Bootstrap(object):
return ret
if isinstance(options, dict):
for k, v in options.items():
if k and v:
user_options.append('--{0}={1}'.format(k, v))
for key, val in options.items():
if key and val:
user_options.append('--{0}={1}'.format(key, unquote(val)))
elif isinstance(options, list):
for opt in options:
if isinstance(opt, str) and option_is_allowed(opt):
@@ -48,7 +98,7 @@ class Bootstrap(object):
if len(keys) != 1 or not isinstance(opt[keys[0]], str) or not option_is_allowed(keys[0]):
error_handler('Error when parsing {0} key-value option {1}: only one key-value is allowed'
' and value should be a string'.format(tool, opt[keys[0]]))
user_options.append('--{0}={1}'.format(keys[0], opt[keys[0]]))
user_options.append('--{0}={1}'.format(keys[0], unquote(opt[keys[0]])))
else:
error_handler('Error when parsing {0} option {1}: value should be string value'
' or a single key-value pair'.format(tool, opt))
@@ -74,9 +124,8 @@ class Bootstrap(object):
os.write(fd, self._postgresql.config.superuser['password'].encode('utf-8'))
os.close(fd)
options.append('--pwfile={0}'.format(pwfile))
options = ['-o', ' '.join(options)] if options else []
ret = self._postgresql.pg_ctl('initdb', *options)
ret = self._postgresql.initdb(*options)
if pwfile:
os.remove(pwfile)
if ret: