#!/usr/bin/env bash
# ======================================================================
#
# DEPLOYMENT POC CLIENT
#
# ----------------------------------------------------------------------
# 2021-04-19  v0.1  <axel.hahn@iml.unibe.ch>  initial version
# 2021-05-09  v0.2  <axel.hahn@iml.unibe.ch>  chown includes dot files
# 2021-05-14  v0.3  <axel.hahn@iml.unibe.ch>  add params (list, force, help)
# 2021-05-27  v0.4  <axel.hahn@iml.unibe.ch>  FIX first install
# 2021-07-08  v0.5  <axel.hahn@iml.unibe.ch>  added function "runas"
# 2021-11-01  v0.6  <axel.hahn@iml.unibe.ch>  save config diffs
# ======================================================================


# ----------------------------------------------------------------------
# CONFIG
# ----------------------------------------------------------------------
cd $( dirname $0 )
selfdir=$( /bin/pwd )

tmpdir=/var/tmp/imldeployment_packages

wait=0
# wait=1

# export variables that will be set in getfile config or project
export IMLCI_PROJECT=TODO
export IMLCI_PHASE=TODO
export cfgdiff=TODO

# ----------------------------------------------------------------------
# FUNCTIONS
# ----------------------------------------------------------------------


# get a list profiles by searching a config.sh
# no param
function getprofiles(){
    find ${selfdir}/profiles/ -name "config.sh" | rev | cut -f 2 -d "/" | rev
}

# set a profile, load it, verify required parameters
# param  string  name of a subdir in ./profiles/
function setprofile(){
    profile=$1

    # source config for software download - as default.
    . ${selfdir}/bin/getfile.sh.cfg 

    # my install dir
    installdir=

    # fileowner
    appowner=

    profiledir=${selfdir}/profiles/${profile}
    . ${profiledir}/config.sh || exit 11

    echo "[${profiledir}/config.sh] was loaded."
    if [ -z "$installdir" -o -z "${IMLCI_PHASE}" -o -z "${IMLCI_PROJECT}" ]; then
        echo "to be defined in ${profiledir}/config.sh:"
        echo "installdir    = $installdir"
        echo "These variables must be set in bin/getfile.sh.cfg or in [profile]/config.sh:"
        echo "IMLCI_PHASE   = $IMLCI_PHASE"
        echo "IMLCI_PROJECT = $IMLCI_PROJECT"
        exit 12
    fi 
    echo "OK, profile [${profile}] was set."

    downloadfile="${tmpdir}/${IMLCI_PROJECT}.tgz"
    downloadtmp="${tmpdir}/${IMLCI_PROJECT}.tgz.tmp"
    cfgdiff="${tmpdir}/${IMLCI_PROJECT}_cfgdiff.txt"
    test -f "${cfgdiff}" && rm -f "${cfgdiff}"
}

# output a colored infoline with date and given message
# param  string  message text
function header(){
    local COLOR="\033[34m"
    local NO_COLOR="\033[0m"
    echo
    echo -en "${COLOR}"
    echo ______________________________________________________________________
    echo -n ">>>>>>>>>> $(date) "
    test ! -z "$profile" && echo -n "${profile} :: " 
    echo -n "$*"
    echo -en "${NO_COLOR}"
    if [ "$wait" = "1" ]; then
        echo -n " RETURN"; read dummy;
    fi 
    echo
}

# run a command as another posix user (even if it does not have a shell)
# to be used in taskas_*install.sh
#
# example:
# runas www-data "./hooks/ondeploy"
#
# param  string  username
# param  string  command to execute. Needs to be quoted.
# param  string  optional: shell (default: /bin/sh)
function runas(){
    local _user=$1
    local _cmd=$2
    local _shell=$3
    test -z "$_shell" && _shell=/bin/sh

    su $_user -s $_shell -c "$_cmd"
}

# execute a task/ hook - if the given task script exists and has executable
# persmissions; if not it is not an error
# param  string  filename
function run_task(){
    local taskscript=$1
    if [ -x "${taskscript}" ]; then
        echo "INFO: starting script ${taskscript}..."
        . "${taskscript}" || exit 10
    else
        test -f "${taskscript}" && ( echo "SKIP: task script ${taskscript} is not executable." ; ls -l "${taskscript}")
        test -f "${taskscript}" || echo "SKIP: task script ${taskscript} does not exist."
    fi
}


