Skip to content

Commit 09d71ba

Browse files
author
v.shepard
committed
PBCKP-152 fix failed tests
1 parent ac77ef7 commit 09d71ba

12 files changed

+182
-75
lines changed

testgres/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@
4646
First, \
4747
Any
4848

49+
import operations
50+
from .operations.os_ops import OsOperations
51+
from .operations.local_ops import LocalOperations
52+
from .operations.remote_ops import RemoteOperations
53+
4954
__all__ = [
5055
"get_new_node",
5156
"NodeBackup",
@@ -56,4 +61,5 @@
5661
"PostgresNode", "NodeApp",
5762
"reserve_port", "release_port", "bound_ports", "get_bin_path", "get_pg_config", "get_pg_version",
5863
"First", "Any",
64+
"OsOperations", "LocalOperations", "RemoteOperations", "operations"
5965
]

testgres/cache.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44

55
from six import raise_from
66

7-
from .os_ops.local_ops import LocalOperations
8-
from .os_ops.os_ops import OsOperations
97
from .config import testgres_config
108

119
from .consts import XLOG_CONTROL_FILE
@@ -20,6 +18,9 @@
2018
get_bin_path, \
2119
execute_utility
2220

21+
from .operations.local_ops import LocalOperations
22+
from .operations.os_ops import OsOperations
23+
2324

2425
def cached_initdb(data_dir, logfile=None, params=None, os_ops: OsOperations = LocalOperations()):
2526
"""
@@ -38,7 +39,7 @@ def call_initdb(initdb_dir, log=None):
3839
call_initdb(data_dir, logfile)
3940
else:
4041
# Fetch cached initdb dir
41-
cached_data_dir = testgres_config.cached_initdb_dir()
42+
cached_data_dir = testgres_config.cached_initdb_dir
4243

4344
# Initialize cached initdb
4445

testgres/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66

77
from contextlib import contextmanager
88

9-
from .os_ops.local_ops import LocalOperations
109
from .consts import TMP_CACHE
10+
from testgres import LocalOperations
1111

1212

1313
class GlobalConfig(object):

testgres/connection.py

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -102,23 +102,15 @@ def rollback(self):
102102
return self
103103

104104
def execute(self, query, *args):
105+
self.cursor.execute(query, args)
105106
try:
106-
with self.connection.cursor() as cursor:
107-
cursor.execute(query, args)
108-
try:
109-
res = cursor.fetchall()
110-
111-
# pg8000 might return tuples
112-
if isinstance(res, tuple):
113-
res = [tuple(t) for t in res]
114-
115-
return res
116-
except (pglib.ProgrammingError, pglib.InternalError) as e:
117-
# An error occurred while trying to fetch results (e.g., no results to fetch)
118-
print(f"Error fetching results: {e}")
119-
return None
120-
except (pglib.Error, Exception) as e:
121-
# Handle other database errors
107+
res = self.cursor.fetchall()
108+
# pg8000 might return tuples
109+
if isinstance(res, tuple):
110+
res = [tuple(t) for t in res]
111+
112+
return res
113+
except Exception as e:
122114
print(f"Error executing query: {e}")
123115
return None
124116

testgres/defaults.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import struct
33
import uuid
44

5-
from .os_ops.local_ops import LocalOperations
5+
from .operations.local_ops import LocalOperations
66

77

88
def default_dbname():

testgres/node.py

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,6 @@
99
import psutil
1010
import time
1111

12-
from .os_ops.local_ops import LocalOperations
13-
from .os_ops.remote_ops import RemoteOperations
14-
1512
try:
1613
from collections.abc import Iterable
1714
except ImportError:
@@ -99,6 +96,9 @@
9996

10097
from .backup import NodeBackup
10198

99+
from .operations.local_ops import LocalOperations
100+
from .operations.remote_ops import RemoteOperations
101+
102102
InternalError = pglib.InternalError
103103
ProgrammingError = pglib.ProgrammingError
104104
OperationalError = pglib.OperationalError
@@ -201,7 +201,7 @@ def pid(self):
201201

202202
if self.status():
203203
pid_file = os.path.join(self.data_dir, PG_PID_FILE)
204-
lines = self.os_ops.readlines(pid_file, num_lines=1)
204+
lines = self.os_ops.readlines(pid_file)
205205
pid = int(lines[0]) if lines else None
206206
return pid
207207

@@ -433,7 +433,8 @@ def _collect_special_files(self):
433433
if not self.os_ops.path_exists(f):
434434
continue
435435

436-
lines = b''.join(self.os_ops.readlines(f, num_lines, encoding='utf-8'))
436+
file_lines = self.os_ops.readlines(f, num_lines, binary=True, encoding=None)
437+
lines = b''.join(file_lines)
437438

438439
# fill list
439440
result.append((f, lines))
@@ -498,7 +499,7 @@ def default_conf(self,
498499
]
499500

500501
# write filtered lines
501-
self.os_ops.write(hba_conf_file, lines, truncate=True)
502+
self.os_ops.write(hba_conf, lines, truncate=True)
502503

503504
# replication-related settings
504505
if allow_streaming:
@@ -960,11 +961,9 @@ def psql(self,
960961
psql_params.append(dbname)
961962

962963
# start psql process
963-
process = self.os_ops.exec_command(psql_params)
964+
status_code, out, err = self.os_ops.exec_command(psql_params, shell=False, verbose=True, input=input)
964965

965-
# wait until it finishes and get stdout and stderr
966-
out, err = process.communicate(input=input)
967-
return process.returncode, out, err
966+
return status_code, out, err
968967

969968
@method_decorator(positional_args_hack(['dbname', 'query']))
970969
def safe_psql(self, query=None, expect_error=False, **kwargs):
@@ -1348,7 +1347,7 @@ def pgbench(self,
13481347
# should be the last one
13491348
_params.append(dbname)
13501349

1351-
proc = self.os_ops.exec_command(_params, wait_exit=True)
1350+
proc = self.os_ops.exec_command(_params, stdout=stdout, stderr=stderr, wait_exit=True, shell=False, proc=True)
13521351

13531352
return proc
13541353

testgres/os_ops/local_ops.py renamed to testgres/operations/local_ops.py

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,25 @@ def __init__(self, username=None):
1919
self.username = username or self.get_user()
2020

2121
# Command execution
22-
def exec_command(self, cmd, wait_exit=False, verbose=False, expect_error=False):
23-
if isinstance(cmd, list):
24-
cmd = " ".join(cmd)
22+
def exec_command(self, cmd, wait_exit=False, verbose=False,
23+
expect_error=False, encoding=None, shell=True, text=False,
24+
input=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, proc=None):
2525
log.debug(f"os_ops.exec_command: `{cmd}`; remote={self.remote}")
2626
# Source global profile file + execute command
2727
try:
28+
if proc:
29+
return subprocess.Popen(cmd,
30+
shell=shell,
31+
stdin=input or subprocess.PIPE,
32+
stdout=stdout,
33+
stderr=stderr)
2834
process = subprocess.run(
2935
cmd,
30-
shell=True,
31-
text=True,
32-
stdout=subprocess.PIPE,
33-
stderr=subprocess.PIPE,
36+
input=input,
37+
shell=shell,
38+
text=text,
39+
stdout=stdout,
40+
stderr=stderr,
3441
timeout=CMD_TIMEOUT_SEC,
3542
)
3643
exit_status = process.returncode
@@ -39,11 +46,11 @@ def exec_command(self, cmd, wait_exit=False, verbose=False, expect_error=False):
3946

4047
if expect_error:
4148
raise Exception(result, error)
42-
if exit_status != 0 or "error" in error.lower():
49+
if exit_status != 0 or "error" in error.lower().decode(encoding or 'utf-8'): # Decode error for comparison
4350
log.error(
44-
f"Problem in executing command: `{cmd}`\nerror: {error}\nexit_code: {exit_status}"
51+
f"Problem in executing command: `{cmd}`\nerror: {error.decode(encoding or 'utf-8')}\nexit_code: {exit_status}"
52+
# Decode for logging
4553
)
46-
exit(1)
4754

4855
if verbose:
4956
return exit_status, result, error
@@ -152,9 +159,9 @@ def write(self, filename, data, truncate=False, binary=False, read_and_write=Fal
152159
"""
153160
mode = "wb" if binary else "w"
154161
if not truncate:
155-
mode = "a" + mode
162+
mode = "ab" if binary else "a"
156163
if read_and_write:
157-
mode = "r+" + mode
164+
mode = "r+b" if binary else "r+"
158165

