leapp-repository/SOURCES/0060-Prevent-failed-upgrade-from-restarting-in-initramfs-.patch
2023-03-29 09:01:41 +00:00

165 lines
6.6 KiB
Diff

From f5a3d626cf97c193ab1523401827c2a4c89310ea Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matej=20Matu=C5=A1ka?= <mmatuska@redhat.com>
Date: Fri, 20 Jan 2023 14:03:59 +0100
Subject: [PATCH 60/63] Prevent failed upgrade from restarting in initramfs
(#996)
* Prevent failed upgrade from restarting in initramfs
When the upgrade fails in the initramfs the dracut shell is entered.
Upon exiting the dracut shell, the upgrade.target is restarted which
causes the upgrade.service, which runs the leapp upgrade, to rerun as
well.
This commit fixes that by creating a "flag" file when the upgrade
fails, whose existence is checked before reruning the upgrade and the
upgrade is prevented in such case.
Also, a new removeupgradeartifacts actor is introduced to clean up leftover upgrade artifacts, including the upgrade failed flag file, at the beginning of the upgrade process.
Jira ref.: OAMG-4224
---
.../dracut/85sys-upgrade-redhat/do-upgrade.sh | 20 +++++++++++++
.../actors/removeupgradeartifacts/actor.py | 23 +++++++++++++++
.../libraries/removeupgradeartifacts.py | 17 +++++++++++
.../tests/test_removeupgradeartifacts.py | 28 +++++++++++++++++++
4 files changed, 88 insertions(+)
create mode 100644 repos/system_upgrade/common/actors/removeupgradeartifacts/actor.py
create mode 100644 repos/system_upgrade/common/actors/removeupgradeartifacts/libraries/removeupgradeartifacts.py
create mode 100644 repos/system_upgrade/common/actors/removeupgradeartifacts/tests/test_removeupgradeartifacts.py
diff --git a/repos/system_upgrade/common/actors/commonleappdracutmodules/files/dracut/85sys-upgrade-redhat/do-upgrade.sh b/repos/system_upgrade/common/actors/commonleappdracutmodules/files/dracut/85sys-upgrade-redhat/do-upgrade.sh
index 0763d5b3..04540c1d 100755
--- a/repos/system_upgrade/common/actors/commonleappdracutmodules/files/dracut/85sys-upgrade-redhat/do-upgrade.sh
+++ b/repos/system_upgrade/common/actors/commonleappdracutmodules/files/dracut/85sys-upgrade-redhat/do-upgrade.sh
@@ -46,6 +46,8 @@ fi
export NSPAWN_OPTS="$NSPAWN_OPTS --keep-unit --register=no --timezone=off --resolv-conf=off"
+export LEAPP_FAILED_FLAG_FILE="/root/tmp_leapp_py3/.leapp_upgrade_failed"
+
#
# Temp for collecting and preparing tarball
#
@@ -268,6 +270,15 @@ do_upgrade() {
rv=$?
fi
+ if [ "$rv" -ne 0 ]; then
+ # set the upgrade failed flag to prevent the upgrade from running again
+ # when the emergency shell exits and the upgrade.target is restarted
+ local dirname
+ dirname="$("$NEWROOT/bin/dirname" "$NEWROOT$LEAPP_FAILED_FLAG_FILE")"
+ [ -d "$dirname" ] || mkdir "$dirname"
+ "$NEWROOT/bin/touch" "$NEWROOT$LEAPP_FAILED_FLAG_FILE"
+ fi
+
# Dump debug data in case something went wrong
if want_inband_dump "$rv"; then
collect_and_dump_debug_data
@@ -338,6 +349,15 @@ mount -o "remount,rw" "$NEWROOT"
##### do the upgrade #######
(
+ # check if leapp previously failed in the initramfs, if it did return to the emergency shell
+ [ -f "$NEWROOT$LEAPP_FAILED_FLAG_FILE" ] && {
+ echo >&2 "Found file $NEWROOT$LEAPP_FAILED_FLAG_FILE"
+ echo >&2 "Error: Leapp previously failed and cannot continue, returning back to emergency shell"
+ echo >&2 "Please file a support case with $NEWROOT/var/log/leapp/leapp-upgrade.log attached"
+ echo >&2 "To rerun the upgrade upon exiting the dracut shell remove the $NEWROOT$LEAPP_FAILED_FLAG_FILE file"
+ exit 1
+ }
+
[ ! -x "$NEWROOT$LEAPPBIN" ] && {
warn "upgrade binary '$LEAPPBIN' missing!"
exit 1
diff --git a/repos/system_upgrade/common/actors/removeupgradeartifacts/actor.py b/repos/system_upgrade/common/actors/removeupgradeartifacts/actor.py
new file mode 100644
index 00000000..5eb60d27
--- /dev/null
+++ b/repos/system_upgrade/common/actors/removeupgradeartifacts/actor.py
@@ -0,0 +1,23 @@
+from leapp.actors import Actor
+from leapp.libraries.actor import removeupgradeartifacts
+from leapp.tags import InterimPreparationPhaseTag, IPUWorkflowTag
+
+
+class RemoveUpgradeArtifacts(Actor):
+ """
+ Removes artifacts left over by previous leapp runs
+
+ After the upgrade process, there might be some leftover files, which need
+ to be cleaned up before running another upgrade.
+
+ Removed artifacts:
+ - /root/tmp_leapp_py3/ directory (includes ".leapp_upgrade_failed" flag file)
+ """
+
+ name = 'remove_upgrade_artifacts'
+ consumes = ()
+ produces = ()
+ tags = (InterimPreparationPhaseTag, IPUWorkflowTag)
+
+ def process(self):
+ removeupgradeartifacts.process()
diff --git a/repos/system_upgrade/common/actors/removeupgradeartifacts/libraries/removeupgradeartifacts.py b/repos/system_upgrade/common/actors/removeupgradeartifacts/libraries/removeupgradeartifacts.py
new file mode 100644
index 00000000..aa748d9d
--- /dev/null
+++ b/repos/system_upgrade/common/actors/removeupgradeartifacts/libraries/removeupgradeartifacts.py
@@ -0,0 +1,17 @@
+import os
+
+from leapp.libraries.stdlib import api, CalledProcessError, run
+
+UPGRADE_ARTIFACTS_DIR = '/root/tmp_leapp_py3/'
+
+
+def process():
+ if os.path.exists(UPGRADE_ARTIFACTS_DIR):
+ api.current_logger().debug(
+ "Removing leftover upgrade artifacts dir: {} ".format(UPGRADE_ARTIFACTS_DIR))
+
+ try:
+ run(['rm', '-rf', UPGRADE_ARTIFACTS_DIR])
+ except (CalledProcessError, OSError) as e:
+ api.current_logger().debug(
+ 'Failed to remove leftover upgrade artifacts dir: {}'.format(e))
diff --git a/repos/system_upgrade/common/actors/removeupgradeartifacts/tests/test_removeupgradeartifacts.py b/repos/system_upgrade/common/actors/removeupgradeartifacts/tests/test_removeupgradeartifacts.py
new file mode 100644
index 00000000..aee4d7c6
--- /dev/null
+++ b/repos/system_upgrade/common/actors/removeupgradeartifacts/tests/test_removeupgradeartifacts.py
@@ -0,0 +1,28 @@
+import os
+
+import pytest
+
+from leapp.libraries.actor import removeupgradeartifacts
+
+
+@pytest.mark.parametrize(('exists', 'should_remove'), [
+ (True, True),
+ (False, False),
+])
+def test_remove_upgrade_artifacts(monkeypatch, exists, should_remove):
+
+ called = [False]
+
+ def mocked_run(cmd, *args, **kwargs):
+ assert cmd[0] == 'rm'
+ assert cmd[1] == '-rf'
+ assert cmd[2] == removeupgradeartifacts.UPGRADE_ARTIFACTS_DIR
+ called[0] = True
+ return {'exit_code': 0, 'stdout': '', 'stderr': ''}
+
+ monkeypatch.setattr(os.path, 'exists', lambda _: exists)
+ monkeypatch.setattr(removeupgradeartifacts, 'run', mocked_run)
+
+ removeupgradeartifacts.process()
+
+ assert called[0] == should_remove
--
2.39.0