From f5fe166eec93c2b727b4ee0145eecdf77d9b38d6 Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Thu, 20 Feb 2025 14:23:34 +0300 Subject: [PATCH 1/6] RemoteOperations::exec_command updated - Exact enumeration of supported 'cmd' types - Refactoring --- testgres/operations/remote_ops.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/testgres/operations/remote_ops.py b/testgres/operations/remote_ops.py index f690e063..a24fce50 100644 --- a/testgres/operations/remote_ops.py +++ b/testgres/operations/remote_ops.py @@ -78,14 +78,17 @@ def exec_command(self, cmd, wait_exit=False, verbose=False, expect_error=False, assert input_prepared is None or (type(input_prepared) == bytes) # noqa: E721 - ssh_cmd = [] - if isinstance(cmd, str): - ssh_cmd = ['ssh', self.ssh_dest] + self.ssh_args + [cmd] - elif isinstance(cmd, list): - ssh_cmd = ['ssh', self.ssh_dest] + self.ssh_args + [subprocess.list2cmdline(cmd)] + if type(cmd) == str: # noqa: E721 + cmd_s = cmd + elif type(cmd) == list: # noqa: E721 + cmd_s = subprocess.list2cmdline(cmd) else: raise ValueError("Invalid 'cmd' argument type - {0}".format(type(cmd).__name__)) + assert type(cmd_s) == str # noqa: E721 + + ssh_cmd = ['ssh', self.ssh_dest] + self.ssh_args + [cmd_s] + process = subprocess.Popen(ssh_cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) assert not (process is None) if get_process: From 2b1db89c0375c1ec3a130e31fa46ab8b7339141e Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Thu, 20 Feb 2025 20:24:09 +0300 Subject: [PATCH 2/6] RemoteOperations::exec_command explicitly transfers LANG, LANGUAGE and LC_* envvars to the server side It should help resolve a problem with replacing a LANG variable by ssh-server. History. On our internal tests we got a problem on the Debian 11 and PostgresPro STD-13. One test returned the error from initdb: initdb: error: collations with different collate and ctype values ("en_US.UTF-8" and "C.UTF-8" accordingly) are not supported by ICU - TestRunner set variable LANG="C" - Python set variable LC_CTYPE="C.UTF-8" - Test call inidb through command "ssh test@localhost inidb -D ...." - SSH-server replaces LANG with value "en_US.UTF-8" (from etc/default/locale) - initdb calculate collate through this value of LANG variable and get en_US.UTF-8 So we have that: - ctype is C.UTF-8 - collate is en_US.UTF-8 ICU on the Debuan-11 (uconv v2.1 ICU 67.1) does not suppot this combination and inidb rturns the error. This patch generates a new command line for ssh: ssh test@localhost "LANG=\"...\";LC_xxx=\"...\";" It resolves this problem with initdb and should help resolve other problems with execution of command through SSH. Amen. --- testgres/operations/remote_ops.py | 43 ++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/testgres/operations/remote_ops.py b/testgres/operations/remote_ops.py index a24fce50..b4d65e95 100644 --- a/testgres/operations/remote_ops.py +++ b/testgres/operations/remote_ops.py @@ -87,7 +87,12 @@ def exec_command(self, cmd, wait_exit=False, verbose=False, expect_error=False, assert type(cmd_s) == str # noqa: E721 - ssh_cmd = ['ssh', self.ssh_dest] + self.ssh_args + [cmd_s] + cmd_items = __class__._make_exec_env_list() + cmd_items.append(cmd_s) + + env_cmd_s = ';'.join(cmd_items) + + ssh_cmd = ['ssh', self.ssh_dest] + self.ssh_args + [env_cmd_s] process = subprocess.Popen(ssh_cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) assert not (process is None) @@ -510,6 +515,42 @@ def db_connect(self, dbname, user, password=None, host="localhost", port=5432): ) return conn + def _make_exec_env_list() -> list[str]: + result = list[str]() + for envvar in os.environ.items(): + if not __class__._does_put_envvar_into_exec_cmd(envvar[0]): + continue + qvalue = __class__._quote_envvar(envvar[1]) + assert type(qvalue) == str # noqa: E721 + result.append(envvar[0] + "=" + qvalue) + continue + + return result + + sm_envs_for_exec_cmd = ["LANG", "LANGUAGE"] + + def _does_put_envvar_into_exec_cmd(name: str) -> bool: + assert type(name) == str # noqa: E721 + name = name.upper() + if name.startswith("LC_"): + return True + if name in __class__.sm_envs_for_exec_cmd: + return True + return False + + def _quote_envvar(value) -> str: + assert type(value) == str # noqa: E721 + result = "\"" + for ch in value: + if ch == "\"": + result += "\\\"" + elif ch == "\\": + result += "\\\\" + else: + result += ch + result += "\"" + return result + def normalize_error(error): if isinstance(error, bytes): From 4eb833040f2f72e93e07a866b52c123e5003cc5d Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Fri, 21 Feb 2025 15:15:50 +0300 Subject: [PATCH 3/6] New tests in TestgresRemoteTests are added MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New tests: - test_init__LANG_С - test_init__unk_LANG_and_LC_CTYPE --- tests/test_simple_remote.py | 56 +++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/tests/test_simple_remote.py b/tests/test_simple_remote.py index c8dd2964..dd110962 100755 --- a/tests/test_simple_remote.py +++ b/tests/test_simple_remote.py @@ -119,6 +119,56 @@ def test_custom_init(self): # there should be no trust entries at all self.assertFalse(any('trust' in s for s in lines)) + def test_init__LANG_С(self): + # PBCKP-1744 + prev_LANG = os.environ.get("LANG") + + try: + os.environ["LANG"] = "C" + + with get_remote_node(conn_params=conn_params) as node: + node.init().start() + finally: + __class__.helper__restore_envvar("LANG", prev_LANG) + + def test_init__unk_LANG_and_LC_CTYPE(self): + # PBCKP-1744 + prev_LANG = os.environ.get("LANG") + prev_LANGUAGE = os.environ.get("LANGUAGE") + prev_LC_CTYPE = os.environ.get("LC_CTYPE") + prev_LC_COLLATE = os.environ.get("LC_COLLATE") + + try: + os.environ["LANG"] = "UNKNOWN_LANG" + os.environ.pop("LANGUAGE", None) + os.environ["LC_CTYPE"] = "UNKNOWN_CTYPE" + os.environ.pop("LC_COLLATE", None) + + assert os.environ.get("LANG") == "UNKNOWN_LANG" + assert not ("LANGUAGE" in os.environ.keys()) + assert os.environ.get("LC_CTYPE") == "UNKNOWN_CTYPE" + assert not ("LC_COLLATE" in os.environ.keys()) + + while True: + try: + with get_remote_node(conn_params=conn_params): + pass + except testgres.exceptions.ExecUtilException as e: + # warning: setlocale: LC_CTYPE: cannot change locale (UNKNOWN_CTYPE): No such file or directory + # postgres (PostgreSQL) 14.12 + errMsg = str(e) + assert "LC_CTYPE" in errMsg + assert "UNKNOWN_CTYPE" in errMsg + assert "warning: setlocale: LC_CTYPE: cannot change locale (UNKNOWN_CTYPE): No such file or directory" in errMsg + assert "postgres" in errMsg + break + raise Exception("We expected an error!") + finally: + __class__.helper__restore_envvar("LANG", prev_LANG) + __class__.helper__restore_envvar("LANGUAGE", prev_LANGUAGE) + __class__.helper__restore_envvar("LC_CTYPE", prev_LC_CTYPE) + __class__.helper__restore_envvar("LC_COLLATE", prev_LC_COLLATE) + def test_double_init(self): with get_remote_node(conn_params=conn_params).init() as node: # can't initialize node more than once @@ -994,6 +1044,12 @@ def test_child_process_dies(self): # try to handle children list -- missing processes will have ptype "ProcessType.Unknown" [ProcessProxy(p) for p in children] + def helper__restore_envvar(name, prev_value): + if prev_value is None: + os.environ.pop(name, None) + else: + os.environ[name] = prev_value + if __name__ == '__main__': if os_ops.environ('ALT_CONFIG'): From f4dc0623954582a7d5c75b028138ad08d297c6f0 Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Fri, 21 Feb 2025 17:59:13 +0300 Subject: [PATCH 4/6] TestgresRemoteTests.test_init__unk_LANG_and_LC_CTYPE is updated Let's test bad data with '\' and '"' symbols. --- tests/test_simple_remote.py | 71 ++++++++++++++++++++++++------------- 1 file changed, 47 insertions(+), 24 deletions(-) diff --git a/tests/test_simple_remote.py b/tests/test_simple_remote.py index dd110962..2b581ac9 100755 --- a/tests/test_simple_remote.py +++ b/tests/test_simple_remote.py @@ -139,30 +139,53 @@ def test_init__unk_LANG_and_LC_CTYPE(self): prev_LC_COLLATE = os.environ.get("LC_COLLATE") try: - os.environ["LANG"] = "UNKNOWN_LANG" - os.environ.pop("LANGUAGE", None) - os.environ["LC_CTYPE"] = "UNKNOWN_CTYPE" - os.environ.pop("LC_COLLATE", None) - - assert os.environ.get("LANG") == "UNKNOWN_LANG" - assert not ("LANGUAGE" in os.environ.keys()) - assert os.environ.get("LC_CTYPE") == "UNKNOWN_CTYPE" - assert not ("LC_COLLATE" in os.environ.keys()) - - while True: - try: - with get_remote_node(conn_params=conn_params): - pass - except testgres.exceptions.ExecUtilException as e: - # warning: setlocale: LC_CTYPE: cannot change locale (UNKNOWN_CTYPE): No such file or directory - # postgres (PostgreSQL) 14.12 - errMsg = str(e) - assert "LC_CTYPE" in errMsg - assert "UNKNOWN_CTYPE" in errMsg - assert "warning: setlocale: LC_CTYPE: cannot change locale (UNKNOWN_CTYPE): No such file or directory" in errMsg - assert "postgres" in errMsg - break - raise Exception("We expected an error!") + # TODO: Pass unkData through test parameter. + unkDatas = [ + ("UNKNOWN_LANG", "UNKNOWN_CTYPE"), + ("\"UNKNOWN_LANG\"", "\"UNKNOWN_CTYPE\""), + ("\\UNKNOWN_LANG\\", "\\UNKNOWN_CTYPE\\"), + ("\"UNKNOWN_LANG", "UNKNOWN_CTYPE\""), + ("\\UNKNOWN_LANG", "UNKNOWN_CTYPE\\"), + ("\\", "\\"), + ("\"", "\""), + ] + + for unkData in unkDatas: + logging.info("----------------------") + logging.info("Unk LANG is [{0}]".format(unkData[0])) + logging.info("Unk LC_CTYPE is [{0}]".format(unkData[1])) + + os.environ["LANG"] = unkData[0] + os.environ.pop("LANGUAGE", None) + os.environ["LC_CTYPE"] = unkData[1] + os.environ.pop("LC_COLLATE", None) + + assert os.environ.get("LANG") == unkData[0] + assert not ("LANGUAGE" in os.environ.keys()) + assert os.environ.get("LC_CTYPE") == unkData[1] + assert not ("LC_COLLATE" in os.environ.keys()) + + while True: + try: + with get_remote_node(conn_params=conn_params): + pass + except testgres.exceptions.ExecUtilException as e: + # + # Example of an error message: + # + # warning: setlocale: LC_CTYPE: cannot change locale (UNKNOWN_CTYPE): No such file or directory + # postgres (PostgreSQL) 14.12 + # + errMsg = str(e) + + logging.info("Error message is: {0}".format(errMsg)) + + assert "LC_CTYPE" in errMsg + assert unkData[1] in errMsg + assert "warning: setlocale: LC_CTYPE: cannot change locale (" + unkData[1] + "): No such file or directory" in errMsg + assert "postgres" in errMsg + break + raise Exception("We expected an error!") finally: __class__.helper__restore_envvar("LANG", prev_LANG) __class__.helper__restore_envvar("LANGUAGE", prev_LANGUAGE) From ee78bcd2fc7002a1c3b5936425b3709c49d136aa Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Fri, 21 Feb 2025 19:09:43 +0300 Subject: [PATCH 5/6] Static methods are marked with @staticmethod [thanks to Victoria Shepard] The following methods of RemoteOperations were corrected: - _make_exec_env_list - _does_put_envvar_into_exec_cmd - _quote_envvar --- testgres/operations/remote_ops.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/testgres/operations/remote_ops.py b/testgres/operations/remote_ops.py index b4d65e95..f368740b 100644 --- a/testgres/operations/remote_ops.py +++ b/testgres/operations/remote_ops.py @@ -515,6 +515,7 @@ def db_connect(self, dbname, user, password=None, host="localhost", port=5432): ) return conn + @staticmethod def _make_exec_env_list() -> list[str]: result = list[str]() for envvar in os.environ.items(): @@ -529,6 +530,7 @@ def _make_exec_env_list() -> list[str]: sm_envs_for_exec_cmd = ["LANG", "LANGUAGE"] + @staticmethod def _does_put_envvar_into_exec_cmd(name: str) -> bool: assert type(name) == str # noqa: E721 name = name.upper() @@ -538,6 +540,7 @@ def _does_put_envvar_into_exec_cmd(name: str) -> bool: return True return False + @staticmethod def _quote_envvar(value) -> str: assert type(value) == str # noqa: E721 result = "\"" From 5564938c8af955f2f6bde67c57ca003d759c94ce Mon Sep 17 00:00:00 2001 From: "d.kovalenko" Date: Fri, 21 Feb 2025 19:13:28 +0300 Subject: [PATCH 6/6] TestRemoteOperations::_quote_envvar is updated (typification) --- testgres/operations/remote_ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testgres/operations/remote_ops.py b/testgres/operations/remote_ops.py index f368740b..af4c59f9 100644 --- a/testgres/operations/remote_ops.py +++ b/testgres/operations/remote_ops.py @@ -541,7 +541,7 @@ def _does_put_envvar_into_exec_cmd(name: str) -> bool: return False @staticmethod - def _quote_envvar(value) -> str: + def _quote_envvar(value: str) -> str: assert type(value) == str # noqa: E721 result = "\"" for ch in value: pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy