diff --git a/convert2rhel/actions/post_conversion/modified_rpm_files_diff.py b/convert2rhel/actions/post_conversion/modified_rpm_files_diff.py
index 0ebed04c44..afa771197d 100644
--- a/convert2rhel/actions/post_conversion/modified_rpm_files_diff.py
+++ b/convert2rhel/actions/post_conversion/modified_rpm_files_diff.py
@@ -49,7 +49,7 @@ def run(self):
level="INFO",
id="SKIPPED_MODIFIED_RPM_FILES_DIFF",
title="Skipped comparison of 'rpm -Va' output from before and after the conversion.",
- description="Comparison of 'rpm -Va' output was skipped due missing output "
+ description="Comparison of 'rpm -Va' output was not performed due to missing output "
"of the 'rpm -Va' run before the conversion.",
diagnosis="This is caused mainly by using '--no-rpm-va' argument for convert2rhel.",
)
diff --git a/convert2rhel/actions/post_conversion/remove_tmp_dir.py b/convert2rhel/actions/post_conversion/remove_tmp_dir.py
new file mode 100644
index 0000000000..ad1fda08aa
--- /dev/null
+++ b/convert2rhel/actions/post_conversion/remove_tmp_dir.py
@@ -0,0 +1,66 @@
+# Copyright(C) 2024 Red Hat, Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+__metaclass__ = type
+
+import errno
+import logging
+import shutil
+
+from convert2rhel import actions
+from convert2rhel.utils import TMP_DIR
+
+
+loggerinst = logging.getLogger(__name__)
+
+
+class RemoveTmpDir(actions.Action):
+ id = "REMOVE_TMP_DIR"
+ dependencies = ("UPDATE_GRUB",)
+ tmp_dir = TMP_DIR
+
+ def run(self):
+ """Remove the temporary directory (used for backups) and its
+ contents (if any) after the conversion is done. Warns if the
+ removal fails.
+
+ This function is idempotent and will do nothing if the
+ temporary directory does not exist.
+ """
+ super(RemoveTmpDir, self).run()
+
+ loggerinst.task("Final: Remove temporary folder %s" % TMP_DIR)
+
+ try:
+ shutil.rmtree(self.tmp_dir)
+ loggerinst.info("Temporary folder %s removed" % self.tmp_dir)
+ except OSError as exc:
+ # We want run() to be idempotent, so do nothing silently if
+ # the path doesn't exist.
+ # In Python 3 this could be changed to FileNotFoundError.
+ if exc.errno == errno.ENOENT:
+ return
+ warning_message = (
+ "The folder %s is left untouched. You may remove the folder manually"
+ " after you ensure there is no preserved data you would need." % self.tmp_dir
+ )
+ loggerinst.warning(warning_message)
+
+ self.add_message(
+ level="WARNING",
+ id="UNSUCCESSFUL_REMOVE_TMP_DIR",
+ title="Temporary folder {tmp_dir} wasn't removed.".format(tmp_dir=self.tmp_dir),
+ description=warning_message,
+ )
diff --git a/convert2rhel/main.py b/convert2rhel/main.py
index c013a51de5..b8aa41d5f1 100644
--- a/convert2rhel/main.py
+++ b/convert2rhel/main.py
@@ -373,9 +373,6 @@ def prepare_system():
def post_ponr_changes():
"""Start the conversion itself"""
- loggerinst.task("Final: Remove temporary folder %s" % utils.TMP_DIR)
- utils.remove_tmp_dir()
-
loggerinst.task("Final: Check kernel boot files")
checks.check_kernel_boot_files()
diff --git a/convert2rhel/unit_tests/actions/post_conversion/modified_rpm_files_diff_test.py b/convert2rhel/unit_tests/actions/post_conversion/modified_rpm_files_diff_test.py
index aa58c5398e..981102a468 100644
--- a/convert2rhel/unit_tests/actions/post_conversion/modified_rpm_files_diff_test.py
+++ b/convert2rhel/unit_tests/actions/post_conversion/modified_rpm_files_diff_test.py
@@ -48,7 +48,7 @@ def test_modified_rpm_files_diff_with_no_rpm_va(monkeypatch, modified_rpm_files_
level="INFO",
id="SKIPPED_MODIFIED_RPM_FILES_DIFF",
title="Skipped comparison of 'rpm -Va' output from before and after the conversion.",
- description="Comparison of 'rpm -Va' output was skipped due missing output "
+ description="Comparison of 'rpm -Va' output was not performed due to missing output "
"of the 'rpm -Va' run before the conversion.",
diagnosis="This is caused mainly by using '--no-rpm-va' argument for convert2rhel.",
),
diff --git a/convert2rhel/unit_tests/actions/post_conversion/remove_tmp_dir_test.py b/convert2rhel/unit_tests/actions/post_conversion/remove_tmp_dir_test.py
new file mode 100644
index 0000000000..09da2b4b05
--- /dev/null
+++ b/convert2rhel/unit_tests/actions/post_conversion/remove_tmp_dir_test.py
@@ -0,0 +1,86 @@
+# Copyright(C) 2024 Red Hat, Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+__metaclass__ = type
+
+import logging
+import os
+
+import pytest
+
+from convert2rhel import actions, unit_tests
+from convert2rhel.actions.post_conversion import remove_tmp_dir
+
+
+@pytest.fixture
+def remove_tmp_dir_instance():
+ return remove_tmp_dir.RemoveTmpDir()
+
+
+def test_remove_tmp_dir(remove_tmp_dir_instance, monkeypatch, tmpdir, caplog):
+ caplog.set_level(logging.INFO)
+ path = str(tmpdir)
+ monkeypatch.setattr(remove_tmp_dir_instance, "tmp_dir", path)
+ assert os.path.isdir(path)
+ remove_tmp_dir_instance.run()
+ assert "Temporary folder %s removed" % path in caplog.text
+ assert not os.path.isdir(path)
+
+
+def test_remove_tmp_dir_non_existent(remove_tmp_dir_instance, monkeypatch, caplog):
+ caplog.set_level(logging.INFO)
+ path = unit_tests.NONEXISTING_DIR
+ monkeypatch.setattr(remove_tmp_dir_instance, "tmp_dir", path)
+ assert not os.path.isdir(path)
+ remove_tmp_dir_instance.run()
+ assert "Temporary folder %s removed" % path not in caplog.text
+
+
+def test_remove_tmp_dir_failure(remove_tmp_dir_instance, monkeypatch, tmpdir, caplog):
+ caplog.set_level(logging.INFO)
+ path = str(tmpdir)
+ monkeypatch.setattr(remove_tmp_dir_instance, "tmp_dir", path)
+ os.chmod(path, 0)
+ remove_tmp_dir_instance.run()
+ expected_message = (
+ "The folder %s is left untouched. You may remove the folder manually"
+ " after you ensure there is no preserved data you would need." % path
+ )
+ expected = set(
+ (
+ actions.ActionMessage(
+ id="UNSUCCESSFUL_REMOVE_TMP_DIR",
+ level="WARNING",
+ title="Temporary folder {tmp_dir} wasn't removed.".format(tmp_dir=path),
+ description=expected_message,
+ ),
+ ),
+ )
+ assert expected_message in caplog.text
+ assert expected.issubset(remove_tmp_dir_instance.messages)
+ assert expected.issuperset(remove_tmp_dir_instance.messages)
+ os.chmod(path, 0o700)
+
+
+def test_remove_tmp_dir_nonempty(remove_tmp_dir_instance, monkeypatch, tmpdir, caplog):
+ caplog.set_level(logging.INFO)
+ path = str(tmpdir)
+ monkeypatch.setattr(remove_tmp_dir_instance, "tmp_dir", path)
+ assert os.path.isdir(path)
+ with open(os.path.join(path, "remove_tmp_dir_test"), "w") as fp:
+ fp.write("This is a file in the temporary directory.\n")
+ remove_tmp_dir_instance.run()
+ assert "Temporary folder " + str(path) + " removed" in caplog.text
+ assert not os.path.isdir(path)
diff --git a/convert2rhel/unit_tests/main_test.py b/convert2rhel/unit_tests/main_test.py
index 2ca1fb7daf..2a76d1685c 100644
--- a/convert2rhel/unit_tests/main_test.py
+++ b/convert2rhel/unit_tests/main_test.py
@@ -241,7 +241,6 @@ def test_main(monkeypatch, tmp_path):
run_post_actions_mock = mock.Mock()
clear_versionlock_mock = mock.Mock()
ask_to_continue_mock = mock.Mock()
- remove_tmp_dir_mock = mock.Mock()
restart_system_mock = mock.Mock()
finish_collection_mock = mock.Mock()
check_kernel_boot_files_mock = mock.Mock()
@@ -266,7 +265,6 @@ def test_main(monkeypatch, tmp_path):
monkeypatch.setattr(main, "_raise_for_skipped_failures", raise_for_skipped_failures_mock)
monkeypatch.setattr(report, "_summary", report_summary_mock)
monkeypatch.setattr(utils, "ask_to_continue", ask_to_continue_mock)
- monkeypatch.setattr(utils, "remove_tmp_dir", remove_tmp_dir_mock)
monkeypatch.setattr(utils, "restart_system", restart_system_mock)
monkeypatch.setattr(breadcrumbs, "finish_collection", finish_collection_mock)
monkeypatch.setattr(checks, "check_kernel_boot_files", check_kernel_boot_files_mock)
@@ -290,7 +288,6 @@ def test_main(monkeypatch, tmp_path):
assert report_summary_mock.call_count == 2
assert clear_versionlock_mock.call_count == 1
assert ask_to_continue_mock.call_count == 1
- assert remove_tmp_dir_mock.call_count == 1
assert restart_system_mock.call_count == 1
assert finish_collection_mock.call_count == 1
assert check_kernel_boot_files_mock.call_count == 1