#!/bin/bash
# @description Upgrade techniques in the configuration repository from the packaged ones
# @man This command will replace the techniques in ${RUDDER_VAR}/configuration-repository/techniques
# @man by the techniques found in ${RUDDER_DIR}/share/techniques which is installed by rudder-technique package.
# @man The upgrade can take care of user defined changes.
# @man This command creates an update branch "rudder_update" with the content of current techniques first time is it run
# @man +
# @man *Options*:
# @man +
# @man *-u*: merge updated techniques into the configuration repository
# @man +
# @man *-i*: create the initial version of the update branch
# @man +
# @man *-o*: override existing technique without looking for local changes
# @man +
# @man *-f*: suppress any warning and run without prompting for input
# @man +
# @man *-c*: use the given commit id as the update branch origin
# @man +
# @man *-a --autoupdate-technique-library*: automatically update technique library if autoupdate-technique-library is true by doing an override of existing Techniques
# @man +
# @man *--set-autoupdate-technique-library=true/false*: set the auto update technique library option at upgrade to true or false (for Rudder 5+)
# @man +
# @man *-s --show*: Display the currently defined options

. "${BASEDIR}/../lib/common.sh"

UPGRADE=false
INITIAL=false
OVERRIDE=false
FORCE=false
PARENT_COMMIT=""
SET_OPTION=false
KEY_OPTION=""
VALUE_OPTION=""
CONFIGURATION_FILE=${RUDDER_DIR}/etc/rudder.conf

AUTO_UPDATE_TECHNIQUE=false
DURING_UPGRADE=false

# detect if any option has been passed to the script
ANY_OPTION_DEFINED=false

# Output usage
function usage()
{
  echo "rudder server upgrade-techniques"
  echo "upgrade techniques in the configuration repository from the packaged ones"
  echo "This command will replace the techniques in ${RUDDER_VAR}/configuration-repository/techniques"
  echo "by the techniques found in ${RUDDER_DIR}/share/techniques which is installed by rudder-technique package."
  echo "The upgrade can take care of user defined changes."
  echo "This command creates an update branch rudder_update with the content of current techniques first time is it run"
  echo ""
  echo "It can also control behaviour of automatic Techniques upgrades in Rudder 5.0 and later"
  echo "By default, instance upgraded from Rudder 4 to 5 will not get automatic technique upgrade,"
  echo "unless option autoupdate-technique-library as been set to true"

  echo "Options:"
  echo "--upgrade -u         : merge updated techniques into the configuration repository"
  echo "--initial -i         : create the initial version of the update branch"
  echo "--override -o        : override existing technique without looking for local changes"
  echo "--force -f           : suppress any warning and run without prompting for input"
  echo "--commit -c COMMITID : use the given commit id as the update branch origin"
  echo "--autoupdate-technique-library -a            : automatically update technique library if autoupdate-technique-library is true by doing an override of existing Techniques"
  echo "--set-autoupdate-technique-library=true/false: set the auto update technique library option at upgrade to true or false (for Rudder 5+)"
  echo "--show -s                                    : Display the currently defined options"
}

# Check if the configuration file exists, and if not, create it with default content
function check_configuration_file_exists()
{
  if [ ! -f ${CONFIGURATION_FILE} ]; then
    cat <<EOF > ${CONFIGURATION_FILE}
[server]
# Configure the behavior for technique update (in Rudder 5+)
# If set to true, Rudder upgrade will automatically replace active Techniques by those installed by rudder-techniques package
autoupdate-technique-library=false
EOF
  fi
}


# Show all defined options in the configuration file
function show_defined_option()
{
  if [ ! -f ${CONFIGURATION_FILE} ]
  then
    echo "Configuration file does not exists, no options set"
  else
    echo "Defined options are"
    grep --color=never = ${CONFIGURATION_FILE}
  fi
}

