diff --git a/bin/sysupdate b/bin/sysupdate new file mode 100755 index 0000000..60a8334 --- /dev/null +++ b/bin/sysupdate @@ -0,0 +1,213 @@ +#!/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 + 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} diff --git a/functions/sysupgrade b/functions/sysupgrade index a252ec9..f47c69c 100644 --- a/functions/sysupgrade +++ b/functions/sysupgrade @@ -4,7 +4,7 @@ source "${ROOTPATH}/functions/check" function sysupgrade_main() { if ! check_local &> /dev/null; then - echo "Sysupgrade true" + ${ROOTPATH}/bin/sysupdate true "$(get_latest_novos_version)" "$(get_latest_alpine_release)" else printc "r" "There is no never version of NOVOS available, thus sysupgrade has been cancelled" fi diff --git a/functions/update b/functions/update index 486a336..cc18e80 100644 --- a/functions/update +++ b/functions/update @@ -1,205 +1,5 @@ #!/usr/bin/env bash -####################################################################################################################### -## -## Script to full update the system -## -## 0. Pre checks -## 1. Generate new boot environment (BE) -## 2. Update and Salt highstate -## 3. Clean up -## -####################################################################################################################### function update_main() { - ####################################################################################################################### - ## Definitions - ####################################################################################################################### - - readonly TEMPDIR=$(mktemp -d /tmp/systemupdate.XXXXXX) - readonly LOCK_FILE="${TEMPDIR}/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 - - - ####################################################################################################################### - ## 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 - - 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} + ${ROOTPATH}/bin/sysupdate false } diff --git a/utils/helpers b/utils/helpers index 9aec8d9..f5e84a3 100644 --- a/utils/helpers +++ b/utils/helpers @@ -4,6 +4,10 @@ function get_latest_novos_version() { curl -sL $(getValueByKey "url")/releases/latest | grep "Novos/ISO/releases/tag" | awk '{$1=$1};1' | cut -d'>' -f2 | cut -d'<' -f1 | cut -d- -f1 } +function get_latest_alpine_release() { + curl -sL $(getValueByKey "url")/releases/latest | grep "Novos/ISO/releases/tag" | awk '{$1=$1};1' | cut -d'>' -f2 | cut -d'<' -f1 | cut -d_ -f2 +} + function get_current_novos_version() { cat /etc/os-release | grep VERSION_ID | cut -d= -f2 | jq -r '.' }