nsm/bin/sysupdate

214 lines
9.8 KiB
Bash
Executable file

#!/usr/bin/env bash
#######################################################################################################################
##
## Script to full update the system
##
## 0. Pre checks
## 1. Generate new boot environment (BE)
## 2. Update and Ansible highstate
## 3. Clean up
##
#######################################################################################################################
#######################################################################################################################
## Definitions
#######################################################################################################################
readonly LOCK_FILE="/tmp/systemupdate.lock"
readonly TIMESTAMP=$(date +"%Y-%m-%d_%H-%M-%S")
readonly BTRFS_ROOT="/btrfs"
readonly CURRENT_SUBVOLUME=$(LC_ALL=C btrfs sub show / | LC_ALL=C grep 'Name' | cut -d: -f2 | awk '{$1=$1};1')
readonly NEW_SUBVOLUME="@root_${TIMESTAMP}"
readonly MOUNTPOINT='/mnt'
readonly EFI_DISK=$(findmnt -T /efi -o SOURCE | tail -n 1)
readonly ROOT_DISK=$(findmnt / -o SOURCE | cut -d"[" -f1 | tail -n 1)
readonly BE_HISTORY_COUNT=5
readonly SYSUPGRADE="$1"
readonly NEW_NOVOS_RELEASE="$2"
readonly NEW_ALPINE_RELEASE="v$3"
#######################################################################################################################
## Errorhandling
#######################################################################################################################
#----------------------------------------------------------------------------------------------------------------------
# systemupdate failed
#----------------------------------------------------------------------------------------------------------------------
systemupdateFailed() {
echo ""
echo "┌──────────────────────────────────────────┐"
echo "│ FAILED => clean up │"
echo "└──────────────────────────────────────────┘"
subtaskTitle "Unmount BE if mounted"
unmountMountpoint
subtaskTitle "Remove BE"
removeBEFromTimestamp ${TIMESTAMP}
rm -f ${LOCK_FILE}
subtaskTitle "Finished with exit code 1"
exit 1
}
# catch ^C and other signals and clean up
trap "echo -e '\n=> Interrupted with CTRL+C' >&2; systemupdateFailed" SIGINT SIGHUP SIGTERM SIGABRT
#######################################################################################################################
## Helper Functions
#######################################################################################################################
#----------------------------------------------------------------------------------------------------------------------
# Subtask title output
#----------------------------------------------------------------------------------------------------------------------
subtaskTitle() {
echo -e "\n=> $1"
}
#----------------------------------------------------------------------------------------------------------------------
# Unmount ${MOUNTPOINT}
#----------------------------------------------------------------------------------------------------------------------
unmountMountpoint() {
# if mountpoint exists -> umount
[[ $(findmnt -M "${MOUNTPOINT}") ]] && umount -R "${MOUNTPOINT}"
}
#----------------------------------------------------------------------------------------------------------------------
# Recursive subvolume delete
#----------------------------------------------------------------------------------------------------------------------
btrfsSubDelRecursive() {
btrfs sub list -o "${BTRFS_ROOT}/${1}" | cut -d " " -f 9 | while read i; do
btrfsSubDelRecursive "$i"
done
btrfs sub del "${BTRFS_ROOT}/${1}"
}
#----------------------------------------------------------------------------------------------------------------------
# Remove BE from timestamp
#----------------------------------------------------------------------------------------------------------------------
removeBEFromTimestamp() {
# remove all subvolume with this timestamp
for f in $(btrfs sub list -o /btrfs | cut -d " " -f 9 | grep "@root"); do
if [[ "$f" =~ "$1" ]]; then
btrfsSubDelRecursive "$f"
fi
done
}
#######################################################################################################################
## Main
#######################################################################################################################
echo "┌──────────────────────────────────────────┐"
echo "│ 0. Pre checks │"
echo "└──────────────────────────────────────────┘"
subtaskTitle "Check if another systemupgrade is in progress"
if [ -f ${LOCK_FILE} ]; then
echo "[ERROR] Another systemupgrade is in progress (lockfile: ${LOCK_FILE}) => exit" >&2
exit 1
fi
subtaskTitle "Check if ${MOUNTPOINT} exists"
if [ ! -d ${MOUNTPOINT} ]; then
mkdir -p "${MOUNTPOINT}"
fi
subtaskTitle "Check if ${MOUNTPOINT} is already a mountpoint"
if [[ $(findmnt -M "${MOUNTPOINT}") ]]; then
echo "[ERROR] ${MOUNTPOINT} is already a mountpoint => exit" >&2
exit 1
fi
subtaskTitle "Checks finished and update can start"
# Create lock file
touch ${LOCK_FILE} || systemupdateFailed
echo ""
echo "┌──────────────────────────────────────────┐"
echo "│ 1. Generate new boot environment (BE) │"
echo "└──────────────────────────────────────────┘"
subtaskTitle "Create snapshot of current running system"
btrfs subvolume snapshot / ${BTRFS_ROOT}/${NEW_SUBVOLUME} || systemupdateFailed
subtaskTitle "Mount new BE to ${MOUNTPOINT}"
mount -o noatime,nodiratime,discard=async,space_cache=v2,subvol="${NEW_SUBVOLUME}" "${ROOT_DISK}" "${MOUNTPOINT}" || systemupdateFailed
mount -o noatime,nodiratime,discard=async,space_cache=v2,subvol=@home "${ROOT_DISK}" "${MOUNTPOINT}/home" || systemupdateFailed
mount -o noatime,nodiratime,discard=async,space_cache=v2,subvol=@podman "${ROOT_DISK}" "${MOUNTPOINT}/opt/podman" || systemupdateFailed
mount -o noatime,nodiratime,discard=async,space_cache=v2,subvol=@mysql "${ROOT_DISK}" "${MOUNTPOINT}/var/lib/mysql" || systemupdateFailed
mount -o noatime,nodiratime,discard=async,space_cache=v2,subvol=/ "${ROOT_DISK}" "${MOUNTPOINT}/btrfs" || systemupdateFailed
mount -o nodev,nosuid,noexec "${EFI_DISK}" "${MOUNTPOINT}/efi" || systemupdateFailed
mount -t proc /proc "${MOUNTPOINT}/proc/" || systemupdateFailed
mount -t sysfs /sys "${MOUNTPOINT}/sys/" || systemupdateFailed
mount -o bind /sys/firmware/efi/efivars "${MOUNTPOINT}/sys/firmware/efi/efivars/" || systemupdateFailed
mount -o bind /dev "${MOUNTPOINT}/dev/" || systemupdateFailed
mount -o bind /run "${MOUNTPOINT}/run/" || systemupdateFailed
subtaskTitle "New BE mounted"
echo ""
echo "┌──────────────────────────────────────────┐"
echo "│ 2. Update and Ansible highstate │"
echo "└──────────────────────────────────────────┘"
subtaskTitle "Update Ansible playbook"
chroot "${MOUNTPOINT}" /bin/bash -c "git -C /srv/ansible/playbooks pull" || systemupdateFailed
if $SYSUPGRADE; then
subtaskTitle "Update release information"
chroot "${MOUNTPOINT}" /bin/bash -c "grep -E '(main|community)' /etc/apk/repositories | sed 's|$(yq .ungrouped.vars.alpine_version /srv/ansible/inventory.yml)|$NEW_ALPINE_RELEASE|' > /etc/apk/repositories"
chroot "${MOUNTPOINT}" /bin/bash -c "yq -i '.ungrouped.vars.release_version = $NEW_NOVOS_RELEASE' /srv/ansible/inventory.yml"
chroot "${MOUNTPOINT}" /bin/bash -c "yq -i '.ungrouped.vars.alpine_version = $NEW_ALPINE_RELEASE' /srv/ansible/inventory.yml"
fi
subtaskTitle "Update bootloader configs"
chroot "${MOUNTPOINT}" /bin/bash -c "ansible-playbook /srv/ansible/playbooks/system/bootloader.ansible.yml" >/dev/null || systemupdateFailed
subtaskTitle "Alpine repositories & keyring update"
chroot "${MOUNTPOINT}" /bin/bash -c "apk update" || systemupdateFailed
subtaskTitle "Alpine packages update"
chroot "${MOUNTPOINT}" /bin/bash -c "apk upgrade" || systemupdateFailed
subtaskTitle "Ansible highstate"
chroot "${MOUNTPOINT}" /bin/bash -c "ansible-playbook /srv/ansible/playbooks/top.ansible.yml" >/dev/null || systemupdateFailed
subtaskTitle "Generate new initial ramdisk"
latest_kernel="$(chroot "${MOUNTPOINT}" /bin/bash -c 'echo $(apk search linux-lts | head -n1 | cut -d- -f3- | sed "s|r||")-lts')"
chroot "${MOUNTPOINT}" /bin/bash -c "mkinitfs $latest_kernel" || systemupdateFailed
subtaskTitle "Update motd"
chroot "${MOUNTPOINT}" /bin/bash -c "/usr/local/noveria/bin/generate_motd" || systemupdateFailed
subtaskTitle "Update GRUB"
chroot "${MOUNTPOINT}" /bin/bash -c "grub-install --target=x86_64-efi --efi-directory=/efi --bootloader-id=alpine" || systemupdateFailed
chroot "${MOUNTPOINT}" /bin/bash -c "/usr/local/noveria/bin/noveriablcgen --noconfirm" || systemupdateFailed
subtaskTitle "Update finished"
subtaskTitle "Unmount BE"
unmountMountpoint
echo ""
echo "┌──────────────────────────────────────────┐"
echo "│ 3. Clean Up │"
echo "└──────────────────────────────────────────┘"
subtaskTitle "Clean up finished"
# Remove lock file
rm -f ${LOCK_FILE}