Skip to content

Commit 1d68e91

Browse files
authored
Fix node cleanup (#135)
* Add expect_error to pg_upgrade * Fix node cleanup - rmdirs * Add cleanup parameter - clean full dir
1 parent a128b12 commit 1d68e91

File tree

2 files changed

+38
-12
lines changed

2 files changed

+38
-12
lines changed

testgres/node.py

+7-6
Original file line numberDiff line numberDiff line change
@@ -914,13 +914,14 @@ def free_port(self):
914914
self._should_free_port = False
915915
release_port(self.port)
916916

917-
def cleanup(self, max_attempts=3):
917+
def cleanup(self, max_attempts=3, full=False):
918918
"""
919919
Stop node if needed and remove its data/logs directory.
920920
NOTE: take a look at TestgresConfig.node_cleanup_full.
921921
922922
Args:
923923
max_attempts: how many times should we try to stop()?
924+
full: clean full base dir
924925
925926
Returns:
926927
This instance of :class:`.PostgresNode`.
@@ -929,12 +930,12 @@ def cleanup(self, max_attempts=3):
929930
self._try_shutdown(max_attempts)
930931

931932
# choose directory to be removed
932-
if testgres_config.node_cleanup_full:
933+
if testgres_config.node_cleanup_full or full:
933934
rm_dir = self.base_dir # everything
934935
else:
935936
rm_dir = self.data_dir # just data, save logs
936937

937-
self.os_ops.rmdirs(rm_dir, ignore_errors=True)
938+
self.os_ops.rmdirs(rm_dir, ignore_errors=False)
938939

939940
return self
940941

@@ -1629,7 +1630,7 @@ def set_auto_conf(self, options, config='postgresql.auto.conf', rm_options={}):
16291630

16301631
self.os_ops.write(path, auto_conf, truncate=True)
16311632

1632-
def upgrade_from(self, old_node, options=None):
1633+
def upgrade_from(self, old_node, options=None, expect_error=False):
16331634
"""
16341635
Upgrade this node from an old node using pg_upgrade.
16351636
@@ -1657,11 +1658,11 @@ def upgrade_from(self, old_node, options=None):
16571658
"--old-datadir", old_node.data_dir,
16581659
"--new-datadir", self.data_dir,
16591660
"--old-port", str(old_node.port),
1660-
"--new-port", str(self.port),
1661+
"--new-port", str(self.port)
16611662
]
16621663
upgrade_command += options
16631664

1664-
return self.os_ops.exec_command(upgrade_command)
1665+
return self.os_ops.exec_command(upgrade_command, expect_error=expect_error)
16651666

16661667
def _get_bin_path(self, filename):
16671668
if self.bin_dir:

testgres/operations/local_ops.py

+31-6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import stat
55
import subprocess
66
import tempfile
7+
import time
78

89
import psutil
910

@@ -19,13 +20,18 @@
1920

2021
CMD_TIMEOUT_SEC = 60
2122
error_markers = [b'error', b'Permission denied', b'fatal']
23+
err_out_markers = [b'Failure']
2224

2325

24-
def has_errors(output):
26+
def has_errors(output=None, error=None):
2527
if output:
2628
if isinstance(output, str):
2729
output = output.encode(get_default_encoding())
28-
return any(marker in output for marker in error_markers)
30+
return any(marker in output for marker in err_out_markers)
31+
if error:
32+
if isinstance(error, str):
33+
error = error.encode(get_default_encoding())
34+
return any(marker in error for marker in error_markers)
2935
return False
3036

3137

@@ -107,8 +113,8 @@ def exec_command(self, cmd, wait_exit=False, verbose=False, expect_error=False,
107113
process, output, error = self._run_command(cmd, shell, input, stdin, stdout, stderr, get_process, timeout, encoding)
108114
if get_process:
109115
return process
110-
if process.returncode != 0 or (has_errors(error) and not expect_error):
111-
self._raise_exec_exception('Utility exited with non-zero code. Error `{}`', cmd, process.returncode, error)
116+
if (process.returncode != 0 or has_errors(output=output, error=error)) and not expect_error:
117+
self._raise_exec_exception('Utility exited with non-zero code. Error `{}`', cmd, process.returncode, error or output)
112118

113119
if verbose:
114120
return process.returncode, output, error
@@ -142,8 +148,27 @@ def makedirs(self, path, remove_existing=False):
142148
except FileExistsError:
143149
pass
144150

145-
def rmdirs(self, path, ignore_errors=True):
146-
return rmtree(path, ignore_errors=ignore_errors)
151+
def rmdirs(self, path, ignore_errors=True, retries=3, delay=1):
152+
"""
153+
Removes a directory and its contents, retrying on failure.
154+
155+
:param path: Path to the directory.
156+
:param ignore_errors: If True, ignore errors.
157+
:param retries: Number of attempts to remove the directory.
158+
:param delay: Delay between attempts in seconds.
159+
"""
160+
for attempt in range(retries):
161+
try:
162+
rmtree(path, ignore_errors=ignore_errors)
163+
if not os.path.exists(path):
164+
return True
165+
except FileNotFoundError:
166+
return True
167+
except Exception as e:
168+
print(f"Error: Failed to remove directory {path} on attempt {attempt + 1}: {e}")
169+
time.sleep(delay)
170+
print(f"Error: Failed to remove directory {path} after {retries} attempts.")
171+
return False
147172

148173
def listdir(self, path):
149174
return os.listdir(path)

0 commit comments

Comments
 (0)