function do_autoupdate_technique_library
{
  if [ ! -f ${CONFIGURATION_FILE} ]
  then
    echo "Configuration file does not exists, won't automatically update Techniques"
    # create default file
    check_configuration_file_exists
  else
    AUTO_UPDATE_TECHNIQUE_CONFIGURED=$(grep "^autoupdate-technique-library="  ${CONFIGURATION_FILE} | sed 's/.*=//')
    if [ "${AUTO_UPDATE_TECHNIQUE_CONFIGURED}" = "true" ]
    then
      # it is an OVERRIDE + FORCE
      OVERRIDE=true
      FORCE=true
      UPGRADE=false
      INITIAL=false
    else
      echo "Configuration file does not set auto update of Techniques by default (value is ${AUTO_UPDATE_TECHNIQUE_CONFIGURED}). Skipping"
    fi
  fi
}

# Defines available options
OPTIONS=uiofc:a:s
LONGOPTS=upgrade,initial,override,force,commit:,autoupdate-technique-library,set-autoupdate-technique-library:,show,during-upgrade


# Use ! to avoid failing with set -e
# Use $PIPESTATUS to get the right return code even with !
! PARSED=$(getopt --options=$OPTIONS --longoptions=$LONGOPTS --name "$0" -- "$@")

if [[ ${PIPESTATUS[0]} -ne 0 ]]; then
    # Wrong arguments
    echo "Wrong parameters for command rudder server upgrade-techniques"
    usage
    exit 1
fi

eval set -- "$PARSED"


while true; do
  case "$1" in
    -u |--upgrade)
      UPGRADE=true
      ANY_OPTION_DEFINED=true
      shift
      ;;
    -i |--initial)
      INITIAL=true
      ANY_OPTION_DEFINED=true
      shift
      ;;
    -o |--override)
      OVERRIDE=true
      ANY_OPTION_DEFINED=true
      shift
      ;;
    -f |--force)
      FORCE=true
      ANY_OPTION_DEFINED=true
      shift
      ;;
    -c|--commit)
      PARENT_COMMIT="$2"
      ANY_OPTION_DEFINED=true
      shift 2
      ;;
    -a|--autoupdate-technique-library)
      AUTO_UPDATE_TECHNIQUE=true
      ANY_OPTION_DEFINED=true
      shift
      ;;
    --set-autoupdate-technique-library)
      SET_OPTION=true
      KEY_OPTION=autoupdate-technique-library
      VALUE_OPTION="$2"

      if [ "${VALUE_OPTION}" != "true" ] && [ "${VALUE_OPTION}" != "false" ]
      then
       echo "--set-autoupdate-technique-library requires 'true' or 'false' parameter, ${VALUE_OPTION} given"
       exit 1
      fi
      ANY_OPTION_DEFINED=true
      shift 2
      ;;
    -s|--show)
      ANY_OPTION_DEFINED=true
      show_defined_option
      shift
      ;;
    --during-upgrade)
      DURING_UPGRADE=true
      shift
      ;;
    --)
      shift
      break
      ;;
    *)
      echo "Programming error - option not correctly handled"
      exit 2
      ;;
  esac
done

# No option given, need to show usage
if [ "${ANY_OPTION_DEFINED}" = "false" ]
then
  usage
  exit 1
fi

if [ "${AUTO_UPDATE_TECHNIQUE}" = "true" ]
then
  do_autoupdate_technique_library
fi

# Exactly one option is allowed for upgrade of technique
[ "${UPGRADE}" = "true" ] && [ "${INITIAL}" = "true" ] && err="error"
[ "${UPGRADE}" = "true" ] && [ "${OVERRIDE}" = "true" ] && err="error"
[ "${INITIAL}" = "true" ] && [ "${OVERRIDE}" = "true" ] && err="error"

if [ "${err}" != "" ]
then
  echo "To automatically upgrade Technique, this command needs exactly one choice. Available choice are:"
  echo " -u: merge updated techniques into the configuration repository"
  echo " -i: create the initial version of the update branch"
  echo " -o: override existing technique without looking for local changes"
  echo ""
  echo "Additional option are :"
  echo " -f: suppress any warning and run without prompting for input"
  echo " -c COMMITID: use the given commit id as the update branch origin"

  exit 1
fi

