forked from rpms/leapp-repository
Update Vendors patch against upstream 47fce173e75408d9a7a26225d389161caf72e244 (0.23.0-1)
The package version 0.23.0-1.elevate.2
This commit is contained in:
parent
15fd026e53
commit
2f853fc90e
@ -3633,6 +3633,217 @@ index 56a94b5d..46c5d9b6 100755
|
|||||||
mount -o "remount,$old_opts" "$NEWROOT"
|
mount -o "remount,$old_opts" "$NEWROOT"
|
||||||
exit $result
|
exit $result
|
||||||
-
|
-
|
||||||
|
diff --git a/repos/system_upgrade/common/actors/commonleappdracutmodules/files/dracut/85sys-upgrade-redhat/module-setup.sh b/repos/system_upgrade/common/actors/commonleappdracutmodules/files/dracut/85sys-upgrade-redhat/module-setup.sh
|
||||||
|
index d73060cb..45f98148 100755
|
||||||
|
--- a/repos/system_upgrade/common/actors/commonleappdracutmodules/files/dracut/85sys-upgrade-redhat/module-setup.sh
|
||||||
|
+++ b/repos/system_upgrade/common/actors/commonleappdracutmodules/files/dracut/85sys-upgrade-redhat/module-setup.sh
|
||||||
|
@@ -102,7 +102,6 @@ install() {
|
||||||
|
inst_binary grep
|
||||||
|
|
||||||
|
# script to actually run the upgrader binary
|
||||||
|
- inst_hook upgrade 49 "$_moddir/mount_usr.sh"
|
||||||
|
inst_hook upgrade 50 "$_moddir/do-upgrade.sh"
|
||||||
|
|
||||||
|
#NOTE: some clean up?.. ideally, everything should be inside the leapp*
|
||||||
|
diff --git a/repos/system_upgrade/common/actors/commonleappdracutmodules/files/dracut/85sys-upgrade-redhat/mount_usr.sh b/repos/system_upgrade/common/actors/commonleappdracutmodules/files/dracut/85sys-upgrade-redhat/mount_usr.sh
|
||||||
|
deleted file mode 100755
|
||||||
|
index 9366ac13..00000000
|
||||||
|
--- a/repos/system_upgrade/common/actors/commonleappdracutmodules/files/dracut/85sys-upgrade-redhat/mount_usr.sh
|
||||||
|
+++ /dev/null
|
||||||
|
@@ -1,148 +0,0 @@
|
||||||
|
-#!/bin/sh
|
||||||
|
-# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
|
||||||
|
-# ex: ts=8 sw=4 sts=4 et filetype=sh
|
||||||
|
-
|
||||||
|
-type info >/dev/null 2>&1 || . /lib/dracut-lib.sh
|
||||||
|
-
|
||||||
|
-export NEWROOT=${NEWROOT:-"/sysroot"}
|
||||||
|
-
|
||||||
|
-filtersubvol() {
|
||||||
|
- _oldifs="$IFS"
|
||||||
|
- IFS=","
|
||||||
|
- set "$@"
|
||||||
|
- IFS="$_oldifs"
|
||||||
|
- while [ $# -gt 0 ]; do
|
||||||
|
- case $1 in
|
||||||
|
- subvol=*) :;;
|
||||||
|
- *) printf '%s' "${1}," ;;
|
||||||
|
- esac
|
||||||
|
- shift
|
||||||
|
- done
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-mount_usr()
|
||||||
|
-{
|
||||||
|
- #
|
||||||
|
- # mount_usr [true | false]
|
||||||
|
- # Expected a "true" value for the last attempt to mount /usr. On the last
|
||||||
|
- # attempt, in case of failure drop to shell.
|
||||||
|
- #
|
||||||
|
- # Return 0 when everything is all right
|
||||||
|
- # In case of failure and /usr has been detected:
|
||||||
|
- # return 2 when $1 is "true" (drop to shell invoked)
|
||||||
|
- # (note: possibly it's nonsense, but to be sure..)
|
||||||
|
- # return 1 otherwise
|
||||||
|
- #
|
||||||
|
- _last_attempt="$1"
|
||||||
|
- # check, if we have to mount the /usr filesystem
|
||||||
|
- while read -r _dev _mp _fs _opts _freq _passno; do
|
||||||
|
- [ "${_dev%%#*}" != "$_dev" ] && continue
|
||||||
|
- if [ "$_mp" = "/usr" ]; then
|
||||||
|
- case "$_dev" in
|
||||||
|
- LABEL=*)
|
||||||
|
- _dev="$(echo "$_dev" | sed 's,/,\\x2f,g')"
|
||||||
|
- _dev="/dev/disk/by-label/${_dev#LABEL=}"
|
||||||
|
- ;;
|
||||||
|
- UUID=*)
|
||||||
|
- _dev="${_dev#block:}"
|
||||||
|
- _dev="/dev/disk/by-uuid/${_dev#UUID=}"
|
||||||
|
- ;;
|
||||||
|
- esac
|
||||||
|
-
|
||||||
|
- # shellcheck disable=SC2154 # Variable root is assigned by dracut
|
||||||
|
- _root_dev=${root#block:}
|
||||||
|
-
|
||||||
|
- if strstr "$_opts" "subvol=" && \
|
||||||
|
- [ "$(stat -c '%D:%i' "$_root_dev")" = "$(stat -c '%D:%i' "$_dev")" ] && \
|
||||||
|
- [ -n "$rflags" ]; then
|
||||||
|
- # for btrfs subvolumes we have to mount /usr with the same rflags
|
||||||
|
- rflags=$(filtersubvol "$rflags")
|
||||||
|
- rflags=${rflags%%,}
|
||||||
|
- _opts="${_opts:+${_opts},}${rflags}"
|
||||||
|
- elif getargbool 0 ro; then
|
||||||
|
- # if "ro" is specified, we want /usr to be mounted read-only
|
||||||
|
- _opts="${_opts:+${_opts},}ro"
|
||||||
|
- elif getargbool 0 rw; then
|
||||||
|
- # if "rw" is specified, we want /usr to be mounted read-write
|
||||||
|
- _opts="${_opts:+${_opts},}rw"
|
||||||
|
- fi
|
||||||
|
- echo "$_dev ${NEWROOT}${_mp} $_fs ${_opts} $_freq $_passno"
|
||||||
|
- _usr_found="1"
|
||||||
|
- break
|
||||||
|
- fi
|
||||||
|
- done < "${NEWROOT}/etc/fstab" >> /etc/fstab
|
||||||
|
-
|
||||||
|
- if [ "$_usr_found" = "" ]; then
|
||||||
|
- # nothing to do
|
||||||
|
- return 0
|
||||||
|
- fi
|
||||||
|
-
|
||||||
|
- info "Mounting /usr with -o $_opts"
|
||||||
|
- mount "${NEWROOT}/usr" 2>&1 | vinfo
|
||||||
|
- mount -o remount,rw "${NEWROOT}/usr"
|
||||||
|
-
|
||||||
|
- if ismounted "${NEWROOT}/usr"; then
|
||||||
|
- # success!!
|
||||||
|
- return 0
|
||||||
|
- fi
|
||||||
|
-
|
||||||
|
- if [ "$_last_attempt" = "true" ]; then
|
||||||
|
- warn "Mounting /usr to ${NEWROOT}/usr failed"
|
||||||
|
- warn "*** Dropping you to a shell; the system will continue"
|
||||||
|
- warn "*** when you leave the shell."
|
||||||
|
- action_on_fail
|
||||||
|
- return 2
|
||||||
|
- fi
|
||||||
|
-
|
||||||
|
- return 1
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-
|
||||||
|
-try_to_mount_usr() {
|
||||||
|
- _last_attempt="$1"
|
||||||
|
- if [ ! -f "${NEWROOT}/etc/fstab" ]; then
|
||||||
|
- warn "File ${NEWROOT}/etc/fstab doesn't exist."
|
||||||
|
- return 1
|
||||||
|
- fi
|
||||||
|
-
|
||||||
|
- # In case we have the LVM command available try make it activate all partitions
|
||||||
|
- if command -v lvm 2>/dev/null 1>/dev/null; then
|
||||||
|
- lvm vgchange --sysinit -a y || {
|
||||||
|
- warn "Detected problem when tried to activate LVM VG."
|
||||||
|
- if [ "$_last_attempt" != "true" ]; then
|
||||||
|
- # this is not last execution, retry
|
||||||
|
- return 1
|
||||||
|
- fi
|
||||||
|
- # NOTE(pstodulk):
|
||||||
|
- # last execution, so call mount_usr anyway
|
||||||
|
- # I am not 100% about lvm vgchange exit codes and I am aware of
|
||||||
|
- # possible warnings, in this last run, let's keep it on mount_usr
|
||||||
|
- # anyway..
|
||||||
|
- }
|
||||||
|
- fi
|
||||||
|
-
|
||||||
|
- mount_usr "$1"
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-_sleep_timeout=15
|
||||||
|
-_last_attempt="false"
|
||||||
|
-for i in 0 1 2 3 4 5 6 7 8 9 10 11; do
|
||||||
|
- info "Storage initialisation: Attempt $i of 11. Wait $_sleep_timeout seconds."
|
||||||
|
- sleep $_sleep_timeout
|
||||||
|
- if [ $i -eq 11 ]; then
|
||||||
|
- _last_attempt="true"
|
||||||
|
- fi
|
||||||
|
- try_to_mount_usr "$_last_attempt" && break
|
||||||
|
-
|
||||||
|
- # something is wrong. In some cases, storage needs more time for the
|
||||||
|
- # initialisation - especially in case of SAN.
|
||||||
|
-
|
||||||
|
- if [ "$_last_attempt" = "true" ]; then
|
||||||
|
- warn "The last attempt to initialize storage has not been successful."
|
||||||
|
- warn "Unknown state of the storage. It is possible that upgrade will be stopped."
|
||||||
|
- break
|
||||||
|
- fi
|
||||||
|
-
|
||||||
|
- warn "Failed attempt to initialize the storage. Retry..."
|
||||||
|
-done
|
||||||
|
-
|
||||||
|
diff --git a/repos/system_upgrade/common/actors/commonleappdracutmodules/files/dracut/90sys-upgrade/initrd-cleanup-override.conf b/repos/system_upgrade/common/actors/commonleappdracutmodules/files/dracut/90sys-upgrade/initrd-cleanup-override.conf
|
||||||
|
new file mode 100644
|
||||||
|
index 00000000..d24e0ef0
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/repos/system_upgrade/common/actors/commonleappdracutmodules/files/dracut/90sys-upgrade/initrd-cleanup-override.conf
|
||||||
|
@@ -0,0 +1,3 @@
|
||||||
|
+[Service]
|
||||||
|
+ExecStart=
|
||||||
|
+ExecStart=-/usr/bin/true
|
||||||
|
diff --git a/repos/system_upgrade/common/actors/commonleappdracutmodules/files/dracut/90sys-upgrade/module-setup.sh b/repos/system_upgrade/common/actors/commonleappdracutmodules/files/dracut/90sys-upgrade/module-setup.sh
|
||||||
|
index 06479fb5..30ae57b3 100755
|
||||||
|
--- a/repos/system_upgrade/common/actors/commonleappdracutmodules/files/dracut/90sys-upgrade/module-setup.sh
|
||||||
|
+++ b/repos/system_upgrade/common/actors/commonleappdracutmodules/files/dracut/90sys-upgrade/module-setup.sh
|
||||||
|
@@ -54,6 +54,17 @@ install() {
|
||||||
|
ln -sf "../${s}.service" "$upgrade_wantsdir"
|
||||||
|
done
|
||||||
|
|
||||||
|
+ # Setup modified initrd-cleanup.service in the upgrade initramfs to enable
|
||||||
|
+ # storage initialisation using systemd-fstab-generator. We want to run the
|
||||||
|
+ # initrd-parse-etc.service but this one triggers also the initrd-cleanup.service
|
||||||
|
+ # which triggers the switch-root and isolated actions that basically kills
|
||||||
|
+ # the original upgrade service when used.
|
||||||
|
+ # The initrd-parse-etc.service has different content across RHEL systems,
|
||||||
|
+ # so we override rather initrd-cleanup.service instead as we do not need
|
||||||
|
+ # that one for the upgrade process.
|
||||||
|
+ mkdir -p "${unitdir}/initrd-cleanup.service.d"
|
||||||
|
+ inst_simple "${_moddir}/initrd-cleanup-override.conf" "${unitdir}/initrd-cleanup.service.d/initrd-cleanup-override.conf"
|
||||||
|
+
|
||||||
|
# just try : set another services into the wantsdir
|
||||||
|
# sysroot.mount \
|
||||||
|
# dracut-mount \
|
||||||
|
diff --git a/repos/system_upgrade/common/actors/commonleappdracutmodules/files/dracut/90sys-upgrade/upgrade.target b/repos/system_upgrade/common/actors/commonleappdracutmodules/files/dracut/90sys-upgrade/upgrade.target
|
||||||
|
index 366b5cab..d2bf7313 100644
|
||||||
|
--- a/repos/system_upgrade/common/actors/commonleappdracutmodules/files/dracut/90sys-upgrade/upgrade.target
|
||||||
|
+++ b/repos/system_upgrade/common/actors/commonleappdracutmodules/files/dracut/90sys-upgrade/upgrade.target
|
||||||
|
@@ -2,7 +2,7 @@
|
||||||
|
Description=System Upgrade
|
||||||
|
Documentation=man:upgrade.target(7)
|
||||||
|
# ##sysinit.target sockets.target initrd-root-fs.target initrd-root-device.target initrd-fs.target
|
||||||
|
-Wants=initrd-root-fs.target initrd-root-device.target initrd-fs.target initrd-usr-fs.target
|
||||||
|
+Wants=initrd-root-fs.target initrd-root-device.target initrd-fs.target initrd-usr-fs.target initrd-parse-etc.service
|
||||||
|
Requires=basic.target sysroot.mount
|
||||||
|
-After=basic.target sysroot.mount
|
||||||
|
+After=basic.target sysroot.mount initrd-fs.target
|
||||||
|
AllowIsolate=yes
|
||||||
diff --git a/repos/system_upgrade/common/actors/distributionsignedrpmscanner/actor.py b/repos/system_upgrade/common/actors/distributionsignedrpmscanner/actor.py
|
diff --git a/repos/system_upgrade/common/actors/distributionsignedrpmscanner/actor.py b/repos/system_upgrade/common/actors/distributionsignedrpmscanner/actor.py
|
||||||
index 003f3fc5..9e7bbf4a 100644
|
index 003f3fc5..9e7bbf4a 100644
|
||||||
--- a/repos/system_upgrade/common/actors/distributionsignedrpmscanner/actor.py
|
--- a/repos/system_upgrade/common/actors/distributionsignedrpmscanner/actor.py
|
||||||
@ -3837,6 +4048,732 @@ index 582a5821..18f2c33f 100644
|
|||||||
+ to_reinstall=list(to_reinstall),
|
+ to_reinstall=list(to_reinstall),
|
||||||
modules_to_reset=list(modules_to_reset.values()),
|
modules_to_reset=list(modules_to_reset.values()),
|
||||||
modules_to_enable=list(modules_to_enable.values())))
|
modules_to_enable=list(modules_to_enable.values())))
|
||||||
|
diff --git a/repos/system_upgrade/common/actors/initramfs/enable_lvm_autoactivation/actor.py b/repos/system_upgrade/common/actors/initramfs/enable_lvm_autoactivation/actor.py
|
||||||
|
new file mode 100644
|
||||||
|
index 00000000..aba60645
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/repos/system_upgrade/common/actors/initramfs/enable_lvm_autoactivation/actor.py
|
||||||
|
@@ -0,0 +1,21 @@
|
||||||
|
+from leapp.actors import Actor
|
||||||
|
+from leapp.libraries.actor import enable_lvm_autoactivation as enable_lvm_autoactivation_lib
|
||||||
|
+from leapp.models import DistributionSignedRPM, UpgradeInitramfsTasks
|
||||||
|
+from leapp.tags import FactsPhaseTag, IPUWorkflowTag
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+class EnableLVMAutoactivation(Actor):
|
||||||
|
+ """
|
||||||
|
+ Enable LVM autoactivation in upgrade initramfs.
|
||||||
|
+
|
||||||
|
+ Produce instructions for upgrade initramfs generation that will result in LVM
|
||||||
|
+ autoactivation in the initramfs.
|
||||||
|
+ """
|
||||||
|
+
|
||||||
|
+ name = 'enable_lvm_autoactivation'
|
||||||
|
+ consumes = (DistributionSignedRPM,)
|
||||||
|
+ produces = (UpgradeInitramfsTasks, )
|
||||||
|
+ tags = (FactsPhaseTag, IPUWorkflowTag)
|
||||||
|
+
|
||||||
|
+ def process(self):
|
||||||
|
+ enable_lvm_autoactivation_lib.emit_lvm_autoactivation_instructions()
|
||||||
|
diff --git a/repos/system_upgrade/common/actors/initramfs/enable_lvm_autoactivation/libraries/enable_lvm_autoactivation.py b/repos/system_upgrade/common/actors/initramfs/enable_lvm_autoactivation/libraries/enable_lvm_autoactivation.py
|
||||||
|
new file mode 100644
|
||||||
|
index 00000000..e312277b
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/repos/system_upgrade/common/actors/initramfs/enable_lvm_autoactivation/libraries/enable_lvm_autoactivation.py
|
||||||
|
@@ -0,0 +1,21 @@
|
||||||
|
+from leapp.libraries.common.rpms import has_package
|
||||||
|
+from leapp.libraries.stdlib import api
|
||||||
|
+from leapp.models import DistributionSignedRPM, UpgradeInitramfsTasks
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def emit_lvm_autoactivation_instructions():
|
||||||
|
+ if not has_package(DistributionSignedRPM, 'lvm2'):
|
||||||
|
+ api.current_logger().debug(
|
||||||
|
+ 'Upgrade initramfs will not autoenable LVM devices - `lvm2` RPM is not installed.'
|
||||||
|
+ )
|
||||||
|
+ return
|
||||||
|
+
|
||||||
|
+ # the 69-dm-lvm.rules trigger pvscan and vgchange when LVM device is detected
|
||||||
|
+ files_to_include = [
|
||||||
|
+ '/usr/sbin/pvscan',
|
||||||
|
+ '/usr/sbin/vgchange',
|
||||||
|
+ '/usr/lib/udev/rules.d/69-dm-lvm.rules'
|
||||||
|
+ ]
|
||||||
|
+ lvm_autoactivation_instructions = UpgradeInitramfsTasks(include_files=files_to_include)
|
||||||
|
+
|
||||||
|
+ api.produce(lvm_autoactivation_instructions)
|
||||||
|
diff --git a/repos/system_upgrade/common/actors/initramfs/enable_lvm_autoactivation/tests/test_lvm_autoactivation_enablement.py b/repos/system_upgrade/common/actors/initramfs/enable_lvm_autoactivation/tests/test_lvm_autoactivation_enablement.py
|
||||||
|
new file mode 100644
|
||||||
|
index 00000000..c5150aea
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/repos/system_upgrade/common/actors/initramfs/enable_lvm_autoactivation/tests/test_lvm_autoactivation_enablement.py
|
||||||
|
@@ -0,0 +1,50 @@
|
||||||
|
+from leapp.libraries.actor import enable_lvm_autoactivation
|
||||||
|
+from leapp.libraries.common.testutils import CurrentActorMocked, produce_mocked
|
||||||
|
+from leapp.libraries.stdlib import api
|
||||||
|
+from leapp.models import DistributionSignedRPM, RPM, UpgradeInitramfsTasks
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def test_emit_lvm_autoactivation_instructions_produces_correct_message(monkeypatch):
|
||||||
|
+ """Test that emit_lvm_autoactivation_instructions produces UpgradeInitramfsTasks with correct files."""
|
||||||
|
+ lvm_package = RPM(
|
||||||
|
+ name='lvm2',
|
||||||
|
+ version='2',
|
||||||
|
+ release='1',
|
||||||
|
+ epoch='1',
|
||||||
|
+ packager='',
|
||||||
|
+ arch='x86_64',
|
||||||
|
+ pgpsig='RSA/SHA256, Mon 01 Jan 1970 00:00:00 AM -03, Key ID 199e2f91fd431d51'
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ msgs = [
|
||||||
|
+ DistributionSignedRPM(items=[lvm_package])
|
||||||
|
+ ]
|
||||||
|
+ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=msgs))
|
||||||
|
+ monkeypatch.setattr(api, 'produce', produce_mocked())
|
||||||
|
+
|
||||||
|
+ enable_lvm_autoactivation.emit_lvm_autoactivation_instructions()
|
||||||
|
+
|
||||||
|
+ assert api.produce.called == 1
|
||||||
|
+
|
||||||
|
+ produced_msg = api.produce.model_instances[0]
|
||||||
|
+
|
||||||
|
+ assert isinstance(produced_msg, UpgradeInitramfsTasks)
|
||||||
|
+
|
||||||
|
+ expected_files = [
|
||||||
|
+ '/usr/sbin/pvscan',
|
||||||
|
+ '/usr/sbin/vgchange',
|
||||||
|
+ '/usr/lib/udev/rules.d/69-dm-lvm.rules'
|
||||||
|
+ ]
|
||||||
|
+ assert produced_msg.include_files == expected_files
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def test_no_action_if_lvm_rpm_missing(monkeypatch):
|
||||||
|
+ msgs = [
|
||||||
|
+ DistributionSignedRPM(items=[])
|
||||||
|
+ ]
|
||||||
|
+ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=msgs))
|
||||||
|
+ monkeypatch.setattr(api, 'produce', produce_mocked())
|
||||||
|
+
|
||||||
|
+ enable_lvm_autoactivation.emit_lvm_autoactivation_instructions()
|
||||||
|
+
|
||||||
|
+ assert api.produce.called == 0
|
||||||
|
diff --git a/repos/system_upgrade/common/actors/initramfs/mount_units_generator/actor.py b/repos/system_upgrade/common/actors/initramfs/mount_units_generator/actor.py
|
||||||
|
new file mode 100644
|
||||||
|
index 00000000..5fe25515
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/repos/system_upgrade/common/actors/initramfs/mount_units_generator/actor.py
|
||||||
|
@@ -0,0 +1,22 @@
|
||||||
|
+from leapp.actors import Actor
|
||||||
|
+from leapp.libraries.actor import mount_unit_generator as mount_unit_generator_lib
|
||||||
|
+from leapp.models import TargetUserSpaceInfo, UpgradeInitramfsTasks
|
||||||
|
+from leapp.tags import InterimPreparationPhaseTag, IPUWorkflowTag
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+class MountUnitGenerator(Actor):
|
||||||
|
+ """
|
||||||
|
+ Sets up storage initialization using systemd's mount units in the upgrade container.
|
||||||
|
+ """
|
||||||
|
+
|
||||||
|
+ name = 'mount_unit_generator'
|
||||||
|
+ consumes = (
|
||||||
|
+ TargetUserSpaceInfo,
|
||||||
|
+ )
|
||||||
|
+ produces = (
|
||||||
|
+ UpgradeInitramfsTasks,
|
||||||
|
+ )
|
||||||
|
+ tags = (IPUWorkflowTag, InterimPreparationPhaseTag)
|
||||||
|
+
|
||||||
|
+ def process(self):
|
||||||
|
+ mount_unit_generator_lib.setup_storage_initialization()
|
||||||
|
diff --git a/repos/system_upgrade/common/actors/initramfs/mount_units_generator/libraries/mount_unit_generator.py b/repos/system_upgrade/common/actors/initramfs/mount_units_generator/libraries/mount_unit_generator.py
|
||||||
|
new file mode 100644
|
||||||
|
index 00000000..e1060559
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/repos/system_upgrade/common/actors/initramfs/mount_units_generator/libraries/mount_unit_generator.py
|
||||||
|
@@ -0,0 +1,307 @@
|
||||||
|
+import os
|
||||||
|
+import shutil
|
||||||
|
+import tempfile
|
||||||
|
+
|
||||||
|
+from leapp.exceptions import StopActorExecutionError
|
||||||
|
+from leapp.libraries.common import mounting
|
||||||
|
+from leapp.libraries.stdlib import api, CalledProcessError, run
|
||||||
|
+from leapp.models import TargetUserSpaceInfo, UpgradeInitramfsTasks
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def run_systemd_fstab_generator(output_directory):
|
||||||
|
+ api.current_logger().debug(
|
||||||
|
+ 'Generating mount units for the source system into {}'.format(output_directory)
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ try:
|
||||||
|
+ generator_cmd = [
|
||||||
|
+ '/usr/lib/systemd/system-generators/systemd-fstab-generator',
|
||||||
|
+ output_directory,
|
||||||
|
+ output_directory,
|
||||||
|
+ output_directory
|
||||||
|
+ ]
|
||||||
|
+ run(generator_cmd)
|
||||||
|
+ except CalledProcessError as error:
|
||||||
|
+ api.current_logger().error(
|
||||||
|
+ 'Failed to generate mount units using systemd-fstab-generator. Error: {}'.format(error)
|
||||||
|
+ )
|
||||||
|
+ details = {'details': str(error)}
|
||||||
|
+ raise StopActorExecutionError(
|
||||||
|
+ 'Failed to generate mount units using systemd-fstab-generator',
|
||||||
|
+ details
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ api.current_logger().debug(
|
||||||
|
+ 'Mount units successfully generated into {}'.format(output_directory)
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def _read_unit_file_lines(unit_file_path): # Encapsulate IO for tests
|
||||||
|
+ with open(unit_file_path) as unit_file:
|
||||||
|
+ return unit_file.readlines()
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def _write_unit_file_lines(unit_file_path, lines): # Encapsulate IO for tests
|
||||||
|
+ with open(unit_file_path, 'w') as unit_file:
|
||||||
|
+ unit_file.write('\n'.join(lines) + '\n')
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def _delete_file(file_path):
|
||||||
|
+ os.unlink(file_path)
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def _prefix_mount_unit_with_sysroot(mount_unit_path, new_unit_destination):
|
||||||
|
+ """
|
||||||
|
+ Prefix the mount target with /sysroot as expected in the upgrade initramfs.
|
||||||
|
+
|
||||||
|
+ A new mount unit file is written to new_unit_destination.
|
||||||
|
+ """
|
||||||
|
+ # NOTE(pstodulk): Note that right now we update just the 'Where' key, however
|
||||||
|
+ # what about RequiresMountsFor, .. there could be some hidden dragons.
|
||||||
|
+ # In case of issues, investigate these values in generated unit files.
|
||||||
|
+ api.current_logger().debug(
|
||||||
|
+ 'Prefixing {}\'s mount target with /sysroot. Output will be written to {}'.format(
|
||||||
|
+ mount_unit_path,
|
||||||
|
+ new_unit_destination
|
||||||
|
+ )
|
||||||
|
+ )
|
||||||
|
+ unit_lines = _read_unit_file_lines(mount_unit_path)
|
||||||
|
+
|
||||||
|
+ output_lines = []
|
||||||
|
+ for line in unit_lines:
|
||||||
|
+ line = line.strip()
|
||||||
|
+ if not line.startswith('Where='):
|
||||||
|
+ output_lines.append(line)
|
||||||
|
+ continue
|
||||||
|
+
|
||||||
|
+ _, destination = line.split('=', 1)
|
||||||
|
+ new_destination = os.path.join('/sysroot', destination.lstrip('/'))
|
||||||
|
+
|
||||||
|
+ output_lines.append('Where={}'.format(new_destination))
|
||||||
|
+
|
||||||
|
+ _write_unit_file_lines(new_unit_destination, output_lines)
|
||||||
|
+
|
||||||
|
+ api.current_logger().debug(
|
||||||
|
+ 'Done. Modified mount unit successfully written to {}'.format(new_unit_destination)
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def prefix_all_mount_units_with_sysroot(dir_containing_units):
|
||||||
|
+ for unit_file_path in os.listdir(dir_containing_units):
|
||||||
|
+ # systemd requires mount path to be in the unit name
|
||||||
|
+ modified_unit_destination = 'sysroot-{}'.format(unit_file_path)
|
||||||
|
+ modified_unit_destination = os.path.join(dir_containing_units, modified_unit_destination)
|
||||||
|
+
|
||||||
|
+ unit_file_path = os.path.join(dir_containing_units, unit_file_path)
|
||||||
|
+
|
||||||
|
+ if not unit_file_path.endswith('.mount'):
|
||||||
|
+ api.current_logger().debug(
|
||||||
|
+ 'Skipping {} when prefixing mount units with /sysroot - not a mount unit.'.format(
|
||||||
|
+ unit_file_path
|
||||||
|
+ )
|
||||||
|
+ )
|
||||||
|
+ continue
|
||||||
|
+
|
||||||
|
+ _prefix_mount_unit_with_sysroot(unit_file_path, modified_unit_destination)
|
||||||
|
+
|
||||||
|
+ _delete_file(unit_file_path)
|
||||||
|
+ api.current_logger().debug('Original mount unit {} removed.'.format(unit_file_path))
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def _fix_symlinks_in_dir(dir_containing_mount_units, target_dir):
|
||||||
|
+ """
|
||||||
|
+ Fix broken symlinks in given target_dir due to us modifying (renaming) the mount units.
|
||||||
|
+
|
||||||
|
+ The target_dir contains symlinks to the (mount) units that are required
|
||||||
|
+ in order for the local-fs.target to be reached. However, we renamed these units to reflect
|
||||||
|
+ that we have changed their mount destinations by prefixing the mount destination with /sysroot.
|
||||||
|
+ Hence, we regenerate the symlinks.
|
||||||
|
+ """
|
||||||
|
+
|
||||||
|
+ target_dir_path = os.path.join(dir_containing_mount_units, target_dir)
|
||||||
|
+ if not os.path.exists(target_dir_path):
|
||||||
|
+ api.current_logger().debug(
|
||||||
|
+ 'The {} directory does not exist. Skipping'
|
||||||
|
+ .format(target_dir)
|
||||||
|
+ )
|
||||||
|
+ return
|
||||||
|
+
|
||||||
|
+ api.current_logger().debug(
|
||||||
|
+ 'Removing the old {} directory from {}.'
|
||||||
|
+ .format(target_dir, dir_containing_mount_units)
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ shutil.rmtree(target_dir_path)
|
||||||
|
+ os.mkdir(target_dir_path)
|
||||||
|
+
|
||||||
|
+ api.current_logger().debug('Populating {} with new symlinks.'.format(target_dir))
|
||||||
|
+
|
||||||
|
+ for unit_file in os.listdir(dir_containing_mount_units):
|
||||||
|
+ if not unit_file.endswith('.mount'):
|
||||||
|
+ continue
|
||||||
|
+
|
||||||
|
+ place_fastlink_at = os.path.join(target_dir_path, unit_file)
|
||||||
|
+ fastlink_points_to = os.path.join('../', unit_file)
|
||||||
|
+ try:
|
||||||
|
+ run(['ln', '-s', fastlink_points_to, place_fastlink_at])
|
||||||
|
+
|
||||||
|
+ api.current_logger().debug(
|
||||||
|
+ 'Dependency on {} created.'.format(unit_file)
|
||||||
|
+ )
|
||||||
|
+ except CalledProcessError as err:
|
||||||
|
+ err_descr = (
|
||||||
|
+ 'Failed to create required unit dependencies under {} for the upgrade initramfs.'
|
||||||
|
+ .format(target_dir)
|
||||||
|
+ )
|
||||||
|
+ details = {'details': str(err)}
|
||||||
|
+ raise StopActorExecutionError(err_descr, details=details)
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def fix_symlinks_in_targets(dir_containing_mount_units):
|
||||||
|
+ """
|
||||||
|
+ Fix broken symlinks in *.target.* directories caused by earlier modified mount units.
|
||||||
|
+
|
||||||
|
+ Generated mount unit files are part of one of systemd targets (list below),
|
||||||
|
+ which means that a symlink from a systemd target to exists for each of
|
||||||
|
+ them. Based on this, systemd knows when (local or remote file systems?)
|
||||||
|
+ they must (".requires" suffix") or could (".wants" suffix) be mounted.
|
||||||
|
+ See the man 5 systemd.mount for more details how mount units are split into
|
||||||
|
+ these targets.
|
||||||
|
+
|
||||||
|
+ The list of possible target directories where these mount units could end:
|
||||||
|
+ * local-fs.target.requires
|
||||||
|
+ * local-fs.target.wants
|
||||||
|
+ * local-fs-pre.target.requires
|
||||||
|
+ * local-fs-pre.target.wants
|
||||||
|
+ * remote-fs.target.requires
|
||||||
|
+ * remote-fs.target.wants
|
||||||
|
+ * remote-fs-pre.target.requires
|
||||||
|
+ * remote-fs-pre.target.wants
|
||||||
|
+ Most likely, unit files are not generated for "*pre*" targets, but to be
|
||||||
|
+ sure really. Longer list does not cause any issues in this code.
|
||||||
|
+
|
||||||
|
+ In most cases, "local-fs.target.requires" is the only important directory
|
||||||
|
+ for us during the upgrade. But in some (sometimes common) cases we will
|
||||||
|
+ need some of the others as well.
|
||||||
|
+
|
||||||
|
+ These directories do not have to necessarily exists if there are no mount
|
||||||
|
+ unit files that could be put there. But most likely "local-fs.target.requires"
|
||||||
|
+ will always exists.
|
||||||
|
+ """
|
||||||
|
+ dir_list = [
|
||||||
|
+ 'local-fs.target.requires',
|
||||||
|
+ 'local-fs.target.wants',
|
||||||
|
+ 'local-fs-pre.target.requires',
|
||||||
|
+ 'local-fs-pre.target.wants',
|
||||||
|
+ 'remote-fs.target.requires',
|
||||||
|
+ 'remote-fs.target.wants',
|
||||||
|
+ 'remote-fs-pre.target.requires',
|
||||||
|
+ 'remote-fs-pre.target.wants',
|
||||||
|
+ ]
|
||||||
|
+ for tdir in dir_list:
|
||||||
|
+ _fix_symlinks_in_dir(dir_containing_mount_units, tdir)
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def copy_units_into_system_location(upgrade_container_ctx, dir_with_our_mount_units):
|
||||||
|
+ """
|
||||||
|
+ Copy units and their .wants/.requires directories into the target userspace container.
|
||||||
|
+
|
||||||
|
+ :return: A list of files in the target userspace that were created by copying.
|
||||||
|
+ :rtype: list[str]
|
||||||
|
+ """
|
||||||
|
+ dest_inside_container = '/usr/lib/systemd/system'
|
||||||
|
+
|
||||||
|
+ api.current_logger().debug(
|
||||||
|
+ 'Copying generated mount units for upgrade from {} to {}'.format(
|
||||||
|
+ dir_with_our_mount_units,
|
||||||
|
+ upgrade_container_ctx.full_path(dest_inside_container)
|
||||||
|
+ )
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ copied_files = []
|
||||||
|
+ prefix_len_to_drop = len(upgrade_container_ctx.base_dir)
|
||||||
|
+
|
||||||
|
+ # We cannot rely on mounting library when copying into container
|
||||||
|
+ # as we want to control what happens to symlinks and
|
||||||
|
+ # shutil.copytree in Python3.6 fails if dst directory exists already
|
||||||
|
+ # - which happens in some cases when copying these files.
|
||||||
|
+ for root, dummy_dirs, files in os.walk(dir_with_our_mount_units):
|
||||||
|
+ rel_path = os.path.relpath(root, dir_with_our_mount_units)
|
||||||
|
+ if rel_path == '.':
|
||||||
|
+ rel_path = ''
|
||||||
|
+ dst_dir = os.path.join(upgrade_container_ctx.full_path(dest_inside_container), rel_path)
|
||||||
|
+ os.makedirs(dst_dir, mode=0o755, exist_ok=True)
|
||||||
|
+
|
||||||
|
+ for file in files:
|
||||||
|
+ src_file = os.path.join(root, file)
|
||||||
|
+ dst_file = os.path.join(dst_dir, file)
|
||||||
|
+ api.current_logger().debug(
|
||||||
|
+ 'Copying mount unit file {} to {}'.format(src_file, dst_file)
|
||||||
|
+ )
|
||||||
|
+ if os.path.islink(dst_file):
|
||||||
|
+ # If the target file already exists and it is a symlink, it will
|
||||||
|
+ # fail and we want to overwrite this.
|
||||||
|
+ # NOTE(pstodulk): You could think that it cannot happen, but
|
||||||
|
+ # in future possibly it could happen, so let's rather be careful
|
||||||
|
+ # and handle it. If the dst file exists, we want to overwrite it
|
||||||
|
+ # for sure
|
||||||
|
+ _delete_file(dst_file)
|
||||||
|
+ shutil.copy2(src_file, dst_file, follow_symlinks=False)
|
||||||
|
+ copied_files.append(dst_file[prefix_len_to_drop:])
|
||||||
|
+
|
||||||
|
+ return copied_files
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def remove_units_for_targets_that_are_already_mounted_by_dracut(dir_with_our_mount_units):
|
||||||
|
+ """
|
||||||
|
+ Remove mount units for mount targets that are already mounted by dracut.
|
||||||
|
+
|
||||||
|
+ Namely, remove mount units:
|
||||||
|
+ '-.mount' (mounts /)
|
||||||
|
+ 'usr.mount' (mounts /usr)
|
||||||
|
+ """
|
||||||
|
+
|
||||||
|
+ # NOTE: remount-fs.service creates dependency cycles that are nondeterministically broken
|
||||||
|
+ # by systemd, causing unpredictable failures. The service is supposed to remount root
|
||||||
|
+ # and /usr, reapplying mount options from /etc/fstab. However, the fstab file present in
|
||||||
|
+ # the initramfs is not the fstab from the source system, and, therefore, it is pointless
|
||||||
|
+ # to require the service. It would make sense after we switched root during normal boot
|
||||||
|
+ # process.
|
||||||
|
+ already_mounted_units = [
|
||||||
|
+ '-.mount',
|
||||||
|
+ 'usr.mount',
|
||||||
|
+ 'local-fs.target.wants/systemd-remount-fs.service'
|
||||||
|
+ ]
|
||||||
|
+
|
||||||
|
+ for unit in already_mounted_units:
|
||||||
|
+ unit_location = os.path.join(dir_with_our_mount_units, unit)
|
||||||
|
+
|
||||||
|
+ if not os.path.exists(unit_location):
|
||||||
|
+ api.current_logger().debug('The {} unit does not exists, no need to remove it.'.format(unit))
|
||||||
|
+ continue
|
||||||
|
+
|
||||||
|
+ _delete_file(unit_location)
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def request_units_inclusion_in_initramfs(files_to_include):
|
||||||
|
+ api.current_logger().debug('Including the following files into initramfs: {}'.format(files_to_include))
|
||||||
|
+
|
||||||
|
+ additional_files = [
|
||||||
|
+ '/usr/sbin/swapon' # If the system has swap, we have also generated a swap unit to activate it
|
||||||
|
+ ]
|
||||||
|
+
|
||||||
|
+ tasks = UpgradeInitramfsTasks(include_files=files_to_include + additional_files)
|
||||||
|
+ api.produce(tasks)
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def setup_storage_initialization():
|
||||||
|
+ userspace_info = next(api.consume(TargetUserSpaceInfo), None)
|
||||||
|
+
|
||||||
|
+ with mounting.NspawnActions(base_dir=userspace_info.path) as upgrade_container_ctx:
|
||||||
|
+ with tempfile.TemporaryDirectory(dir='/var/lib/leapp/', prefix='tmp_systemd_fstab_') as workspace_path:
|
||||||
|
+ run_systemd_fstab_generator(workspace_path)
|
||||||
|
+ remove_units_for_targets_that_are_already_mounted_by_dracut(workspace_path)
|
||||||
|
+ prefix_all_mount_units_with_sysroot(workspace_path)
|
||||||
|
+ fix_symlinks_in_targets(workspace_path)
|
||||||
|
+ mount_unit_files = copy_units_into_system_location(upgrade_container_ctx, workspace_path)
|
||||||
|
+ request_units_inclusion_in_initramfs(mount_unit_files)
|
||||||
|
diff --git a/repos/system_upgrade/common/actors/initramfs/mount_units_generator/tests/test_mount_unit_generation.py b/repos/system_upgrade/common/actors/initramfs/mount_units_generator/tests/test_mount_unit_generation.py
|
||||||
|
new file mode 100644
|
||||||
|
index 00000000..b814f6ce
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/repos/system_upgrade/common/actors/initramfs/mount_units_generator/tests/test_mount_unit_generation.py
|
||||||
|
@@ -0,0 +1,269 @@
|
||||||
|
+import os
|
||||||
|
+import shutil
|
||||||
|
+
|
||||||
|
+import pytest
|
||||||
|
+
|
||||||
|
+from leapp.exceptions import StopActorExecutionError
|
||||||
|
+from leapp.libraries.actor import mount_unit_generator
|
||||||
|
+from leapp.libraries.common.testutils import logger_mocked
|
||||||
|
+from leapp.libraries.stdlib import api, CalledProcessError
|
||||||
|
+from leapp.models import TargetUserSpaceInfo, UpgradeInitramfsTasks
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def test_run_systemd_fstab_generator_successful_generation(monkeypatch):
|
||||||
|
+ """Test successful mount unit generation."""
|
||||||
|
+
|
||||||
|
+ output_dir = '/tmp/test_output'
|
||||||
|
+ expected_cmd = [
|
||||||
|
+ '/usr/lib/systemd/system-generators/systemd-fstab-generator',
|
||||||
|
+ output_dir,
|
||||||
|
+ output_dir,
|
||||||
|
+ output_dir
|
||||||
|
+ ]
|
||||||
|
+
|
||||||
|
+ def mock_run(command):
|
||||||
|
+ assert command == expected_cmd
|
||||||
|
+
|
||||||
|
+ return {
|
||||||
|
+ "stdout": "",
|
||||||
|
+ "stderr": "",
|
||||||
|
+ "exit_code": 0,
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ monkeypatch.setattr(mount_unit_generator, 'run', mock_run)
|
||||||
|
+ mount_unit_generator.run_systemd_fstab_generator(output_dir)
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def test_run_systemd_fstab_generator_failure(monkeypatch):
|
||||||
|
+ """Test handling of systemd-fstab-generator failure."""
|
||||||
|
+ output_dir = '/tmp/test_output'
|
||||||
|
+ expected_cmd = [
|
||||||
|
+ '/usr/lib/systemd/system-generators/systemd-fstab-generator',
|
||||||
|
+ output_dir,
|
||||||
|
+ output_dir,
|
||||||
|
+ output_dir
|
||||||
|
+ ]
|
||||||
|
+
|
||||||
|
+ def mock_run(command):
|
||||||
|
+ assert command == expected_cmd
|
||||||
|
+ raise CalledProcessError(message='Generator failed', command=['test'], result={'exit_code': 1})
|
||||||
|
+
|
||||||
|
+ monkeypatch.setattr(mount_unit_generator, 'run', mock_run)
|
||||||
|
+ monkeypatch.setattr(api, 'current_logger', logger_mocked())
|
||||||
|
+
|
||||||
|
+ with pytest.raises(StopActorExecutionError):
|
||||||
|
+ mount_unit_generator.run_systemd_fstab_generator(output_dir)
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def test_prefix_mount_unit_with_sysroot(monkeypatch):
|
||||||
|
+ """Test prefixing a single mount unit with /sysroot."""
|
||||||
|
+ monkeypatch.setattr(api, 'current_logger', logger_mocked())
|
||||||
|
+
|
||||||
|
+ input_content = [
|
||||||
|
+ "[Unit]\n",
|
||||||
|
+ "Description=Test Mount\n",
|
||||||
|
+ "[Mount]\n",
|
||||||
|
+ "Where=/home\n",
|
||||||
|
+ "What=/dev/sda1\n"
|
||||||
|
+ ]
|
||||||
|
+
|
||||||
|
+ expected_output_lines = [
|
||||||
|
+ "[Unit]",
|
||||||
|
+ "Description=Test Mount",
|
||||||
|
+ "[Mount]",
|
||||||
|
+ "Where=/sysroot/home",
|
||||||
|
+ "What=/dev/sda1"
|
||||||
|
+ ]
|
||||||
|
+
|
||||||
|
+ def mock_read_unit_file_lines(unit_file_path):
|
||||||
|
+ return input_content
|
||||||
|
+
|
||||||
|
+ def mock_write_unit_file_lines(unit_file_path, lines):
|
||||||
|
+ assert unit_file_path == '/test/output.mount'
|
||||||
|
+ assert lines == expected_output_lines
|
||||||
|
+
|
||||||
|
+ monkeypatch.setattr(mount_unit_generator, '_read_unit_file_lines', mock_read_unit_file_lines)
|
||||||
|
+ monkeypatch.setattr(mount_unit_generator, '_write_unit_file_lines', mock_write_unit_file_lines)
|
||||||
|
+
|
||||||
|
+ mount_unit_generator._prefix_mount_unit_with_sysroot(
|
||||||
|
+ '/test/input.mount',
|
||||||
|
+ '/test/output.mount'
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def test_prefix_all_mount_units_with_sysroot(monkeypatch):
|
||||||
|
+ """Test prefixing all mount units in a directory."""
|
||||||
|
+
|
||||||
|
+ expected_changes = {
|
||||||
|
+ '/test/dir/home.mount': {
|
||||||
|
+ 'new_unit_destination': '/test/dir/sysroot-home.mount',
|
||||||
|
+ 'should_be_deleted': True,
|
||||||
|
+ 'deleted': False,
|
||||||
|
+ },
|
||||||
|
+ '/test/dir/var.mount': {
|
||||||
|
+ 'new_unit_destination': '/test/dir/sysroot-var.mount',
|
||||||
|
+ 'should_be_deleted': True,
|
||||||
|
+ 'deleted': False,
|
||||||
|
+ },
|
||||||
|
+ '/test/dir/not-a-mount.service': {
|
||||||
|
+ 'new_unit_destination': None,
|
||||||
|
+ 'should_be_deleted': False,
|
||||||
|
+ 'deleted': False,
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ def mock_listdir(dir_path):
|
||||||
|
+ return ['home.mount', 'var.mount', 'not-a-mount.service']
|
||||||
|
+
|
||||||
|
+ def mock_delete_file(file_path):
|
||||||
|
+ assert file_path in expected_changes
|
||||||
|
+ expected_changes[file_path]['deleted'] = True
|
||||||
|
+
|
||||||
|
+ def mock_prefix(unit_file_path, new_unit_destination):
|
||||||
|
+ assert expected_changes[unit_file_path]['new_unit_destination'] == new_unit_destination
|
||||||
|
+
|
||||||
|
+ monkeypatch.setattr('os.listdir', mock_listdir)
|
||||||
|
+ monkeypatch.setattr(mount_unit_generator, '_delete_file', mock_delete_file)
|
||||||
|
+ monkeypatch.setattr(mount_unit_generator, '_prefix_mount_unit_with_sysroot', mock_prefix)
|
||||||
|
+
|
||||||
|
+ mount_unit_generator.prefix_all_mount_units_with_sysroot('/test/dir')
|
||||||
|
+
|
||||||
|
+ for original_mount_unit_location in expected_changes:
|
||||||
|
+ should_be_deleted = expected_changes[original_mount_unit_location]['should_be_deleted']
|
||||||
|
+ was_deleted = expected_changes[original_mount_unit_location]['deleted']
|
||||||
|
+ assert should_be_deleted == was_deleted
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+@pytest.mark.parametrize('dirname', (
|
||||||
|
+ 'local-fs.target.requires',
|
||||||
|
+ 'local-fs.target.wants',
|
||||||
|
+ 'local-fs-pre.target.requires',
|
||||||
|
+ 'local-fs-pre.target.wants',
|
||||||
|
+ 'remote-fs.target.requires',
|
||||||
|
+ 'remote-fs.target.wants',
|
||||||
|
+ 'remote-fs-pre.target.requires',
|
||||||
|
+ 'remote-fs-pre.target.wants',
|
||||||
|
+))
|
||||||
|
+def test_fix_symlinks_in_dir(monkeypatch, dirname):
|
||||||
|
+ """Test fixing local-fs.target.requires symlinks."""
|
||||||
|
+
|
||||||
|
+ DIR_PATH = os.path.join('/test/dir/', dirname)
|
||||||
|
+
|
||||||
|
+ def mock_rmtree(dir_path):
|
||||||
|
+ assert dir_path == DIR_PATH
|
||||||
|
+
|
||||||
|
+ def mock_mkdir(dir_path):
|
||||||
|
+ assert dir_path == DIR_PATH
|
||||||
|
+
|
||||||
|
+ def mock_listdir(dir_path):
|
||||||
|
+ return ['sysroot-home.mount', 'sysroot-var.mount', 'not-a-mount.service']
|
||||||
|
+
|
||||||
|
+ def mock_os_path_exist(dir_path):
|
||||||
|
+ assert dir_path == DIR_PATH
|
||||||
|
+ return dir_path == DIR_PATH
|
||||||
|
+
|
||||||
|
+ expected_calls = [
|
||||||
|
+ ['ln', '-s', '../sysroot-home.mount', os.path.join(DIR_PATH, 'sysroot-home.mount')],
|
||||||
|
+ ['ln', '-s', '../sysroot-var.mount', os.path.join(DIR_PATH, 'sysroot-var.mount')]
|
||||||
|
+ ]
|
||||||
|
+ call_count = 0
|
||||||
|
+
|
||||||
|
+ def mock_run(command):
|
||||||
|
+ nonlocal call_count
|
||||||
|
+ assert command in expected_calls
|
||||||
|
+ call_count += 1
|
||||||
|
+ return {
|
||||||
|
+ "stdout": "",
|
||||||
|
+ "stderr": "",
|
||||||
|
+ "exit_code": 0,
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ monkeypatch.setattr('shutil.rmtree', mock_rmtree)
|
||||||
|
+ monkeypatch.setattr('os.mkdir', mock_mkdir)
|
||||||
|
+ monkeypatch.setattr('os.listdir', mock_listdir)
|
||||||
|
+ monkeypatch.setattr('os.path.exists', mock_os_path_exist)
|
||||||
|
+ monkeypatch.setattr(mount_unit_generator, 'run', mock_run)
|
||||||
|
+
|
||||||
|
+ mount_unit_generator._fix_symlinks_in_dir('/test/dir', dirname)
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+# Test the copy_units_into_system_location function
|
||||||
|
+def test_copy_units_mixed_content(monkeypatch):
|
||||||
|
+ """Test copying units with mixed files and directories."""
|
||||||
|
+
|
||||||
|
+ def mock_walk(dir_path):
|
||||||
|
+ tuples_to_yield = [
|
||||||
|
+ ('/source/dir', ['local-fs.target.requires'], ['unit1.mount', 'unit2.mount']),
|
||||||
|
+ ('/source/dir/local-fs.target.requires', [], ['unit1.mount', 'unit2.mount']),
|
||||||
|
+ ]
|
||||||
|
+ for i in tuples_to_yield:
|
||||||
|
+ yield i
|
||||||
|
+
|
||||||
|
+ def mock_isdir(path):
|
||||||
|
+ return 'local-fs.target.requires' in path
|
||||||
|
+
|
||||||
|
+ def _make_couple(sub_path):
|
||||||
|
+ return (
|
||||||
|
+ os.path.join('/source/dir/', sub_path),
|
||||||
|
+ os.path.join('/container/usr/lib/systemd/system/', sub_path)
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ def mock_copy2(src, dst, follow_symlinks=True):
|
||||||
|
+ valid_combinations = [
|
||||||
|
+ _make_couple('unit1.mount'),
|
||||||
|
+ _make_couple('unit2.mount'),
|
||||||
|
+ _make_couple('local-fs.target.requires/unit1.mount'),
|
||||||
|
+ _make_couple('local-fs.target.requires/unit2.mount'),
|
||||||
|
+ ]
|
||||||
|
+ assert not follow_symlinks
|
||||||
|
+ assert (src, dst) in valid_combinations
|
||||||
|
+
|
||||||
|
+ def mock_islink(file_path):
|
||||||
|
+ return file_path == '/container/usr/lib/systemd/system/local-fs.target.requires/unit2.mount'
|
||||||
|
+
|
||||||
|
+ class MockedDeleteFile:
|
||||||
|
+ def __init__(self):
|
||||||
|
+ self.removal_called = False
|
||||||
|
+
|
||||||
|
+ def __call__(self, file_path):
|
||||||
|
+ assert file_path == '/container/usr/lib/systemd/system/local-fs.target.requires/unit2.mount'
|
||||||
|
+ self.removal_called = True
|
||||||
|
+
|
||||||
|
+ def mock_makedirs(dst_dir, mode=0o777, exist_ok=False):
|
||||||
|
+ assert exist_ok
|
||||||
|
+ assert mode == 0o755
|
||||||
|
+
|
||||||
|
+ allowed_paths = [
|
||||||
|
+ '/container/usr/lib/systemd/system',
|
||||||
|
+ '/container/usr/lib/systemd/system/local-fs.target.requires'
|
||||||
|
+ ]
|
||||||
|
+ assert dst_dir.rstrip('/') in allowed_paths
|
||||||
|
+
|
||||||
|
+ monkeypatch.setattr(os, 'walk', mock_walk)
|
||||||
|
+ monkeypatch.setattr(os, 'makedirs', mock_makedirs)
|
||||||
|
+ monkeypatch.setattr(os.path, 'isdir', mock_isdir)
|
||||||
|
+ monkeypatch.setattr(os.path, 'islink', mock_islink)
|
||||||
|
+ monkeypatch.setattr(mount_unit_generator, '_delete_file', MockedDeleteFile())
|
||||||
|
+ monkeypatch.setattr(shutil, 'copy2', mock_copy2)
|
||||||
|
+
|
||||||
|
+ class MockedContainerContext:
|
||||||
|
+ def __init__(self):
|
||||||
|
+ self.base_dir = '/container'
|
||||||
|
+
|
||||||
|
+ def full_path(self, path):
|
||||||
|
+ return os.path.join('/container', path.lstrip('/'))
|
||||||
|
+
|
||||||
|
+ mock_container = MockedContainerContext()
|
||||||
|
+
|
||||||
|
+ files = mount_unit_generator.copy_units_into_system_location(
|
||||||
|
+ mock_container, '/source/dir'
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ expected_files = [
|
||||||
|
+ '/usr/lib/systemd/system/unit1.mount',
|
||||||
|
+ '/usr/lib/systemd/system/unit2.mount',
|
||||||
|
+ '/usr/lib/systemd/system/local-fs.target.requires/unit1.mount',
|
||||||
|
+ '/usr/lib/systemd/system/local-fs.target.requires/unit2.mount',
|
||||||
|
+ ]
|
||||||
|
+ assert sorted(files) == sorted(expected_files)
|
||||||
|
+ assert mount_unit_generator._delete_file.removal_called
|
||||||
diff --git a/repos/system_upgrade/common/actors/missinggpgkeysinhibitor/libraries/missinggpgkey.py b/repos/system_upgrade/common/actors/missinggpgkeysinhibitor/libraries/missinggpgkey.py
|
diff --git a/repos/system_upgrade/common/actors/missinggpgkeysinhibitor/libraries/missinggpgkey.py b/repos/system_upgrade/common/actors/missinggpgkeysinhibitor/libraries/missinggpgkey.py
|
||||||
index 32e4527b..1e595e9a 100644
|
index 32e4527b..1e595e9a 100644
|
||||||
--- a/repos/system_upgrade/common/actors/missinggpgkeysinhibitor/libraries/missinggpgkey.py
|
--- a/repos/system_upgrade/common/actors/missinggpgkeysinhibitor/libraries/missinggpgkey.py
|
||||||
|
@ -53,7 +53,7 @@ py2_byte_compile "%1" "%2"}
|
|||||||
Epoch: 1
|
Epoch: 1
|
||||||
Name: leapp-repository
|
Name: leapp-repository
|
||||||
Version: 0.23.0
|
Version: 0.23.0
|
||||||
Release: 1%{?dist}.elevate.1
|
Release: 1%{?dist}.elevate.2
|
||||||
Summary: Repositories for leapp
|
Summary: Repositories for leapp
|
||||||
|
|
||||||
License: ASL 2.0
|
License: ASL 2.0
|
||||||
@ -350,6 +350,9 @@ fi
|
|||||||
|
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Tue Sep 30 2025 Yuriy Kohut <ykohut@almalinux.org> - 0.23.0-1.elevate.2
|
||||||
|
- ELevate vendors support for upstream 0.23.0-1 version (47fce173e75408d9a7a26225d389161caf72e244)
|
||||||
|
|
||||||
* Fri Sep 12 2025 Yuriy Kohut <ykohut@almalinux.org> - 0.23.0-1.elevate.1
|
* Fri Sep 12 2025 Yuriy Kohut <ykohut@almalinux.org> - 0.23.0-1.elevate.1
|
||||||
- ELevate vendors support for upstream 0.23.0-1 version (dcf53c28ea9c3fdd03277abcdeb1d124660f7f8e)
|
- ELevate vendors support for upstream 0.23.0-1 version (dcf53c28ea9c3fdd03277abcdeb1d124660f7f8e)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user