testgres 1.11.0__tar.gz → 1.11.1__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.
Files changed (61) hide show
  1. {testgres-1.11.0/testgres.egg-info → testgres-1.11.1}/PKG-INFO +2 -2
  2. {testgres-1.11.0 → testgres-1.11.1}/README.md +1 -1
  3. {testgres-1.11.0 → testgres-1.11.1}/setup.py +2 -2
  4. {testgres-1.11.0 → testgres-1.11.1}/testgres/__init__.py +5 -3
  5. {testgres-1.11.0 → testgres-1.11.1}/testgres/backup.py +17 -13
  6. {testgres-1.11.0 → testgres-1.11.1}/testgres/cache.py +8 -6
  7. {testgres-1.11.0 → testgres-1.11.1}/testgres/config.py +2 -1
  8. testgres-1.11.0/testgres/port_manager.py → testgres-1.11.1/testgres/impl/port_manager__generic.py +38 -44
  9. testgres-1.11.1/testgres/impl/port_manager__this_host.py +33 -0
  10. {testgres-1.11.0 → testgres-1.11.1}/testgres/node.py +283 -230
  11. testgres-1.11.1/testgres/node_app.py +317 -0
  12. {testgres-1.11.0 → testgres-1.11.1}/testgres/operations/local_ops.py +108 -9
  13. {testgres-1.11.0 → testgres-1.11.1}/testgres/operations/os_ops.py +25 -4
  14. {testgres-1.11.0 → testgres-1.11.1}/testgres/operations/remote_ops.py +166 -33
  15. {testgres-1.11.0 → testgres-1.11.1}/testgres/plugins/pg_probackup2/pg_probackup2/app.py +65 -22
  16. {testgres-1.11.0 → testgres-1.11.1}/testgres/plugins/pg_probackup2/pg_probackup2/gdb.py +0 -8
  17. {testgres-1.11.0 → testgres-1.11.1}/testgres/plugins/pg_probackup2/pg_probackup2/init_helpers.py +2 -0
  18. {testgres-1.11.0 → testgres-1.11.1}/testgres/plugins/pg_probackup2/pg_probackup2/tests/test_basic.py +23 -14
  19. {testgres-1.11.0 → testgres-1.11.1}/testgres/plugins/pg_probackup2/setup.py +1 -1
  20. testgres-1.11.1/testgres/port_manager.py +10 -0
  21. {testgres-1.11.0 → testgres-1.11.1}/testgres/utils.py +25 -34
  22. {testgres-1.11.0 → testgres-1.11.1/testgres.egg-info}/PKG-INFO +2 -2
  23. {testgres-1.11.0 → testgres-1.11.1}/testgres.egg-info/SOURCES.txt +3 -0
  24. {testgres-1.11.0 → testgres-1.11.1}/tests/conftest.py +308 -142
  25. {testgres-1.11.0 → testgres-1.11.1}/tests/helpers/global_data.py +9 -9
  26. {testgres-1.11.0 → testgres-1.11.1}/tests/test_config.py +5 -5
  27. {testgres-1.11.0 → testgres-1.11.1}/tests/test_os_ops_common.py +327 -2
  28. {testgres-1.11.0 → testgres-1.11.1}/tests/test_os_ops_remote.py +1 -1
  29. {testgres-1.11.0 → testgres-1.11.1}/tests/test_testgres_common.py +484 -31
  30. {testgres-1.11.0 → testgres-1.11.1}/tests/test_testgres_local.py +25 -25
  31. {testgres-1.11.0 → testgres-1.11.1}/tests/test_testgres_remote.py +7 -8
  32. {testgres-1.11.0 → testgres-1.11.1}/tests/test_utils.py +3 -3
  33. {testgres-1.11.0 → testgres-1.11.1}/LICENSE +0 -0
  34. {testgres-1.11.0 → testgres-1.11.1}/MANIFEST.in +0 -0
  35. {testgres-1.11.0 → testgres-1.11.1}/setup.cfg +0 -0
  36. {testgres-1.11.0 → testgres-1.11.1}/testgres/api.py +0 -0
  37. {testgres-1.11.0 → testgres-1.11.1}/testgres/connection.py +0 -0
  38. {testgres-1.11.0 → testgres-1.11.1}/testgres/consts.py +0 -0
  39. {testgres-1.11.0 → testgres-1.11.1}/testgres/decorators.py +0 -0
  40. {testgres-1.11.0 → testgres-1.11.1}/testgres/defaults.py +0 -0
  41. {testgres-1.11.0 → testgres-1.11.1}/testgres/enums.py +0 -0
  42. {testgres-1.11.0 → testgres-1.11.1}/testgres/exceptions.py +0 -0
  43. {testgres-1.11.0 → testgres-1.11.1}/testgres/logger.py +0 -0
  44. {testgres-1.11.0 → testgres-1.11.1}/testgres/operations/__init__.py +0 -0
  45. {testgres-1.11.0 → testgres-1.11.1}/testgres/operations/helpers.py +0 -0
  46. {testgres-1.11.0 → testgres-1.11.1}/testgres/operations/raise_error.py +0 -0
  47. {testgres-1.11.0 → testgres-1.11.1}/testgres/plugins/__init__.py +0 -0
  48. {testgres-1.11.0 → testgres-1.11.1}/testgres/plugins/pg_probackup2/__init__.py +0 -0
  49. {testgres-1.11.0 → testgres-1.11.1}/testgres/plugins/pg_probackup2/pg_probackup2/__init__.py +0 -0
  50. {testgres-1.11.0 → testgres-1.11.1}/testgres/plugins/pg_probackup2/pg_probackup2/storage/__init__.py +0 -0
  51. {testgres-1.11.0 → testgres-1.11.1}/testgres/plugins/pg_probackup2/pg_probackup2/storage/fs_backup.py +0 -0
  52. {testgres-1.11.0 → testgres-1.11.1}/testgres/plugins/pg_probackup2/pg_probackup2/tests/__init__.py +0 -0
  53. {testgres-1.11.0 → testgres-1.11.1}/testgres/pubsub.py +0 -0
  54. {testgres-1.11.0 → testgres-1.11.1}/testgres/standby.py +0 -0
  55. {testgres-1.11.0 → testgres-1.11.1}/testgres.egg-info/dependency_links.txt +0 -0
  56. {testgres-1.11.0 → testgres-1.11.1}/testgres.egg-info/requires.txt +0 -0
  57. {testgres-1.11.0 → testgres-1.11.1}/testgres.egg-info/top_level.txt +0 -0
  58. {testgres-1.11.0 → testgres-1.11.1}/tests/__init__.py +0 -0
  59. {testgres-1.11.0 → testgres-1.11.1}/tests/helpers/__init__.py +0 -0
  60. {testgres-1.11.0 → testgres-1.11.1}/tests/helpers/run_conditions.py +0 -0
  61. {testgres-1.11.0 → testgres-1.11.1}/tests/test_os_ops_local.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: testgres
