From 36b1c7a83c35fb73b355a5de1121e0bab2657ed4 Mon Sep 17 00:00:00 2001 From: Andrus Suvalau Date: Fri, 3 Oct 2025 07:50:45 +0200 Subject: [PATCH 01/39] feat(rear): exclude hard disks with COVE_RESCUE label Since Cove Rescue Media is VHD disk in Azure env, it is considered a hard disk by ReaR and, as a result, might be used as a possible target for disks mapping. To avoid this, we are excluding the rescue system by the COVE_RESCUE label. Jira-Ref: BCF-5849: Support Bare Metal Recovery in Azure --- .../rear/layout/prepare/default/250_compare_disks.sh | 9 ++++++++- .../rear/layout/prepare/default/300_map_disks.sh | 12 +++++++++++- usr/share/rear/lib/cove-functions.sh | 9 +++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/usr/share/rear/layout/prepare/default/250_compare_disks.sh b/usr/share/rear/layout/prepare/default/250_compare_disks.sh index 8fdb2b76cf..30b4ae842a 100644 --- a/usr/share/rear/layout/prepare/default/250_compare_disks.sh +++ b/usr/share/rear/layout/prepare/default/250_compare_disks.sh @@ -110,6 +110,9 @@ for current_device_path in /sys/block/* ; do test "$( < $current_device_path/removable )" = "1" && continue # Continue with next block device if the current one is designated as write-protected: is_write_protected $current_device_path && continue + # Continue with next block device if the current one is the COVE_RESCUE disk + device_name="$( get_device_name $current_device_path )" + is_cove_rescue_device "$device_name" && continue # Continue with next block device if no size can be read for the current one: test -r $current_device_path/size || continue current_size=$( get_disk_size $current_disk_name ) @@ -151,7 +154,11 @@ if ! is_true "$MIGRATION_MODE" ; then if test -e "/sys/block/$dev" ; then Log "Device /sys/block/$dev exists" newsize=$( get_disk_size $dev ) - if test "$newsize" -eq "$size" ; then + device_name="$( get_device_name /"sys/block/$dev" )" + if is_cove_rescue_device "$device_name" ; then + LogPrint "Device $dev is the COVE_RESCUE disk (needs manual configuration)" + MIGRATION_MODE='true' + elif test "$newsize" -eq "$size" ; then if is_write_protected "/sys/block/$dev"; then LogPrint "Device $dev is designated as write-protected (needs manual configuration)" MIGRATION_MODE='true' diff --git a/usr/share/rear/layout/prepare/default/300_map_disks.sh b/usr/share/rear/layout/prepare/default/300_map_disks.sh index c88477a151..6285770d4c 100644 --- a/usr/share/rear/layout/prepare/default/300_map_disks.sh +++ b/usr/share/rear/layout/prepare/default/300_map_disks.sh @@ -136,7 +136,7 @@ while read keyword orig_device orig_size junk ; do # its matching actual block device (e.g. /dev/sda) must be determined: preferred_target_device_name="$( get_device_name $current_device )" # Use the current one if it is of same size as the old one: - if has_mapping_hint "$orig_device" || test "$orig_size" -eq "$current_size" ; then + if ! is_cove_rescue_device "$preferred_target_device_name" && { has_mapping_hint "$orig_device" || test "$orig_size" -eq "$current_size"; }; then # Ensure the target device is really a block device on the replacement hardware. # Here the target device has same name as the original device which was a block device on the original hardware # but it might perhaps happen that this device name is not a block device on the replacement hardware: @@ -177,6 +177,11 @@ while read keyword orig_device orig_size junk ; do # The current_device_path (e.g. /sys/block/sdb) is not a block device so that # its matching actual block device (e.g. /dev/sdb) must be determined: preferred_target_device_name="$( get_device_name $current_device_path )" + # Continue with next block device if the current one is the COVE_RESCUE disk + if is_cove_rescue_device "$preferred_target_device_name" ; then + DebugPrint "Cannot use $preferred_target_device_name for recreating $orig_device ($preferred_target_device_name is COVE_RESCUE)" + continue + fi # Ensure the determined target device is really a block device (cf. above): test -b "$preferred_target_device_name" || continue # Continue with next block device if the current one is not of same size as the original: @@ -250,6 +255,11 @@ while read keyword orig_device orig_size junk ; do Log "$preferred_target_device_name excluded from device mapping choices (has no queue directory)" continue fi + # Continue with next block device if the current one is the COVE_RESCUE disk + if is_cove_rescue_device "$preferred_target_device_name" ; then + Log "$preferred_target_device_name excluded from device mapping choices (is COVE_RESCUE)" + continue + fi # Continue with next block device if the current one is already used as target in the mapping file: if is_mapping_target "$preferred_target_device_name" ; then Log "$preferred_target_device_name excluded from device mapping choices (is already used as mapping target)" diff --git a/usr/share/rear/lib/cove-functions.sh b/usr/share/rear/lib/cove-functions.sh index a443f22d65..7bf2d1bdf4 100644 --- a/usr/share/rear/lib/cove-functions.sh +++ b/usr/share/rear/lib/cove-functions.sh @@ -88,3 +88,12 @@ function cove_error_if_container() { Error "The system is detected as ${container} container. System state is not supported for containers." } + +function is_cove_rescue_device() { + local device_name="$1" + + local label + label=$(blkid_label_of_device "$device_name") + + [ "$label" = "COVE_RESCUE" ] +} From 5b8fc5ca9fb93e1b162d1533294a7544987533a4 Mon Sep 17 00:00:00 2001 From: Andrus Suvalau Date: Fri, 3 Oct 2025 09:37:10 +0200 Subject: [PATCH 02/39] feat(rear): fix device path Jira-Ref: BCF-5849: Support Bare Metal Recovery in Azure --- usr/share/rear/layout/prepare/default/250_compare_disks.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usr/share/rear/layout/prepare/default/250_compare_disks.sh b/usr/share/rear/layout/prepare/default/250_compare_disks.sh index 30b4ae842a..6b5c67f648 100644 --- a/usr/share/rear/layout/prepare/default/250_compare_disks.sh +++ b/usr/share/rear/layout/prepare/default/250_compare_disks.sh @@ -154,7 +154,7 @@ if ! is_true "$MIGRATION_MODE" ; then if test -e "/sys/block/$dev" ; then Log "Device /sys/block/$dev exists" newsize=$( get_disk_size $dev ) - device_name="$( get_device_name /"sys/block/$dev" )" + device_name="$( get_device_name "/sys/block/$dev" )" if is_cove_rescue_device "$device_name" ; then LogPrint "Device $dev is the COVE_RESCUE disk (needs manual configuration)" MIGRATION_MODE='true' From 1808d7b334a236a48449b05f0b27f6f5cf9af6c4 Mon Sep 17 00:00:00 2001 From: Andrus Suvalau Date: Mon, 6 Oct 2025 07:48:12 +0200 Subject: [PATCH 03/39] feat(rear): fix review comments Jira-Ref: BCF-5849: Support Bare Metal Recovery in Azure --- usr/share/rear/layout/prepare/default/250_compare_disks.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usr/share/rear/layout/prepare/default/250_compare_disks.sh b/usr/share/rear/layout/prepare/default/250_compare_disks.sh index 6b5c67f648..985ffcc65a 100644 --- a/usr/share/rear/layout/prepare/default/250_compare_disks.sh +++ b/usr/share/rear/layout/prepare/default/250_compare_disks.sh @@ -111,7 +111,7 @@ for current_device_path in /sys/block/* ; do # Continue with next block device if the current one is designated as write-protected: is_write_protected $current_device_path && continue # Continue with next block device if the current one is the COVE_RESCUE disk - device_name="$( get_device_name $current_device_path )" + device_name="$( get_device_name "$current_device_path" )" is_cove_rescue_device "$device_name" && continue # Continue with next block device if no size can be read for the current one: test -r $current_device_path/size || continue From c9eb11cf33ff0dd0af87df6f1a51961017881a1a Mon Sep 17 00:00:00 2001 From: Andrus Suvalau Date: Fri, 10 Oct 2025 12:25:57 +0200 Subject: [PATCH 04/39] feat(rear): chagens for Azure environment - Do not add EFI entries in Azure env - Unmount /mnt in Azure env Jira-Ref: BCF-5849: Support Bare Metal Recovery in Azure --- usr/share/rear/finalize/Linux-i386/670_run_efibootmgr.sh | 5 +++++ .../rear/layout/prepare/COVE/default/020_umount_in_azure.sh | 5 +++++ usr/share/rear/lib/cove-functions.sh | 4 ++++ 3 files changed, 14 insertions(+) create mode 100644 usr/share/rear/layout/prepare/COVE/default/020_umount_in_azure.sh diff --git a/usr/share/rear/finalize/Linux-i386/670_run_efibootmgr.sh b/usr/share/rear/finalize/Linux-i386/670_run_efibootmgr.sh index 71a4bab551..7b1dc1f86f 100644 --- a/usr/share/rear/finalize/Linux-i386/670_run_efibootmgr.sh +++ b/usr/share/rear/finalize/Linux-i386/670_run_efibootmgr.sh @@ -8,6 +8,11 @@ is_true $USING_UEFI_BOOTLOADER || return 0 # (cf. finalize/Linux-i386/610_EFISTUB_run_efibootmgr.sh): is_true $EFI_STUB && return +# Do not create EFI Boot Manager entries in Azure, because these are created +# for OS disks. Having incorrect EFI entries for the Cove Rescue Media OS disk +# can make it non-bootable for further use. +is_cove_in_azure && return + LogPrint "Creating EFI Boot Manager entries..." local esp_mountpoint esp_mountpoint_inside boot_efi_parts boot_efi_dev diff --git a/usr/share/rear/layout/prepare/COVE/default/020_umount_in_azure.sh b/usr/share/rear/layout/prepare/COVE/default/020_umount_in_azure.sh new file mode 100644 index 0000000000..38c54c60eb --- /dev/null +++ b/usr/share/rear/layout/prepare/COVE/default/020_umount_in_azure.sh @@ -0,0 +1,5 @@ +# Unmount /mnt, which is mounted by cloud-init in the Azure environment + +if is_cove_in_azure; then + umount_mountpoint /mnt +fi diff --git a/usr/share/rear/lib/cove-functions.sh b/usr/share/rear/lib/cove-functions.sh index 7bf2d1bdf4..6aaf999227 100644 --- a/usr/share/rear/lib/cove-functions.sh +++ b/usr/share/rear/lib/cove-functions.sh @@ -48,6 +48,10 @@ function is_cove() { [ "$BACKUP" = "COVE" ] } +function is_cove_in_azure() { + is_cove && grep -qw "cove_azure" /proc/cmdline +} + # Since there is no reliable way of detecting whether it is running in a container or not, # it only tries to guess. function is_container() { From 9492b7b76af10115dbefa82ad8fb07499d5ee833 Mon Sep 17 00:00:00 2001 From: Andrus Suvalau Date: Fri, 10 Oct 2025 14:50:59 +0200 Subject: [PATCH 05/39] feat(rear): add logging Jira-Ref: BCF-5849: Support Bare Metal Recovery in Azure --- usr/share/rear/finalize/Linux-i386/670_run_efibootmgr.sh | 5 ++++- .../rear/layout/prepare/COVE/default/020_umount_in_azure.sh | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/usr/share/rear/finalize/Linux-i386/670_run_efibootmgr.sh b/usr/share/rear/finalize/Linux-i386/670_run_efibootmgr.sh index 7b1dc1f86f..6f4a714c5f 100644 --- a/usr/share/rear/finalize/Linux-i386/670_run_efibootmgr.sh +++ b/usr/share/rear/finalize/Linux-i386/670_run_efibootmgr.sh @@ -11,7 +11,10 @@ is_true $EFI_STUB && return # Do not create EFI Boot Manager entries in Azure, because these are created # for OS disks. Having incorrect EFI entries for the Cove Rescue Media OS disk # can make it non-bootable for further use. -is_cove_in_azure && return +if is_cove_in_azurel; then + LogPrint "Skip creating EFI Boot Manager entries in Azure environments" + return 0 +fi LogPrint "Creating EFI Boot Manager entries..." diff --git a/usr/share/rear/layout/prepare/COVE/default/020_umount_in_azure.sh b/usr/share/rear/layout/prepare/COVE/default/020_umount_in_azure.sh index 38c54c60eb..bca1cdb11f 100644 --- a/usr/share/rear/layout/prepare/COVE/default/020_umount_in_azure.sh +++ b/usr/share/rear/layout/prepare/COVE/default/020_umount_in_azure.sh @@ -1,5 +1,6 @@ # Unmount /mnt, which is mounted by cloud-init in the Azure environment if is_cove_in_azure; then + LogPrint "Unmounting '/mnt' in Azure environments" umount_mountpoint /mnt fi From 49f63b3a2bc56c21198953b18388a17a599fa252 Mon Sep 17 00:00:00 2001 From: Andrus Suvalau Date: Fri, 10 Oct 2025 15:38:41 +0200 Subject: [PATCH 06/39] feat(rear): fix typo Jira-Ref: BCF-5849: Support Bare Metal Recovery in Azure --- usr/share/rear/finalize/Linux-i386/670_run_efibootmgr.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usr/share/rear/finalize/Linux-i386/670_run_efibootmgr.sh b/usr/share/rear/finalize/Linux-i386/670_run_efibootmgr.sh index 6f4a714c5f..eff7a00c29 100644 --- a/usr/share/rear/finalize/Linux-i386/670_run_efibootmgr.sh +++ b/usr/share/rear/finalize/Linux-i386/670_run_efibootmgr.sh @@ -11,7 +11,7 @@ is_true $EFI_STUB && return # Do not create EFI Boot Manager entries in Azure, because these are created # for OS disks. Having incorrect EFI entries for the Cove Rescue Media OS disk # can make it non-bootable for further use. -if is_cove_in_azurel; then +if is_cove_in_azure; then LogPrint "Skip creating EFI Boot Manager entries in Azure environments" return 0 fi From 136e3401171ad9f291e78fb509c197cd7d65c8a9 Mon Sep 17 00:00:00 2001 From: Andrus Suvalau Date: Tue, 14 Oct 2025 13:46:51 +0200 Subject: [PATCH 07/39] feat(rear): add more strict check for Azure - Avoid affecting BMR in Hyper-V via VHD disk Jira-Ref: BCF-5849: Support Bare Metal Recovery in Azure --- usr/share/rear/finalize/Linux-i386/670_run_efibootmgr.sh | 3 ++- .../rear/layout/prepare/COVE/default/020_umount_in_azure.sh | 2 +- usr/share/rear/lib/cove-functions.sh | 4 +++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/usr/share/rear/finalize/Linux-i386/670_run_efibootmgr.sh b/usr/share/rear/finalize/Linux-i386/670_run_efibootmgr.sh index eff7a00c29..179cf96d7d 100644 --- a/usr/share/rear/finalize/Linux-i386/670_run_efibootmgr.sh +++ b/usr/share/rear/finalize/Linux-i386/670_run_efibootmgr.sh @@ -12,7 +12,8 @@ is_true $EFI_STUB && return # for OS disks. Having incorrect EFI entries for the Cove Rescue Media OS disk # can make it non-bootable for further use. if is_cove_in_azure; then - LogPrint "Skip creating EFI Boot Manager entries in Azure environments" + LogPrint "Skip creating EFI Boot Manager entries in Azure" + NOBOOTLOADER="" return 0 fi diff --git a/usr/share/rear/layout/prepare/COVE/default/020_umount_in_azure.sh b/usr/share/rear/layout/prepare/COVE/default/020_umount_in_azure.sh index bca1cdb11f..4d97d5f26c 100644 --- a/usr/share/rear/layout/prepare/COVE/default/020_umount_in_azure.sh +++ b/usr/share/rear/layout/prepare/COVE/default/020_umount_in_azure.sh @@ -1,6 +1,6 @@ # Unmount /mnt, which is mounted by cloud-init in the Azure environment if is_cove_in_azure; then - LogPrint "Unmounting '/mnt' in Azure environments" + LogPrint "Unmounting '/mnt' in Azure" umount_mountpoint /mnt fi diff --git a/usr/share/rear/lib/cove-functions.sh b/usr/share/rear/lib/cove-functions.sh index 5e75ebf609..f1d2598d6a 100644 --- a/usr/share/rear/lib/cove-functions.sh +++ b/usr/share/rear/lib/cove-functions.sh @@ -49,7 +49,9 @@ function is_cove() { } function is_cove_in_azure() { - is_cove && grep -qw "cove_azure" /proc/cmdline + is_cove && grep -qw "cove_azure" /proc/cmdline && \ + curl -H "Metadata: true" "http://169.254.169.254/metadata/instance?api-version=2021-02-01" \ + --connect-timeout 3 1>/dev/null 2>&1 } # Since there is no reliable way of detecting whether it is running in a container or not, From 95c814994eefbd2b294dfa3dfe9986f4bb687943 Mon Sep 17 00:00:00 2001 From: Andrus Suvalau Date: Wed, 15 Oct 2025 08:22:48 +0200 Subject: [PATCH 08/39] feat(rear): correct detecting bootloder type in Azure Jira-Ref: BCF-5849: Support Bare Metal Recovery in Azure --- .../layout/save/default/445_guess_bootloader.sh | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/usr/share/rear/layout/save/default/445_guess_bootloader.sh b/usr/share/rear/layout/save/default/445_guess_bootloader.sh index d06d26b56e..1f76400259 100644 --- a/usr/share/rear/layout/save/default/445_guess_bootloader.sh +++ b/usr/share/rear/layout/save/default/445_guess_bootloader.sh @@ -101,6 +101,21 @@ for block_device in /sys/block/* ; do else LogPrint "Using guessed bootloader '$known_bootloader' for 'rear recover' (found in first bytes on $disk_device)" fi + # In cloud providers such as Azure, VMs can be created from images + # that are dual-boot capable — GPT + EFI and GPT + BIOS. + if is_true "$USING_UEFI_BOOTLOADER"; then + case "$known_bootloader" in + (GRUB2) + known_bootloader="GRUB2-EFI" + ;; + (LILO) + known_bootloader="ELILO" + ;; + (*) + known_bootloader="EFI" + ;; + esac + fi echo "$known_bootloader" >$bootloader_file return fi From 127a9ca3d72a5ed819ece224a66d2bf0a679c49f Mon Sep 17 00:00:00 2001 From: Andrus Suvalau Date: Mon, 20 Oct 2025 14:40:34 +0200 Subject: [PATCH 09/39] feat(cove): backup and restore partition guids Jira-Ref: BCF-5872: [ReaR] Backup and restore PARTUUID --- doc/user-guide/06-layout-configuration.adoc | 2 +- .../GNU/Linux/100_include_partition_code.sh | 10 ++++++- .../save/GNU/Linux/100_create_layout_file.sh | 2 +- .../save/GNU/Linux/200_partition_layout.sh | 26 ++++++++++++++----- usr/share/rear/lib/layout-functions.sh | 23 ++++++++++++++++ 5 files changed, 54 insertions(+), 9 deletions(-) diff --git a/doc/user-guide/06-layout-configuration.adoc b/doc/user-guide/06-layout-configuration.adoc index f1f4a47326..3f90e72294 100644 --- a/doc/user-guide/06-layout-configuration.adoc +++ b/doc/user-guide/06-layout-configuration.adoc @@ -631,7 +631,7 @@ disk === Partitions === ---------------------------------- -part +part ---------------------------------- === Software RAID arrays === diff --git a/usr/share/rear/layout/prepare/GNU/Linux/100_include_partition_code.sh b/usr/share/rear/layout/prepare/GNU/Linux/100_include_partition_code.sh index 355db98608..08ad36cf70 100644 --- a/usr/share/rear/layout/prepare/GNU/Linux/100_include_partition_code.sh +++ b/usr/share/rear/layout/prepare/GNU/Linux/100_include_partition_code.sh @@ -161,7 +161,7 @@ EOF let last_number=0 local flags partition - while read part disk size pstart name flags partition junk; do + while read part disk size pstart name flags partition partuuid junk; do # Get the partition number from the name number=$( get_partition_number "$partition" ) @@ -332,6 +332,14 @@ EOF echo "my_udevsettle" ) >> $LAYOUT_CODE fi + + if [ "$label" = "gpt" ] && [ -n "$partuuid" ] && [ "$partuuid" != "no-partuuid" ]; then + ( + echo "my_udevsettle" + echo "set_partition_guid $device $number $partuuid || true" + echo "my_udevsettle" + ) >> "$LAYOUT_CODE" + fi done < <(grep "^part $device " $LAYOUT_FILE) # This will override all partition setup previously made, diff --git a/usr/share/rear/layout/save/GNU/Linux/100_create_layout_file.sh b/usr/share/rear/layout/save/GNU/Linux/100_create_layout_file.sh index 186c4233d1..095d076c41 100644 --- a/usr/share/rear/layout/save/GNU/Linux/100_create_layout_file.sh +++ b/usr/share/rear/layout/save/GNU/Linux/100_create_layout_file.sh @@ -34,7 +34,7 @@ echo "Disk layout dated $START_DATE_TIME_NUMBER (YYYYmmddHHMMSS)" >$DISKLAYOUT_F # lsblk -io NAME,KNAME,FSTYPE,LABEL,SIZE,MOUNTPOINT,UUID # and as fallback try 'lsblk -i' and finally try plain 'lsblk'. # When there is no 'lsblk' command there is no output (bad luck, no harm): -{ lsblk -ipo NAME,KNAME,PKNAME,TRAN,TYPE,FSTYPE,LABEL,SIZE,MOUNTPOINTS,UUID,WWN || lsblk -ipo NAME,KNAME,PKNAME,TRAN,TYPE,FSTYPE,LABEL,SIZE,MOUNTPOINT,UUID,WWN || lsblk -io NAME,KNAME,FSTYPE,LABEL,SIZE,MOUNTPOINT,UUID || lsblk -i || lsblk ; } >>$DISKLAYOUT_FILE +{ lsblk -ipo NAME,KNAME,PKNAME,TRAN,TYPE,FSTYPE,LABEL,SIZE,MOUNTPOINTS,UUID,PARTUUID,WWN || lsblk -ipo NAME,KNAME,PKNAME,TRAN,TYPE,FSTYPE,LABEL,SIZE,MOUNTPOINT,UUID,PARTUUID,WWN || lsblk -io NAME,KNAME,FSTYPE,LABEL,SIZE,MOUNTPOINT,UUID,PARTUUID || lsblk -i || lsblk ; } >>$DISKLAYOUT_FILE # Make all lines in disklayout.conf up to now as header comments: sed -i -e 's/^/# /' $DISKLAYOUT_FILE diff --git a/usr/share/rear/layout/save/GNU/Linux/200_partition_layout.sh b/usr/share/rear/layout/save/GNU/Linux/200_partition_layout.sh index f6b7fdd914..5bf8edd971 100644 --- a/usr/share/rear/layout/save/GNU/Linux/200_partition_layout.sh +++ b/usr/share/rear/layout/save/GNU/Linux/200_partition_layout.sh @@ -39,9 +39,9 @@ extract_partitions() { ### Therefore we ignore these special partitions, see also ### https://github.com/rear/rear/issues/2087 for possible_sysfs_partition in "${sysfs_paths_unfiltered[@]}"; do - if [[ ! ( $possible_sysfs_partition = *'/mmcblk'+([0-9])'rpmb' || $possible_sysfs_partition = *'/mmcblk'+([0-9])'boot'+([0-9]) ) ]] ; then - sysfs_paths+=($possible_sysfs_partition) - fi + if [[ ! ( $possible_sysfs_partition = *'/mmcblk'+([0-9])'rpmb' || $possible_sysfs_partition = *'/mmcblk'+([0-9])'boot'+([0-9]) ) ]] ; then + sysfs_paths+=($possible_sysfs_partition) + fi done declare path sysfs_path @@ -352,8 +352,22 @@ extract_partitions() { done < $TMP_DIR/partitions-data fi + while read -r partition_nr size start type flags junk ; do + local partuuid="no-partuuid" + if [ "$disk_label" = "gpt" ]; then + local partition_name="${device%/*}/${partition_prefix#*/}$partition_nr" + local partition_device + partition_device="$(get_device_name "$partition_name")" + partuuid="$(get_partition_guid "$partition_device")" + if [ -z "$partuuid" ]; then + partuuid="no-partuuid" + fi + fi + sed -i /^$partition_nr\ /s/$/\ $partuuid/ "$TMP_DIR/partitions" + done < "$TMP_DIR/partitions-data" + ### Write to layout file - while read partition_nr size start type flags junk ; do + while read partition_nr size start type flags partuuid junk ; do # determine the name of the partition using the number # device=/dev/cciss/c0d0 ; partition_prefix=cciss/c0d0p # device=/dev/md127 ; partition_prefix=md127p @@ -388,7 +402,7 @@ extract_partitions() { fi fi # Some basic checks on the values happen in layout/save/default/950_verify_disklayout_file.sh - echo "part $device $size $start $type $flags $partition_device" + echo "part $device $size $start $type $flags $partition_device $partuuid" done < $TMP_DIR/partitions } @@ -483,7 +497,7 @@ Log "Saving disks and their partitions" Error "Invalid 'disk $devname' entry (DASD partition label on non-s390 arch $ARCH)" fi echo "# Partitions on $devname" - echo "# Format: part /dev/" + echo "# Format: part /dev/ " extract_partitions "$devname" fi fi diff --git a/usr/share/rear/lib/layout-functions.sh b/usr/share/rear/lib/layout-functions.sh index 5782ea9f02..a4bf3f14c4 100644 --- a/usr/share/rear/lib/layout-functions.sh +++ b/usr/share/rear/lib/layout-functions.sh @@ -1531,4 +1531,27 @@ delete_dummy_partitions_and_resize_real_ones() { last_partition_number=0 } +get_partition_guid() { + local device=$1 + + local guid="" + if has_binary blkid; then + guid="$(blkid -s PARTUUID -o value "$device" 2>/dev/null)" + if [ -n "$guid" ]; then + echo "$guid" + return 0 + fi + fi +} + +set_partition_guid() { + local device=$1 + local partnum=$2 + local guid=$3 + + if has_binary sgdisk; then + sgdisk --partition-guid="$partnum":"$guid" "$device" + fi +} + # vim: set et ts=4 sw=4: From 4a4288861ac3f1dec0e52a832edf47179e45b500 Mon Sep 17 00:00:00 2001 From: Andrus Suvalau Date: Tue, 21 Oct 2025 15:02:05 +0200 Subject: [PATCH 10/39] feat(cove): add fallback for cases when blkid is not available Jira-Ref: BCF-5872: [ReaR] Backup and restore PARTUUID --- usr/share/rear/conf/GNU/Linux.conf | 1 + usr/share/rear/lib/layout-functions.sh | 13 +++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/usr/share/rear/conf/GNU/Linux.conf b/usr/share/rear/conf/GNU/Linux.conf index 9ebc3e70a4..ed22783725 100644 --- a/usr/share/rear/conf/GNU/Linux.conf +++ b/usr/share/rear/conf/GNU/Linux.conf @@ -15,6 +15,7 @@ partprobe fdisk cfdisk sfdisk +sgdisk ) # progs to take along diff --git a/usr/share/rear/lib/layout-functions.sh b/usr/share/rear/lib/layout-functions.sh index a4bf3f14c4..97426183ad 100644 --- a/usr/share/rear/lib/layout-functions.sh +++ b/usr/share/rear/lib/layout-functions.sh @@ -1537,11 +1537,16 @@ get_partition_guid() { local guid="" if has_binary blkid; then guid="$(blkid -s PARTUUID -o value "$device" 2>/dev/null)" - if [ -n "$guid" ]; then - echo "$guid" - return 0 - fi + # Fallback for cases when blkid is not available + elif [ -d /dev/disk/by-partuuid ]; then + local disk + for disk in /dev/disk/by-partuuid/*; do + if [ -h "$disk" ] && [ "$(readlink -f "$disk")" = "$device" ]; then + guid="${disk#/dev/disk/by-partuuid/}" + fi + done fi + echo "$guid" } set_partition_guid() { From 5db6c3b257a2cb46c4c8cc0fbd8b86acfb21bf5f Mon Sep 17 00:00:00 2001 From: Andrus Suvalau Date: Thu, 23 Oct 2025 09:59:34 +0200 Subject: [PATCH 11/39] feat(cove): apply review comments Jira-Ref: BCF-5872: [ReaR] Backup and restore PARTUUID --- .../layout/save/GNU/Linux/200_partition_layout.sh | 12 +++++++----- usr/share/rear/lib/layout-functions.sh | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/usr/share/rear/layout/save/GNU/Linux/200_partition_layout.sh b/usr/share/rear/layout/save/GNU/Linux/200_partition_layout.sh index 5bf8edd971..6f07f31922 100644 --- a/usr/share/rear/layout/save/GNU/Linux/200_partition_layout.sh +++ b/usr/share/rear/layout/save/GNU/Linux/200_partition_layout.sh @@ -353,17 +353,19 @@ extract_partitions() { fi while read -r partition_nr size start type flags junk ; do - local partuuid="no-partuuid" + local partuuid if [ "$disk_label" = "gpt" ]; then local partition_name="${device%/*}/${partition_prefix#*/}$partition_nr" local partition_device partition_device="$(get_device_name "$partition_name")" partuuid="$(get_partition_guid "$partition_device")" - if [ -z "$partuuid" ]; then - partuuid="no-partuuid" - fi fi - sed -i /^$partition_nr\ /s/$/\ $partuuid/ "$TMP_DIR/partitions" + + if [ -z "$partuuid" ]; then + partuuid="no-partuuid" + fi + + sed -i /^"$partition_nr"\ /s/$/\ $partuuid/ "$TMP_DIR/partitions" done < "$TMP_DIR/partitions-data" ### Write to layout file diff --git a/usr/share/rear/lib/layout-functions.sh b/usr/share/rear/lib/layout-functions.sh index 97426183ad..9a92da57d6 100644 --- a/usr/share/rear/lib/layout-functions.sh +++ b/usr/share/rear/lib/layout-functions.sh @@ -1538,7 +1538,7 @@ get_partition_guid() { if has_binary blkid; then guid="$(blkid -s PARTUUID -o value "$device" 2>/dev/null)" # Fallback for cases when blkid is not available - elif [ -d /dev/disk/by-partuuid ]; then + elif [ -d /dev/disk/by-partuuid ]; then local disk for disk in /dev/disk/by-partuuid/*; do if [ -h "$disk" ] && [ "$(readlink -f "$disk")" = "$device" ]; then From e6e25a3730bf8ad40bb04e2ff578f2136fd7586a Mon Sep 17 00:00:00 2001 From: Andrus Suvalau Date: Fri, 24 Oct 2025 06:28:04 +0200 Subject: [PATCH 12/39] feat(cove): non-consecutive partitions are fine - Do not fail at backup time if parted lacks the necessary features to recover non-consecutive partitions, because at recovery time, updated version of parted from Cove Rescue Media is used. Jira-Ref: BCF-5930: [ReaR] System state backup fails on RHEL 6 if there are non consecutive partitions --- .../rear/layout/save/default/950_verify_disklayout_file.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/usr/share/rear/layout/save/default/950_verify_disklayout_file.sh b/usr/share/rear/layout/save/default/950_verify_disklayout_file.sh index afe82ca911..ef04bf2b7e 100644 --- a/usr/share/rear/layout/save/default/950_verify_disklayout_file.sh +++ b/usr/share/rear/layout/save/default/950_verify_disklayout_file.sh @@ -244,7 +244,9 @@ for error_message in "${broken_part_errors[@]}" ; do disklayout_file_is_broken="yes" done # Non consecutive partitions are supported unless parted tells otherwise: -if is_false $FEATURE_PARTED_RESIZEPART && is_false $FEATURE_PARTED_RESIZE ; then +# Cove is an exception because Cove Rescue Media is used during recovery, +# which provides updated parted tool. +if is_false "$FEATURE_PARTED_RESIZEPART" && is_false "$FEATURE_PARTED_RESIZE" && ! is_cove; then for error_message in "${non_consecutive_part_errors[@]}" ; do contains_visible_char "$error_message" || continue LogPrintError "$error_message" From 863800cff262046689af7a05762902314a5e8ec0 Mon Sep 17 00:00:00 2001 From: Andrus Suvalau Date: Fri, 24 Oct 2025 09:08:21 +0200 Subject: [PATCH 13/39] feat(cove): unknown partition type is fine - Having blank disks attached on RHEL 6 can lead to backup failures. However, this should be fine because disks without file systems are later commented out if auto-exclusion is turned on. Jira-Ref: BCF-5816: [ReaR] System state backup fails on RHEL 6 systems when they have blank disks --- .../layout/save/GNU/Linux/200_partition_layout.sh | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/usr/share/rear/layout/save/GNU/Linux/200_partition_layout.sh b/usr/share/rear/layout/save/GNU/Linux/200_partition_layout.sh index f6b7fdd914..71f0cc1d5e 100644 --- a/usr/share/rear/layout/save/GNU/Linux/200_partition_layout.sh +++ b/usr/share/rear/layout/save/GNU/Linux/200_partition_layout.sh @@ -449,7 +449,16 @@ Log "Saving disks and their partitions" continue fi disktype=$(parted -s $devname print | grep -E "Partition Table|Disk label" | cut -d ":" -f "2" | tr -d " ") - test $disktype || Error "Invalid 'disk $devname' entry (no partition table type for '$devname')" + if [ -z "$disktype" ]; then + err_msg="Invalid 'disk $devname' entry (no partition table type for '$devname')" + if is_true "$AUTOEXCLUDE_DISKS"; then + disktype="unknown" + LogPrintError "$err_msg" + else + Error "$err_msg" + fi + unset err_msg + fi if [ "$disktype" != "dasd" ]; then echo "# Disk $devname" echo "# Format: disk " From c95123bad4b56de4020692a446154087e25d5287 Mon Sep 17 00:00:00 2001 From: Andrus Suvalau Date: Fri, 24 Oct 2025 11:11:04 +0200 Subject: [PATCH 14/39] feat(cove): add break from loop Jira-Ref: BCF-5872: [ReaR] Backup and restore PARTUUID --- usr/share/rear/lib/layout-functions.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/usr/share/rear/lib/layout-functions.sh b/usr/share/rear/lib/layout-functions.sh index 9a92da57d6..99d9991219 100644 --- a/usr/share/rear/lib/layout-functions.sh +++ b/usr/share/rear/lib/layout-functions.sh @@ -1543,6 +1543,7 @@ get_partition_guid() { for disk in /dev/disk/by-partuuid/*; do if [ -h "$disk" ] && [ "$(readlink -f "$disk")" = "$device" ]; then guid="${disk#/dev/disk/by-partuuid/}" + break fi done fi From 782a5812879d52373246c96c7b2c636a02bf2de5 Mon Sep 17 00:00:00 2001 From: Andrus Suvalau Date: Fri, 24 Oct 2025 12:23:05 +0200 Subject: [PATCH 15/39] feat(cove): try lsblk without PARTUUID - lsblk v2.17.2 on RHEL 6 and CentOS 6 is not aware of PARTUUID Jira-Ref: BCF-5872: [ReaR] Backup and restore PARTUUID --- .../rear/layout/save/GNU/Linux/100_create_layout_file.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/usr/share/rear/layout/save/GNU/Linux/100_create_layout_file.sh b/usr/share/rear/layout/save/GNU/Linux/100_create_layout_file.sh index 095d076c41..a69164945b 100644 --- a/usr/share/rear/layout/save/GNU/Linux/100_create_layout_file.sh +++ b/usr/share/rear/layout/save/GNU/Linux/100_create_layout_file.sh @@ -34,7 +34,11 @@ echo "Disk layout dated $START_DATE_TIME_NUMBER (YYYYmmddHHMMSS)" >$DISKLAYOUT_F # lsblk -io NAME,KNAME,FSTYPE,LABEL,SIZE,MOUNTPOINT,UUID # and as fallback try 'lsblk -i' and finally try plain 'lsblk'. # When there is no 'lsblk' command there is no output (bad luck, no harm): -{ lsblk -ipo NAME,KNAME,PKNAME,TRAN,TYPE,FSTYPE,LABEL,SIZE,MOUNTPOINTS,UUID,PARTUUID,WWN || lsblk -ipo NAME,KNAME,PKNAME,TRAN,TYPE,FSTYPE,LABEL,SIZE,MOUNTPOINT,UUID,PARTUUID,WWN || lsblk -io NAME,KNAME,FSTYPE,LABEL,SIZE,MOUNTPOINT,UUID,PARTUUID || lsblk -i || lsblk ; } >>$DISKLAYOUT_FILE +{ lsblk -ipo NAME,KNAME,PKNAME,TRAN,TYPE,FSTYPE,LABEL,SIZE,MOUNTPOINTS,UUID,PARTUUID,WWN || \ + lsblk -ipo NAME,KNAME,PKNAME,TRAN,TYPE,FSTYPE,LABEL,SIZE,MOUNTPOINT,UUID,PARTUUID,WWN || \ + lsblk -io NAME,KNAME,FSTYPE,LABEL,SIZE,MOUNTPOINT,UUID,PARTUUID || \ + lsblk -io NAME,KNAME,FSTYPE,LABEL,SIZE,MOUNTPOINT,UUID || \ + lsblk -i || lsblk ; } >>"$DISKLAYOUT_FILE" # Make all lines in disklayout.conf up to now as header comments: sed -i -e 's/^/# /' $DISKLAYOUT_FILE From 9632de87556374c9f4263f62c7f475ed1ca9aa92 Mon Sep 17 00:00:00 2001 From: Krzysztof Grobelny Date: Fri, 31 Oct 2025 05:11:32 -0400 Subject: [PATCH 16/39] feat(bm): Added notification if BMR session was interrupted Jira-Ref: BCF-5911: Notify a user that the restore should be started from the very beggining if a bmr session was interrupted --- .../layout/recreate/COVE/119_check_if_reboot_required.sh | 7 +++++++ .../rear/layout/recreate/COVE/201_mark_layout_code_done.sh | 1 + 2 files changed, 8 insertions(+) create mode 100755 usr/share/rear/layout/recreate/COVE/119_check_if_reboot_required.sh create mode 100755 usr/share/rear/layout/recreate/COVE/201_mark_layout_code_done.sh diff --git a/usr/share/rear/layout/recreate/COVE/119_check_if_reboot_required.sh b/usr/share/rear/layout/recreate/COVE/119_check_if_reboot_required.sh new file mode 100755 index 0000000000..3a2c25206a --- /dev/null +++ b/usr/share/rear/layout/recreate/COVE/119_check_if_reboot_required.sh @@ -0,0 +1,7 @@ +if [ -e /tmp/cove_rear_layout_code_done ]; then + text="A new recovery attempt has been detected. \ +Some devices may have been mounted since the last attempt, \ +which could prevent the recovery from completing. \ +Please reboot the system and start the recovery again." + cove_print_in_frame "WARNING" "$text" +fi diff --git a/usr/share/rear/layout/recreate/COVE/201_mark_layout_code_done.sh b/usr/share/rear/layout/recreate/COVE/201_mark_layout_code_done.sh new file mode 100755 index 0000000000..18a430d34f --- /dev/null +++ b/usr/share/rear/layout/recreate/COVE/201_mark_layout_code_done.sh @@ -0,0 +1 @@ +touch /tmp/cove_rear_layout_code_done From 5c83444a7e981d14762f75adcf7cd4f4bf5d33fe Mon Sep 17 00:00:00 2001 From: Krzysztof Grobelny Date: Wed, 5 Nov 2025 07:17:36 -0500 Subject: [PATCH 17/39] feat(bm): Review fixes Jira-Ref: BCF-5911: Notify a user that the restore should be started from the very beggining if a bmr session was interrupted --- .../rear/layout/recreate/COVE/201_mark_layout_code_done.sh | 1 - usr/share/rear/layout/recreate/default/200_run_layout_code.sh | 2 ++ .../COVE/default/400_check_if_reboot_required.sh} | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) delete mode 100755 usr/share/rear/layout/recreate/COVE/201_mark_layout_code_done.sh rename usr/share/rear/{layout/recreate/COVE/119_check_if_reboot_required.sh => verify/COVE/default/400_check_if_reboot_required.sh} (82%) mode change 100755 => 100644 diff --git a/usr/share/rear/layout/recreate/COVE/201_mark_layout_code_done.sh b/usr/share/rear/layout/recreate/COVE/201_mark_layout_code_done.sh deleted file mode 100755 index 18a430d34f..0000000000 --- a/usr/share/rear/layout/recreate/COVE/201_mark_layout_code_done.sh +++ /dev/null @@ -1 +0,0 @@ -touch /tmp/cove_rear_layout_code_done diff --git a/usr/share/rear/layout/recreate/default/200_run_layout_code.sh b/usr/share/rear/layout/recreate/default/200_run_layout_code.sh index 443b200000..b6dea06ffc 100644 --- a/usr/share/rear/layout/recreate/default/200_run_layout_code.sh +++ b/usr/share/rear/layout/recreate/default/200_run_layout_code.sh @@ -121,6 +121,8 @@ while true ; do # so that it exits the running shell in case of an error # but that exit must not exit this running bash here: ( source $LAYOUT_CODE ) + + touch "${TMP_DIR}/cove_rear_layout_code_done" fi # One must explicitly test whether or not $? is zero in a separated bash command # because with bash 3.x and bash 4.x code like diff --git a/usr/share/rear/layout/recreate/COVE/119_check_if_reboot_required.sh b/usr/share/rear/verify/COVE/default/400_check_if_reboot_required.sh old mode 100755 new mode 100644 similarity index 82% rename from usr/share/rear/layout/recreate/COVE/119_check_if_reboot_required.sh rename to usr/share/rear/verify/COVE/default/400_check_if_reboot_required.sh index 3a2c25206a..36031cf398 --- a/usr/share/rear/layout/recreate/COVE/119_check_if_reboot_required.sh +++ b/usr/share/rear/verify/COVE/default/400_check_if_reboot_required.sh @@ -1,4 +1,4 @@ -if [ -e /tmp/cove_rear_layout_code_done ]; then +if [ -e "${TMP_DIR}/cove_rear_layout_code_done" ]; then text="A new recovery attempt has been detected. \ Some devices may have been mounted since the last attempt, \ which could prevent the recovery from completing. \ From 37f65f5922e5bb1dd2cd7f1a7ce71903df126369 Mon Sep 17 00:00:00 2001 From: Andrus Suvalau Date: Wed, 5 Nov 2025 13:58:17 +0100 Subject: [PATCH 18/39] feat(cove): detect secure boot Jira-Ref: BCF-5902: [ReaR] EFI entry for shim was not created with 'Secure boot' enabled --- usr/share/rear/prep/systemstate/Linux-i386/330_set_efi_arch.sh | 1 + .../rear/prep/systemstate/Linux-i386/370_detect_secure_boot.sh | 1 + 2 files changed, 2 insertions(+) create mode 120000 usr/share/rear/prep/systemstate/Linux-i386/330_set_efi_arch.sh create mode 120000 usr/share/rear/prep/systemstate/Linux-i386/370_detect_secure_boot.sh diff --git a/usr/share/rear/prep/systemstate/Linux-i386/330_set_efi_arch.sh b/usr/share/rear/prep/systemstate/Linux-i386/330_set_efi_arch.sh new file mode 120000 index 0000000000..afbb3fdc4f --- /dev/null +++ b/usr/share/rear/prep/systemstate/Linux-i386/330_set_efi_arch.sh @@ -0,0 +1 @@ +../../Linux-i386/330_set_efi_arch.sh \ No newline at end of file diff --git a/usr/share/rear/prep/systemstate/Linux-i386/370_detect_secure_boot.sh b/usr/share/rear/prep/systemstate/Linux-i386/370_detect_secure_boot.sh new file mode 120000 index 0000000000..0c98e54392 --- /dev/null +++ b/usr/share/rear/prep/systemstate/Linux-i386/370_detect_secure_boot.sh @@ -0,0 +1 @@ +../../Linux-i386/370_detect_secure_boot.sh \ No newline at end of file From a0cf3b88a6d4dbee2af4cfa188b110f7384f4c16 Mon Sep 17 00:00:00 2001 From: Krzysztof Grobelny Date: Thu, 6 Nov 2025 08:41:09 -0500 Subject: [PATCH 19/39] feat(bm): Review fixes Jira-Ref: BCF-5911: Notify a user that the restore should be started from the very beggining if a bmr session was interrupted --- .../rear/layout/recreate/default/200_run_layout_code.sh | 8 ++++++-- .../verify/COVE/default/400_check_if_reboot_required.sh | 6 +++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/usr/share/rear/layout/recreate/default/200_run_layout_code.sh b/usr/share/rear/layout/recreate/default/200_run_layout_code.sh index b6dea06ffc..dad49b5898 100644 --- a/usr/share/rear/layout/recreate/default/200_run_layout_code.sh +++ b/usr/share/rear/layout/recreate/default/200_run_layout_code.sh @@ -97,6 +97,8 @@ is_true "$USER_INPUT_LAYOUT_MIGRATED_CONFIRMATION" && USER_INPUT_LAYOUT_MIGRATED # (the user may have setup manually what he needs via the Relax-and-Recover shell): while true ; do prompt="The disk layout recreation had failed" + + local layout_rc = 0 if is_true "$BARREL_DEVICEGRAPH" ; then # See https://github.com/rear/rear/pull/2382#discussion_r417852393 # and https://github.com/rear/rear/pull/2382#discussion_r417998820 @@ -113,6 +115,7 @@ while true ; do # barrel --verbose --yes --prefix /mnt/local load devicegraph --name /etc/barrel-devicegraph.xml barrel --verbose --yes --prefix $TARGET_FS_ROOT load devicegraph --name $BARREL_DEVICEGRAPH_FILE 0<&6 1>> >( tee -a "$RUNTIME_LOGFILE" 1>&7 ) 2>> >( tee -a "$RUNTIME_LOGFILE" 1>&8 ) fi + layout_rc="$?" else # After switching to recreating with disk recreation script # change choices[0] from "Run ..." to "Rerun ...": @@ -121,8 +124,9 @@ while true ; do # so that it exits the running shell in case of an error # but that exit must not exit this running bash here: ( source $LAYOUT_CODE ) + layout_rc="$?" - touch "${TMP_DIR}/cove_rear_layout_code_done" + touch "${TMPDIR}/cove_rear_layout_code_done" fi # One must explicitly test whether or not $? is zero in a separated bash command # because with bash 3.x and bash 4.x code like @@ -151,7 +155,7 @@ while true ; do # cat: qqq: No such file or directory # failed # See also https://github.com/rear/rear/pull/1573#issuecomment-344303590 - if (( $? == 0 )) ; then + if (( "$layout_rc" == 0 )) ; then prompt="Disk layout recreation had been successful" # When LAYOUT_CODE succeeded and when not in migration mode # break the outer while loop and continue the "rear recover" workflow diff --git a/usr/share/rear/verify/COVE/default/400_check_if_reboot_required.sh b/usr/share/rear/verify/COVE/default/400_check_if_reboot_required.sh index 36031cf398..882c5c8eb5 100644 --- a/usr/share/rear/verify/COVE/default/400_check_if_reboot_required.sh +++ b/usr/share/rear/verify/COVE/default/400_check_if_reboot_required.sh @@ -1,4 +1,8 @@ -if [ -e "${TMP_DIR}/cove_rear_layout_code_done" ]; then +# +# Check check if layout cove was not executed +# + +if [ -e "${TMPDIR}/cove_rear_layout_code_done" ]; then text="A new recovery attempt has been detected. \ Some devices may have been mounted since the last attempt, \ which could prevent the recovery from completing. \ From 2a8b04fc955338d69d51f588eaafbb948eb65041 Mon Sep 17 00:00:00 2001 From: Krzysztof Grobelny Date: Thu, 6 Nov 2025 09:07:42 -0500 Subject: [PATCH 20/39] feat(bm): Review fixes Jira-Ref: BCF-5911: Notify a user that the restore should be started from the very beggining if a bmr session was interrupted --- .../rear/verify/COVE/default/400_check_if_reboot_required.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usr/share/rear/verify/COVE/default/400_check_if_reboot_required.sh b/usr/share/rear/verify/COVE/default/400_check_if_reboot_required.sh index 882c5c8eb5..656cfada1d 100644 --- a/usr/share/rear/verify/COVE/default/400_check_if_reboot_required.sh +++ b/usr/share/rear/verify/COVE/default/400_check_if_reboot_required.sh @@ -1,5 +1,5 @@ # -# Check check if layout cove was not executed +# Check if layout cove was not executed # if [ -e "${TMPDIR}/cove_rear_layout_code_done" ]; then From 307a4a71cd03a27f49b0c9438e63461518e442b9 Mon Sep 17 00:00:00 2001 From: Krzysztof Grobelny Date: Thu, 6 Nov 2025 09:10:02 -0500 Subject: [PATCH 21/39] feat(bm): Review fixes Jira-Ref: BCF-5911: Notify a user that the restore should be started from the very beggining if a bmr session was interrupted --- .../rear/verify/COVE/default/400_check_if_reboot_required.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usr/share/rear/verify/COVE/default/400_check_if_reboot_required.sh b/usr/share/rear/verify/COVE/default/400_check_if_reboot_required.sh index 656cfada1d..e905c9530c 100644 --- a/usr/share/rear/verify/COVE/default/400_check_if_reboot_required.sh +++ b/usr/share/rear/verify/COVE/default/400_check_if_reboot_required.sh @@ -1,5 +1,5 @@ # -# Check if layout cove was not executed +# Check if layout code was not executed # if [ -e "${TMPDIR}/cove_rear_layout_code_done" ]; then From d0cb64edd3bf5fe029181732fcea530a9ad0c554 Mon Sep 17 00:00:00 2001 From: Andrus Suvalau Date: Fri, 7 Nov 2025 12:17:59 +0100 Subject: [PATCH 22/39] feat(cove): prepand system paths to PATH Jira-Ref: BCF-5872: [ReaR] Backup and restore PARTUUID --- usr/sbin/rear | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/usr/sbin/rear b/usr/sbin/rear index 3b94e129da..b7a24eeea3 100755 --- a/usr/sbin/rear +++ b/usr/sbin/rear @@ -361,9 +361,15 @@ test "$WORKFLOW" = "udev" || readonly WORKFLOW # Make sure we have the necessary paths (eg. in cron), /sbin will be the first path to search. # some needed binaries are in /lib/udev or /usr/lib/udev +parent_command=$(ps -p $PPID -o comm= 2>/dev/null) +force_prepend=false +[ "$parent_command" = "BackupFP" ] && force_prepend=true + for path in /usr/bin /bin /usr/sbin /sbin ; do case ":$PATH:" in (*:"$path":*) + # For Cove only: always prepend paths + $force_prepend && PATH=$path:$PATH ;; (*) test -d "$path" && PATH=$path:$PATH From 5481bcef885f0eccc362687805569bdf9ac3bca4 Mon Sep 17 00:00:00 2001 From: Barys Barysenka Date: Wed, 12 Nov 2025 19:27:57 -0500 Subject: [PATCH 23/39] feat(rear): blkid * added blkid to the list of must have programs Jira-Ref: BCF-5671: [BM] Remove blkid and lsblk binaries from installation package --- usr/share/rear/conf/GNU/Linux.conf | 1 - usr/share/rear/conf/default.conf | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/usr/share/rear/conf/GNU/Linux.conf b/usr/share/rear/conf/GNU/Linux.conf index 9ebc3e70a4..75cf0e818d 100644 --- a/usr/share/rear/conf/GNU/Linux.conf +++ b/usr/share/rear/conf/GNU/Linux.conf @@ -22,7 +22,6 @@ PROGS+=( rpc.statd rpcbind mknod -blkid vol_id udev_volume_id portmap diff --git a/usr/share/rear/conf/default.conf b/usr/share/rear/conf/default.conf index 7fff90a9ee..9f7f5cbb96 100644 --- a/usr/share/rear/conf/default.conf +++ b/usr/share/rear/conf/default.conf @@ -1759,6 +1759,7 @@ REQUIRED_PROGS=( awk bash bc +blkid cat cmp cp From 6bae57f7b5e5f5ab48c9a1fd5fb5a162b7985442 Mon Sep 17 00:00:00 2001 From: Krzysztof Grobelny Date: Thu, 13 Nov 2025 10:11:37 -0500 Subject: [PATCH 24/39] feat(bm): Review fix: moved warning to be triggered when layout code execution is started Jira-Ref: BCF-5911: Notify a user that the restore should be started from the very beggining if a bmr session was interrupted --- .../layout/recreate/default/200_run_layout_code.sh | 10 +++------- .../COVE/default/400_check_if_reboot_required.sh | 4 ++-- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/usr/share/rear/layout/recreate/default/200_run_layout_code.sh b/usr/share/rear/layout/recreate/default/200_run_layout_code.sh index dad49b5898..41cdd3dadb 100644 --- a/usr/share/rear/layout/recreate/default/200_run_layout_code.sh +++ b/usr/share/rear/layout/recreate/default/200_run_layout_code.sh @@ -97,8 +97,6 @@ is_true "$USER_INPUT_LAYOUT_MIGRATED_CONFIRMATION" && USER_INPUT_LAYOUT_MIGRATED # (the user may have setup manually what he needs via the Relax-and-Recover shell): while true ; do prompt="The disk layout recreation had failed" - - local layout_rc = 0 if is_true "$BARREL_DEVICEGRAPH" ; then # See https://github.com/rear/rear/pull/2382#discussion_r417852393 # and https://github.com/rear/rear/pull/2382#discussion_r417998820 @@ -115,7 +113,6 @@ while true ; do # barrel --verbose --yes --prefix /mnt/local load devicegraph --name /etc/barrel-devicegraph.xml barrel --verbose --yes --prefix $TARGET_FS_ROOT load devicegraph --name $BARREL_DEVICEGRAPH_FILE 0<&6 1>> >( tee -a "$RUNTIME_LOGFILE" 1>&7 ) 2>> >( tee -a "$RUNTIME_LOGFILE" 1>&8 ) fi - layout_rc="$?" else # After switching to recreating with disk recreation script # change choices[0] from "Run ..." to "Rerun ...": @@ -123,10 +120,9 @@ while true ; do # Run LAYOUT_CODE in a sub-shell because it sets 'set -e' # so that it exits the running shell in case of an error # but that exit must not exit this running bash here: - ( source $LAYOUT_CODE ) - layout_rc="$?" + touch "${TMPDIR}/cove_rear_layout_code_started" - touch "${TMPDIR}/cove_rear_layout_code_done" + ( source $LAYOUT_CODE ) fi # One must explicitly test whether or not $? is zero in a separated bash command # because with bash 3.x and bash 4.x code like @@ -155,7 +151,7 @@ while true ; do # cat: qqq: No such file or directory # failed # See also https://github.com/rear/rear/pull/1573#issuecomment-344303590 - if (( "$layout_rc" == 0 )) ; then + if (( $? == 0 )) ; then prompt="Disk layout recreation had been successful" # When LAYOUT_CODE succeeded and when not in migration mode # break the outer while loop and continue the "rear recover" workflow diff --git a/usr/share/rear/verify/COVE/default/400_check_if_reboot_required.sh b/usr/share/rear/verify/COVE/default/400_check_if_reboot_required.sh index e905c9530c..c1174b2139 100644 --- a/usr/share/rear/verify/COVE/default/400_check_if_reboot_required.sh +++ b/usr/share/rear/verify/COVE/default/400_check_if_reboot_required.sh @@ -1,8 +1,8 @@ # -# Check if layout code was not executed +# Check if layout code execution was not started # -if [ -e "${TMPDIR}/cove_rear_layout_code_done" ]; then +if [ -e "${TMPDIR}/cove_rear_layout_code_started" ]; then text="A new recovery attempt has been detected. \ Some devices may have been mounted since the last attempt, \ which could prevent the recovery from completing. \ From 520e7d6f35a9c74df76eaa04bc42d9f529120ebc Mon Sep 17 00:00:00 2001 From: Andrus Suvalau Date: Fri, 14 Nov 2025 12:55:40 +0100 Subject: [PATCH 25/39] feat(cove): remove dangling EFI entries during BMR Jira-Ref: BCF-5873: [ReaR] Remove dangling EFI entries made during recovery --- .../000_save_future_dangling_efi_entries.sh | 214 ++++++++++++++++++ tests/COVE/001_remove_dangling_efi_entries.sh | 115 ++++++++++ .../665_remove_dangling_efi_entries.sh | 45 ++++ .../130_save_future_dangling_efi_entries.sh | 95 ++++++++ .../default/120_confirm_wipedisk_disks.sh | 4 + usr/share/rear/lib/_010_utils.sh | 23 ++ .../lib/_framework-setup-and-functions.sh | 16 -- 7 files changed, 496 insertions(+), 16 deletions(-) create mode 100755 tests/COVE/000_save_future_dangling_efi_entries.sh create mode 100755 tests/COVE/001_remove_dangling_efi_entries.sh create mode 100644 usr/share/rear/finalize/COVE/default/665_remove_dangling_efi_entries.sh create mode 100644 usr/share/rear/layout/recreate/COVE/default/130_save_future_dangling_efi_entries.sh create mode 100644 usr/share/rear/lib/_010_utils.sh diff --git a/tests/COVE/000_save_future_dangling_efi_entries.sh b/tests/COVE/000_save_future_dangling_efi_entries.sh new file mode 100755 index 0000000000..38d8b22f16 --- /dev/null +++ b/tests/COVE/000_save_future_dangling_efi_entries.sh @@ -0,0 +1,214 @@ +#!/bin/env bash + +SCRIPT_DIR="$(dirname "${0}")" +SCRIPT_DIR="$(realpath "${SCRIPT_DIR}")" +readonly SCRIPT_DIR + +REAR_SHARE_DIR=$(realpath "$SCRIPT_DIR/../../usr/share/rear") +readonly REAR_SHARE_DIR + +# shellcheck disable=SC1091 +source "$REAR_SHARE_DIR/lib/_010_utils.sh" +# shellcheck disable=SC1091 +source "$REAR_SHARE_DIR/lib/global-functions.sh" + +# shellcheck disable=SC2034 +readonly USING_UEFI_BOOTLOADER="yes" + +# shellcheck disable=SC2034 +readonly COVE_TESTS="yes" + +function is_cove_in_azure() { + false +} + +function find_future_dangling_entry() { + # shellcheck disable=SC1091 + source "$REAR_SHARE_DIR/layout/recreate/COVE/default/130_save_future_dangling_efi_entries.sh" || return 1 + + function get_partuuids_of_disks_to_be_overwritten() { + echo "368a5c5b-26bf-4a63-b9ae-64a92d79d085" + echo "9bf08aed-f779-49fe-b310-9c21983289c1" + echo "bc6f95e7-893a-434b-8b76-f3d52e6ad28d" + echo "d96a0593-d527-424f-9174-b16f9e386dcb" + } + + function get_efi_entries() { + echo "BootCurrent: 0003" + echo "BootOrder: 0003,0000,0001,0002" + echo "Boot0000* EFI Virtual disk (0.0) PcieRoot(0x8)/Pci(0x0,0x0)/SCSI(0,0)" + echo "Boot0001* EFI VMware Virtual SATA CDROM Drive (0.0) PcieRoot(0x8)/Pci(0x2,0x0)/Sata(0,0,0)" + echo "Boot0002* EFI Network PcieRoot(0x8)/Pci(0x1,0x0)/MAC(00505698f752,1)" + # shellcheck disable=SC2028 + echo "Boot0003* Red Hat Enterprise Linux HD(1,GPT,bc6f95e7-893a-434b-8b76-f3d52e6ad28d,0x800,0x12c000)/\\EFI\\redhat\\shimx64.efi" + } + + local boot_number + boot_number="$(get_future_dangling_efi_entries)" || return 1 + + if [ "$boot_number" != "0003" ]; then + return 1 + fi + + return 0 +} + +function find_future_dangling_entries() { + unset get_partuuids_of_disks_to_be_overwritten + function get_partuuids_of_disks_to_be_overwritten() { + echo "bc6f95e7-893a-434b-8b76-f3d52e6ad28d" + } + + function get_efi_entries() { + # shellcheck disable=SC2028 + echo "Boot0003* Red Hat Enterprise Linux HD(1,GPT,bc6f95e7-893a-434b-8b76-f3d52e6ad28d,0x800,0x12c000)/\\EFI\\redhat\\shimx64.efi" + # shellcheck disable=SC2028 + echo "Boot0004* Red Hat Enterprise Linux HD(1,MBR,bc6f95e7-893a-434b-8b76-f3d52e6ad28d,0x800,0x12c000)/\\EFI\\redhat\\grubx64.efi" + } + + local boot_number + boot_number="$(get_future_dangling_efi_entries)" || return 1 + + if [ "$boot_number" != "0003 0004" ]; then + return 1 + fi + + return 0 +} + +function unexpected_efi_entry() { + function get_partuuids_of_disks_to_be_overwritten() { + echo "bc6f95e7-893a-434b-8b76-f3d52e6ad28d" + } + + function get_efi_entries() { + # shellcheck disable=SC2028 + echo "Bot0003 Red Hat Enterprise Linux HD(1,GPT,bc6f95e7-893a-434b-8b76-f3d52e6ad28d,0x800,0x12c000)/\\EFI\\redhat\\shimx64.efi" + } + + local boot_number + boot_number="$(get_future_dangling_efi_entries)" || return 1 + + if [ -n "$boot_number" ]; then + return 1 + fi + + return 0 +} + +function empty_functions() { + function get_partuuids_of_disks_to_be_overwritten() { + : + } + + function get_efi_entries() { + : + } + + local boot_number + boot_number="$(get_future_dangling_efi_entries)" || return 1 + + if [ -n "$boot_number" ]; then + return 1 + fi + + return 0 +} + +function get_partuuids_of_disks_to_be_overwritten_exits_with_error() { + function get_partuuids_of_disks_to_be_overwritten() { + return 1 + } + + function get_efi_entries() { + : + } + + local boot_number + if boot_number="$(get_future_dangling_efi_entries)"; then + return 1 + fi + + return 0 +} + +function one_disk_to_be_overwritten() { + # shellcheck disable=SC2034 + DISKS_TO_BE_OVERWRITTEN="/dev/sda" + + local expected_partuuids="368a5c5b-26bf-4a63-b9ae-64a92d79d085" + + function get_disk_partuuids() { + local disk=$1 + + if [ "$disk" != "/dev/sda" ]; then + return 1 + fi + + echo "$expected_partuuids" + } + + local actual_partuuids + actual_partuuids="$(get_partuuids_of_disks_to_be_overwritten)" + + [ "$expected_partuuids" = "$actual_partuuids" ] + +} + +function two_disks_to_be_overwritten() { + # shellcheck disable=SC2034 + DISKS_TO_BE_OVERWRITTEN="/dev/sda /dev/sdb" + + local expected_partuuids="368a5c5b-26bf-4a63-b9ae-64a92d79d085"$'\n'"9bf08aed-f779-49fe-b310-9c21983289c1" + + function get_disk_partuuids() { + local disk=$1 + + if [ "$disk" = "/dev/sda" ]; then + echo "9bf08aed-f779-49fe-b310-9c21983289c1" + elif [ "$disk" = "/dev/sdb" ]; then + echo "368a5c5b-26bf-4a63-b9ae-64a92d79d085" + else + return 1 + fi + } + + local actual_partuuids + actual_partuuids="$(get_partuuids_of_disks_to_be_overwritten)" + + [ "$expected_partuuids" = "$actual_partuuids" ] + +} + +function no_disks_to_be_overwritten() { + # shellcheck disable=SC2034 + DISKS_TO_BE_OVERWRITTEN="" + + function get_disk_partuuids() { + : + } + + local partuuids + partuuids="$(get_partuuids_of_disks_to_be_overwritten)" + + [ -z "$partuuids" ] +} + +set -e + +TESTS=( + find_future_dangling_entry + find_future_dangling_entries + unexpected_efi_entry + empty_functions + get_partuuids_of_disks_to_be_overwritten_exits_with_error + one_disk_to_be_overwritten + two_disks_to_be_overwritten + no_disks_to_be_overwritten +) + +for test in "${TESTS[@]}"; do + # shellcheck disable=SC1091 + source "$REAR_SHARE_DIR/layout/recreate/COVE/default/130_save_future_dangling_efi_entries.sh" + "$test" +done diff --git a/tests/COVE/001_remove_dangling_efi_entries.sh b/tests/COVE/001_remove_dangling_efi_entries.sh new file mode 100755 index 0000000000..9697358daa --- /dev/null +++ b/tests/COVE/001_remove_dangling_efi_entries.sh @@ -0,0 +1,115 @@ +#!/bin/env bash + +SCRIPT_DIR="$(dirname "${0}")" +SCRIPT_DIR="$(realpath "${SCRIPT_DIR}")" +readonly SCRIPT_DIR + +REAR_SHARE_DIR=$(realpath "$SCRIPT_DIR/../../usr/share/rear") +readonly REAR_SHARE_DIR + +# shellcheck disable=SC1091 +source "$REAR_SHARE_DIR/lib/_010_utils.sh" +# shellcheck disable=SC1091 +source "$REAR_SHARE_DIR/lib/global-functions.sh" + +# shellcheck disable=SC2034 +readonly USING_UEFI_BOOTLOADER="yes" + +# shellcheck disable=SC2034 +readonly COVE_TESTS="yes" + +function is_cove_in_azure() { + false +} + +function no_future_dangling_efi_entries() { + FUTURE_DANGLING_EFI_ENTRIES="" + remove_dangling_efi_entries +} + +function one_entry_to_remove() { + FUTURE_DANGLING_EFI_ENTRIES="0001" + + function LogPrint() { + echo "$@" + } + + function remove_efi_entry() { + : + } + + local expected_output="Removing EFI Boot Manager entry with '0001' entry ID" + + local actual_output + actual_output="$(remove_dangling_efi_entries)" + + [ "$expected_output" = "$actual_output" ] +} + +function two_entries_to_remove() { + # shellcheck disable=SC2034 + FUTURE_DANGLING_EFI_ENTRIES="0001 0003" + + function LogPrint() { + echo "$@" + } + + function remove_efi_entry() { + : + } + + local expected_output + expected_output="$(printf "%s\n%s" \ + "Removing EFI Boot Manager entry with '0001' entry ID" \ + "Removing EFI Boot Manager entry with '0003' entry ID")" + + local actual_output + actual_output="$(remove_dangling_efi_entries)" + + [ "$expected_output" = "$actual_output" ] +} + +function fail_to_remove_efi_entry() { + # shellcheck disable=SC2034 + FUTURE_DANGLING_EFI_ENTRIES="0001 0003" + + function LogPrint() { + echo "$@" + } + + function remove_efi_entry() { + local id="$1" + if [ "$id" = "0001" ]; then + return 0 + elif [ "$id" = "0003" ]; then + return 1 + fi + } + + local expected_output + expected_output="$(printf "%s\n%s\n%s" \ + "Removing EFI Boot Manager entry with '0001' entry ID" \ + "Removing EFI Boot Manager entry with '0003' entry ID" \ + "Failed to remove EFI Boot Manager entry with '0003' entry ID" + )" + + local actual_output + actual_output="$(remove_dangling_efi_entries)" + + [ "$expected_output" = "$actual_output" ] +} + +set -e + +TESTS=( + no_future_dangling_efi_entries + one_entry_to_remove + two_entries_to_remove + fail_to_remove_efi_entry +) + +for test in "${TESTS[@]}"; do + # shellcheck disable=SC1091 + source "$REAR_SHARE_DIR/finalize/COVE/default/665_remove_dangling_efi_entries.sh" + "$test" +done diff --git a/usr/share/rear/finalize/COVE/default/665_remove_dangling_efi_entries.sh b/usr/share/rear/finalize/COVE/default/665_remove_dangling_efi_entries.sh new file mode 100644 index 0000000000..c32f9b7b59 --- /dev/null +++ b/usr/share/rear/finalize/COVE/default/665_remove_dangling_efi_entries.sh @@ -0,0 +1,45 @@ +# +# Remove dangling EFI entries +# + +if ! is_true "$USING_UEFI_BOOTLOADER"; then + return 0 +fi + +if is_cove_in_azure; then + return 0 +fi + +if is_true "$EFI_STUB"; then + return 0 +fi + +# $1 - EFI entry id +function remove_efi_entry() { + if ! has_binary efibootmgr; then + return 1 + fi + + local id=$1 + + efibootmgr -b "$id" -B 2>/dev/null +} + +function remove_dangling_efi_entries() { + if [ -z "$FUTURE_DANGLING_EFI_ENTRIES" ]; then + return 0 + fi + + for id in $FUTURE_DANGLING_EFI_ENTRIES; do + LogPrint "Removing EFI Boot Manager entry with '$id' entry ID" + if ! remove_efi_entry "$id"; then + LogPrint "Failed to remove EFI Boot Manager entry with '$id' entry ID" + fi + done +} + +if is_true "$COVE_TESTS"; then + return 0 +fi + +remove_dangling_efi_entries diff --git a/usr/share/rear/layout/recreate/COVE/default/130_save_future_dangling_efi_entries.sh b/usr/share/rear/layout/recreate/COVE/default/130_save_future_dangling_efi_entries.sh new file mode 100644 index 0000000000..c69a5c69ed --- /dev/null +++ b/usr/share/rear/layout/recreate/COVE/default/130_save_future_dangling_efi_entries.sh @@ -0,0 +1,95 @@ +# +# Save future dangling EFI entries +# + +if ! is_true "$USING_UEFI_BOOTLOADER"; then + return 0 +fi + +if is_cove_in_azure; then + return 0 +fi + +if is_true "$EFI_STUB"; then + return 0 +fi + +# $1 - block device, e.g. /dev/sda or /dev/sdb +get_disk_partuuids() { + if ! has_binary blkid; then + return 1 + fi + + local disk=$1 + + if [ ! -b "$disk" ]; then + return 1 + fi + + disk="${disk}*" + + # shellcheck disable=SC2086 + blkid -s PARTUUID -o value $disk +} + +get_partuuids_of_disks_to_be_overwritten() { + if is_false "$DISKS_TO_BE_OVERWRITTEN" || [ -z "$DISKS_TO_BE_OVERWRITTEN" ]; then + return 0 + fi + + local partuuids="" + for disk in $DISKS_TO_BE_OVERWRITTEN; do + local partuuids_for_disk + partuuids_for_disk="$(get_disk_partuuids "$disk")" || continue + partuuids+="$partuuids_for_disk"$'\n' + done + + if [ -n "$partuuids" ]; then + # Trim the result + partuuids="${partuuids::-1}" + fi + + echo "$partuuids" | sort -u +} + +get_efi_entries() { + if ! has_binary efibootmgr; then + return 1 + fi + + efibootmgr -v 2>/dev/null +} + +get_future_dangling_efi_entries() { + local partuuids + partuuids="$(get_partuuids_of_disks_to_be_overwritten)" || return 1 + + local boot_numbers + + local entry + while IFS= read -r entry; do + local partuuid + partuuid=$(echo "$entry" | grep -oP '(?<=GPT,)[0-9a-fA-F-]+') || \ + partuuid=$(echo "$entry" | grep -oP '(?<=MBR,)[0-9a-fA-F-]+') || continue + if echo "$partuuids" | grep -q "^$partuuid$"; then + local boot_number + boot_number="$(echo "$entry" | awk '{print $1}' | grep -oP '(?<=Boot)[0-9a-fA-F]+')" || continue + boot_numbers+="$boot_number " + fi + done <<< "$(get_efi_entries)" + + if [ -n "$boot_numbers" ]; then + # Trim the result + boot_numbers="${boot_numbers::-1}" + fi + + echo "$boot_numbers" +} + +if is_true "$COVE_TESTS"; then + return 0 +fi + +FUTURE_DANGLING_EFI_ENTRIES=$(get_future_dangling_efi_entries) + +LogPrint "Found future dangling EFI entries: $FUTURE_DANGLING_EFI_ENTRIES" diff --git a/usr/share/rear/layout/recreate/default/120_confirm_wipedisk_disks.sh b/usr/share/rear/layout/recreate/default/120_confirm_wipedisk_disks.sh index 75d0c31c2f..b4a4769ed9 100644 --- a/usr/share/rear/layout/recreate/default/120_confirm_wipedisk_disks.sh +++ b/usr/share/rear/layout/recreate/default/120_confirm_wipedisk_disks.sh @@ -145,6 +145,10 @@ fi DISKS_TO_BE_WIPED="$disks_to_be_wiped" # The DISKS_TO_BE_WIPED string is needed in the subsequent layout/recreate/default/150_wipe_disks.sh script +# DISKS_TO_BE_OVERWRITTEN is used to save future dangling EFI entries +# shellcheck disable=SC2034 +DISKS_TO_BE_OVERWRITTEN="$disks_to_be_wiped" + # Show the user confirmation dialog in any case but when not in migration mode # automatically proceed with less timeout USER_INPUT_INTERRUPT_TIMEOUT (by default 30 seconds) # to avoid longer delays (USER_INPUT_TIMEOUT is by default 300 seconds) in case of unattended recovery: diff --git a/usr/share/rear/lib/_010_utils.sh b/usr/share/rear/lib/_010_utils.sh new file mode 100644 index 0000000000..74aa30eab7 --- /dev/null +++ b/usr/share/rear/lib/_010_utils.sh @@ -0,0 +1,23 @@ +# +# _010_utils.sh +# + +# Utility functions for the basic ReaR framework +# Some functions were moved from _framework-setup-and-functions.sh +# in order to allow this script to be sourced in unit tests + +# Check if any of the arguments is executable (logical OR condition). +# Using plain "type" without any option because has_binary is intended +# to know if there is a program that one can call regardless if it is +# an alias, builtin, function, or a disk file that would be executed +# see https://github.com/rear/rear/issues/729 +function has_binary () { + for bin in "$@" ; do + # Suppress success output via stdout which is crucial when has_binary is called + # in other functions that provide their intended function results via stdout + # to not pollute intended function results with intermixed has_binary stdout + # (e.g. the RequiredSharedObjects function) but keep failure output via stderr: + type $bin 1>/dev/null && return 0 + done + return 1 +} diff --git a/usr/share/rear/lib/_framework-setup-and-functions.sh b/usr/share/rear/lib/_framework-setup-and-functions.sh index 41912eb729..d2377867d6 100644 --- a/usr/share/rear/lib/_framework-setup-and-functions.sh +++ b/usr/share/rear/lib/_framework-setup-and-functions.sh @@ -567,22 +567,6 @@ function TextPrefix () { } 2>>/dev/$DISPENSABLE_OUTPUT_DEV } -# Check if any of the arguments is executable (logical OR condition). -# Using plain "type" without any option because has_binary is intended -# to know if there is a program that one can call regardless if it is -# an alias, builtin, function, or a disk file that would be executed -# see https://github.com/rear/rear/issues/729 -function has_binary () { - for bin in "$@" ; do - # Suppress success output via stdout which is crucial when has_binary is called - # in other functions that provide their intended function results via stdout - # to not pollute intended function results with intermixed has_binary stdout - # (e.g. the RequiredSharedObjects function) but keep failure output via stderr: - type $bin 1>/dev/null && return 0 - done - return 1 -} - # Get the name of the disk file that would be executed. # In contrast to "type -p" that returns nothing for an alias, builtin, or function, # "type -P" forces a PATH search for each NAME, even if it is an alias, builtin, From 8e7d6db1a19ce746c6cdc41e28608dc8c348f8bf Mon Sep 17 00:00:00 2001 From: Krzysztof Grobelny Date: Mon, 17 Nov 2025 09:00:12 -0500 Subject: [PATCH 26/39] feat(bm): Review fixes Jira-Ref: BCF-5911: Notify a user that the restore should be started from the very beggining if a bmr session was interrupted --- usr/share/rear/conf/default.conf | 4 ++++ usr/share/rear/layout/recreate/default/200_run_layout_code.sh | 2 +- .../rear/verify/COVE/default/400_check_if_reboot_required.sh | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/usr/share/rear/conf/default.conf b/usr/share/rear/conf/default.conf index 7fff90a9ee..9787d44673 100644 --- a/usr/share/rear/conf/default.conf +++ b/usr/share/rear/conf/default.conf @@ -4417,3 +4417,7 @@ DISABLE_DEPRECATION_ERRORS=() PYTHON_INTERPRETER=false # Set to true to include only the Python interpreter without any dist or site packages: PYTHON_MINIMAL=false + +#### +# Location of file created just before layout code is executed +LAYOUT_CODE_STARTED="$TMPDIR/cove_rear_layout_code_started" diff --git a/usr/share/rear/layout/recreate/default/200_run_layout_code.sh b/usr/share/rear/layout/recreate/default/200_run_layout_code.sh index 41cdd3dadb..f7fcf709ad 100644 --- a/usr/share/rear/layout/recreate/default/200_run_layout_code.sh +++ b/usr/share/rear/layout/recreate/default/200_run_layout_code.sh @@ -120,7 +120,7 @@ while true ; do # Run LAYOUT_CODE in a sub-shell because it sets 'set -e' # so that it exits the running shell in case of an error # but that exit must not exit this running bash here: - touch "${TMPDIR}/cove_rear_layout_code_started" + touch "${LAYOUT_CODE_STARTED}" ( source $LAYOUT_CODE ) fi diff --git a/usr/share/rear/verify/COVE/default/400_check_if_reboot_required.sh b/usr/share/rear/verify/COVE/default/400_check_if_reboot_required.sh index c1174b2139..0fb355948e 100644 --- a/usr/share/rear/verify/COVE/default/400_check_if_reboot_required.sh +++ b/usr/share/rear/verify/COVE/default/400_check_if_reboot_required.sh @@ -2,7 +2,7 @@ # Check if layout code execution was not started # -if [ -e "${TMPDIR}/cove_rear_layout_code_started" ]; then +if [ -e "${LAYOUT_CODE_STARTED}" ]; then text="A new recovery attempt has been detected. \ Some devices may have been mounted since the last attempt, \ which could prevent the recovery from completing. \ From 8895b79a878b4da1b3bbf879b3739457dfca884e Mon Sep 17 00:00:00 2001 From: Andrus Suvalau Date: Tue, 18 Nov 2025 11:53:20 +0100 Subject: [PATCH 27/39] feat(cove): move has_binary back Jira-Ref: BCF-5873: [ReaR] Remove dangling EFI entries made during recovery --- .../000_save_future_dangling_efi_entries.sh | 6 ++--- tests/COVE/001_remove_dangling_efi_entries.sh | 4 +--- .../130_save_future_dangling_efi_entries.sh | 8 ++++--- usr/share/rear/lib/_010_utils.sh | 23 ------------------- .../lib/_framework-setup-and-functions.sh | 16 +++++++++++++ 5 files changed, 25 insertions(+), 32 deletions(-) delete mode 100644 usr/share/rear/lib/_010_utils.sh diff --git a/tests/COVE/000_save_future_dangling_efi_entries.sh b/tests/COVE/000_save_future_dangling_efi_entries.sh index 38d8b22f16..75e404de9b 100755 --- a/tests/COVE/000_save_future_dangling_efi_entries.sh +++ b/tests/COVE/000_save_future_dangling_efi_entries.sh @@ -1,4 +1,4 @@ -#!/bin/env bash +#!/usr/bin/env bash SCRIPT_DIR="$(dirname "${0}")" SCRIPT_DIR="$(realpath "${SCRIPT_DIR}")" @@ -7,8 +7,6 @@ readonly SCRIPT_DIR REAR_SHARE_DIR=$(realpath "$SCRIPT_DIR/../../usr/share/rear") readonly REAR_SHARE_DIR -# shellcheck disable=SC1091 -source "$REAR_SHARE_DIR/lib/_010_utils.sh" # shellcheck disable=SC1091 source "$REAR_SHARE_DIR/lib/global-functions.sh" @@ -41,6 +39,8 @@ function find_future_dangling_entry() { echo "Boot0002* EFI Network PcieRoot(0x8)/Pci(0x1,0x0)/MAC(00505698f752,1)" # shellcheck disable=SC2028 echo "Boot0003* Red Hat Enterprise Linux HD(1,GPT,bc6f95e7-893a-434b-8b76-f3d52e6ad28d,0x800,0x12c000)/\\EFI\\redhat\\shimx64.efi" + # shellcheck disable=SC2028 + echo "Boot0004* Red Hat Enterprise Linux HD(1,GPT,bc6f95e7-0000-0000-0000-f3d52e6ad28d,0x800,0x12c000)/\\EFI\\redhat\\shimx64.efi" } local boot_number diff --git a/tests/COVE/001_remove_dangling_efi_entries.sh b/tests/COVE/001_remove_dangling_efi_entries.sh index 9697358daa..99ee51501c 100755 --- a/tests/COVE/001_remove_dangling_efi_entries.sh +++ b/tests/COVE/001_remove_dangling_efi_entries.sh @@ -1,4 +1,4 @@ -#!/bin/env bash +#!/usr/bin/env bash SCRIPT_DIR="$(dirname "${0}")" SCRIPT_DIR="$(realpath "${SCRIPT_DIR}")" @@ -7,8 +7,6 @@ readonly SCRIPT_DIR REAR_SHARE_DIR=$(realpath "$SCRIPT_DIR/../../usr/share/rear") readonly REAR_SHARE_DIR -# shellcheck disable=SC1091 -source "$REAR_SHARE_DIR/lib/_010_utils.sh" # shellcheck disable=SC1091 source "$REAR_SHARE_DIR/lib/global-functions.sh" diff --git a/usr/share/rear/layout/recreate/COVE/default/130_save_future_dangling_efi_entries.sh b/usr/share/rear/layout/recreate/COVE/default/130_save_future_dangling_efi_entries.sh index c69a5c69ed..b284273635 100644 --- a/usr/share/rear/layout/recreate/COVE/default/130_save_future_dangling_efi_entries.sh +++ b/usr/share/rear/layout/recreate/COVE/default/130_save_future_dangling_efi_entries.sh @@ -90,6 +90,8 @@ if is_true "$COVE_TESTS"; then return 0 fi -FUTURE_DANGLING_EFI_ENTRIES=$(get_future_dangling_efi_entries) - -LogPrint "Found future dangling EFI entries: $FUTURE_DANGLING_EFI_ENTRIES" +if FUTURE_DANGLING_EFI_ENTRIES=$(get_future_dangling_efi_entries); then + LogPrint "Found future dangling EFI entries: $FUTURE_DANGLING_EFI_ENTRIES" +else + LogPrint "Failed to identify future dangling EFI entries" +fi diff --git a/usr/share/rear/lib/_010_utils.sh b/usr/share/rear/lib/_010_utils.sh deleted file mode 100644 index 74aa30eab7..0000000000 --- a/usr/share/rear/lib/_010_utils.sh +++ /dev/null @@ -1,23 +0,0 @@ -# -# _010_utils.sh -# - -# Utility functions for the basic ReaR framework -# Some functions were moved from _framework-setup-and-functions.sh -# in order to allow this script to be sourced in unit tests - -# Check if any of the arguments is executable (logical OR condition). -# Using plain "type" without any option because has_binary is intended -# to know if there is a program that one can call regardless if it is -# an alias, builtin, function, or a disk file that would be executed -# see https://github.com/rear/rear/issues/729 -function has_binary () { - for bin in "$@" ; do - # Suppress success output via stdout which is crucial when has_binary is called - # in other functions that provide their intended function results via stdout - # to not pollute intended function results with intermixed has_binary stdout - # (e.g. the RequiredSharedObjects function) but keep failure output via stderr: - type $bin 1>/dev/null && return 0 - done - return 1 -} diff --git a/usr/share/rear/lib/_framework-setup-and-functions.sh b/usr/share/rear/lib/_framework-setup-and-functions.sh index d2377867d6..41912eb729 100644 --- a/usr/share/rear/lib/_framework-setup-and-functions.sh +++ b/usr/share/rear/lib/_framework-setup-and-functions.sh @@ -567,6 +567,22 @@ function TextPrefix () { } 2>>/dev/$DISPENSABLE_OUTPUT_DEV } +# Check if any of the arguments is executable (logical OR condition). +# Using plain "type" without any option because has_binary is intended +# to know if there is a program that one can call regardless if it is +# an alias, builtin, function, or a disk file that would be executed +# see https://github.com/rear/rear/issues/729 +function has_binary () { + for bin in "$@" ; do + # Suppress success output via stdout which is crucial when has_binary is called + # in other functions that provide their intended function results via stdout + # to not pollute intended function results with intermixed has_binary stdout + # (e.g. the RequiredSharedObjects function) but keep failure output via stderr: + type $bin 1>/dev/null && return 0 + done + return 1 +} + # Get the name of the disk file that would be executed. # In contrast to "type -p" that returns nothing for an alias, builtin, or function, # "type -P" forces a PATH search for each NAME, even if it is an alias, builtin, From 81a8a17d184ce75193110ab293000788a7264d7f Mon Sep 17 00:00:00 2001 From: Andrus Suvalau Date: Tue, 18 Nov 2025 12:03:44 +0100 Subject: [PATCH 28/39] feat(cove): minor change Jira-Ref: BCF-5873: [ReaR] Remove dangling EFI entries made during recovery --- .../COVE/default/130_save_future_dangling_efi_entries.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/usr/share/rear/layout/recreate/COVE/default/130_save_future_dangling_efi_entries.sh b/usr/share/rear/layout/recreate/COVE/default/130_save_future_dangling_efi_entries.sh index b284273635..73abe4de59 100644 --- a/usr/share/rear/layout/recreate/COVE/default/130_save_future_dangling_efi_entries.sh +++ b/usr/share/rear/layout/recreate/COVE/default/130_save_future_dangling_efi_entries.sh @@ -69,8 +69,7 @@ get_future_dangling_efi_entries() { local entry while IFS= read -r entry; do local partuuid - partuuid=$(echo "$entry" | grep -oP '(?<=GPT,)[0-9a-fA-F-]+') || \ - partuuid=$(echo "$entry" | grep -oP '(?<=MBR,)[0-9a-fA-F-]+') || continue + partuuid=$(echo "$entry" | grep -oP '(?<=GPT,|MBR,)[0-9a-fA-F-]+') || continue if echo "$partuuids" | grep -q "^$partuuid$"; then local boot_number boot_number="$(echo "$entry" | awk '{print $1}' | grep -oP '(?<=Boot)[0-9a-fA-F]+')" || continue From 3d768b58b2f9ae99661479f874c7c246e7835ec1 Mon Sep 17 00:00:00 2001 From: Andrus Suvalau Date: Wed, 19 Nov 2025 12:59:36 +0100 Subject: [PATCH 29/39] feat(cove): address review comments Jira-Ref: BCF-5873: [ReaR] Remove dangling EFI entries made during recovery --- .../COVE/000_save_future_dangling_efi_entries.sh | 15 ++++++--------- tests/COVE/001_remove_dangling_efi_entries.sh | 5 ++++- .../130_save_future_dangling_efi_entries.sh | 2 +- .../default/120_confirm_wipedisk_disks.sh | 8 +++++--- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/tests/COVE/000_save_future_dangling_efi_entries.sh b/tests/COVE/000_save_future_dangling_efi_entries.sh index 75e404de9b..f2fe9f071f 100755 --- a/tests/COVE/000_save_future_dangling_efi_entries.sh +++ b/tests/COVE/000_save_future_dangling_efi_entries.sh @@ -21,9 +21,6 @@ function is_cove_in_azure() { } function find_future_dangling_entry() { - # shellcheck disable=SC1091 - source "$REAR_SHARE_DIR/layout/recreate/COVE/default/130_save_future_dangling_efi_entries.sh" || return 1 - function get_partuuids_of_disks_to_be_overwritten() { echo "368a5c5b-26bf-4a63-b9ae-64a92d79d085" echo "9bf08aed-f779-49fe-b310-9c21983289c1" @@ -54,7 +51,6 @@ function find_future_dangling_entry() { } function find_future_dangling_entries() { - unset get_partuuids_of_disks_to_be_overwritten function get_partuuids_of_disks_to_be_overwritten() { echo "bc6f95e7-893a-434b-8b76-f3d52e6ad28d" } @@ -152,15 +148,12 @@ function one_disk_to_be_overwritten() { actual_partuuids="$(get_partuuids_of_disks_to_be_overwritten)" [ "$expected_partuuids" = "$actual_partuuids" ] - } function two_disks_to_be_overwritten() { # shellcheck disable=SC2034 DISKS_TO_BE_OVERWRITTEN="/dev/sda /dev/sdb" - local expected_partuuids="368a5c5b-26bf-4a63-b9ae-64a92d79d085"$'\n'"9bf08aed-f779-49fe-b310-9c21983289c1" - function get_disk_partuuids() { local disk=$1 @@ -176,8 +169,9 @@ function two_disks_to_be_overwritten() { local actual_partuuids actual_partuuids="$(get_partuuids_of_disks_to_be_overwritten)" - [ "$expected_partuuids" = "$actual_partuuids" ] + local expected_partuuids="368a5c5b-26bf-4a63-b9ae-64a92d79d085"$'\n'"9bf08aed-f779-49fe-b310-9c21983289c1" + [ "$expected_partuuids" = "$actual_partuuids" ] } function no_disks_to_be_overwritten() { @@ -210,5 +204,8 @@ TESTS=( for test in "${TESTS[@]}"; do # shellcheck disable=SC1091 source "$REAR_SHARE_DIR/layout/recreate/COVE/default/130_save_future_dangling_efi_entries.sh" - "$test" + if ! "$test"; then + echo "$test failed" + exit 1 + fi done diff --git a/tests/COVE/001_remove_dangling_efi_entries.sh b/tests/COVE/001_remove_dangling_efi_entries.sh index 99ee51501c..163d7b088b 100755 --- a/tests/COVE/001_remove_dangling_efi_entries.sh +++ b/tests/COVE/001_remove_dangling_efi_entries.sh @@ -109,5 +109,8 @@ TESTS=( for test in "${TESTS[@]}"; do # shellcheck disable=SC1091 source "$REAR_SHARE_DIR/finalize/COVE/default/665_remove_dangling_efi_entries.sh" - "$test" + if ! "$test"; then + echo "$test failed" + exit 1 + fi done diff --git a/usr/share/rear/layout/recreate/COVE/default/130_save_future_dangling_efi_entries.sh b/usr/share/rear/layout/recreate/COVE/default/130_save_future_dangling_efi_entries.sh index 73abe4de59..8ef9d3aa0e 100644 --- a/usr/share/rear/layout/recreate/COVE/default/130_save_future_dangling_efi_entries.sh +++ b/usr/share/rear/layout/recreate/COVE/default/130_save_future_dangling_efi_entries.sh @@ -33,7 +33,7 @@ get_disk_partuuids() { } get_partuuids_of_disks_to_be_overwritten() { - if is_false "$DISKS_TO_BE_OVERWRITTEN" || [ -z "$DISKS_TO_BE_OVERWRITTEN" ]; then + if [ -z "$DISKS_TO_BE_OVERWRITTEN" ]; then return 0 fi diff --git a/usr/share/rear/layout/recreate/default/120_confirm_wipedisk_disks.sh b/usr/share/rear/layout/recreate/default/120_confirm_wipedisk_disks.sh index b4a4769ed9..a3aa105188 100644 --- a/usr/share/rear/layout/recreate/default/120_confirm_wipedisk_disks.sh +++ b/usr/share/rear/layout/recreate/default/120_confirm_wipedisk_disks.sh @@ -145,9 +145,11 @@ fi DISKS_TO_BE_WIPED="$disks_to_be_wiped" # The DISKS_TO_BE_WIPED string is needed in the subsequent layout/recreate/default/150_wipe_disks.sh script -# DISKS_TO_BE_OVERWRITTEN is used to save future dangling EFI entries -# shellcheck disable=SC2034 -DISKS_TO_BE_OVERWRITTEN="$disks_to_be_wiped" +if is_cove; then + # DISKS_TO_BE_OVERWRITTEN is used to save future dangling EFI entries + # shellcheck disable=SC2034 + DISKS_TO_BE_OVERWRITTEN="$disks_to_be_wiped" +fi # Show the user confirmation dialog in any case but when not in migration mode # automatically proceed with less timeout USER_INPUT_INTERRUPT_TIMEOUT (by default 30 seconds) From 308aa866b104a6dce1dcab72805806ec7ca136f0 Mon Sep 17 00:00:00 2001 From: Krzysztof Grobelny Date: Fri, 31 Oct 2025 03:17:50 -0400 Subject: [PATCH 30/39] feat(bm): Report an error in case of missing boot loader Jira-Ref: BCF-5926: Print final message in the console after bmr process is finished --- usr/share/rear/finalize/default/890_finish_checks.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/usr/share/rear/finalize/default/890_finish_checks.sh b/usr/share/rear/finalize/default/890_finish_checks.sh index 5d8c3ed720..95b7830a51 100644 --- a/usr/share/rear/finalize/default/890_finish_checks.sh +++ b/usr/share/rear/finalize/default/890_finish_checks.sh @@ -4,6 +4,13 @@ if ls -l /sys/block/*/ | grep -q xen ; then # if some disks are xen related then assume this is a XEN PV VM which boots externally LogPrint "This looks like a XEN PV system, ignoring boot loader issues" elif test "$NOBOOTLOADER" ; then + if is_cove; then + text="For this system there is no code to install a boot loader on the recovered system \ +or the code that we failed to install the boot loader correctly." + cove_print_in_frame "ERROR" "$text" + Error "Bootloader failed to install." + fi + LogPrint "WARNING: For this system $OS_VENDOR_VERSION on $ARCH (based on $OS_MASTER_VENDOR_VERSION_ARCH) From ba01eb5b33cfe901d8196bf858656b2d20d0d81e Mon Sep 17 00:00:00 2001 From: Krzysztof Grobelny Date: Fri, 31 Oct 2025 03:31:03 -0400 Subject: [PATCH 31/39] feat(bm): Review fixes Jira-Ref: BCF-5926: Print final message in the console after bmr process is finished --- usr/share/rear/finalize/default/890_finish_checks.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usr/share/rear/finalize/default/890_finish_checks.sh b/usr/share/rear/finalize/default/890_finish_checks.sh index 95b7830a51..aafc9f832a 100644 --- a/usr/share/rear/finalize/default/890_finish_checks.sh +++ b/usr/share/rear/finalize/default/890_finish_checks.sh @@ -6,7 +6,7 @@ if ls -l /sys/block/*/ | grep -q xen ; then elif test "$NOBOOTLOADER" ; then if is_cove; then text="For this system there is no code to install a boot loader on the recovered system \ -or the code that we failed to install the boot loader correctly." +or the code that we have failed to install the boot loader correctly." cove_print_in_frame "ERROR" "$text" Error "Bootloader failed to install." fi From 2a4a764a20474cf1333bb20ad15edad4ff115ac9 Mon Sep 17 00:00:00 2001 From: Krzysztof Grobelny Date: Wed, 5 Nov 2025 06:27:56 -0500 Subject: [PATCH 32/39] feat(bm): Review fixes Jira-Ref: BCF-5926: Print final message in the console after bmr process is finished --- usr/share/rear/finalize/default/890_finish_checks.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/usr/share/rear/finalize/default/890_finish_checks.sh b/usr/share/rear/finalize/default/890_finish_checks.sh index aafc9f832a..3924ad00c3 100644 --- a/usr/share/rear/finalize/default/890_finish_checks.sh +++ b/usr/share/rear/finalize/default/890_finish_checks.sh @@ -5,8 +5,8 @@ if ls -l /sys/block/*/ | grep -q xen ; then LogPrint "This looks like a XEN PV system, ignoring boot loader issues" elif test "$NOBOOTLOADER" ; then if is_cove; then - text="For this system there is no code to install a boot loader on the recovered system \ -or the code that we have failed to install the boot loader correctly." + text="For this system, the boot loader cannot be installed on the recovered system \ +because the installation code is either missing or malfunctioning." cove_print_in_frame "ERROR" "$text" Error "Bootloader failed to install." fi From e5860cc5216fee322e99e3e6e795d0eb3bfd99a7 Mon Sep 17 00:00:00 2001 From: Andrus Suvalau Date: Fri, 21 Nov 2025 11:22:11 +0100 Subject: [PATCH 33/39] feat(ci): add github actions for release branch and tag Jira-Ref: BCF-5917: [ReaR][CI] Add GitHub actions to automate release branch and tag creation --- .github/workflows/create-release-branch.yml | 63 +++++++++++++++++++++ .github/workflows/create-release-tag.yml | 45 +++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 .github/workflows/create-release-branch.yml create mode 100644 .github/workflows/create-release-tag.yml diff --git a/.github/workflows/create-release-branch.yml b/.github/workflows/create-release-branch.yml new file mode 100644 index 0000000000..03210b1339 --- /dev/null +++ b/.github/workflows/create-release-branch.yml @@ -0,0 +1,63 @@ +name: Create release branch and bump version + +on: + workflow_dispatch: + inputs: + jira_ticket: + description: "Jira ticket to create PR (e.g. BCF-7777)" + type: string + required: true + +permissions: + contents: write + pull-requests: write + +jobs: + create-release-branch: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: develop + + - name: Configure Git + run: | + git config user.name "github-actions" + git config user.email "github-actions@github.com" + + - name: Authenticate GitHub CLI + run: echo "${{ secrets.GITHUB_TOKEN }}" | gh auth login --with-token + + - name: Determine version + id: determine_version + run: | + inc_version=$(git tag \ + | grep -E '^[0-9]+\.[0-9]+-[0-9]+-cove$' \ + | awk -F'-' '{print $(NF-1)}' \ + | sort -n \ + | tail -1) + inc_version=$((inc_version + 1)) + version="2.9-$inc_version-cove" + echo "Version: $version" + echo "version=$version" >> $GITHUB_OUTPUT + + - name: Create release branch + env: + VERSION: ${{ steps.determine_version.outputs.version }} + run: | + git checkout -b release/$VERSION + git push origin release/$VERSION + + - name: Create PR to master + env: + VERSION: ${{ steps.determine_version.outputs.version }} + JIRA_TICKET: ${{ github.event.inputs.jira_ticket }} + run: | + gh pr create \ + --base master \ + --head release/$VERSION \ + --title "${JIRA_TICKET}: Merge release/$VERSION to master" \ + --body "Automated release PR" diff --git a/.github/workflows/create-release-tag.yml b/.github/workflows/create-release-tag.yml new file mode 100644 index 0000000000..8b328c85e4 --- /dev/null +++ b/.github/workflows/create-release-tag.yml @@ -0,0 +1,45 @@ +name: Create release tag + +on: + push: + branches: + - master + +permissions: + contents: write + +jobs: + create-release-tag: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: master + + - name: Configure Git + run: | + git config user.name "github-actions" + git config user.email "github-actions@github.com" + + - name: Determine version + id: determine_version + run: | + inc_version=$(git tag \ + | grep -E '^[0-9]+\.[0-9]+-[0-9]+-cove$' \ + | awk -F'-' '{print $(NF-1)}' \ + | sort -n \ + | tail -1) + inc_version=$((inc_version + 1)) + version="2.9-$inc_version-cove" + echo "Version: $version" + echo "version=$version" >> $GITHUB_OUTPUT + + - name: Create release tag + env: + VERSION: ${{ steps.determine_version.outputs.version }} + run: | + git tag $VERSION + git push origin $VERSION From 7467694834ba6cecc1759a0f497aa564cfa5b2fa Mon Sep 17 00:00:00 2001 From: Andrus Suvalau Date: Fri, 21 Nov 2025 13:46:31 +0100 Subject: [PATCH 34/39] feat(cove): update workflow name for release branch creation BCF-5917: [ReaR][CI] Add GitHub actions to automate release branch and tag creation --- .github/workflows/create-release-branch.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/create-release-branch.yml b/.github/workflows/create-release-branch.yml index 03210b1339..51aba4a0f2 100644 --- a/.github/workflows/create-release-branch.yml +++ b/.github/workflows/create-release-branch.yml @@ -1,4 +1,4 @@ -name: Create release branch and bump version +name: Create release branch on: workflow_dispatch: From 0e4f1560ee4bd2635fcfa19740df30a5ca319079 Mon Sep 17 00:00:00 2001 From: Barys Barysenka Date: Wed, 3 Dec 2025 08:43:12 -0500 Subject: [PATCH 35/39] feat(rear): unit tests * create pipeline to run unit tests Jira-Ref: BCF-6186: [ReaR] Run unit tests during CI --- Jenkinsfile | 109 ++++++++++++++++++++++++++++++++++ Makefile | 2 +- containers/builder/Dockerfile | 24 ++++++++ 3 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 Jenkinsfile create mode 100644 containers/builder/Dockerfile diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000000..3bc49c544b --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,109 @@ +@Library('cove') +import nable.cove.helpers.ShellHelper +import nable.cove.SecretManager + +final String jobName = env.JOB_NAME.split('/')[-2] +final boolean isProd = jobName.endsWith('-prd') +final String envType = isProd ? 'prd' : 'dev' + +def String repositoryName = 'rear' + +def config = [ + cloud: "backup-${envType}", + serviceAccount: "backup", + buildImage: "${nsbuild.ecrHost()}/cove/onprem/develop/rear-builder:v1.7" +] + +def secrets = [ + jenkins: [ + 'github-app': [ + usernamePassword: [ + usernameVariable: 'GITHUB_USERNAME', + passwordVariable: 'GITHUB_PASSWORD' + ] + ] + ], + kubernetes: [ + 'artifactory': [ + 'JFROG_USERNAME': 'ARTIFACTORY_USERNAME_COVE', + 'JFROG_ACCESS_TOKEN': 'ARTIFACTORY_TOKEN_COVE' + ] + ] +] + +def secretManager +def shellHelper + +pipeline { + agent { + kubernetes { + cloud "${config.cloud}" + yaml nsbuild.agentYaml(config) + defaultContainer nsbuild.defaultContainer(config) + } + } + + options { + ansiColor('xterm') + } + + stages { + stage('Prepare') { + steps { + script { + secretManager = new SecretManager( + this, + envType, + jenkinsWhitelist: [ + 'github-app' + ], + k8sWhitelist: [ + 'artifactory' + ] + ) + shellHelper = new ShellHelper(this, isUnix: true) + } + } + } + stage('Load secrets') { + agent { + kubernetes { + cloud "${config.cloud}" + yaml secretManager.getK8sPodYaml(secrets) + defaultContainer 'secrets' + customWorkspace 'w' + } + } + steps { + script { + secretManager.loadJenkinsSecrets(secrets.jenkins) + secretManager.loadK8sSecrets(secrets.kubernetes, shellHelper) + } + } + } + stage('Build') { + environment { + ARTIFACTORY_URL = 'https://mspsolarwinds.jfrog.io/artifactory' + } + steps { + script { + secretManager.withSecrets { + shellHelper.exec('Validate', """ + make validate + """) + shellHelper.exec('Build', """ + make dist + """) + def repository = (envType == 'dev') ? 'cove-generic-develop-local' : 'cove-generic-release-local' + shellHelper.exec('Upload', """ + PACKAGE="rear-\$(make version).tar.gz" + curl -sSf -X PUT -T dist/\${PACKAGE} \ + -u \${ARTIFACTORY_USERNAME_COVE}:\${ARTIFACTORY_TOKEN_COVE} \ + \${ARTIFACTORY_URL}/${repository}/rear/\${PACKAGE} + """) + } + } + } + } + } +} diff --git a/Makefile b/Makefile index 4097de5f6f..bde0448d88 100644 --- a/Makefile +++ b/Makefile @@ -34,7 +34,7 @@ else git_date := $(shell git log -n 1 --format="%ai") git_ref := $(shell git rev-parse HEAD | cut -c 1-8) git_count := $(shell git rev-list HEAD --no-merges | wc -l) - git_branch_suffix = $(shell git symbolic-ref HEAD | sed -e 's,^.*/,,' -e "s/[^A-Za-z0-9]//g") + git_branch_suffix = $(shell echo "$${CHANGE_BRANCH:-$${BRANCH_NAME:-$$(git symbolic-ref HEAD || echo unknown)}}" | sed -e 's,^.*/,,' -e "s/[^A-Za-z0-9]//g") git_status := $(shell git status --porcelain) git_stamp := $(git_count).$(git_ref).$(git_branch_suffix) ifneq ($(git_status),) diff --git a/containers/builder/Dockerfile b/containers/builder/Dockerfile new file mode 100644 index 0000000000..aafd2cb0c3 --- /dev/null +++ b/containers/builder/Dockerfile @@ -0,0 +1,24 @@ +ARG CONTAINER_REPO=263262308774.dkr.ecr.eu-west-1.amazonaws.com + +FROM ${CONTAINER_REPO}/ns/mirror/prd/library/debian:12.11-slim + +RUN apt-get update && \ + apt-get install -y \ + git \ + bats \ + bash \ + coreutils \ + curl \ + make \ + && rm -rf /var/lib/apt/lists/* + +ARG user=build +ARG group=build +ARG uid=1000 +ARG gid=1000 + +RUN groupadd -g "${gid}" "${group}" \ + && useradd -l -c "Build user" -d "/${user}" -u "${uid}" -g "${gid}" \ + -s /bin/bash -m "${user}" + +USER build From 0824d2b88456868880b5380747c7ed434488be47 Mon Sep 17 00:00:00 2001 From: Barys Barysenka Date: Thu, 27 Nov 2025 05:07:22 -0500 Subject: [PATCH 36/39] feat(rear): unit tests * added docker file to give the possibility to execute unit tests Jira-Ref: BCF-6186: [ReaR] Run unit tests during CI --- Jenkinsfile | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Jenkinsfile b/Jenkinsfile index 3bc49c544b..276c181382 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -94,6 +94,16 @@ pipeline { shellHelper.exec('Build', """ make dist """) + shellHelper.exec('Run unit tests', """ + cd tests/COVE && \ + for test_file in *.sh; do \ + echo "Running \$test_file..." && \ + ./"\$test_file" && \ + echo "✓ \$test_file passed" && \ + echo ""; \ + done && \ + echo "All COVE tests passed!" + """) def repository = (envType == 'dev') ? 'cove-generic-develop-local' : 'cove-generic-release-local' shellHelper.exec('Upload', """ PACKAGE="rear-\$(make version).tar.gz" From 8c81006c3892de9293e719c11b059a02162e185a Mon Sep 17 00:00:00 2001 From: Barys Barysenka Date: Wed, 3 Dec 2025 07:10:04 -0500 Subject: [PATCH 37/39] feat(rear): unit tests * create pipeline to run unit tests Jira-Ref: BCF-6186: [ReaR] Run unit tests during CI --- Jenkinsfile | 13 +- Makefile | 11 ++ ...000_save_future_dangling_efi_entries.bats} | 125 ++++++------------ .../COVE/001_remove_dangling_efi_entries.bats | 87 ++++++++++++ tests/COVE/001_remove_dangling_efi_entries.sh | 116 ---------------- 5 files changed, 141 insertions(+), 211 deletions(-) rename tests/COVE/{000_save_future_dangling_efi_entries.sh => 000_save_future_dangling_efi_entries.bats} (64%) create mode 100755 tests/COVE/001_remove_dangling_efi_entries.bats delete mode 100755 tests/COVE/001_remove_dangling_efi_entries.sh diff --git a/Jenkinsfile b/Jenkinsfile index 276c181382..5c7c324bb5 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -91,19 +91,12 @@ pipeline { shellHelper.exec('Validate', """ make validate """) + shellHelper.exec('Run unit tests', """ + make test-cove + """) shellHelper.exec('Build', """ make dist """) - shellHelper.exec('Run unit tests', """ - cd tests/COVE && \ - for test_file in *.sh; do \ - echo "Running \$test_file..." && \ - ./"\$test_file" && \ - echo "✓ \$test_file passed" && \ - echo ""; \ - done && \ - echo "All COVE tests passed!" - """) def repository = (envType == 'dev') ? 'cove-generic-develop-local' : 'cove-generic-release-local' shellHelper.exec('Upload', """ PACKAGE="rear-\$(make version).tar.gz" diff --git a/Makefile b/Makefile index bde0448d88..2e77d2959d 100644 --- a/Makefile +++ b/Makefile @@ -101,6 +101,7 @@ help: \n\ version - Show ReaR version used\n\ validate - Check source code\n\ + test-cove - Run COVE unit tests with bats\n\ install - Install Relax-and-Recover (may replace files)\n\ uninstall - Uninstall Relax-and-Recover (may remove files)\n\ dist - Create tar file in dist/\n\ @@ -153,6 +154,16 @@ validate: fi; \ done +test-cove: + @echo "== Running COVE unit tests ==" + @for test_file in tests/COVE/*.bats; do \ + echo ""; \ + echo "========================================"; \ + echo "Executing: $${test_file}"; \ + echo "========================================"; \ + bats "$${test_file}"; \ + done + man: @echo -e "\033[1m== Prepare manual ==\033[0;0m" $(MAKE) -C doc man diff --git a/tests/COVE/000_save_future_dangling_efi_entries.sh b/tests/COVE/000_save_future_dangling_efi_entries.bats similarity index 64% rename from tests/COVE/000_save_future_dangling_efi_entries.sh rename to tests/COVE/000_save_future_dangling_efi_entries.bats index f2fe9f071f..41a62b5fbd 100755 --- a/tests/COVE/000_save_future_dangling_efi_entries.sh +++ b/tests/COVE/000_save_future_dangling_efi_entries.bats @@ -1,26 +1,27 @@ -#!/usr/bin/env bash - -SCRIPT_DIR="$(dirname "${0}")" -SCRIPT_DIR="$(realpath "${SCRIPT_DIR}")" -readonly SCRIPT_DIR - -REAR_SHARE_DIR=$(realpath "$SCRIPT_DIR/../../usr/share/rear") -readonly REAR_SHARE_DIR - -# shellcheck disable=SC1091 -source "$REAR_SHARE_DIR/lib/global-functions.sh" +setup_file() { + export REAR_SHARE_DIR="$(realpath "$BATS_TEST_DIRNAME/../../usr/share/rear")" + export USING_UEFI_BOOTLOADER="yes" + export COVE_TESTS="yes" +} -# shellcheck disable=SC2034 -readonly USING_UEFI_BOOTLOADER="yes" +setup() { + function is_cove_in_azure() { + false + } -# shellcheck disable=SC2034 -readonly COVE_TESTS="yes" + # shellcheck disable=SC1091 + source "$REAR_SHARE_DIR/lib/global-functions.sh" + + # shellcheck disable=SC1091 + source "$REAR_SHARE_DIR/layout/recreate/COVE/default/130_save_future_dangling_efi_entries.sh" +} -function is_cove_in_azure() { - false +teardown() { + # Clean up any test-specific state if needed + unset DISKS_TO_BE_OVERWRITTEN } -function find_future_dangling_entry() { +@test "find single future dangling EFI entry" { function get_partuuids_of_disks_to_be_overwritten() { echo "368a5c5b-26bf-4a63-b9ae-64a92d79d085" echo "9bf08aed-f779-49fe-b310-9c21983289c1" @@ -41,16 +42,12 @@ function find_future_dangling_entry() { } local boot_number - boot_number="$(get_future_dangling_efi_entries)" || return 1 - - if [ "$boot_number" != "0003" ]; then - return 1 - fi - - return 0 + boot_number="$(get_future_dangling_efi_entries)" + + [ "$boot_number" = "0003" ] } -function find_future_dangling_entries() { +@test "find multiple future dangling EFI entries" { function get_partuuids_of_disks_to_be_overwritten() { echo "bc6f95e7-893a-434b-8b76-f3d52e6ad28d" } @@ -63,16 +60,12 @@ function find_future_dangling_entries() { } local boot_number - boot_number="$(get_future_dangling_efi_entries)" || return 1 - - if [ "$boot_number" != "0003 0004" ]; then - return 1 - fi - - return 0 + boot_number="$(get_future_dangling_efi_entries)" + + [ "$boot_number" = "0003 0004" ] } -function unexpected_efi_entry() { +@test "handle unexpected EFI entry format" { function get_partuuids_of_disks_to_be_overwritten() { echo "bc6f95e7-893a-434b-8b76-f3d52e6ad28d" } @@ -83,16 +76,12 @@ function unexpected_efi_entry() { } local boot_number - boot_number="$(get_future_dangling_efi_entries)" || return 1 - - if [ -n "$boot_number" ]; then - return 1 - fi - - return 0 + boot_number="$(get_future_dangling_efi_entries)" + + [ -z "$boot_number" ] } -function empty_functions() { +@test "handle empty functions" { function get_partuuids_of_disks_to_be_overwritten() { : } @@ -102,16 +91,12 @@ function empty_functions() { } local boot_number - boot_number="$(get_future_dangling_efi_entries)" || return 1 - - if [ -n "$boot_number" ]; then - return 1 - fi - - return 0 + boot_number="$(get_future_dangling_efi_entries)" + + [ -z "$boot_number" ] } -function get_partuuids_of_disks_to_be_overwritten_exits_with_error() { +@test "handle get_partuuids_of_disks_to_be_overwritten error" { function get_partuuids_of_disks_to_be_overwritten() { return 1 } @@ -120,18 +105,12 @@ function get_partuuids_of_disks_to_be_overwritten_exits_with_error() { : } - local boot_number - if boot_number="$(get_future_dangling_efi_entries)"; then - return 1 - fi - - return 0 + run get_future_dangling_efi_entries + [ "$status" -ne 0 ] } -function one_disk_to_be_overwritten() { - # shellcheck disable=SC2034 +@test "get partuuids for one disk to be overwritten" { DISKS_TO_BE_OVERWRITTEN="/dev/sda" - local expected_partuuids="368a5c5b-26bf-4a63-b9ae-64a92d79d085" function get_disk_partuuids() { @@ -150,8 +129,7 @@ function one_disk_to_be_overwritten() { [ "$expected_partuuids" = "$actual_partuuids" ] } -function two_disks_to_be_overwritten() { - # shellcheck disable=SC2034 +@test "get partuuids for two disks to be overwritten" { DISKS_TO_BE_OVERWRITTEN="/dev/sda /dev/sdb" function get_disk_partuuids() { @@ -174,8 +152,7 @@ function two_disks_to_be_overwritten() { [ "$expected_partuuids" = "$actual_partuuids" ] } -function no_disks_to_be_overwritten() { - # shellcheck disable=SC2034 +@test "handle no disks to be overwritten" { DISKS_TO_BE_OVERWRITTEN="" function get_disk_partuuids() { @@ -187,25 +164,3 @@ function no_disks_to_be_overwritten() { [ -z "$partuuids" ] } - -set -e - -TESTS=( - find_future_dangling_entry - find_future_dangling_entries - unexpected_efi_entry - empty_functions - get_partuuids_of_disks_to_be_overwritten_exits_with_error - one_disk_to_be_overwritten - two_disks_to_be_overwritten - no_disks_to_be_overwritten -) - -for test in "${TESTS[@]}"; do - # shellcheck disable=SC1091 - source "$REAR_SHARE_DIR/layout/recreate/COVE/default/130_save_future_dangling_efi_entries.sh" - if ! "$test"; then - echo "$test failed" - exit 1 - fi -done diff --git a/tests/COVE/001_remove_dangling_efi_entries.bats b/tests/COVE/001_remove_dangling_efi_entries.bats new file mode 100755 index 0000000000..026a2769d0 --- /dev/null +++ b/tests/COVE/001_remove_dangling_efi_entries.bats @@ -0,0 +1,87 @@ +setup_file() { + export REAR_SHARE_DIR="$(realpath "$BATS_TEST_DIRNAME/../../usr/share/rear")" + export USING_UEFI_BOOTLOADER="yes" + export COVE_TESTS="yes" +} + +setup() { + function is_cove_in_azure() { + false + } + + function LogPrint() { + echo "$@" + } + + # shellcheck disable=SC1091 + source "$REAR_SHARE_DIR/lib/global-functions.sh" + + # shellcheck disable=SC1091 + source "$REAR_SHARE_DIR/finalize/COVE/default/665_remove_dangling_efi_entries.sh" +} + +@test "no future dangling efi entries" { + FUTURE_DANGLING_EFI_ENTRIES="" + + run remove_dangling_efi_entries + + [ "$status" -eq 0 ] +} + +@test "one entry to remove" { + FUTURE_DANGLING_EFI_ENTRIES="0001" + + function remove_efi_entry() { + : + } + + local expected_output="Removing EFI Boot Manager entry with '0001' entry ID" + + run remove_dangling_efi_entries + + [ "$status" -eq 0 ] + [ "$output" = "$expected_output" ] +} + +@test "two entries to remove" { + FUTURE_DANGLING_EFI_ENTRIES="0001 0003" + + function remove_efi_entry() { + : + } + + local expected_output + expected_output="$(printf "%s\n%s" \ + "Removing EFI Boot Manager entry with '0001' entry ID" \ + "Removing EFI Boot Manager entry with '0003' entry ID")" + + run remove_dangling_efi_entries + + [ "$status" -eq 0 ] + [ "$output" = "$expected_output" ] +} + +@test "fail to remove efi entry" { + FUTURE_DANGLING_EFI_ENTRIES="0001 0003" + + function remove_efi_entry() { + local id="$1" + if [ "$id" = "0001" ]; then + return 0 + elif [ "$id" = "0003" ]; then + return 1 + fi + } + + local expected_output + expected_output="$(printf "%s\n%s\n%s" \ + "Removing EFI Boot Manager entry with '0001' entry ID" \ + "Removing EFI Boot Manager entry with '0003' entry ID" \ + "Failed to remove EFI Boot Manager entry with '0003' entry ID" + )" + + run remove_dangling_efi_entries + + [ "$status" -eq 0 ] + [ "$output" = "$expected_output" ] +} diff --git a/tests/COVE/001_remove_dangling_efi_entries.sh b/tests/COVE/001_remove_dangling_efi_entries.sh deleted file mode 100755 index 163d7b088b..0000000000 --- a/tests/COVE/001_remove_dangling_efi_entries.sh +++ /dev/null @@ -1,116 +0,0 @@ -#!/usr/bin/env bash - -SCRIPT_DIR="$(dirname "${0}")" -SCRIPT_DIR="$(realpath "${SCRIPT_DIR}")" -readonly SCRIPT_DIR - -REAR_SHARE_DIR=$(realpath "$SCRIPT_DIR/../../usr/share/rear") -readonly REAR_SHARE_DIR - -# shellcheck disable=SC1091 -source "$REAR_SHARE_DIR/lib/global-functions.sh" - -# shellcheck disable=SC2034 -readonly USING_UEFI_BOOTLOADER="yes" - -# shellcheck disable=SC2034 -readonly COVE_TESTS="yes" - -function is_cove_in_azure() { - false -} - -function no_future_dangling_efi_entries() { - FUTURE_DANGLING_EFI_ENTRIES="" - remove_dangling_efi_entries -} - -function one_entry_to_remove() { - FUTURE_DANGLING_EFI_ENTRIES="0001" - - function LogPrint() { - echo "$@" - } - - function remove_efi_entry() { - : - } - - local expected_output="Removing EFI Boot Manager entry with '0001' entry ID" - - local actual_output - actual_output="$(remove_dangling_efi_entries)" - - [ "$expected_output" = "$actual_output" ] -} - -function two_entries_to_remove() { - # shellcheck disable=SC2034 - FUTURE_DANGLING_EFI_ENTRIES="0001 0003" - - function LogPrint() { - echo "$@" - } - - function remove_efi_entry() { - : - } - - local expected_output - expected_output="$(printf "%s\n%s" \ - "Removing EFI Boot Manager entry with '0001' entry ID" \ - "Removing EFI Boot Manager entry with '0003' entry ID")" - - local actual_output - actual_output="$(remove_dangling_efi_entries)" - - [ "$expected_output" = "$actual_output" ] -} - -function fail_to_remove_efi_entry() { - # shellcheck disable=SC2034 - FUTURE_DANGLING_EFI_ENTRIES="0001 0003" - - function LogPrint() { - echo "$@" - } - - function remove_efi_entry() { - local id="$1" - if [ "$id" = "0001" ]; then - return 0 - elif [ "$id" = "0003" ]; then - return 1 - fi - } - - local expected_output - expected_output="$(printf "%s\n%s\n%s" \ - "Removing EFI Boot Manager entry with '0001' entry ID" \ - "Removing EFI Boot Manager entry with '0003' entry ID" \ - "Failed to remove EFI Boot Manager entry with '0003' entry ID" - )" - - local actual_output - actual_output="$(remove_dangling_efi_entries)" - - [ "$expected_output" = "$actual_output" ] -} - -set -e - -TESTS=( - no_future_dangling_efi_entries - one_entry_to_remove - two_entries_to_remove - fail_to_remove_efi_entry -) - -for test in "${TESTS[@]}"; do - # shellcheck disable=SC1091 - source "$REAR_SHARE_DIR/finalize/COVE/default/665_remove_dangling_efi_entries.sh" - if ! "$test"; then - echo "$test failed" - exit 1 - fi -done From d748c2038f18b61c754b32f0f023f0d09a0eb078 Mon Sep 17 00:00:00 2001 From: Barys Barysenka Date: Fri, 5 Dec 2025 08:26:38 -0500 Subject: [PATCH 38/39] feat(rear): unit tests * added env validation Jira-Ref: BCF-6186: [ReaR] Run unit tests during CI --- Jenkinsfile | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Jenkinsfile b/Jenkinsfile index 5c7c324bb5..7fc08b0684 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -3,7 +3,9 @@ import nable.cove.helpers.ShellHelper import nable.cove.SecretManager final String jobName = env.JOB_NAME.split('/')[-2] +final String branchName = env.CHANGE_BRANCH ?: env.BRANCH_NAME final boolean isProd = jobName.endsWith('-prd') +final boolean isProdBranch = branchName == 'master' || branchName.startsWith('release') final String envType = isProd ? 'prd' : 'dev' def String repositoryName = 'rear' @@ -33,6 +35,7 @@ def secrets = [ def secretManager def shellHelper +def shouldBuild = true pipeline { agent { @@ -51,6 +54,11 @@ pipeline { stage('Prepare') { steps { script { + if (isProd != isProdBranch) { + echo "Environment mismatch: isProd=${isProd}, isProdBranch=${isProdBranch}. Skipping build." + shouldBuild = false + } + secretManager = new SecretManager( this, envType, @@ -66,6 +74,9 @@ pipeline { } } stage('Load secrets') { + when { + expression { shouldBuild } + } agent { kubernetes { cloud "${config.cloud}" @@ -82,6 +93,9 @@ pipeline { } } stage('Build') { + when { + expression { shouldBuild } + } environment { ARTIFACTORY_URL = 'https://mspsolarwinds.jfrog.io/artifactory' } From 6babb40da5875f9ee41a356ce81330880fb8f910 Mon Sep 17 00:00:00 2001 From: Barys Barysenka Date: Fri, 5 Dec 2025 06:08:06 -0500 Subject: [PATCH 39/39] feat(rear): unit tests * fixed pipeline names Jira-Ref: BCF-6186: [ReaR] Run unit tests during CI --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8e9655dc14..83cf685bcc 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Relax-and-Recover Linux Disaster Recovery & Bare Metal Restore -Relax-and-Recover (abbreviated ReaR) is the de facto standard disaster recovery framework on Linux. +Relax-and-Recover (abbreviated ReaR) is the de facto standard disaster recovery framework on Linux. It is in particular used on enterprise Linux distributions like Red Hat Enterprise Linux (RHEL) and SUSE Linux Enterprise Server (SLES). @@ -10,7 +10,7 @@ for bare metal disaster recovery with data backup restore on physical or virtual For bare metal disaster recovery the ReaR recovery system is booted on pristine replacement hardware. On replacement hardware first the storage setup/layout is recreated (disk partitioning, filesystems, mount points), -then a backup restore program is called to restore the data (system files) into the recreated storage, +then a backup restore program is called to restore the data (system files) into the recreated storage, and finally a boot loader is installed. System administrators use the ReaR framework to set up a disaster recovery procedure