#!/bin/bash # This script will do an inplace OS upgrade of a debian host. It will primarily # be used for going from stretch to buster but should work for future varients # also. # # When run, it will set config variables and update files such that it's a # fully unattended install. Once you confirm, no going back. # Functions function error_out { error_message=$1 echo echo -e "\e[31mError! Something went wrong: ${error_message}\e[0m" echo exit 1 } function help { echo " This script is used to upgrade a debian host from it's current version to the next version in line. It should be run via sudo or as root " usage } function usage { echo "Usage: $0 [-h]" exit 1 } ### Getopt land # Note that we use "$@" to let each command-line parameter expand to a # separate word. The quotes around "$@" are essential! # We need TEMP as the 'eval set --' would nuke the return value of getopt. ARGS=$(getopt -o 'h' --long ',help' -n "$0" -- "$@") if [ $? -ne 0 ]; then usage fi # Note the quotes around "$ARGS": they are essential! eval set -- "${ARGS}" unset ARGS while true; do # If these have a required argument. As such the argument will be in $2 case "$1" in '-h'|'--help') help break ;; '--') shift break ;; *) echo 'Internal error!' >&2 exit 1 ;; esac done ### Verification # Then check if we are root due to directory perms for getting du stats if [ "$(id -u)" -ne 0 ] then echo "Sorry, due to the commands needed, you must run this as root or through sudo" >&2 echo usage fi ### Prep work # Set mappings for debian and mariadb releases declare -A debian_releases debian_releases[10]=buster debian_releases[11]=bullseye declare -A mariadb_releases mariadb_releases[10]="10.4" mariadb_releases[11]="10.5" # Collect info on current release CURRENT_VERSION=`cut -d. -f 1 /etc/debian_version` echo "Current release : ${CURRENT_VERSION}" echo " : ${debian_releases[$CURRENT_VERSION]}" NEXT_VERSION=`expr ${CURRENT_VERSION} + 1` echo "Next release : ${NEXT_VERSION}" echo " : ${debian_releases[$NEXT_VERSION]}" echo # Ask if we really want to do this confirmation='yesiamsure' echo "You are about to upgrade this host from ${debian_releases[$CURRENT_VERSION]} to ${debian_releases[$NEXT_VERSION]}" read -p " Do you really want to upgrade? This can not be reversed: [${confirmation}]: " ReallySure if [ ${ReallySure} = ${confirmation} ] then echo echo "Ok, we're upgrading this host." echo else echo "You aren't sure. Exiting without deleting." exit 1 fi OLD_RELEASE=${debian_releases[$CURRENT_VERSION]} NEW_RELEASE=${debian_releases[$NEXT_VERSION]} OLD_MARIADB_RELEASE=${mariadb_releases[$CURRENT_VERSION]} NEW_MARIADB_RELEASE=${mariadb_releases[$NEXT_VERSION]} ### Let's do this echo "Moving OS from ${OLD_RELEASE} to ${NEW_RELEASE}" # stop & disable puppet echo "Disabling puppet" systemctl stop puppet if [ $? -ne 0 ]; then error_out "Couldn't stop puppet" fi rm /etc/cron.d/puppet if [ $? -ne 0 ]; then error_out "Couldn't remove puppet cron" fi echo # rename all ${OLD_RELEASE}-* files in /etc/apt/sources.list.d to ${NEW_RELEASE}-* echo "Renaming source files in /etc/apt/sources.list.d" rename.ul "${OLD_RELEASE}" "${NEW_RELEASE}" /etc/apt/sources.list.d/* if [ $? -ne 0 ]; then error_out "Couldn't rename sources files" fi echo # edit the contents of such files to replace ${OLD_RELEASE} with ${NEW_RELEASE} echo "Editing source files to reflect new release" perl -pi -e "s/${OLD_RELEASE}/${NEW_RELEASE}/g" /etc/apt/sources.list.d/* if [ $? -ne 0 ]; then error_out "Couldn't update sources files from old to new release" fi echo if [ -s /etc/apt/sources.list.d/bullseye-security-updates.list ]; then echo "Editing bullseye-security-updates.list to reflect new distribution" sed -i 's/bullseye\/updates/bullseye-security/' /etc/apt/sources.list.d/bullseye-security-updates.list if [ $? -ne 0 ]; then error_out "Couldn't fix bullseye-security-updates.list" fi echo fi # edit the mariadb relase file to update ${OLD_MARIADB_RELEASE} with # ${NEW_MARIADB_RELEASE} (if it exists on disk) if [ -s /etc/apt/sources.list.d/${NEW_RELEASE}-mariadb.list ]; then echo "Editing mariadb source file to reflect mariadb release" perl -pi -e "s/${OLD_MARIADB_RELEASE}/${NEW_MARIADB_RELEASE}/g" /etc/apt/sources.list.d/${NEW_RELEASE}-mariadb.list if [ $? -ne 0 ]; then error_out "Couldn't update mariadb source file from old to new release" fi echo fi # check to see if percona-toolkit is installed and remove it (using purge). # we are doing this because it has a tendancy to want to be downgraded by the # stock versions and will error out the script. it will get reinstalled by the # puppet run at the end if it is required dpkg -l percona-toolkit if [ $? -eq 0 ]; then apt-get -y purge percona-toolkit fi # run apt-get update echo "Running apt update" apt-get update if [ $? -ne 0 ]; then error_out "apt-get update failed" fi echo # run apt-get upgrade echo "Running apt upgrade" # Set a chunk of settings to make the upgrade truely unattended export DEBIAN_FRONTEND=noninteractive # lib6c is an issue since ignored the DEBIAN_FRONTEND environment variable and fired a prompt anyway. This should fix it echo "libc6 libraries/restart-without-asking boolean true" | debconf-set-selections # pam - keep local version echo "libpam-runtime libpam-runtime/override boolean false" | debconf-set-selections # openssh-server config - keep local version # unattended-upgrades config - keep local version perl -pi -e "s/^# conf_force_conffold=YES/conf_force_conffold=YES/g" /etc/ucf.conf # actually do the upgrade /usr/bin/apt-get -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" dist-upgrade if [ $? -ne 0 ]; then error_out "dist-upgrade failed" fi echo # autoremove crufty packages echo "autoremove crufty packages" /usr/bin/apt-get -y autoremove if [ $? -ne 0 ]; then error_out "apt autoremove failed" fi echo # purge residual packages echo "purge residual packages" /usr/bin/apt-get -y purge if [ $? -ne 0 ]; then error_out "apt purge failed" fi echo # clean the apt cache from disk echo "clean the apt cache from disk" /usr/bin/apt-get -y clean if [ $? -ne 0 ]; then error_out "apt clean failed" fi echo # purge packages that were removed but have leftover config files on disk echo "clean removed package config files" dpkg --purge `dpkg -l | grep ^rc | awk '{print $2}'` if [ $? -ne 0 ]; then echo "\e[31mError clearing old package config files\e[0m - Continuing anyway" fi echo # Reset the settings to their previous values echo "libc6 libraries/restart-without-asking boolean false" | debconf-set-selections perl -pi -e "s/^conf_force_conffold=YES/# conf_force_conffold=YES/g" /etc/ucf.conf # Run puppet just in case - not checking the return code since we are at the # end anyway and any errors will be visible to the user running the script. echo "Doing a puppet run to get files up to date" puppet agent -tv echo # Time to reboot echo -e "upgrade finished. you \e[31mREALLY\e[0m need to reboot now."