3
- Version: 1.11.0
3
+ Version: 1.11.1
4
4
  Summary: Testing utility for PostgreSQL and its extensions
5
5
  Home-page: https://github.com/postgrespro/testgres
6
6
  Author: Postgres Professional
@@ -33,7 +33,7 @@ Dynamic: summary
33
33
 
34
34
  # testgres
35
35
 
36
- PostgreSQL testing utility. Both Python 2.7 and 3.3+ are supported.
36
+ PostgreSQL testing utility. Python 3.7.17+ is supported.
37
37
 
38
38
 
39
39
  ## Installation
@@ -6,7 +6,7 @@
6
6
 
7
7
  # testgres
8
8
 
9
- PostgreSQL testing utility. Both Python 2.7 and 3.3+ are supported.
9
+ PostgreSQL testing utility. Python 3.7.17+ is supported.
10
10
 
11
11
 
12
12
  ## Installation
@@ -27,9 +27,9 @@ with open('README.md', 'r') as f:
27
27
  readme = f.read()
28
28
 
29
29
  setup(
30
- version='1.11.0',
30
+ version='1.11.1',
31
31
  name='testgres',
32
- packages=['testgres', 'testgres.operations'],
32
+ packages=['testgres', 'testgres.operations', 'testgres.impl'],
33
33
  description='Testing utility for PostgreSQL and its extensions',
34
34
  url='https://github.com/postgrespro/testgres',
35
35
  long_description=readme,
@@ -33,8 +33,9 @@ from .enums import \
33
33
  ProcessType, \
34
34
  DumpFormat
35
35
 
36
- from .node import PostgresNode, NodeApp
36
+ from .node import PostgresNode
37
37
  from .node import PortManager
38
+ from .node_app import NodeApp
38
39
 
39
40
  from .utils import \
40
41
  reserve_port, \
@@ -62,8 +63,9 @@ __all__ = [
62
63
  "NodeConnection", "DatabaseError", "InternalError", "ProgrammingError", "OperationalError",
63
64
  "TestgresException", "ExecUtilException", "QueryException", "TimeoutException", "CatchUpException", "StartNodeException", "InitNodeException", "BackupException", "InvalidOperationException",
64
65
  "XLogMethod", "IsolationLevel", "NodeStatus", "ProcessType", "DumpFormat",
65
- "PostgresNode", "NodeApp",
66
- "PortManager",
66
+ NodeApp.__name__,
67
+ PostgresNode.__name__,
68
+ PortManager.__name__,
67
69
  "reserve_port", "release_port", "bound_ports", "get_bin_path", "get_pg_config", "get_pg_version",
68
70
  "First", "Any",
69
71
  "OsOperations", "LocalOperations", "RemoteOperations", "ConnectionParams"
@@ -1,7 +1,5 @@
1
1
  # coding: utf-8
2
2
 
3
- import os
4
-
5
3
  from six import raise_from
6
4
 
7
5
  from .enums import XLogMethod
@@ -29,7 +27,9 @@ class NodeBackup(object):
29
27
  """
30
28
  @property
31
29
  def log_file(self):
32
- return os.path.join(self.base_dir, BACKUP_LOG_FILE)
30
+ assert self.os_ops is not None
31
+ assert isinstance(self.os_ops, OsOperations)
32
+ return self.os_ops.build_path(self.base_dir, BACKUP_LOG_FILE)
33
33
 
34
34
  def __init__(self,
35
35
  node,
@@ -75,7 +75,7 @@ class NodeBackup(object):
75
75
  # private
76
76
  self._available = True
77
77
 
78
- data_dir = os.path.join(self.base_dir, DATA_DIR)
78
+ data_dir = self.os_ops.build_path(self.base_dir, DATA_DIR)
79
79
 
80
80
  _params = [
81
81
  get_bin_path2(self.os_ops, "pg_basebackup"),
@@ -112,10 +112,13 @@ class NodeBackup(object):
112
112
  available = not destroy
113
113
 
114
114
  if available:
115
+ assert self.os_ops is not None
116
+ assert isinstance(self.os_ops, OsOperations)
117
+
115
118
  dest_base_dir = self.os_ops.mkdtemp(prefix=TMP_NODE)
116
119
 
117
- data1 = os.path.join(self.base_dir, DATA_DIR)
118
- data2 = os.path.join(dest_base_dir, DATA_DIR)
120
+ data1 = self.os_ops.build_path(self.base_dir, DATA_DIR)
121
+ data2 = self.os_ops.build_path(dest_base_dir, DATA_DIR)
119
122
 
120
123
  try:
121
124
  # Copy backup to new data dir
@@ -160,10 +163,6 @@ class NodeBackup(object):
160
163
  assert type(node) == self.original_node.__class__ # noqa: E721
161
164
 
162
165
  with clean_on_error(node) as node:
163
-
164
- # New nodes should always remove dir tree
165
- node._should_rm_dirs = True
166
-
167
166
  # Set a new port
168
167
  node.append_conf(filename=PG_CONF_FILE, line='\n')
169
168
  node.append_conf(filename=PG_CONF_FILE, port=node.port)
@@ -184,14 +183,19 @@ class NodeBackup(object):
184
183
  """
185
184
 
186
185
  # Build a new PostgresNode
187
- with clean_on_error(self.spawn_primary(name=name,
188
- destroy=destroy)) as node:
186
+ node = self.spawn_primary(name=name, destroy=destroy)
187
+ assert node is not None
189
188
 
189
+ try:
190
190
  # Assign it a master and a recovery file (private magic)
191
191
  node._assign_master(self.original_node)
192
192
  node._create_recovery_conf(username=self.username, slot=slot)
193
+ except: # noqa: E722
194
+ # TODO: Pass 'final=True' ?
195
+ node.cleanup(release_resources=True)
196
+ raise
193
197
 
194
- return node
198
+ return node
195
199
 
196
200
  def cleanup(self):
197
201
  """
@@ -1,7 +1,5 @@
1
1
  # coding: utf-8
2
2
 
3
- import os
4
-
5
3
  from six import raise_from
6
4
 
7
5
  from .config import testgres_config
@@ -22,12 +20,16 @@ from .operations.local_ops import LocalOperations
22
20
  from .operations.os_ops import OsOperations
23
21
 
24
22
 
25
- def cached_initdb(data_dir, logfile=None, params=None, os_ops: OsOperations = LocalOperations(), bin_path=None, cached=True):
23
+ def cached_initdb(data_dir, logfile=None, params=None, os_ops: OsOperations = None, bin_path=None, cached=True):
26
24
  """
27
25
  Perform initdb or use cached node files.
28
26
  """
29
27
 
30
- assert os_ops is not None
28
+ assert os_ops is None or isinstance(os_ops, OsOperations)
29
+
30
+ if os_ops is None:
31
+ os_ops = LocalOperations.get_single_instance()
32
+
31
33
  assert isinstance(os_ops, OsOperations)
32
34
 
33
35
  def make_utility_path(name):
@@ -35,7 +37,7 @@ def cached_initdb(data_dir, logfile=None, params=None, os_ops: OsOperations = Lo
35
37
  assert type(name) == str # noqa: E721
36
38
 
37
39
  if bin_path:
38
- return os.path.join(bin_path, name)
40
+ return os_ops.build_path(bin_path, name)
39
41
 
40
42
  return get_bin_path2(os_ops, name)
41
43
 
@@ -68,7 +70,7 @@ def cached_initdb(data_dir, logfile=None, params=None, os_ops: OsOperations = Lo
68
70
  # XXX: write new unique system id to control file
69
71
  # Some users might rely upon unique system ids, but
70
72
  # our initdb caching mechanism breaks this contract.
71
- pg_control = os.path.join(data_dir, XLOG_CONTROL_FILE)
73
+ pg_control = os_ops.build_path(data_dir, XLOG_CONTROL_FILE)
72
74
  system_id = generate_system_id()
73
75
  cur_pg_control = os_ops.read(pg_control, binary=True)
74
76
  new_pg_control = system_id + cur_pg_control[len(system_id):]
@@ -50,8 +50,9 @@ class GlobalConfig(object):
50
50
  _cached_initdb_dir = None
51
51
  """ underlying class attribute for cached_initdb_dir property """
52
52
 
53
- os_ops = LocalOperations()
53
+ os_ops = LocalOperations.get_single_instance()
54
54
  """ OsOperation object that allows work on remote host """
55
+
55
56
  @property
56
57
  def cached_initdb_dir(self):
57
58
  """ path to a temp directory for cached initdb. """
@@ -1,53 +1,18 @@
1
- from .operations.os_ops import OsOperations
1
+ from ..operations.os_ops import OsOperations
2
2
 
3
- from .exceptions import PortForException
4
-
5
- from . import utils
3
+ from ..port_manager import PortManager
4
+ from ..exceptions import PortForException
6
5
 
7
6
  import threading
8
7
  import random
9
8
  import typing
10
-
11
-
12
- class PortManager:
13
- def __init__(self):
14
- super().__init__()
15
-
16
- def reserve_port(self) -> int:
17
- raise NotImplementedError("PortManager::reserve_port is not implemented.")
18
-
19
- def release_port(self, number: int) -> None:
20
- assert type(number) == int # noqa: E721
21
- raise NotImplementedError("PortManager::release_port is not implemented.")
22
-
23
-
24
- class PortManager__ThisHost(PortManager):
25
- sm_single_instance: PortManager = None
26
- sm_single_instance_guard = threading.Lock()
27
-
28
- def __init__(self):
29
- pass
30
-
31
- def __new__(cls) -> PortManager:
32
- assert __class__ == PortManager__ThisHost
33
- assert __class__.sm_single_instance_guard is not None
34
-
35
- if __class__.sm_single_instance is None:
36
- with __class__.sm_single_instance_guard:
37
- __class__.sm_single_instance = super().__new__(cls)
38
- assert __class__.sm_single_instance
39
- assert type(__class__.sm_single_instance) == __class__ # noqa: E721
40
- return __class__.sm_single_instance
41
-
42
- def reserve_port(self) -> int:
43
- return utils.reserve_port()
44
-
45
- def release_port(self, number: int) -> None:
46
- assert type(number) == int # noqa: E721
47
- return utils.release_port(number)
9
+ import logging
48
10
 
49
11
 
50
12
  class PortManager__Generic(PortManager):
13
+ _C_MIN_PORT_NUMBER = 1024
14
+ _C_MAX_PORT_NUMBER = 65535
15
+
51
16
  _os_ops: OsOperations
52
17
  _guard: object
53
18
  # TODO: is there better to use bitmap fot _available_ports?
@@ -55,12 +20,21 @@ class PortManager__Generic(PortManager):
55
20
  _reserved_ports: typing.Set[int]
56
21
 
57
22
  def __init__(self, os_ops: OsOperations):
23
+ assert __class__._C_MIN_PORT_NUMBER <= __class__._C_MAX_PORT_NUMBER
24
+
58
25
  assert os_ops is not None
59
26
  assert isinstance(os_ops, OsOperations)
60
27
  self._os_ops = os_ops
61
28
  self._guard = threading.Lock()
62
- self._available_ports: typing.Set[int] = set(range(1024, 65535))
63
- self._reserved_ports: typing.Set[int] = set()
29
+
30
+ self._available_ports = set(
31
+ range(__class__._C_MIN_PORT_NUMBER, __class__._C_MAX_PORT_NUMBER + 1)
32
+ )
33
+ assert len(self._available_ports) == (
34
+ (__class__._C_MAX_PORT_NUMBER - __class__._C_MIN_PORT_NUMBER) + 1
35
+ )
36
+ self._reserved_ports = set()
37
+ return
64
38
 
65
39
  def reserve_port(self) -> int:
66
40
  assert self._guard is not None
@@ -74,9 +48,13 @@ class PortManager__Generic(PortManager):
74
48
  t = None
75
49
 
76
50
  for port in sampled_ports:
51
+ assert type(port) == int # noqa: E721
77
52
  assert not (port in self._reserved_ports)
78
53
  assert port in self._available_ports
79
54
 
55
+ assert port >= __class__._C_MIN_PORT_NUMBER
56
+ assert port <= __class__._C_MAX_PORT_NUMBER
57
+
80
58
  if not self._os_ops.is_port_free(port):
81
59
  continue
82
60
 
@@ -84,12 +62,15 @@ class PortManager__Generic(PortManager):
84
62
  self._available_ports.discard(port)
85
63
  assert port in self._reserved_ports
86
64
  assert not (port in self._available_ports)
65
+ __class__.helper__send_debug_msg("Port {} is reserved.", port)
87
66
  return port
88
67
 
89
68
  raise PortForException("Can't select a port.")
90
69
 
91
70
  def release_port(self, number: int) -> None:
92
71
  assert type(number) == int # noqa: E721
72
+ assert number >= __class__._C_MIN_PORT_NUMBER
73
+ assert number <= __class__._C_MAX_PORT_NUMBER
93
74
 
94
75
  assert self._guard is not None
95
76
  assert type(self._reserved_ports) == set # noqa: E721
@@ -101,3 +82,16 @@ class PortManager__Generic(PortManager):
101
82
  self._reserved_ports.discard(number)
102
83
  assert not (number in self._reserved_ports)
103
84
  assert number in self._available_ports
85
+ __class__.helper__send_debug_msg("Port {} is released.", number)
86
+ return
87
+
88
+ @staticmethod
89
+ def helper__send_debug_msg(msg_template: str, *args) -> None:
90
+ assert msg_template is not None
91
+ assert args is not None
92
+ assert type(msg_template) == str # noqa: E721
93
+ assert type(args) == tuple # noqa: E721
94
+ assert msg_template != ""
95
+ s = "[port manager] "
96
+ s += msg_template.format(*args)
97
+ logging.debug(s)
@@ -0,0 +1,33 @@
1
+ from ..port_manager import PortManager
2
+
3
+ from .. import utils
4
+
5
+ import threading
6
+
7
+
8
+ class PortManager__ThisHost(PortManager):
9
+ sm_single_instance: PortManager = None
10
+ sm_single_instance_guard = threading.Lock()
11
+
12
+ @staticmethod
13
+ def get_single_instance() -> PortManager:
14
+ assert __class__ == PortManager__ThisHost
15
+ assert __class__.sm_single_instance_guard is not None
16
+
17
+ if __class__.sm_single_instance is not None:
18
+ assert type(__class__.sm_single_instance) == __class__ # noqa: E721
19
+ return __class__.sm_single_instance
20
+
21
+ with __class__.sm_single_instance_guard:
22
+ if __class__.sm_single_instance is None:
23
+ __class__.sm_single_instance = __class__()
24
+ assert __class__.sm_single_instance is not None
25
+ assert type(__class__.sm_single_instance) == __class__ # noqa: E721
26
+ return __class__.sm_single_instance
27
+
28
+ def reserve_port(self) -> int:
29
+ return utils.reserve_port()
30
+
31
+ def release_port(self, number: int) -> None:
32
+ assert type(number) == int # noqa: E721
33
+ return utils.release_port(number)