#!/bin/bash

set -e

fatal_missing_var() {
  local var_name="$1"
  local var_value="${!var_name}"

  if [[ -z "${var_value}" ]]; then
    echo "Missing mandatory environment variable '\$${var_name}', exiting." >&2
    exit 1
  fi
}

DEV_MODE="${DEV_MODE:-no}"

echo "[$(date)] NGCP update to install the latest hotfixes has been requested."

NGCP_RELEASE_FILE="/etc/ngcp_version"
NGCP_ROLES_FILE='/etc/default/ngcp-roles'
NGCP_HOSTNAME="$(ngcp-hostname)"
NGCP_DB_SCHEMA_OPTS=()

if [ "$(id -u 2>/dev/null)" != 0 ] ; then
  echo "Error: this script requires root permissions." >&2
  exit 1
fi

if [ -f "$NGCP_RELEASE_FILE" ]; then
  NGCP_RELEASE="$(cat $NGCP_RELEASE_FILE)"
else
  echo "Error: Cannot detect current release version, file $NGCP_RELEASE_FILE is missed." >&2
  exit 1
fi

if [ -r "$NGCP_ROLES_FILE" ]; then
  # shellcheck disable=SC1090
  . $NGCP_ROLES_FILE
else
  echo "error: cannot read $NGCP_ROLES_FILE" >&2
  exit 1
fi

NGCP_LOG_PATH="/var/log"
NGCP_LOG_FILE="${NGCP_LOG_PATH}/ngcp-update-${NGCP_RELEASE}-$(date +%s).log"
mkdir -p "$NGCP_LOG_PATH"
exec  > >(tee -a "$NGCP_LOG_FILE"    )
exec 2> >(tee -a "$NGCP_LOG_FILE" >&2)

echo "[$(date)] Checking ngcpcfg status."
ngcpcfg_status=$(ngcpcfg status --local-only | grep ACTION_NEEDED | grep -v 'outstanding changes to pull' 2>&1) || true
if [[ -n "${ngcpcfg_status}" ]]; then
  echo "Error: ngcpcfg status --local-only shows some ACTION_NEEDED messages, please fix them before upgrade." >&2
  echo "${ngcpcfg_status}" >&2
  exit 1
fi

echo "[$(date)] Updating current host (${NGCP_HOSTNAME}) to the latest hotfix in release '$NGCP_RELEASE'..."
echo "[$(date)] (you can find update details in the log file: $NGCP_LOG_FILE )"

if [ "$FORCE" != "yes" ] ; then
  read -r -p "[$(date)] Do you want to proceed? y/N: " a
  case "$a" in
    y|Y|yes|YES)
      ;;
    *)
      echo "[$(date)] Aborting update as requested."
      exit 0
      ;;
  esac
  unset a
else
  echo "[$(date)] NOTE: FORCE update has been requested!"
fi

if [ "$NGCP_TYPE" != "spce" ]; then
  if [ "$FORCE_ACTIVE" == "yes" ]; then
    echo "[$(date)] NOTE: FORCE_ACTIVE update has been requested!"
    NGCP_DB_SCHEMA_OPTS+=(-f)
  elif ngcp-check-active -q ; then
    echo "Error: active HA node detected. Aborting to prevent any damage." >&2
    echo "Note: it is NOT recommended to upgrade active node due to production services restart." >&2
    echo "Hint: use 'FORCE_ACTIVE=yes ngcp-update' to force update on active node on your own risk." >&2
    exit 1
  fi

  echo "[$(date)] Pulling configs from ngcpcfg shared storage..."
  ngcpcfg pull >> "$NGCP_LOG_FILE"
fi

