From 9a26b18e2a149474ef5b386d05f2945822186aba Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Mon, 2 Feb 2026 17:03:39 +0300 Subject: [PATCH 1/4] os_ops v2.1.0 us used --- pyproject.toml | 2 +- tests/requirements.txt | 2 +- tests/test_os_ops_common.py | 14 ++++++++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 3cbc771e..fa9206a6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -62,7 +62,7 @@ dependencies = [ "six>=1.9.0", "psutil", "packaging", - "testgres.os_ops>=2.0.2,<3.0.0", + "testgres.os_ops>=2.1.0,<3.0.0", ] [project.urls] diff --git a/tests/requirements.txt b/tests/requirements.txt index 3d5d1f6e..7cafbb32 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -4,4 +4,4 @@ pytest-env pytest-xdist psycopg2 six -testgres.os_ops>=2.0.2,<3.0.0 +testgres.os_ops>=2.1.0,<3.0.0 diff --git a/tests/test_os_ops_common.py b/tests/test_os_ops_common.py index 38055ca2..d530f523 100644 --- a/tests/test_os_ops_common.py +++ b/tests/test_os_ops_common.py @@ -42,6 +42,20 @@ def os_ops(self, request: pytest.FixtureRequest) -> OsOperations: assert isinstance(request.param, OsOperations) return request.param + def test_get_platform(self, os_ops: OsOperations): + assert isinstance(os_ops, OsOperations) + p = os_ops.get_platform() + assert p is not None + assert type(p) == str # noqa: E721 + assert p == sys.platform + + def test_get_platform__is_known(self, os_ops: OsOperations): + assert isinstance(os_ops, OsOperations) + p = os_ops.get_platform() + assert p is not None + assert type(p) == str # noqa: E721 + assert p in {"win32", "linux"} + def test_create_clone(self, os_ops: OsOperations): assert isinstance(os_ops, OsOperations) clone = os_ops.create_clone() From ee4e74a55d4db7f0b860ba80e9ea39e29d35a287 Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Tue, 3 Feb 2026 00:35:53 +0300 Subject: [PATCH 2/4] [#329] get_pg_node_state looks for postmaster to avoid "PID file is empty" error This patch suggests a solution to fix an error "PID file is empty" during getting a server state data. When pg_ctl returns this error, we are trying to find postmaster process for our data directory. If we find it then we wait a few seconds and do the next attempt to get server state data. Closes #329. --- src/impl/internal_utils.py | 20 ++ src/impl/platforms/internal_platform_utils.py | 62 +++++ .../internal_platform_utils_factory.py | 23 ++ .../linux/internal_platform_utils.py | 112 +++++++++ .../win32/internal_platform_utils.py | 17 ++ src/utils.py | 234 ++++++++++++------ tests/test_testgres_common.py | 78 ++++++ tests/units/impl/__init__.py | 0 tests/units/impl/platforms/__init__.py | 0 .../InternalPlatformUtils/__init__.py | 0 .../internal_platform_utils/__init__.py | 0 11 files changed, 470 insertions(+), 76 deletions(-) create mode 100644 src/impl/internal_utils.py create mode 100644 src/impl/platforms/internal_platform_utils.py create mode 100644 src/impl/platforms/internal_platform_utils_factory.py create mode 100644 src/impl/platforms/linux/internal_platform_utils.py create mode 100644 src/impl/platforms/win32/internal_platform_utils.py create mode 100644 tests/units/impl/__init__.py create mode 100644 tests/units/impl/platforms/__init__.py create mode 100644 tests/units/impl/platforms/internal_platform_utils/InternalPlatformUtils/__init__.py create mode 100644 tests/units/impl/platforms/internal_platform_utils/__init__.py diff --git a/src/impl/internal_utils.py b/src/impl/internal_utils.py new file mode 100644 index 00000000..dae7ccd3 --- /dev/null +++ b/src/impl/internal_utils.py @@ -0,0 +1,20 @@ +import logging + + +def send_log(level: int, msg: str) -> None: + assert type(level) == int # noqa: E721 + assert type(msg) == str # noqa: E721 + + return logging.log(level, "[testgres] " + msg) + + +def send_log_info(msg: str) -> None: + assert type(msg) == str # noqa: E721 + + return send_log(logging.INFO, msg) + + +def send_log_debug(msg: str) -> None: + assert type(msg) == str # noqa: E721 + + return send_log(logging.DEBUG, msg) diff --git a/src/impl/platforms/internal_platform_utils.py b/src/impl/platforms/internal_platform_utils.py new file mode 100644 index 00000000..021753f7 --- /dev/null +++ b/src/impl/platforms/internal_platform_utils.py @@ -0,0 +1,62 @@ +from __future__ import annotations + +import enum +import typing + +from testgres.operations.os_ops import OsOperations + + +class InternalPlatformUtils: + class FindPostmasterResultCode(enum.Enum): + ok = 0 + not_found = 1, + not_implemented = 2 + many_processes = 3 + has_problems = 4 + + class FindPostmasterResult: + code: InternalPlatformUtils.FindPostmasterResultCode + pid: typing.Optional[int] + + def __init__( + self, + code: InternalPlatformUtils.FindPostmasterResultCode, + pid: typing.Optional[int] + ): + assert type(code) == InternalPlatformUtils.FindPostmasterResultCode # noqa: E721 + assert pid is None or type(pid) == int # noqa: E721 + self.code = code + self.pid = pid + return + + @staticmethod + def create_ok(pid: int) -> InternalPlatformUtils.FindPostmasterResult: + assert type(pid) == int # noqa: E721 + return __class__(InternalPlatformUtils.FindPostmasterResultCode.ok, pid) + + @staticmethod + def create_not_found() -> InternalPlatformUtils.FindPostmasterResult: + return __class__(InternalPlatformUtils.FindPostmasterResultCode.not_found, None) + + @staticmethod + def create_not_implemented() -> InternalPlatformUtils.FindPostmasterResult: + return __class__(InternalPlatformUtils.FindPostmasterResultCode.not_implemented, None) + + @staticmethod + def create_many_processes() -> InternalPlatformUtils.FindPostmasterResult: + return __class__(InternalPlatformUtils.FindPostmasterResultCode.many_processes, None) + + @staticmethod + def create_has_problems() -> InternalPlatformUtils.FindPostmasterResult: + return __class__(InternalPlatformUtils.FindPostmasterResultCode.has_problems, None) + + def FindPostmaster( + self, + os_ops: OsOperations, + bin_dir: str, + data_dir: str + ) -> FindPostmasterResult: + assert isinstance(os_ops, OsOperations) + assert type(bin_dir) == str # noqa: E721 + assert type(data_dir) == str # noqa: E721 + raise NotImplementedError("InternalPlatformUtils::FindPostmaster is not implemented.") diff --git a/src/impl/platforms/internal_platform_utils_factory.py b/src/impl/platforms/internal_platform_utils_factory.py new file mode 100644 index 00000000..d34fde92 --- /dev/null +++ b/src/impl/platforms/internal_platform_utils_factory.py @@ -0,0 +1,23 @@ +from .internal_platform_utils import InternalPlatformUtils + +from testgres.operations.os_ops import OsOperations + + +def create_internal_platform_utils( + os_ops: OsOperations +) -> InternalPlatformUtils: + assert isinstance(os_ops, OsOperations) + + platform_name = os_ops.get_platform() + assert type(platform_name) == str # noqa: E721 + + if platform_name == "linux": + from .linux import internal_platform_utils as x + return x.InternalPlatformUtils() + + if platform_name == "win32": + from .win32 import internal_platform_utils as x + return x.InternalPlatformUtils() + + # not implemented + return InternalPlatformUtils() diff --git a/src/impl/platforms/linux/internal_platform_utils.py b/src/impl/platforms/linux/internal_platform_utils.py new file mode 100644 index 00000000..02580c6d --- /dev/null +++ b/src/impl/platforms/linux/internal_platform_utils.py @@ -0,0 +1,112 @@ +from __future__ import annotations + +from .. import internal_platform_utils as base +from ... import internal_utils + +from testgres.operations.os_ops import OsOperations +from testgres.operations.exceptions import ExecUtilException + +import re +import shlex + + +class InternalPlatformUtils(base.InternalPlatformUtils): + def FindPostmaster( + self, + os_ops: OsOperations, + bin_dir: str, + data_dir: str + ) -> InternalPlatformUtils.FindPostmasterResult: + assert isinstance(os_ops, OsOperations) + assert type(bin_dir) == str # noqa: E721 + assert type(data_dir) == str # noqa: E721 + + pg_path_e = re.escape(os_ops.build_path(bin_dir, "postgres")) + data_dir_e = re.escape(data_dir) + + assert type(pg_path_e) == str # noqa: E721 + assert type(data_dir_e) == str # noqa: E721 + + exec_env = { + "LANG": "en_US.UTF-8", + "LC_ALL": "en_US.UTF-8", + } + + regexp = r"^\s*[0-9]+\s+" + pg_path_e + r"(\s+.*)?\s+\-[D]\s+" + data_dir_e + r"(\s+.*)?" + + cmd = [ + "/bin/bash", + "-c", + "ps -ewwo \"pid=,args=\" | grep -E " + shlex.quote(regexp), + ] + + exit_status, output_b, error_b = os_ops.exec_command( + cmd=cmd, + ignore_errors=True, + verbose=True, + exec_env=exec_env, + ) + + assert type(output_b) == bytes # noqa: E721 + assert type(error_b) == bytes # noqa: E721 + + output = output_b.decode("utf-8") + error = error_b.decode("utf-8") + + assert type(output) == str # noqa: E721 + assert type(error) == str # noqa: E721 + + if exit_status == 1: + return __class__.FindPostmasterResult.create_not_found() + + if exit_status != 0: + errMsg = f"test command returned an unexpected exit code: {exit_status}" + raise ExecUtilException( + message=errMsg, + command=cmd, + exit_code=exit_status, + out=output, + error=error, + ) + + lines = output.splitlines() + assert type(lines) == list # noqa: E721 + + if len(lines) == 0: + return __class__.FindPostmasterResult.create_not_found() + + if len(lines) > 1: + msgs = [] + msgs.append("Many processes like a postmaster are found: {}.".format(len(lines))) + + for i in range(len(lines)): + assert type(lines[i]) == str # noqa: E721 + lines.append("[{}] '{}'".format(i, lines[i])) + continue + + internal_utils.send_log_debug("\n".join(lines)) + return __class__.FindPostmasterResult.create_many_processes() + + def is_space_or_tab(ch) -> bool: + assert type(ch) == str # noqa: E721 + return ch == " " or ch == "\t" + + line = lines[0] + start = 0 + while start < len(line) and is_space_or_tab(line[start]): + start += 1 + + pos = start + while pos < len(line) and line[pos].isnumeric(): + pos += 1 + + if pos == start: + return __class__.FindPostmasterResult.create_has_problems() + + if pos != len(line) and not line[pos].isspace(): + return __class__.FindPostmasterResult.create_has_problems() + + pid = int(line[start:pos]) + assert type(pid) == int # noqa: E721 + + return __class__.FindPostmasterResult.create_ok(pid) diff --git a/src/impl/platforms/win32/internal_platform_utils.py b/src/impl/platforms/win32/internal_platform_utils.py new file mode 100644 index 00000000..661bd234 --- /dev/null +++ b/src/impl/platforms/win32/internal_platform_utils.py @@ -0,0 +1,17 @@ +from __future__ import annotations + +from .. import internal_platform_utils as base +from testgres.operations.os_ops import OsOperations + + +class InternalPlatformUtils(base.InternalPlatformUtils): + def FindPostmaster( + self, + os_ops: OsOperations, + bin_dir: str, + data_dir: str + ) -> InternalPlatformUtils.FindPostmasterResult: + assert isinstance(os_ops, OsOperations) + assert type(bin_dir) == str # noqa: E721 + assert type(data_dir) == str # noqa: E721 + return __class__.FindPostmasterResult.create_not_implemented() diff --git a/src/utils.py b/src/utils.py index f716152a..dd9268de 100644 --- a/src/utils.py +++ b/src/utils.py @@ -4,8 +4,8 @@ from __future__ import print_function import os - import sys +import time from contextlib import contextmanager from packaging.version import Version, InvalidVersion @@ -28,6 +28,9 @@ from .impl.port_manager__generic import PortManager__Generic +from .impl.platforms import internal_platform_utils_factory +from .impl import internal_utils + # rows returned by PG_CONFIG _pg_config_data = {} @@ -351,6 +354,10 @@ def get_pg_node_state( assert type(data_dir) == str # noqa: E721 assert utils_log_file is None or type(utils_log_file) == str # noqa: E721 + C_MAX_ATTEMPTS = 3 + C_SLEEP_TIME1 = 1 + C_SLEEP_TIME_MULT = 2 + _params = [ os_ops.build_path(bin_dir, "pg_ctl"), "-D", @@ -358,102 +365,177 @@ def get_pg_node_state( "status", ] - status_code, out, error = execute_utility2( - os_ops, - _params, - utils_log_file, - verbose=True, - ignore_errors=True, - ) + attempt = 0 + sleep_time = C_SLEEP_TIME1 - assert type(status_code) == int # noqa: E721 - assert type(out) == str # noqa: E721 - assert type(error) == str # noqa: E721 + platform_utils: typing.Optional[internal_platform_utils_factory.InternalPlatformUtils] = None - # ----------------- - if status_code == PG_CTL__STATUS__NODE_IS_STOPPED: - return PostgresNodeState(NodeStatus.Stopped, None) + while True: + assert type(attempt) == int # noqa: E721 + assert attempt >= 0 + assert attempt < C_MAX_ATTEMPTS + + attempt += 1 + + if attempt > 1: + internal_utils.send_log_debug("Sleep {} second(s) before an attempt #{}".format( + sleep_time, + attempt + )) + time.sleep(sleep_time) + sleep_time = sleep_time * C_SLEEP_TIME_MULT + + status_code, out, error = execute_utility2( + os_ops, + _params, + utils_log_file, + verbose=True, + ignore_errors=True, + ) - # ----------------- - if status_code == PG_CTL__STATUS__BAD_DATADIR: - return PostgresNodeState(NodeStatus.Uninitialized, None) + assert type(status_code) == int # noqa: E721 + assert type(out) == str # noqa: E721 + assert type(error) == str # noqa: E721 - # ----------------- - if status_code != PG_CTL__STATUS__OK: - errMsg = "Getting of a node status [data_dir is {0}] failed.".format( - data_dir - ) + # ----------------- + if status_code == PG_CTL__STATUS__NODE_IS_STOPPED: + return PostgresNodeState(NodeStatus.Stopped, None) - raise ExecUtilException( - message=errMsg, - command=_params, - exit_code=status_code, - out=out, - error=error, - ) + # ----------------- + if status_code == PG_CTL__STATUS__BAD_DATADIR: + return PostgresNodeState(NodeStatus.Uninitialized, None) - if out == "": - RaiseError.pg_ctl_returns_an_empty_string( - _params - ) + # ----------------- + if status_code == PG_CTL__STATUS__OK: + if out == "": + RaiseError.pg_ctl_returns_an_empty_string( + _params + ) - C_PID_PREFIX = "(PID: " + C_PID_PREFIX = "(PID: " - i = out.find(C_PID_PREFIX) + i = out.find(C_PID_PREFIX) - if i == -1: - RaiseError.pg_ctl_returns_an_unexpected_string( - out, - _params - ) + if i == -1: + RaiseError.pg_ctl_returns_an_unexpected_string( + out, + _params + ) - assert i > 0 - assert i < len(out) - assert len(C_PID_PREFIX) <= len(out) - assert i <= len(out) - len(C_PID_PREFIX) + assert i > 0 + assert i < len(out) + assert len(C_PID_PREFIX) <= len(out) + assert i <= len(out) - len(C_PID_PREFIX) - i += len(C_PID_PREFIX) - start_pid_s = i + i += len(C_PID_PREFIX) + start_pid_s = i - while True: - if i == len(out): - RaiseError.pg_ctl_returns_an_unexpected_string( - out, - _params - ) + while True: + if i == len(out): + RaiseError.pg_ctl_returns_an_unexpected_string( + out, + _params + ) - ch = out[i] + ch = out[i] - if ch == ")": - break + if ch == ")": + break - if ch.isdigit(): - i += 1 - continue + if ch.isdigit(): + i += 1 + continue - RaiseError.pg_ctl_returns_an_unexpected_string( - out, - _params - ) - assert False + RaiseError.pg_ctl_returns_an_unexpected_string( + out, + _params + ) + assert False + + if i == start_pid_s: + RaiseError.pg_ctl_returns_an_unexpected_string( + out, + _params + ) + + # TODO: Let's verify a length of pid string. + + pid = int(out[start_pid_s:i]) - if i == start_pid_s: - RaiseError.pg_ctl_returns_an_unexpected_string( - out, - _params + if pid == 0: + RaiseError.pg_ctl_returns_a_zero_pid( + out, + _params + ) + + assert pid != 0 + + # ----------------- + return PostgresNodeState(NodeStatus.Running, pid) + + assert status_code != PG_CTL__STATUS__OK + + errMsg = "Getting of a node status [data_dir is {0}] failed.".format( + data_dir ) - # TODO: Let's verify a length of pid string. + e1 = ExecUtilException( + message=errMsg, + command=_params, + exit_code=status_code, + out=out, + error=error, + ) - pid = int(out[start_pid_s:i]) + pid_file = os_ops.build_path(data_dir, "postmaster.pid") - if pid == 0: - RaiseError.pg_ctl_returns_a_zero_pid( - out, - _params + postmaster_pid_is_empty = "pg_ctl: the PID file \"{}\" is empty\n".format( + pid_file, ) - assert pid != 0 + if error == postmaster_pid_is_empty: + internal_utils.send_log_debug( + "PID file [{}] is empty. A check is being carried out to ensure that the postmaster is alive [bindir: {}] ...".format( + pid_file, + bin_dir, + )) + + if platform_utils is None: + platform_utils = internal_platform_utils_factory.create_internal_platform_utils(os_ops) + assert isinstance(platform_utils, internal_platform_utils_factory.InternalPlatformUtils) + + assert isinstance(platform_utils, internal_platform_utils_factory.InternalPlatformUtils) + + try: + find_postmaster_r = platform_utils.FindPostmaster( + os_ops, + bin_dir, + data_dir, + ) + except Exception as e2: + e2.__cause__ = e1 + raise e2 + + assert type(find_postmaster_r) == internal_platform_utils_factory.InternalPlatformUtils.FindPostmasterResult # noqa: E721 + + if find_postmaster_r.code == internal_platform_utils_factory.InternalPlatformUtils.FindPostmasterResultCode.ok: + # Postmaster is alive. Let's wait a few seconds and check its status again. + internal_utils.send_log_debug( + "Postmaster is found and has PID {}.".format( + find_postmaster_r.pid + )) + + if attempt < C_MAX_ATTEMPTS: + continue - # ----------------- - return PostgresNodeState(NodeStatus.Running, pid) + errMsg = "Getting of a node status [data_dir is {0}] failed.".format( + data_dir + ) + + raise ExecUtilException( + message=errMsg, + command=_params, + exit_code=status_code, + out=out, + error=error, + ) diff --git a/tests/test_testgres_common.py b/tests/test_testgres_common.py index e8d1f322..3bc178d2 100644 --- a/tests/test_testgres_common.py +++ b/tests/test_testgres_common.py @@ -416,6 +416,84 @@ def test_status(self, node_svc: PostgresNodeService): assert (node.pid == 0) assert (node.status() == NodeStatus.Uninitialized) + def test_status__empty_postmaster_pid(self, node_svc: PostgresNodeService): + assert isinstance(node_svc, PostgresNodeService) + + assert (NodeStatus.Running) + assert not (NodeStatus.Stopped) + assert not (NodeStatus.Uninitialized) + + # check statuses after each operation + with __class__.helper__get_node(node_svc) as node: + assert (node.pid == 0) + assert (node.status() == NodeStatus.Uninitialized) + + node.init() + + postmaster_pid_file = node.os_ops.build_path(node.data_dir, "postmaster.pid") + + node.os_ops.write( + postmaster_pid_file, + "" + ) + + with pytest.raises(expected_exception=ExecUtilException) as x: + node.status() + + expected_msg = "pg_ctl: the PID file \"{}\" is empty\n".format( + postmaster_pid_file + ) + + assert expected_msg == x.value.error + return + + def test_status__force_clean_postmaster_pid(self, node_svc: PostgresNodeService): + assert isinstance(node_svc, PostgresNodeService) + + assert (NodeStatus.Running) + assert not (NodeStatus.Stopped) + assert not (NodeStatus.Uninitialized) + + # check statuses after each operation + with __class__.helper__get_node(node_svc) as node: + assert (node.pid == 0) + assert (node.status() == NodeStatus.Uninitialized) + + node.init() + node.start() + + assert node.status() == NodeStatus.Running + logging.info("Postmaster PID is {}.".format(node.pid)) + + postmaster_pid_file = node.os_ops.build_path(node.data_dir, "postmaster.pid") + + logging.info("Clean postmaster pid file [{}].".format( + postmaster_pid_file + )) + + node.os_ops.write( + postmaster_pid_file, + "", + truncate=True, + ) + + x = node.os_ops.read( + postmaster_pid_file, + encoding="utf-8", + binary=False + ) + assert x == "" + + with pytest.raises(expected_exception=ExecUtilException) as x: + node.status() + + expected_msg = "pg_ctl: the PID file \"{}\" is empty\n".format( + postmaster_pid_file + ) + + assert expected_msg == x.value.error + return + def test_kill__is_not_initialized( self, node_svc: PostgresNodeService diff --git a/tests/units/impl/__init__.py b/tests/units/impl/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/units/impl/platforms/__init__.py b/tests/units/impl/platforms/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/units/impl/platforms/internal_platform_utils/InternalPlatformUtils/__init__.py b/tests/units/impl/platforms/internal_platform_utils/InternalPlatformUtils/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/units/impl/platforms/internal_platform_utils/__init__.py b/tests/units/impl/platforms/internal_platform_utils/__init__.py new file mode 100644 index 00000000..e69de29b From 1b7f2ce8f487415db5db48b963064a983a9365eb Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Tue, 3 Feb 2026 00:37:22 +0300 Subject: [PATCH 3/4] run_tests.sh is updated (-vvv) --- run_tests.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/run_tests.sh b/run_tests.sh index d0192275..ebb02490 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -26,21 +26,21 @@ rm -f $COVERAGE_FILE pip install coverage # run tests (PATH) -time coverage run -a -m pytest -l -v -n 4 -k "${TEST_FILTER}" +time coverage run -a -m pytest -l -vvv -n 4 -k "${TEST_FILTER}" # run tests (PG_BIN) PG_BIN=$(pg_config --bindir) \ -time coverage run -a -m pytest -l -v -n 4 -k "${TEST_FILTER}" +time coverage run -a -m pytest -l -vvv -n 4 -k "${TEST_FILTER}" # run tests (PG_CONFIG) PG_CONFIG=$(pg_config --bindir)/pg_config \ -time coverage run -a -m pytest -l -v -n 4 -k "${TEST_FILTER}" +time coverage run -a -m pytest -l -vvv -n 4 -k "${TEST_FILTER}" # test pg8000 pip uninstall -y psycopg2 pip install pg8000 PG_CONFIG=$(pg_config --bindir)/pg_config \ -time coverage run -a -m pytest -l -v -n 4 -k "${TEST_FILTER}" +time coverage run -a -m pytest -l -vvv -n 4 -k "${TEST_FILTER}" # show coverage coverage report From fe9eb967d8eacfd240f9b8f6d8f49f7017dded2b Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Tue, 3 Feb 2026 10:23:27 +0300 Subject: [PATCH 4/4] linux.InternalPlatformUtils is refactored (normalization) --- .../linux/internal_platform_utils.py | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/impl/platforms/linux/internal_platform_utils.py b/src/impl/platforms/linux/internal_platform_utils.py index 02580c6d..86eb197d 100644 --- a/src/impl/platforms/linux/internal_platform_utils.py +++ b/src/impl/platforms/linux/internal_platform_utils.py @@ -11,6 +11,14 @@ class InternalPlatformUtils(base.InternalPlatformUtils): + C_BASH_EXE = "/bin/bash" + + sm_exec_env = { + "LANG": "en_US.UTF-8", + "LC_ALL": "en_US.UTF-8", + } + + # -------------------------------------------------------------------- def FindPostmaster( self, os_ops: OsOperations, @@ -20,6 +28,11 @@ def FindPostmaster( assert isinstance(os_ops, OsOperations) assert type(bin_dir) == str # noqa: E721 assert type(data_dir) == str # noqa: E721 + assert type(__class__.C_BASH_EXE) == str # noqa: E721 + assert type(__class__.sm_exec_env) == dict # noqa: E721 + assert len(__class__.C_BASH_EXE) > 0 + assert len(bin_dir) > 0 + assert len(data_dir) > 0 pg_path_e = re.escape(os_ops.build_path(bin_dir, "postgres")) data_dir_e = re.escape(data_dir) @@ -27,15 +40,10 @@ def FindPostmaster( assert type(pg_path_e) == str # noqa: E721 assert type(data_dir_e) == str # noqa: E721 - exec_env = { - "LANG": "en_US.UTF-8", - "LC_ALL": "en_US.UTF-8", - } - regexp = r"^\s*[0-9]+\s+" + pg_path_e + r"(\s+.*)?\s+\-[D]\s+" + data_dir_e + r"(\s+.*)?" cmd = [ - "/bin/bash", + __class__.C_BASH_EXE, "-c", "ps -ewwo \"pid=,args=\" | grep -E " + shlex.quote(regexp), ] @@ -44,7 +52,7 @@ def FindPostmaster( cmd=cmd, ignore_errors=True, verbose=True, - exec_env=exec_env, + exec_env=__class__.sm_exec_env, ) assert type(output_b) == bytes # noqa: E721