#!/bin/bash
# mr7.1: Check connectivity with repos for current version (TT#4104).

set -e
set -E

# set expected umask to avoid apt-key issues or apt warnings
umask 0022

debug() {
  local debug="${DEBUG:-false}"
  local message="$*"

  if [[ "${debug}" == "true" ]] ; then
    echo "DEBUG: ${message}" >&2
  fi
}

die() {
  local message="$*"

  echo "ERROR: ${message}" >&2
  exit 1
}

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

  if [[ -z "${var_value}" ]] ; then
    die "Missing mandatory environment variable '\$${var_name}', exiting."
  fi
}

fatal_missing_var FORCE_UPGRADE
fatal_missing_var DIST
fatal_missing_var OLD_VERSION
fatal_missing_var UPGRADE_VERSION
fatal_missing_var CE_EDITION
fatal_missing_var PRO_EDITION
fatal_missing_var CARRIER_EDITION
if [[ "${CE_EDITION}" != "true" ]]; then
  fatal_missing_var APPROX_RO_PORT
fi

GLOBAL_TMPDIR="$(mktemp -d)"
cleanup() {
  exit_status=$?
  trap '' EXIT HUP INT TERM
  rm -fr "${GLOBAL_TMPDIR}"
  echo
  exit $exit_status
}
trap cleanup EXIT HUP INT TERM

dump_file() {
  local file="$1"
  if [[ ! -r "${file}" ]] ; then
    die "Cannot read from file ${file}"
  fi

  echo "----"
  cat "${file}"
  echo "----"
}

abort_if_max_tries() {
  local try="$1"
  local max_tries="$2"

  if [[ "${try}" -ge "${max_tries}" ]] ; then
    # try to flush previous output to stdout
    sync

    die "max retries ${max_tries} reached"
  fi
}