function deploy(){
    skipmessage="SKIP: no newer download file. You can use parameter -f to fore reinstall."

    # ----------------------------------------------------------------------
    header "Set profile [$1]"
    setprofile $1


    # ----------------------------------------------------------------------
    header "Download ${IMLCI_PROJECT}.tgz"
    typeset -i local isupdate=$defaultupdate
    ${selfdir}/bin/getfile.sh -f ${IMLCI_PROJECT}.tgz -o ${downloadtmp}
    if [ $? -ne 0 ]; then
        echo Download failed.
        echo Repeating request with debug param -d to get the error...
        
        # added sleep to repeat the request with another hashed secret
        sleep 2
        
        ${selfdir}/bin/getfile.sh -d -f ${IMLCI_PROJECT}.tgz -o ${downloadtmp}
        exit 1
    fi


    # ----------------------------------------------------------------------
    header "Detect if download is newer than last download."
    if [ -f ${downloadfile} ]; then
        # ls -l "${downloadfile}" "${downloadtmp}" 
        diff "${downloadfile}" "${downloadtmp}"
        if [ $? -eq 0 ]; then
            echo "INFO: the downloaded file is the same like last download."
            rm -f "${downloadtmp}"
        else
            echo "OK: donwload contains an update."
            isupdate=$isupdate+1
            mv "${downloadtmp}" "${downloadfile}"
        fi
    else
        echo "INFO: last download not available - first install or a forced update."
        isupdate=$isupdate+1
        mv "${downloadtmp}" "${downloadfile}"
    fi
    ls -l "${downloadfile}"


    # ----------------------------------------------------------------------
    header "Switch into install dir ${installdir} ..."
    test -d "${installdir}" || mkdir -p "${installdir}"
    cd ${installdir} || exit 2


    # ----------------------------------------------------------------------
    header "PRE tasks"
    # what you could do here:
    # - enable maintenance flag
    # - stop service
    # - cleanup directory ... up to remove all current files
    test $isupdate -eq 0 && echo $skipmessage
    test $isupdate -eq 0 || run_task "${profiledir}/tasks_preinstall.sh"


    # ----------------------------------------------------------------------
    header "PRE tasks II - cleanup"
    if [ $isupdate -eq 0 ]; then
        echo $skipmessage
    else
        test "$cleanup_preview" -eq "1" || echo "SKIP: preview of cleanup is disabled."
        test "$cleanup_preview" -eq "1" && "${selfdir}/bin/preinstall_cleanup.sh" "${installdir}" "${downloadfile}"

        test "$cleanup_force" -eq "1"   || echo "SKIP: cleanup files is disabled."
        test "$cleanup_force" -eq "1"   && "${selfdir}/bin/preinstall_cleanup.sh" "${installdir}" "${downloadfile}" "force"
    fi


    # ----------------------------------------------------------------------
    header "Extract ${downloadfile} $( pwd )"
    test $isupdate -eq 0 && echo $skipmessage
    test $isupdate -eq 0 || tar -xzf "${downloadfile}" . || exit 3
    ls -l


    # ----------------------------------------------------------------------
    # header "Remove download archive ${IMLCI_PROJECT}.tgz"
    # echo rm -f  ${IMLCI_PROJECT}.tgz


    # ----------------------------------------------------------------------
    header "Update config files"
    echo "Showing replacements:" ; grep '@replace\[' hooks/templates/*
    run_task "${profiledir}/tasks_config.sh"


    # ----------------------------------------------------------------------
    header "Set file owner [${appowner}]"
    if [ $isupdate -eq 0 ]; then
        echo $skipmessage
    else
        if [ ! -z "${appowner}" ]; then
            sudo chown -R $appowner * .* || exit 5
            ls -la
        else
            echo "SKIP: variable appowner was not set"
        fi
    fi


    # ----------------------------------------------------------------------
    header "POST tasks"
    # what you could do here:
    # - start current deploy scripts
    # - apply database updates
    # - set permissions
    # - start service
    # - remove maintenance flag
    # - send success message as email/ slack/ [another fancy tool]
    test $isupdate -eq 0 && echo $skipmessage
    test $isupdate -eq 0 || run_task "${profiledir}/tasks_postinstall.sh"
    grep . $cfgdiff
    if [ $? -eq 0 ]; then
        run_task "${profiledir}/tasks_postchanges.sh"
    else
        echo SKIP: No config file was changed.
    fi
    
    cd $( dirname $0 )

}
# ----------------------------------------------------------------------
# MAIN
# ----------------------------------------------------------------------


cd $( dirname $0 )

action="deploy"
typeset -i defaultupdate=0

echo
echo
echo "<<<<<<<<<<##########|  IML - DEPLOYMENT SCRIPT  |##########>>>>>>>>>>"
echo

while getopts 'hfl' arg; do
  case ${arg} in
    h) 
        echo "HELP"
        echo "Load one or more profiles profile to deploy an application."
        echo "If the download file is not newer then it does not extract files and does not"
        echo "run pre and post hooks - it updates the config files only and sets the owner."
        echo
        echo "Syntax:"
        echo "  $( basename $0 ) [OPTION] [PROFILE(S)]"
        echo
        echo "Optioms:"
        echo "  -h | show this help and exit"
        echo "  -f | force full installation even if the download file is not newer"
        echo "  -l | list exiting profile names"
        echo
        echo "Profile(s):"
        echo "  Set one or more valid profile names. By default it loops over all profiles."
        echo "  This prameter limits the execution to the given profiles." 
        echo "  Use option -l to get a list of profiles." 
        echo
        exit 0
        ;;
    f)
      echo "FORCE update"
      defaultupdate=1
      shift 1
      ;;
    l)
      echo "LIST of existing profiles:"
      getprofiles
      echo
      exit 0
      ;;
    ?)
      echo "Invalid option: -${OPTARG}."
      exit 2
      ;;
  esac
done


if [ $# -eq 0 ]; then
    header "looping over all profiles"
    getprofiles
    echo
    allprofiles=$( getprofiles )
else
    allprofiles="$*"
fi

for myprofile in $allprofiles
do
    deploy $myprofile
    profile=
done

rc=$?
profile=
header "All done."
echo exiting with statuscode $rc
exit $rc



# ----------------------------------------------------------------------
header "DONE :-)"


# ----------------------------------------------------------------------