159166
with open(filename, mode) as file:
160167
if isinstance(data, list):
@@ -174,26 +181,26 @@ def touch(self, filename):
174181
with open(filename, "a"):
175182
os.utime(filename, None)
176183

177-
def read(self, filename):
178-
with open(filename, "r") as file:
184+
def read(self, filename, encoding=None):
185+
with open(filename, "r", encoding=encoding) as file:
179186
return file.read()
180187

181-
def readlines(self, filename, num_lines=0, encoding=None):
188+
def readlines(self, filename, num_lines=0, binary=False, encoding=None):
182189
"""
183190
Read lines from a local file.
184191
If num_lines is greater than 0, only the last num_lines lines will be read.
185192
"""
186193
assert num_lines >= 0
187-
194+
mode = 'rb' if binary else 'r'
188195
if num_lines == 0:
189-
with open(filename, "r", encoding=encoding) as file:
196+
with open(filename, mode, encoding=encoding) as file: # open in binary mode
190197
return file.readlines()
191198

192199
else:
193200
bufsize = 8192
194201
buffers = 1
195202

196-
with open(filename, "r", encoding=encoding) as file:
203+
with open(filename, mode, encoding=encoding) as file: # open in binary mode
197204
file.seek(0, os.SEEK_END)
198205
end_pos = file.tell()
199206

@@ -205,7 +212,7 @@ def readlines(self, filename, num_lines=0, encoding=None):
205212
cur_lines = len(lines)
206213