if [ "$APPROX" = "yes" ]; then
  echo "[$(date)] Change apt repositories to approx..."
  sed -i 's/:9998/:9999/g' /etc/apt/sources.list.d/*list
fi

echo "[$(date)] Updating Debian repository..."
apt-get update >> "$NGCP_LOG_FILE"

echo "[$(date)] Upgrading Debian packages..."
APT_CMD="apt-get"
APT_OPTS=(--assume-yes -o DPkg::Options::=--force-confask -o DPkg::Options::=--force-confnew)
export DEBIAN_FRONTEND=noninteractive

if [ "$DEV_MODE" = "yes" ]; then
  echo "[$(date)] NOTE: DEV_MODE update has been requested! allow-downgrades enabled"
  APT_OPTS+=(--allow-downgrades)
fi
$APT_CMD "${APT_OPTS[@]}" dist-upgrade >> "$NGCP_LOG_FILE"

unset DEBIAN_FRONTEND

echo "[$(date)] Cleanup unnecessary Debian packages..."
$APT_CMD "${APT_OPTS[@]}" --purge autoremove >> "$NGCP_LOG_FILE"

if [ "$APPROX" = "yes" ]; then
  echo "[$(date)] Change apt repositories to nginx..."
  sed -i 's/:9999/:9998/g' /etc/apt/sources.list.d/*list
fi

echo "[$(date)] Updating NGCP config schema..."
VERBOSE=yes ngcp-update-cfg-schema "${NGCP_DB_SCHEMA_OPTS[@]}" >> "$NGCP_LOG_FILE"

if grep -E '^Unpacking (mariadb|ngcp-templates)-' "$NGCP_LOG_FILE" &>/dev/null; then
  echo "[$(date)] DB or configuration templates were upgraded, special handling..."
  echo "[$(date)] - rebuilding DB configuration..."
  mariadb_paths=(/etc/mysql/)
  if [[ -f /etc/default/mysql ]]; then
    mariadb_paths+=(/etc/default/mysql)
  fi
  if [[ -f /etc/init.d/mysql ]]; then
    mariadb_paths+=(/etc/init.d/mysql)
  fi
  ngcpcfg build "${mariadb_paths[@]}" >> "$NGCP_LOG_FILE"
  echo "[$(date)] - rebuilding and reloading systemd configuration..."
  ngcpcfg build /etc/systemd/ >> "$NGCP_LOG_FILE"
  systemctl daemon-reload >> "$NGCP_LOG_FILE"
  echo "[$(date)] - restarting main DB..."
  ngcp-service restart mysql >> "$NGCP_LOG_FILE"
  if systemctl | grep mariadb@2 &>/dev/null; then
    echo "[$(date)] - restarting second DB..."
    ngcp-service restart mysql_cluster >> "$NGCP_LOG_FILE"
  fi
else
  echo "[$(date)] DB or configuration not upgraded, skipping special handling"
fi

if grep -E '^Unpacking linux-image-' "${NGCP_LOG_FILE}" &>/dev/null; then
  echo "[$(date)] Kernel was upgraded, adding it to grub configuration"
  file_number=90
  file="$(find /etc/grub.d/ -type f -name "${file_number}_*")"
  while [[ -n "${file}" ]]; do
    if [[ "${file_number}" -eq 99 ]]; then
      echo "ERROR: Can't find a free number for /etc/grub.d/ file" >&2
      echo "ERROR: There are /etc/grub.d/90..99_ files" >&2
      echo "ERROR: Please check them and free some number" >&2
      exit 1
    fi
    file_number=$(( file_number + 1 ))
    file="$(find /etc/grub.d/ -type f -name "${file_number}_*")"
  done

  CURRENT_PARTITION="$(findmnt --target / --output SOURCE --noheadings)"
  fatal_missing_var CURRENT_PARTITION

  TO_PARTITION="${CURRENT_PARTITION}"

  NGCP_VERSION="$(cat /etc/ngcp_version)"
  fatal_missing_var NGCP_VERSION

  BLK_ID="$(blkid "${CURRENT_PARTITION}" -o value | head -1)"
  fatal_missing_var BLK_ID

  current_lvm='root'
  if [[ "${CURRENT_PARTITION}" =~ fallback ]]; then
    current_lvm='fallback'
  fi
  LVM_ID="$(lvs -o lv_name,lv_uuid | awk "/${current_lvm}/ {print \$2}")"
  fatal_missing_var LVM_ID

  K_VERSION=$(linux-version list | sort -u -r -V | head -1)
  fatal_missing_var K_VERSION

  echo "INFO TARGET PARTITION: '${NGCP_VERSION}' '${BLK_ID}' '${LVM_ID}' '${K_VERSION}' '${TO_PARTITION}'"

  cp /usr/share/ngcp-upgrade/grub-template /etc/grub.d/"${file_number}"_ngcp_"${NGCP_VERSION}"
  sed -ri \
    -e "s|__NGCP_VERSION__|${NGCP_VERSION}|g" \
    -e "s|__BLK_ID__|${BLK_ID}|g" \
    -e "s|__LVM_ID__|${LVM_ID}|g" \
    -e "s|__K_VERSION__|${K_VERSION}|g" \
    -e "s|__NGCP_PARTITION__|${TO_PARTITION}|g" \
    /etc/grub.d/"${file_number}"_ngcp_"${NGCP_VERSION}"
  chmod +x /etc/grub.d/"${file_number}"_ngcp_"${NGCP_VERSION}"

  sed -ri "s|^GRUB_DEFAULT=.*|GRUB_DEFAULT='NGCP Sipwise ${NGCP_VERSION} ${K_VERSION}'|" /etc/default/grub

  update-grub
fi

echo "[$(date)] Updating NGCP DB schema..."
VERBOSE=yes ngcp-update-db-schema "${NGCP_DB_SCHEMA_OPTS[@]}" >> "$NGCP_LOG_FILE"

echo "[$(date)] Applying NGCP config schema..."
ngcpcfg apply "applying via ngcp-update from system-tools" >> "$NGCP_LOG_FILE"

if [ "$NGCP_TYPE" != "spce" ]; then
  echo "[$(date)] Pushing NGCP configs to shared storage..."
  ngcpcfg push --shared-only >> "$NGCP_LOG_FILE"
fi

curr_kernel_ver="$(uname -r)"
# Get list of installed kernel packages but the newest and previous ones
mapfile -t kernel_packages < <(dpkg-query -f '${Package}\n' -W | grep -E 'linux-image-[[:digit:]]+.*' | sort -r | tail -n +3 | grep -v "${curr_kernel_ver}")
if [[ -n "${kernel_packages[*]}" ]]; then
  echo "[$(date)] The following old kernel packages are installed: ${kernel_packages[*]}"
  if [[ "${FORCE}" == "yes" ]]; then
    echo "Removing them..."
  else
    read -r -p "[$(date)] Do you want to remove them? y/N: " a
    case "$a" in
      y|Y|yes|YES)
        ;;
      *)
        echo "[$(date)] Aborting update as requested."
        exit 0
        ;;
    esac
    unset a
  fi
fi
"${APT_CMD}" "${APT_OPTS[@]}" purge "${kernel_packages[@]}"

echo "[$(date)] NGCP has been updated successfully."