# We are setting option in the configuration file
if [ "${SET_OPTION}" = "true" ]
then
  check_configuration_file_exists
  sed -i  "/^\[server\]/,/^\[/{s/^${KEY_OPTION}=.*/${KEY_OPTION}=${VALUE_OPTION}/}" ${CONFIGURATION_FILE}

  echo "Correctly set option ${KEY_OPTION} to value ${VALUE_OPTION}"
fi




REPO="${RUDDER_VAR}/configuration-repository"
BASE="${RUDDER_DIR}/share/techniques"
UPDATE_BRANCH="rudder_update"

cd "${REPO}"
if [ "${UPGRADE}" = "true" ]
then
  # Upgrading needs the upgrade branch
  if git rev-parse --verify --quiet "${UPDATE_BRANCH}" > /dev/null
  then
    # Checkout can fail because of unstaged change
    if git checkout "${UPDATE_BRANCH}"
    then
      cp -a "${BASE}"/* techniques/
      ${RUDDER_DIR}/bin/rudder-fix-repository-permissions
      git add techniques/
      git commit -q -m "${TAG_MESSAGE} Standard technique upgrade from version ${package_version} on $(date +"${DATE_FORMAT}")"
      git checkout master
      git merge "${UPDATE_BRANCH}"
      ${RUDDER_DIR}/bin/rudder-fix-repository-permissions
      # TODO Now's a good time for a user shell
      rudder server reload-techniques
    else
      echo "You have unstagged changes in current branch"
      echo "Aborting"
      exit 1
    fi
  else
    echo "You need an update branch."
    echo "- If you have not made any change in the techniques, you can override techniques with the updated ones using the -o option."
    echo "- If you have made changes you would like to keep, you should first create the initial branch with the -i option."
    exit 1
  fi
  exit 0
fi

if [ "${INITIAL}" = "true" ]
then
  echo "Creating initial version of the branch for Techniques update"
  if git rev-parse --verify --quiet "${UPDATE_BRANCH}" > /dev/null
  then
    echo "The upgrade branch '${UPDATE_BRANCH}' already exists, aborting"
    exit 1
  fi
  # find the first commit with the Techniques directory in it to minimize merging difficulties
  if [ "${PARENT_COMMIT}" = "" ]
  then
    if [ "${FORCE}" != "true" ]
    then
      echo "You have not provided an initial commit, we will use the first commit with techniques."
      echo "This can make the upgrade merge complex."
      echo "Use -c option to specify a more recent commit with no patch."
      echo ""
      echo "Type ctrl-c to abort now and return to continue."
      read a
    fi
    PARENT_COMMIT=$(git log --pretty=oneline --no-abbrev-commit -- techniques | tail -1 | awk '{print $1}')
  fi
  git branch "${UPDATE_BRANCH}" "${PARENT_COMMIT}"
  # Fix permissions
  ${RUDDER_DIR}/bin/rudder-fix-repository-permissions
  exit 0
fi

if [ "${OVERRIDE}" = "true" ]
then
  echo "Overriding existing techniques"
  if [ "${FORCE}" != "true" ]
  then
    echo "Please keep in mind that if you did manual modifications on the Techniques in existing directories, they will be overwritten."
    echo ""
    echo "Type ctrl-c to abort now and return to continue."
    read a
  fi
  cp -a "${BASE}"/* techniques/
  ${RUDDER_DIR}/bin/rudder-fix-repository-permissions
  git add techniques/
  git reset HEAD techniques/**/resources/*
  git commit -q -m "${TAG_MESSAGE} Forced technique upgrade from version ${package_version} on $(date +"${DATE_FORMAT}")"
  # consider this commit as the common ancestor for future update
  # TODO: better ?
  if git rev-parse --verify --quiet "${UPDATE_BRANCH}" > /dev/null
  then
    # it's easier to remove then create the branch
    git branch -D "${UPDATE_BRANCH}" > /dev/null
  fi
  git branch "${UPDATE_BRANCH}"
  if [ "${DURING_UPGRADE}" = "false" ]
  then
    rudder server reload-techniques
  else
    # webapp is down, so we have to touch file for reloading after reboot
    touch ${RUDDER_DIR}/etc/force_technique_reload
  fi
  echo "Techniques have been updated, and update branch set to current state of the Techniques"
fi