apt_update() {
  if [[ ! -d "${GLOBAL_TMPDIR}" || ! -w "${GLOBAL_TMPDIR}" ]] ; then
    die "Cannot use \$GLOBAL_TMPDIR: ${GLOBAL_TMPDIR}"
  fi

  local tmpfile=""
  tmpfile="$(mktemp --tmpdir="${GLOBAL_TMPDIR}")"
  if [[ ! -f "${tmpfile}" || ! -w "${tmpfile}" ]] ; then
    die "Not a regular file or cannot write to ${tmpfile}"
  fi

  local max_tries=3
  local regex1="^E: "
  local regex2="Some index files failed to download"
  local rc=0
  for try in $(seq 1 "${max_tries}") ; do
    echo "Performing check on repositories for the configured version (try #${try})..."
    rc=0
    rm -rf "${GLOBAL_TMPDIR}"/{cachedir,statedir,etc/apt/trusted.gpg.d}
    mkdir -p "${GLOBAL_TMPDIR}"/{cachedir,statedir,etc/apt/trusted.gpg.d}
    cp -a /etc/apt/trusted.gpg.d/* "${GLOBAL_TMPDIR}"/etc/apt/trusted.gpg.d/

    apt-get \
      -o dir::cache="${GLOBAL_TMPDIR}/cachedir" \
      -o dir::etc="${GLOBAL_TMPDIR}/etc/apt/" \
      -o dir::state="${GLOBAL_TMPDIR}/statedir" \
      -o dir::etc::trustedparts="${GLOBAL_TMPDIR}/etc/apt/trusted.gpg.d/" \
      update &> "${tmpfile}" || rc=$?

    if [[ "${rc}" -ne 0 ]] || grep -q "${regex1}" "${tmpfile}" || grep -q "${regex2}" "${tmpfile}" ; then
      echo " - WARNING: The repositories cannot update available packages with all configured repos (try #${try}):"
      dump_file "${tmpfile}"

      abort_if_max_tries "${try}" "${max_tries}"
    else
      echo " - OK, repositories for the configured versions are reachable."
      break
    fi
  done
}

check_repos() {
  local debian_codename="$1"
  if [[ -z "${debian_codename}" ]] ; then
    die "check_repos(): Missing 1st parameter to use as \$debian_codename"
  fi

  local repos="$2"
  if [[ -z "${repos}" ]] ; then
    die "check_repos(): Missing 2nd parameter to use as \$repos"
  fi

  echo -e "Checking repo accessibility for:\n" \
       "- ${REPOS_BASE_URL}/debian/ ${debian_codename}\n" \
       "    (and related -security, -debug, etc.)\n" \
       "- ${repos}"
  apt_update
}

write_initial_config() {
  # use clean ${GLOBAL_TMPDIR}/etc/apt/sources.list and .../sources.list.d/* files
  debug "Checking repository connectivity for upgrade"

  DEBIAN_CODENAME="$(lsb_release -cs)"
  if [[ -z "${DEBIAN_CODENAME}" ]] ; then
    die "Could not detect Debian release codename"
  fi

  UPGRADE_DEBIAN_CODENAME="${DIST}"
  debug "Using base Debian system '${UPGRADE_DEBIAN_CODENAME}' as guessed for upgrade"

  local ngcp_type=""
  ngcp_type="$(ngcp-type)"
  case "${ngcp_type}" in
    spce)
      : # correct value
      ;;
    sppro|carrier)
      ngcp_type="sppro"
      ;;
    *)
      die "Could not detect or understand NGCP release type: '${ngcp_type}'"
      ;;
  esac

  if [[ "${CE_EDITION}" == "true" ]] ; then
    local REPOS_BASE_URL="https://deb.sipwise.com"
  else
    local ngcp_mgmt_node="/etc/ngcp_mgmt_node"
    if [[ ! -f "${ngcp_mgmt_node}" ]] ; then
      die "Missing file ${ngcp_mgmt_node}, cannot continue!"
    fi

    local mgmt_node=""
    mgmt_node="$(cat ${ngcp_mgmt_node})"
    if [[ -z "${mgmt_node}" ]] ; then
      die "Missing management node name! Check content of file ${ngcp_mgmt_node}, cannot continue!"
    fi

    local REPOS_BASE_URL="http://${mgmt_node}:${APPROX_RO_PORT}"
  fi

  # create default URL, special case for trunk
  local REPOS_DEFAULT="${REPOS_BASE_URL}/${ngcp_type}/${OLD_VERSION}/ ${DEBIAN_CODENAME} main"
  if [[ "${UPGRADE_VERSION}" == "trunk" ]] ; then
    REPOS_DEFAULT="${REPOS_BASE_URL}/autobuild/ release-${UPGRADE_VERSION}-${DIST} main"
  fi

  # support overriding via env variable, and use as global variable
  REPOS="${REPOS:-${REPOS_DEFAULT}}"

  echo "Configuring repositories for the current version: ${OLD_VERSION} based on ${DEBIAN_CODENAME}"

  # exported as global variable for other functions to use
  SOURCES_DIR_PATH="${GLOBAL_TMPDIR}/etc/apt/sources.list.d"

  mkdir -p "${SOURCES_DIR_PATH}"

  # create files
  local sources_path="/etc/apt/sources.list"
  debug "Creating ${sources_path}"
  echo '# Please visit /etc/apt/sources.list.d/ instead.' > "${GLOBAL_TMPDIR}/${sources_path}"

  debug "Creating ${SOURCES_DIR_PATH}/debian.list"
  cat > "${SOURCES_DIR_PATH}"/debian.list <<EOF
# NGCP_MANAGED_FILE
# Debian repositories, deployed via upgrade ${OLD_VERSION}->${UPGRADE_VERSION}

deb ${REPOS_BASE_URL}/debian/ ${DEBIAN_CODENAME} main contrib non-free
#deb-src ${REPOS_BASE_URL}/debian/ ${DEBIAN_CODENAME} main contrib non-free

deb ${REPOS_BASE_URL}/debian-security/ ${DEBIAN_CODENAME}-security main contrib non-free
#deb-src ${REPOS_BASE_URL}/debian-security/ ${DEBIAN_CODENAME}-security main contrib non-free

deb ${REPOS_BASE_URL}/debian/ ${DEBIAN_CODENAME}-updates main contrib non-free
#deb-src ${REPOS_BASE_URL}/debian/ ${DEBIAN_CODENAME}-updates main contrib non-free

deb ${REPOS_BASE_URL}/debian-debug/ ${DEBIAN_CODENAME}-debug main contrib non-free
#deb-src ${REPOS_BASE_URL}/debian-debug/ ${DEBIAN_CODENAME}-debug main contrib non-free
EOF

  debug "Creating ${SOURCES_DIR_PATH}/sipwise.list"
  cat > "${SOURCES_DIR_PATH}"/sipwise.list <<EOF
# NGCP_MANAGED_FILE - do not remove this line if it should be automatically handled
# Sipwise repository, deployed via upgrade ${OLD_VERSION}->${UPGRADE_VERSION}

deb [arch=amd64] ${REPOS}
#deb-src ${REPOS}
EOF
}

convert_upgrade_configuration() {
  if [[ -z "${SOURCES_DIR_PATH}" ]] ; then
    die "convert_upgrade_configuration(): \$SOURCES_DIR_PATH not defined"
  fi
  if [[ -z "${DEBIAN_CODENAME}" ]] ; then
    die "convert_upgrade_configuration(): \$DEBIAN_CODENAME not defined"
  fi
  if [[ -z "${UPGRADE_DEBIAN_CODENAME}" ]] ; then
    die "convert_upgrade_configuration(): \$UPGRADE_DEBIAN_CODENAME not defined"
  fi

  # replace with repos for UPGRADE_VERSION
  echo "Reconfiguring repositories for the version to upgrade: ${UPGRADE_VERSION} based on ${UPGRADE_DEBIAN_CODENAME}"

  debug "Updating ${SOURCES_DIR_PATH}/sipwise.list: ${OLD_VERSION}->${UPGRADE_VERSION}"
  sed -i "s/${OLD_VERSION}/${UPGRADE_VERSION}/" "${SOURCES_DIR_PATH}"/sipwise.list

  if [[ "${DEBIAN_CODENAME}" != "${UPGRADE_DEBIAN_CODENAME}" ]] ; then
    debug "Updating ${SOURCES_DIR_PATH}/debian.list: ${DEBIAN_CODENAME}->${UPGRADE_DEBIAN_CODENAME}"
    sed -i "s/${DEBIAN_CODENAME}/${UPGRADE_DEBIAN_CODENAME}/" "${SOURCES_DIR_PATH}"/debian.list

    debug "Updating ${SOURCES_DIR_PATH}/sipwise.list: ${DEBIAN_CODENAME}->${UPGRADE_DEBIAN_CODENAME}"
    sed -i "s/${DEBIAN_CODENAME}/${UPGRADE_DEBIAN_CODENAME}/" "${SOURCES_DIR_PATH}"/sipwise.list
  fi
}


# main execution

write_initial_config
check_repos "${DEBIAN_CODENAME}" "${REPOS}"

convert_upgrade_configuration
check_repos "${UPGRADE_DEBIAN_CODENAME}" "${REPOS/${OLD_VERSION}/${UPGRADE_VERSION}}"