207214
if cur_lines >= num_lines or pos == 0:
208-
return lines[-num_lines:]
215+
return lines[-num_lines:] # get last num_lines from lines
209216

210217
buffers = int(
211218
buffers * max(2, int(num_lines / max(cur_lines, 1)))
File renamed without changes.

testgres/os_ops/remote_ops.py renamed to testgres/operations/remote_ops.py

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import io
12
import os
23
import tempfile
34
from contextlib import contextmanager
@@ -65,30 +66,41 @@ def connect(self):
6566
return ssh
6667

6768
# Command execution
68-
def exec_command(
69-
self, cmd, wait_exit=False, verbose=False, expect_error=False, encoding="utf-8"
70-
):
69+
def exec_command(self, cmd, wait_exit=False, verbose=False,
70+
expect_error=False, encoding=None, shell=True, text=False,
71+
input=None, stdout=None, stderr=None, proc=None):
7172
if isinstance(cmd, list):
7273
cmd = " ".join(cmd)
7374
log.debug(f"os_ops.exec_command: `{cmd}`; remote={self.remote}")
7475
# Source global profile file + execute command
7576
try:
7677
cmd = f"source /etc/profile.d/custom.sh; {cmd}"
7778
with self.ssh_connect() as ssh:
78-
stdin, stdout, stderr = ssh.exec_command(cmd)
79+
if input:
80+
# encode input and feed it to stdin
81+
stdin, stdout, stderr = ssh.exec_command(cmd)
82+
stdin.write(input)
83+
stdin.flush()
84+
else:
85+
stdin, stdout, stderr = ssh.exec_command(cmd)
7986
exit_status = 0
8087
if wait_exit:
8188
exit_status = stdout.channel.recv_exit_status()
82-
result = stdout.read().decode(encoding)
83-
error = stderr.read().decode(encoding)
89+
if encoding:
90+
result = stdout.read().decode(encoding)
91+
error = stderr.read().decode(encoding)
92+
else:
93+
# Save as binary string
94+
result = io.BytesIO(stdout.read()).getvalue()
95+
error = io.BytesIO(stderr.read()).getvalue()
96+
error_str = stderr.read()
8497

8598
if expect_error:
8699
raise Exception(result, error)
87-
if exit_status != 0 or "error" in error.lower():
100+
if exit_status != 0 or 'error' in error_str:
88101
log.error(
89102
f"Problem in executing command: `{cmd}`\nerror: {error}\nexit_code: {exit_status}"
90103
)
91-
exit(1)
92104

93105
if verbose:
94106
return exit_status, result, error
@@ -203,9 +215,9 @@ def write(self, filename, data, truncate=False, binary=False, read_and_write=Fal
203215
"""
204216
mode = "wb" if binary else "w"
205217
if not truncate:
206-
mode = "a" + mode
218+
mode = "ab" if binary else "a"
207219
if read_and_write:
208-
mode = "r+" + mode
220+
mode = "r+b" if binary else "r+"
209221

210222
with tempfile.NamedTemporaryFile(mode=mode) as tmp_file:
211223
if isinstance(data, list):
@@ -229,17 +241,28 @@ def touch(self, filename):
229241
"""
230242
self.exec_command(f"touch {filename}")
231243

232-
def read(self, filename, encoding="utf-8"):
244+
def read(self, filename, binary=False, encoding=None):
233245
cmd = f"cat {filename}"
234-
return self.exec_command(cmd, encoding=encoding)
246+
result = self.exec_command(cmd, encoding=encoding)
247+
248+
if not binary and result:
249+
result = result.decode(encoding or 'utf-8')
250+
251+
return result
235252

236-
def readlines(self, filename, num_lines=0, encoding=None):
237-
encoding = encoding or "utf-8"
253+
def readlines(self, filename, num_lines=0, binary=False, encoding=None):
238254
if num_lines > 0:
239255
cmd = f"tail -n {num_lines} {filename}"
240-
lines = self.exec_command(cmd, encoding)
241256
else:
242-
lines = self.read(filename, encoding=encoding).splitlines()
257+
cmd = f"cat {filename}"
258+
259+
result = self.exec_command(cmd, encoding=encoding)
260+
261+
if not binary and result:
262+
lines = result.decode(encoding or 'utf-8').splitlines()
263+
else:
264+
lines = result.splitlines()
265+
243266
return lines
244267

245268
def isfile(self, remote_file):

testgres/utils.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,17 @@
1313
from contextlib import contextmanager
1414
from packaging.version import Version
1515

16-
from .os_ops.remote_ops import RemoteOperations
17-
1816
try:
1917
from shutil import which as find_executable
2018
except ImportError:
2119
from distutils.spawn import find_executable
2220
from six import iteritems
2321

2422
from fabric import Connection
25-
from .os_ops.local_ops import LocalOperations
26-
from .os_ops.os_ops import OsOperations
23+
24+
from .operations.remote_ops import RemoteOperations
25+
from .operations.local_ops import LocalOperations
26+
from .operations.os_ops import OsOperations
2727

2828
from .config import testgres_config
2929
from .exceptions import ExecUtilException

0 commit comments

Comments
 (0)