From 497ea263fd17582b135a876055e72c76f2a118aa Mon Sep 17 00:00:00 2001 From: Christoph Date: Sat, 19 Sep 2026 00:27:27 +0200 Subject: [PATCH] upgrade-mattermost.sh: mprove Mattermost upgrade robustness and edition handling. --- upgrade-mattermost.sh | 233 ++++++++++++++++++++++++++++++++---------- 1 file changed, 178 insertions(+), 55 deletions(-) diff --git a/upgrade-mattermost.sh b/upgrade-mattermost.sh index e286894..ce145d8 100755 --- a/upgrade-mattermost.sh +++ b/upgrade-mattermost.sh @@ -9,7 +9,7 @@ conf_file="${working_dir}/conf/mattermost.conf" LOCK_DIR="/tmp/$(basename $0).$$.LOCK" log_file="${LOCK_DIR}/${script_name%%.*}.log" -backup_date="$(date +%Y-%m-%d-%H%M)" +backup_date="$(date +%Y-%m-%d-%H%M%S)" # ---------- @@ -64,8 +64,7 @@ fatal(){ echo -e " Script terminated.." fi echo "" - rm -rf $LOCK_DIR - exit 1 + clean_up 1 } error (){ echo "" @@ -136,14 +135,14 @@ blank_line() { detect_os () { - if $(which lsb_release > /dev/null 2>&1) ; then + if command -v lsb_release > /dev/null 2>&1 ; then DIST="$(lsb_release -i | awk '{print tolower($3)}')" DIST_VERSION="$(lsb_release -r | awk '{print tolower($2)}')" DIST_CODENAME="$(lsb_release -c | awk '{print tolower($2)}')" if [[ "$DIST" = "debian" ]]; then - if $(echo "$DIST_VERSION" | grep -q '\.') ; then + if echo "$DIST_VERSION" | grep -q '\.' ; then DIST_VERSION=$(echo "$DIST_VERSION" | cut --delimiter='.' -f1) fi fi @@ -171,7 +170,7 @@ detect_os () { # - Run 'clean_up' for signals SIGHUP SIGINT SIGTERM # - -trap clean_up SIGHUP SIGINT SIGTERM +trap 'clean_up 1' SIGHUP SIGINT SIGTERM # - Create lock directory '$LOCK_DIR" # @@ -314,7 +313,12 @@ else done fi -MATTERMOST_VERSION_OUTPUT="$(${MATTERMOST_BASE_INSTALL_PATH}/mattermost/bin/mattermost version 2> /dev/null)" +if [[ ! -x "${MATTERMOST_BASE_INSTALL_PATH}/mattermost/bin/mattermost" ]]; then + fatal "Mattermost binary '${MATTERMOST_BASE_INSTALL_PATH}/mattermost/bin/mattermost' does not exist or is not executable." +fi +if ! MATTERMOST_VERSION_OUTPUT="$("${MATTERMOST_BASE_INSTALL_PATH}/mattermost/bin/mattermost" version 2> /dev/null)" ; then + fatal "Failed to determine the currently installed Mattermost version." +fi MATTERMOST_CURRENT_VERSION="$(echo "$MATTERMOST_VERSION_OUTPUT" | grep -E "^Version:" | cut -d' ' -f2)" MATTERMOST_CURRENT_BUILD_NUMBER="$(echo "$MATTERMOST_VERSION_OUTPUT" | grep -E "^Build Number:" | cut -d' ' -f3)" MATTERMOST_CURRENT_ENTERPRISE_READY="$(echo "$MATTERMOST_VERSION_OUTPUT" | grep -E "^Build Enterprise Ready:" | awk '{print $4}')" @@ -493,7 +497,7 @@ done if [[ "$DB_TYPE" = "mysql" ]] ; then if [[ -n "$MYSQL_CREDENTIAL_ARGS" ]] ; then - if ! $(mysql $MYSQL_CREDENTIAL_ARGS -N -s -e 'quit' > /dev/null 2>&1) ; then + if ! mysql $MYSQL_CREDENTIAL_ARGS -N -s -e 'quit' > /dev/null 2>&1 ; then fatal "Parameter MYSQL_CREDENTIAL_ARGS is given, but a connection to MySQL Service failed.!" fi USE_MYSQL_CREDENTIAL_ARGS=true @@ -514,8 +518,8 @@ if [[ "$DB_TYPE" = "mysql" ]] ; then echo -e "\n\t\033[33m\033[1mPassworteingabe erforderlich!\033[m\n" continue fi - if $(pgrep mysqld_safe > /dev/null 2>&1) || $(pgrep mysqld > /dev/null 2>&1); then - if $(mysql --user="root" --password="$_MYSQL_ROOT_PW" -N -s -e 'quit' > /dev/null 2>&1) ; then + if pgrep mysqld_safe > /dev/null 2>&1 || pgrep mysqld > /dev/null 2>&1; then + if mysql --user="root" --password="$_MYSQL_ROOT_PW" -N -s -e 'quit' > /dev/null 2>&1 ; then MYSQL_ROOT_PW=$_MYSQL_ROOT_PW else echo -e "\n\t\033[33m\033[1mFalsches Passwort\033[m\n" @@ -560,7 +564,7 @@ fi echo "" if [[ "${DB_TYPE}" = "mysql" ]]; then if $USE_MYSQL_CREDENTIAL_ARGS ; then - echo -e " MYSQL_CREDENTIAL_ARGS.............: $MYSQL_CREDENTIAL_ARGS" + echo -e " MYSQL_CREDENTIAL_ARGS.............: **" else echo -e " Root password MySQL...............: **" fi @@ -568,7 +572,7 @@ fi echo "" echo -e " Database Name.....................: $DB_NAME" echo -e " Database User.....................: $DB_USER" -echo -e " Database Password.................: $DB_PASS" +echo -e " Database Password.................: **" echo "" echononl "einverstanden (yes/no): " read OK @@ -584,9 +588,9 @@ done blank_line blank_line -if ! $USE_MYSQL_CREDENTIAL_ARGS ; then - MYSQL_CREDENTIAL_ARGS="--user='root' --password=$_MYSQL_ROOT_PW" - if ! $(mysql $MYSQL_CREDENTIAL_ARGS -N -s -e 'quit' > /dev/null 2>&1) ; then +if [[ "$DB_TYPE" = "mysql" ]] && ! $USE_MYSQL_CREDENTIAL_ARGS ; then + MYSQL_CREDENTIAL_ARGS="--user=root --password=$_MYSQL_ROOT_PW" + if ! mysql $MYSQL_CREDENTIAL_ARGS -N -s -e 'quit' > /dev/null 2>&1 ; then fatal "Parameter MYSQL_CREDENTIAL_ARGS is given, but a connection to MySQL Service failed.!" fi fi @@ -619,6 +623,17 @@ else echo_skipped fi +echononl "Check Mattermost Server archive.." +if [[ -f "${working_dir}/${DOWNLOAD_ARCHIVE}" ]] \ + && tar -tzf "${working_dir}/${DOWNLOAD_ARCHIVE}" > /dev/null 2> "$log_file" ; then + echo_ok +else + echo_failed + fatal "Mattermost Server archive '${working_dir}/${DOWNLOAD_ARCHIVE}' is missing, invalid or incomplete." +fi + +blank_line + # - Deaktiviere Cronjobs # - @@ -642,42 +657,35 @@ else fi +echononl "Remove existing temporary Mattermost upgrade directory.." +if [[ -d "${MATTERMOST_TMP_DIR}/mattermost-upgrade" ]]; then + rm -rf "${MATTERMOST_TMP_DIR}/mattermost-upgrade" > "$log_file" 2>&1 + if [[ $? -ne 0 ]]; then + echo_failed + fatal "$(cat "$log_file")" + else + echo_ok + fi +else + echo_skipped +fi + echononl "Extract the Mattermost Server files into TMP directory.." tar -xf "${working_dir}/${DOWNLOAD_ARCHIVE}" \ - -C ${MATTERMOST_TMP_DIR} --transform='s,^[^/]\+,\0-upgrade,' + -C "${MATTERMOST_TMP_DIR}" --transform='s,^[^/]\+,\0-upgrade,' > "$log_file" 2>&1 if [[ $? -ne 0 ]]; then echo_failed - error "$(cat "$log_file")" - - echononl "continue anyway [yes/no]: " - read OK - OK="$(echo "$OK" | tr '[:upper:]' '[:lower:]')" - while [[ "$OK" != "yes" ]] && [[ "$OK" != "no" ]] ; do - echononl "Wrong entry! - repeat [yes/no]: " - read OK - done - [[ $OK = "yes" ]] || fatal "Stopped by user" - + fatal "$(cat "$log_file")" else echo_ok fi echononl "Stop Mattermost Service.." -if $(systemctl is-active --quiet service mattermost.service) ; then +if systemctl is-active --quiet mattermost.service ; then systemctl stop mattermost.service > "$log_file" 2>&1 if [[ $? -ne 0 ]]; then echo_failed - error "$(cat "$log_file")" - - echononl "continue anyway [yes/no]: " - read OK - OK="$(echo "$OK" | tr '[:upper:]' '[:lower:]')" - while [[ "$OK" != "yes" ]] && [[ "$OK" != "no" ]] ; do - echononl "Wrong entry! - repeat [yes/no]: " - read OK - done - [[ $OK = "yes" ]] || fatal "Stopped by user" - + fatal "$(cat "$log_file")" else echo_ok fi @@ -687,8 +695,8 @@ fi echononl "Backup mattermost database.." if [[ "${DB_TYPE}" = "mysql" ]]; then - mysqldump --login-path=local --opt $DB_NAME \ - > ${MATTERMOST_BASE_INSTALL_PATH}/${DB_NAME}-${backup_date}.sql 2> $log_file + mysqldump $MYSQL_CREDENTIAL_ARGS --opt "$DB_NAME" \ + > "${MATTERMOST_BASE_INSTALL_PATH}/${DB_NAME}-${backup_date}.sql" 2> "$log_file" if [[ $? -ne 0 ]]; then echo_failed error "$(cat "$log_file")" @@ -724,8 +732,8 @@ else fi echononl "Backup mattermost installation directory.." -cp -ra ${MATTERMOST_BASE_INSTALL_PATH}/mattermost/ \ - ${MATTERMOST_BASE_INSTALL_PATH}/mattermost-back-${backup_date}/ > "$log_file" 2>&1 +cp -ra "${MATTERMOST_BASE_INSTALL_PATH}/mattermost/" \ + "${MATTERMOST_BASE_INSTALL_PATH}/mattermost-back-${backup_date}/" > "$log_file" 2>&1 if [[ $? -ne 0 ]]; then echo_failed error "$(cat "$log_file")" @@ -743,20 +751,89 @@ else fi +# - When switching from Enterprise Ready to Team Edition, remove stale +# - Enterprise-only plugin state before installing the Team Edition files. +# - Keep Playbooks explicitly disabled so AutomaticPrepackagedPlugins does +# - not try to install/activate it again on Team Edition startup. +# +if [[ "$MATTERMOST_CURRENT_EDITION" = "enterprise" ]] && [[ "$MATTERMOST_EDITION" = "team" ]]; then + + blank_line + echo -e "\033[37m\033[1mPrepare Enterprise Ready -> Team Edition switch..\033[m" + blank_line + + echononl "Check for Python 3 (needed to adjust Mattermost plugin configuration).." + if command -v python3 > /dev/null 2>&1 ; then + echo_ok + else + echo_failed + fatal "Python 3 is required for a safe Enterprise Ready -> Team Edition switch." + fi + + echononl "Clean Enterprise-only plugin configuration for Team Edition.." + MM_CONFIG_FILE="${MATTERMOST_BASE_INSTALL_PATH}/mattermost/config/config.json" + python3 - "$MM_CONFIG_FILE" > "$log_file" 2>&1 <<'PY' +import json +import sys + +path = sys.argv[1] +with open(path, "r", encoding="utf-8") as f: + data = json.load(f) + +plugin_settings = data.setdefault("PluginSettings", {}) +plugins = plugin_settings.setdefault("Plugins", {}) +states = plugin_settings.setdefault("PluginStates", {}) + +plugins.pop("playbooks", None) +states.pop("com.mattermost.nps", None) +states["playbooks"] = {"Enable": False} + +with open(path, "w", encoding="utf-8") as f: + json.dump(data, f, indent=4) + f.write("\n") +PY + if [[ $? -ne 0 ]]; then + echo_failed + fatal "Failed to adjust plugin configuration:\n$(cat "$log_file")" + else + echo_ok + fi + + echononl "Remove installed Playbooks/NPS plugin files.." + rm -rf \ + "${MATTERMOST_BASE_INSTALL_PATH}/mattermost/plugins/playbooks" \ + "${MATTERMOST_BASE_INSTALL_PATH}/mattermost/plugins/com.mattermost.nps" \ + "${MATTERMOST_BASE_INSTALL_PATH}/mattermost/client/plugins/playbooks" \ + "${MATTERMOST_BASE_INSTALL_PATH}/mattermost/client/plugins/com.mattermost.nps" \ + > "$log_file" 2>&1 + if [[ $? -ne 0 ]]; then + echo_failed + fatal "Failed to remove Enterprise-only plugin files:\n$(cat "$log_file")" + else + echo_ok + fi + +fi + + echo echo -e "\033[37m\033[1mUpgrade Mattermost to version $MM_NEW_VERSION ..\033[m" echo echononl "Remove all files except \e[3mspecial directories\e[0m from within the current mattermost directory." -find ${MATTERMOST_BASE_INSTALL_PATH}/mattermost/ ${MATTERMOST_BASE_INSTALL_PATH}/mattermost/client/ \ - -mindepth 1 -maxdepth 1 \! \( -type d \( -path ${MATTERMOST_BASE_INSTALL_PATH}/mattermost/client \ - -o -path ${MATTERMOST_BASE_INSTALL_PATH}/mattermost/client/plugins \ - -o -path ${MATTERMOST_BASE_INSTALL_PATH}/mattermost/config \ - -o -path ${MATTERMOST_BASE_INSTALL_PATH}/mattermost/logs \ - -o -path ${MATTERMOST_BASE_INSTALL_PATH}/mattermost/plugins \ - -o -path ${MATTERMOST_BASE_INSTALL_PATH}/mattermost/data \) -prune \) | sort | sudo xargs rm -r > "$log_file" 2>&1 -if [[ $? -ne 0 ]]; then +set -o pipefail +find "${MATTERMOST_BASE_INSTALL_PATH}/mattermost/" "${MATTERMOST_BASE_INSTALL_PATH}/mattermost/client/" \ + -mindepth 1 -maxdepth 1 \! \( -type d \( -path "${MATTERMOST_BASE_INSTALL_PATH}/mattermost/client" \ + -o -path "${MATTERMOST_BASE_INSTALL_PATH}/mattermost/client/plugins" \ + -o -path "${MATTERMOST_BASE_INSTALL_PATH}/mattermost/config" \ + -o -path "${MATTERMOST_BASE_INSTALL_PATH}/mattermost/logs" \ + -o -path "${MATTERMOST_BASE_INSTALL_PATH}/mattermost/plugins" \ + -o -path "${MATTERMOST_BASE_INSTALL_PATH}/mattermost/data" \) -prune \) -print0 \ + | sort -z | xargs -0 -r rm -r > "$log_file" 2>&1 +_pipe_status=$? +set +o pipefail +if [[ $_pipe_status -ne 0 ]]; then echo_failed error "$(cat "$log_file")" @@ -775,7 +852,7 @@ fi echononl "Change ownership of the new files before copying them.." -chown -hR ${MATTERMOST_USER}:${MATTERMOST_GROUP} ${MATTERMOST_TMP_DIR}/mattermost-upgrade/ > "$log_file" 2>&1 +chown -hR "${MATTERMOST_USER}:${MATTERMOST_GROUP}" "${MATTERMOST_TMP_DIR}/mattermost-upgrade/" > "$log_file" 2>&1 if [[ $? -ne 0 ]]; then echo_failed error "$(cat "$log_file")" @@ -795,7 +872,7 @@ fi echononl "Copy the new files to your install directory.." -cp -an ${MATTERMOST_TMP_DIR}/mattermost-upgrade/. ${MATTERMOST_BASE_INSTALL_PATH}/mattermost/ > "$log_file" 2>&1 +cp -an "${MATTERMOST_TMP_DIR}/mattermost-upgrade/." "${MATTERMOST_BASE_INSTALL_PATH}/mattermost/" > "$log_file" 2>&1 if [[ $? -ne 0 ]]; then echo_failed error "$(cat "$log_file")" @@ -817,7 +894,7 @@ fi # to bind to low ports. # echononl "Allow the new Mattermost binary to bind to low ports.." -setcap cap_net_bind_service=+ep ${MATTERMOST_BASE_INSTALL_PATH}/mattermost/bin/mattermost > "$log_file" 2>&1 +setcap cap_net_bind_service=+ep "${MATTERMOST_BASE_INSTALL_PATH}/mattermost/bin/mattermost" > "$log_file" 2>&1 if [[ $? -ne 0 ]]; then echo_failed error "$(cat "$log_file")" @@ -835,12 +912,46 @@ else echo_ok fi +echononl "Verify installed Mattermost binary.." +if ! INSTALLED_MATTERMOST_VERSION_OUTPUT="$("${MATTERMOST_BASE_INSTALL_PATH}/mattermost/bin/mattermost" version 2>&1)" ; then + echo_failed + fatal "Failed to determine the installed Mattermost version." +else + echo_ok +fi + +echononl "Verify Mattermost Version.." +INSTALLED_MATTERMOST_VERSION="$(echo "$INSTALLED_MATTERMOST_VERSION_OUTPUT" | awk '/^Version:/ {print $2}')" +if [[ "$INSTALLED_MATTERMOST_VERSION" = "$MM_NEW_VERSION" ]]; then + echo_ok +else + echo_failed + fatal "Installed Mattermost version '${INSTALLED_MATTERMOST_VERSION}' does not match expected version '${MM_NEW_VERSION}'." +fi + +echononl "Verify Mattermost Edition.." +if [[ "$MATTERMOST_EDITION" == "enterprise" ]]; then + if echo "$INSTALLED_MATTERMOST_VERSION_OUTPUT" | grep -q "Build Enterprise Ready: true"; then + echo_ok + else + echo_failed + fatal "Installed Mattermost binary is not Enterprise Ready." + fi +else + if echo "$INSTALLED_MATTERMOST_VERSION_OUTPUT" | grep -q "Build Enterprise Ready: false"; then + echo_ok + else + echo_failed + fatal "Installed Mattermost binary is not Team Edition." + fi +fi + blank_line echo -e "\033[37m\033[1mSome post-installation stuff..\033[m" blank_line echononl "Remove the temporary files.." -rm -r ${MATTERMOST_TMP_DIR}/mattermost-upgrade/ > "$log_file" 2>&1 +rm -r "${MATTERMOST_TMP_DIR}/mattermost-upgrade/" > "$log_file" 2>&1 if [[ $? -ne 0 ]]; then echo_failed error "$(cat "$log_file")" @@ -863,9 +974,21 @@ systemctl start mattermost.service > "$log_file" 2>&1 if [[ $? -ne 0 ]]; then echo_failed error "$(cat "$log_file")" + clean_up 1 else echo_ok fi +echononl "Check Mattermost Service.." +sleep 3 +if systemctl is-active --quiet mattermost.service ; then + echo_ok +else + echo_failed + systemctl status mattermost.service --no-pager >> "$log_file" 2>&1 + error "$(cat "$log_file")" + clean_up 1 +fi + clean_up 0