testgres 1.16.1__tar.gz → 1.16.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {testgres-1.16.1/testgres.egg-info → testgres-1.16.3}/PKG-INFO +2 -3
- {testgres-1.16.1 → testgres-1.16.3}/pyproject.toml +1 -2
- {testgres-1.16.1 → testgres-1.16.3}/src/__init__.py +1 -1
- {testgres-1.16.1 → testgres-1.16.3}/src/backup.py +2 -4
- {testgres-1.16.1 → testgres-1.16.3}/src/cache.py +3 -5
- {testgres-1.16.1 → testgres-1.16.3}/src/config.py +1 -1
- {testgres-1.16.1 → testgres-1.16.3}/src/connection.py +1 -1
- {testgres-1.16.1 → testgres-1.16.3}/src/consts.py +3 -0
- {testgres-1.16.1 → testgres-1.16.3}/src/decorators.py +2 -3
- {testgres-1.16.1 → testgres-1.16.3}/src/enums.py +1 -2
- {testgres-1.16.1 → testgres-1.16.3}/src/exceptions.py +2 -5
- testgres-1.16.3/src/impl/internal_node_utils.py +221 -0
- {testgres-1.16.1 → testgres-1.16.3}/src/impl/internal_utils.py +58 -0
- testgres-1.16.3/src/impl/platforms/darwin/internal_platform_utils.py +456 -0
- {testgres-1.16.1 → testgres-1.16.3}/src/impl/platforms/internal_platform_utils.py +6 -4
- {testgres-1.16.1 → testgres-1.16.3}/src/impl/platforms/internal_platform_utils_factory.py +4 -0
- testgres-1.16.3/src/impl/platforms/internal_platform_utils_provider.py +32 -0
- {testgres-1.16.1 → testgres-1.16.3}/src/impl/platforms/linux/internal_platform_utils.py +15 -7
- {testgres-1.16.1 → testgres-1.16.3}/src/impl/platforms/win32/internal_platform_utils.py +1 -1
- {testgres-1.16.1 → testgres-1.16.3}/src/node.py +13 -21
- {testgres-1.16.1 → testgres-1.16.3}/src/node_app.py +1 -1
- testgres-1.16.3/src/node_state.py +20 -0
- {testgres-1.16.1 → testgres-1.16.3}/src/port_manager.py +5 -2
- {testgres-1.16.1 → testgres-1.16.3}/src/pubsub.py +3 -5
- {testgres-1.16.1 → testgres-1.16.3}/src/raise_error.py +14 -0
- {testgres-1.16.1 → testgres-1.16.3}/src/standby.py +6 -8
- {testgres-1.16.1 → testgres-1.16.3}/src/utils.py +17 -271
- {testgres-1.16.1 → testgres-1.16.3/testgres.egg-info}/PKG-INFO +2 -3
- {testgres-1.16.1 → testgres-1.16.3}/testgres.egg-info/SOURCES.txt +4 -0
- testgres-1.16.3/testgres.egg-info/requires.txt +5 -0
- {testgres-1.16.1 → testgres-1.16.3}/tests/test_os_ops_common.py +181 -10
- {testgres-1.16.1 → testgres-1.16.3}/tests/test_testgres_common.py +258 -31
- testgres-1.16.1/testgres.egg-info/requires.txt +0 -6
- {testgres-1.16.1 → testgres-1.16.3}/LICENSE +0 -0
- {testgres-1.16.1 → testgres-1.16.3}/README.md +0 -0
- {testgres-1.16.1 → testgres-1.16.3}/setup.cfg +0 -0
- {testgres-1.16.1 → testgres-1.16.3}/src/api.py +0 -0
- {testgres-1.16.1 → testgres-1.16.3}/src/defaults.py +0 -0
- {testgres-1.16.1 → testgres-1.16.3}/src/impl/file_line_reader.py +0 -0
- {testgres-1.16.1 → testgres-1.16.3}/src/impl/port_manager__generic.py +0 -0
- {testgres-1.16.1 → testgres-1.16.3}/src/impl/port_manager__generic2.py +0 -0
- {testgres-1.16.1 → testgres-1.16.3}/src/impl/port_manager__this_host.py +0 -0
- {testgres-1.16.1 → testgres-1.16.3}/src/logger.py +0 -0
- {testgres-1.16.1 → testgres-1.16.3}/testgres.egg-info/dependency_links.txt +0 -0
- {testgres-1.16.1 → testgres-1.16.3}/testgres.egg-info/top_level.txt +0 -0
- {testgres-1.16.1 → testgres-1.16.3}/tests/test_api.py +0 -0
- {testgres-1.16.1 → testgres-1.16.3}/tests/test_config.py +0 -0
- {testgres-1.16.1 → testgres-1.16.3}/tests/test_os_ops_local.py +0 -0
- {testgres-1.16.1 → testgres-1.16.3}/tests/test_os_ops_remote.py +0 -0
- {testgres-1.16.1 → testgres-1.16.3}/tests/test_raise_error.py +0 -0
- {testgres-1.16.1 → testgres-1.16.3}/tests/test_testgres_local.py +0 -0
- {testgres-1.16.1 → testgres-1.16.3}/tests/test_testgres_remote.py +0 -0
- {testgres-1.16.1 → testgres-1.16.3}/tests/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: testgres
|
|
3
|
-
Version: 1.16.
|
|
3
|
+
Version: 1.16.3
|
|
4
4
|
Summary: Testing utility for PostgreSQL and its extensions
|
|
5
5
|
Author-email: Postgres Professional <testgres@postgrespro.ru>
|
|
6
6
|
License: PostgreSQL
|
|
@@ -24,10 +24,9 @@ Description-Content-Type: text/markdown
|
|
|
24
24
|
License-File: LICENSE
|
|
25
25
|
Requires-Dist: pg8000
|
|
26
26
|
Requires-Dist: port-for>=0.4
|
|
27
|
-
Requires-Dist: six>=1.9.0
|
|
28
27
|
Requires-Dist: psutil
|
|
29
28
|
Requires-Dist: packaging
|
|
30
|
-
Requires-Dist: testgres.os_ops<4.0.0,>=3.3.
|
|
29
|
+
Requires-Dist: testgres.os_ops<4.0.0,>=3.3.3
|
|
31
30
|
Dynamic: license-file
|
|
32
31
|
|
|
33
32
|
[](https://github.com/postgrespro/testgres/actions/workflows/ci.yml)
|
|
@@ -60,7 +60,7 @@ from testgres.operations.os_ops import OsProcessController
|
|
|
60
60
|
from testgres.operations.local_ops import LocalOperations
|
|
61
61
|
from testgres.operations.remote_ops import RemoteOperations
|
|
62
62
|
|
|
63
|
-
__version__ = "1.16.
|
|
63
|
+
__version__ = "1.16.3"
|
|
64
64
|
|
|
65
65
|
__all__ = [
|
|
66
66
|
"get_new_node",
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# coding: utf-8
|
|
2
2
|
|
|
3
|
-
from six import raise_from
|
|
4
|
-
|
|
5
3
|
from .enums import XLogMethod
|
|
6
4
|
|
|
7
5
|
from .consts import \
|
|
@@ -22,7 +20,7 @@ from .utils import \
|
|
|
22
20
|
clean_on_error
|
|
23
21
|
|
|
24
22
|
|
|
25
|
-
class NodeBackup
|
|
23
|
+
class NodeBackup:
|
|
26
24
|
"""
|
|
27
25
|
Smart object responsible for backups
|
|
28
26
|
"""
|
|
@@ -131,7 +129,7 @@ class NodeBackup(object):
|
|
|
131
129
|
# Copy backup to new data dir
|
|
132
130
|
self.os_ops.copytree(data1, data2)
|
|
133
131
|
except Exception as e:
|
|
134
|
-
|
|
132
|
+
raise BackupException('Failed to copy files') from e
|
|
135
133
|
else:
|
|
136
134
|
dest_base_dir = self.base_dir
|
|
137
135
|
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# coding: utf-8
|
|
2
2
|
|
|
3
|
-
from six import raise_from
|
|
4
|
-
|
|
5
3
|
from .config import testgres_config
|
|
6
4
|
|
|
7
5
|
from .consts import XLOG_CONTROL_FILE
|
|
@@ -49,7 +47,7 @@ def cached_initdb(data_dir, logfile=None, params=None, os_ops: OsOperations = No
|
|
|
49
47
|
log,
|
|
50
48
|
)
|
|
51
49
|
except ExecUtilException as e:
|
|
52
|
-
|
|
50
|
+
raise InitNodeException("Failed to run initdb") from e
|
|
53
51
|
|
|
54
52
|
if params or not testgres_config.cache_initdb or not cached:
|
|
55
53
|
call_initdb(data_dir, logfile)
|
|
@@ -89,7 +87,7 @@ def cached_initdb(data_dir, logfile=None, params=None, os_ops: OsOperations = No
|
|
|
89
87
|
|
|
90
88
|
except ExecUtilException as e:
|
|
91
89
|
msg = "Failed to reset WAL for system id"
|
|
92
|
-
|
|
90
|
+
raise InitNodeException(msg) from e
|
|
93
91
|
|
|
94
92
|
except Exception as e:
|
|
95
|
-
|
|
93
|
+
raise InitNodeException("Failed to spawn a node") from e
|
|
@@ -17,7 +17,7 @@ log_format = os.getenv('LOGGING_FORMAT', '%(asctime)s - %(levelname)s - %(messag
|
|
|
17
17
|
logging.basicConfig(level=log_level, format=log_format)
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
class GlobalConfig
|
|
20
|
+
class GlobalConfig:
|
|
21
21
|
"""
|
|
22
22
|
Global configuration object which allows user to override default settings.
|
|
23
23
|
"""
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# binary names
|
|
4
4
|
BINARY_NAME__POSTGRES = "postgres"
|
|
5
|
+
BINARY_NAME__PG_CTL = "pg_ctl"
|
|
5
6
|
|
|
6
7
|
# names for dirs in base_dir
|
|
7
8
|
DATA_DIR = "data"
|
|
@@ -44,5 +45,7 @@ MAX_WAL_SENDERS = 10
|
|
|
44
45
|
LOGICAL_REPL_MAX_CATCHUP_ATTEMPTS = 60
|
|
45
46
|
|
|
46
47
|
PG_CTL__STATUS__OK = 0
|
|
48
|
+
|
|
49
|
+
PG_CTL__STATUS__FAILED = 1
|
|
47
50
|
PG_CTL__STATUS__NODE_IS_STOPPED = 3
|
|
48
51
|
PG_CTL__STATUS__BAD_DATADIR = 4
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import six
|
|
2
1
|
import functools
|
|
3
2
|
|
|
4
3
|
|
|
@@ -18,7 +17,7 @@ def positional_args_hack(*special_cases):
|
|
|
18
17
|
|
|
19
18
|
for case in special_cases:
|
|
20
19
|
k = len(case)
|
|
21
|
-
assert k not in
|
|
20
|
+
assert k not in cases, 'len must be unique'
|
|
22
21
|
cases[k] = case
|
|
23
22
|
|
|
24
23
|
def decorator(function):
|
|
@@ -26,7 +25,7 @@ def positional_args_hack(*special_cases):
|
|
|
26
25
|
def wrapper(*args, **kwargs):
|
|
27
26
|
k = len(args)
|
|
28
27
|
|
|
29
|
-
if k in
|
|
28
|
+
if k in cases:
|
|
30
29
|
case = cases[k]
|
|
31
30
|
|
|
32
31
|
for i in range(0, k):
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
from enum import Enum, IntEnum
|
|
2
|
-
from six import iteritems
|
|
3
2
|
from psutil import NoSuchProcess
|
|
4
3
|
|
|
5
4
|
|
|
@@ -83,7 +82,7 @@ class ProcessType(Enum):
|
|
|
83
82
|
if cmdline.startswith(ptype.value.replace(' ', '')):
|
|
84
83
|
return ptype
|
|
85
84
|
|
|
86
|
-
for ptype, names in
|
|
85
|
+
for ptype, names in alternative_names.items():
|
|
87
86
|
for name in names:
|
|
88
87
|
if cmdline.startswith(name.replace(' ', '')):
|
|
89
88
|
return ptype
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# coding: utf-8
|
|
2
2
|
|
|
3
|
-
import six
|
|
4
3
|
import typing
|
|
5
4
|
|
|
6
5
|
from testgres.operations.exceptions import TestgresException
|
|
@@ -44,7 +43,6 @@ class PortForException(TestgresException):
|
|
|
44
43
|
return result
|
|
45
44
|
|
|
46
45
|
|
|
47
|
-
@six.python_2_unicode_compatible
|
|
48
46
|
class QueryException(TestgresException):
|
|
49
47
|
_description: typing.Optional[str]
|
|
50
48
|
_query: typing.Optional[str]
|
|
@@ -76,7 +74,7 @@ class QueryException(TestgresException):
|
|
|
76
74
|
if self._query:
|
|
77
75
|
msg.append(u'Query: {}'.format(self._query))
|
|
78
76
|
|
|
79
|
-
r =
|
|
77
|
+
r = '\n'.join(msg)
|
|
80
78
|
assert type(r) is str
|
|
81
79
|
return r
|
|
82
80
|
|
|
@@ -162,7 +160,6 @@ class CatchUpException(TestgresException):
|
|
|
162
160
|
return result
|
|
163
161
|
|
|
164
162
|
|
|
165
|
-
@six.python_2_unicode_compatible
|
|
166
163
|
class StartNodeException(TestgresException):
|
|
167
164
|
_description: typing.Optional[str]
|
|
168
165
|
_files: typing.Optional[typing.Iterable]
|
|
@@ -196,7 +193,7 @@ class StartNodeException(TestgresException):
|
|
|
196
193
|
assert type(lines) in [str, bytes]
|
|
197
194
|
msg.append(u'{}\n----\n{}\n'.format(f, lines))
|
|
198
195
|
|
|
199
|
-
return
|
|
196
|
+
return '\n'.join(msg)
|
|
200
197
|
|
|
201
198
|
@property
|
|
202
199
|
def description(self) -> typing.Optional[str]:
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from . import internal_utils
|
|
4
|
+
from . platforms.internal_platform_utils_provider import InternalPlaformUtilsProvider
|
|
5
|
+
|
|
6
|
+
from .. import consts
|
|
7
|
+
from ..node_state import PostgresNodeState
|
|
8
|
+
from ..enums import NodeStatus
|
|
9
|
+
from ..raise_error import RaiseError
|
|
10
|
+
from ..exceptions import ExecUtilException
|
|
11
|
+
|
|
12
|
+
from testgres.operations.os_ops import OsOperations
|
|
13
|
+
|
|
14
|
+
import typing
|
|
15
|
+
import time
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class InternalNodeUtils:
|
|
19
|
+
T_PLATFORM_UTILS = InternalPlaformUtilsProvider.T_PLATFORM_UTILS
|
|
20
|
+
|
|
21
|
+
@staticmethod
|
|
22
|
+
def get_pg_node_state(
|
|
23
|
+
os_ops: OsOperations,
|
|
24
|
+
bin_dir: str,
|
|
25
|
+
data_dir: str,
|
|
26
|
+
utils_log_file: typing.Optional[str],
|
|
27
|
+
) -> PostgresNodeState:
|
|
28
|
+
assert isinstance(os_ops, OsOperations)
|
|
29
|
+
assert type(bin_dir) is str
|
|
30
|
+
assert type(data_dir) is str
|
|
31
|
+
assert utils_log_file is None or type(utils_log_file) is str
|
|
32
|
+
|
|
33
|
+
C_MAX_ATTEMPTS = 3
|
|
34
|
+
C_SLEEP_TIME1 = 1
|
|
35
|
+
C_SLEEP_TIME_MULT = 2
|
|
36
|
+
|
|
37
|
+
pg_ctl_params = [
|
|
38
|
+
os_ops.build_path(bin_dir, consts.BINARY_NAME__PG_CTL),
|
|
39
|
+
"-D",
|
|
40
|
+
data_dir,
|
|
41
|
+
"status",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
attempt = 0
|
|
45
|
+
sleep_time = C_SLEEP_TIME1
|
|
46
|
+
|
|
47
|
+
platform_utils_provider = InternalPlaformUtilsProvider(
|
|
48
|
+
os_ops,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
while True:
|
|
52
|
+
assert type(attempt) is int
|
|
53
|
+
assert attempt >= 0
|
|
54
|
+
assert attempt < C_MAX_ATTEMPTS
|
|
55
|
+
|
|
56
|
+
attempt += 1
|
|
57
|
+
|
|
58
|
+
if attempt > 1:
|
|
59
|
+
internal_utils.send_log_debug("Sleep {} second(s) before an attempt #{}".format(
|
|
60
|
+
sleep_time,
|
|
61
|
+
attempt,
|
|
62
|
+
))
|
|
63
|
+
time.sleep(sleep_time)
|
|
64
|
+
sleep_time = sleep_time * C_SLEEP_TIME_MULT
|
|
65
|
+
|
|
66
|
+
exec_r = internal_utils.execute_utility3(
|
|
67
|
+
os_ops,
|
|
68
|
+
pg_ctl_params,
|
|
69
|
+
utils_log_file,
|
|
70
|
+
check=False,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
status_code = exec_r.returncode
|
|
74
|
+
out = exec_r.stdout
|
|
75
|
+
error = exec_r.stderr
|
|
76
|
+
|
|
77
|
+
assert type(status_code) is int
|
|
78
|
+
assert type(out) is str
|
|
79
|
+
assert type(error) is str
|
|
80
|
+
|
|
81
|
+
# -----------------
|
|
82
|
+
if status_code == consts.PG_CTL__STATUS__NODE_IS_STOPPED:
|
|
83
|
+
return PostgresNodeState(NodeStatus.Stopped, None)
|
|
84
|
+
|
|
85
|
+
# -----------------
|
|
86
|
+
if status_code == consts.PG_CTL__STATUS__BAD_DATADIR:
|
|
87
|
+
return PostgresNodeState(NodeStatus.Uninitialized, None)
|
|
88
|
+
|
|
89
|
+
# -----------------
|
|
90
|
+
if status_code == consts.PG_CTL__STATUS__OK:
|
|
91
|
+
pid = __class__._parse_pid(
|
|
92
|
+
out,
|
|
93
|
+
pg_ctl_params,
|
|
94
|
+
)
|
|
95
|
+
assert type(pid) is int
|
|
96
|
+
assert pid != 0
|
|
97
|
+
|
|
98
|
+
# ----------------- detect zombie
|
|
99
|
+
if platform_utils_provider.get().ProcessIsZombie_soft_check(os_ops, pid) is True:
|
|
100
|
+
internal_utils.send_log_debug("Postmaster process {} is a zombie.".format(
|
|
101
|
+
pid,
|
|
102
|
+
))
|
|
103
|
+
return PostgresNodeState(NodeStatus.Zombie, pid)
|
|
104
|
+
|
|
105
|
+
# -----------------
|
|
106
|
+
return PostgresNodeState(NodeStatus.Running, pid)
|
|
107
|
+
|
|
108
|
+
assert status_code != consts.PG_CTL__STATUS__OK
|
|
109
|
+
|
|
110
|
+
errMsg = "Getting of a node status [data_dir is {0}] failed.".format(
|
|
111
|
+
data_dir,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
e1 = ExecUtilException(
|
|
115
|
+
message=errMsg,
|
|
116
|
+
command=pg_ctl_params,
|
|
117
|
+
exit_code=status_code,
|
|
118
|
+
out=out,
|
|
119
|
+
error=error,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
if status_code == consts.PG_CTL__STATUS__FAILED:
|
|
123
|
+
internal_utils.send_log_debug(
|
|
124
|
+
"pg_ctl fails with an error: {}".format(
|
|
125
|
+
exec_r.stderr,
|
|
126
|
+
))
|
|
127
|
+
|
|
128
|
+
try:
|
|
129
|
+
find_postmaster_r = platform_utils_provider.get().FindPostmaster(
|
|
130
|
+
os_ops,
|
|
131
|
+
bin_dir,
|
|
132
|
+
data_dir,
|
|
133
|
+
)
|
|
134
|
+
except Exception as e2:
|
|
135
|
+
raise e2 from e1
|
|
136
|
+
|
|
137
|
+
assert type(find_postmaster_r) is __class__.T_PLATFORM_UTILS.FindPostmasterResult
|
|
138
|
+
|
|
139
|
+
if find_postmaster_r.code == __class__.T_PLATFORM_UTILS.FindPostmasterResultCode.ok:
|
|
140
|
+
# Postmaster is alive. Let's wait a few seconds and check its status again.
|
|
141
|
+
internal_utils.send_log_debug(
|
|
142
|
+
"Postmaster is found and has PID {}.".format(
|
|
143
|
+
find_postmaster_r.pid,
|
|
144
|
+
))
|
|
145
|
+
|
|
146
|
+
if attempt < C_MAX_ATTEMPTS:
|
|
147
|
+
continue
|
|
148
|
+
|
|
149
|
+
raise e1
|
|
150
|
+
|
|
151
|
+
@staticmethod
|
|
152
|
+
def _parse_pid(
|
|
153
|
+
out: str,
|
|
154
|
+
pg_ctl_params,
|
|
155
|
+
) -> int:
|
|
156
|
+
assert type(out) is str
|
|
157
|
+
|
|
158
|
+
if out == "":
|
|
159
|
+
RaiseError.pg_ctl_returns_an_empty_string(
|
|
160
|
+
pg_ctl_params,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
C_PID_PREFIX = "(PID: "
|
|
164
|
+
|
|
165
|
+
i = out.find(C_PID_PREFIX)
|
|
166
|
+
|
|
167
|
+
if i == -1:
|
|
168
|
+
RaiseError.pg_ctl_returns_an_unexpected_string(
|
|
169
|
+
out,
|
|
170
|
+
pg_ctl_params,
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
assert i > 0
|
|
174
|
+
assert i < len(out)
|
|
175
|
+
assert len(C_PID_PREFIX) <= len(out)
|
|
176
|
+
assert i <= len(out) - len(C_PID_PREFIX)
|
|
177
|
+
|
|
178
|
+
i += len(C_PID_PREFIX)
|
|
179
|
+
start_pid_s = i
|
|
180
|
+
|
|
181
|
+
while True:
|
|
182
|
+
if i == len(out):
|
|
183
|
+
RaiseError.pg_ctl_returns_an_unexpected_string(
|
|
184
|
+
out,
|
|
185
|
+
pg_ctl_params,
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
ch = out[i]
|
|
189
|
+
|
|
190
|
+
if ch == ")":
|
|
191
|
+
break
|
|
192
|
+
|
|
193
|
+
if ch.isdigit():
|
|
194
|
+
i += 1
|
|
195
|
+
continue
|
|
196
|
+
|
|
197
|
+
RaiseError.pg_ctl_returns_an_unexpected_string(
|
|
198
|
+
out,
|
|
199
|
+
pg_ctl_params,
|
|
200
|
+
)
|
|
201
|
+
assert False
|
|
202
|
+
|
|
203
|
+
if i == start_pid_s:
|
|
204
|
+
RaiseError.pg_ctl_returns_an_unexpected_string(
|
|
205
|
+
out,
|
|
206
|
+
pg_ctl_params,
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
# TODO: Let's verify a length of pid string.
|
|
210
|
+
|
|
211
|
+
pid = int(out[start_pid_s:i])
|
|
212
|
+
|
|
213
|
+
if pid == 0:
|
|
214
|
+
RaiseError.pg_ctl_returns_a_zero_pid(
|
|
215
|
+
out,
|
|
216
|
+
pg_ctl_params,
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
assert pid != 0
|
|
220
|
+
|
|
221
|
+
return pid
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
from testgres.operations.os_ops import OsOperations
|
|
2
|
+
from testgres.operations.os_ops import OsCommandResult
|
|
3
|
+
from testgres.operations.types import T_OS_CMD
|
|
4
|
+
from testgres.operations.types import T_OS_EXEC_ENV
|
|
5
|
+
from testgres.operations.helpers import Helpers as OsHelpers
|
|
6
|
+
|
|
7
|
+
from ..exceptions import ExecUtilException
|
|
2
8
|
|
|
3
9
|
import logging
|
|
4
10
|
import typing
|
|
@@ -90,3 +96,55 @@ def read_line_to_pos__bin(
|
|
|
90
96
|
assert type(result) is bytes
|
|
91
97
|
assert len(result) <= (position - read_position)
|
|
92
98
|
return result
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def execute_utility3(
|
|
102
|
+
os_ops: OsOperations,
|
|
103
|
+
args: T_OS_CMD,
|
|
104
|
+
logfile: typing.Optional[str] = None,
|
|
105
|
+
check: bool = True,
|
|
106
|
+
exec_env: typing.Optional[T_OS_EXEC_ENV] = None,
|
|
107
|
+
) -> OsCommandResult:
|
|
108
|
+
assert os_ops is not None
|
|
109
|
+
assert isinstance(os_ops, OsOperations)
|
|
110
|
+
assert type(check) is bool
|
|
111
|
+
assert exec_env is None or type(exec_env) is dict
|
|
112
|
+
|
|
113
|
+
exec_r = os_ops.run(
|
|
114
|
+
args,
|
|
115
|
+
check=check,
|
|
116
|
+
encoding=OsHelpers.GetDefaultEncoding(),
|
|
117
|
+
exec_env=exec_env,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
assert type(exec_r) is OsCommandResult
|
|
121
|
+
|
|
122
|
+
# write new log entry if possible
|
|
123
|
+
if logfile:
|
|
124
|
+
try:
|
|
125
|
+
log_lines = [
|
|
126
|
+
os_ops.join_command_arguments(args),
|
|
127
|
+
]
|
|
128
|
+
|
|
129
|
+
if exec_r.stdout is None:
|
|
130
|
+
log_lines.append("# #NONE#")
|
|
131
|
+
else:
|
|
132
|
+
# comment-out lines
|
|
133
|
+
assert type(exec_r.stdout) is str
|
|
134
|
+
log_lines += ['# ' + line for line in exec_r.stdout.splitlines()]
|
|
135
|
+
|
|
136
|
+
log_lines.append("")
|
|
137
|
+
|
|
138
|
+
os_ops.write(
|
|
139
|
+
filename=logfile,
|
|
140
|
+
data="\n".join(log_lines),
|
|
141
|
+
truncate=False,
|
|
142
|
+
)
|
|
143
|
+
except IOError:
|
|
144
|
+
raise ExecUtilException(
|
|
145
|
+
"Problem with writing to logfile `{}` during run command `{}`".format(
|
|
146
|
+
logfile,
|
|
147
|
+
args,
|
|
148
|
+
))
|
|
149
|
+
|
|
150
|
+
return exec_r
|