Select Git revision
deploy_app.sh 11.30 KiB
#!/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
# 2021-11-02 v0.7 <axel.hahn@iml.unibe.ch> delete logs keping N files
# ======================================================================
# ----------------------------------------------------------------------
# CONFIG
# ----------------------------------------------------------------------
cd $( dirname $0 )
selfdir=$( /bin/pwd )
tmpdir=/var/tmp/imldeployment_packages
logdir=/var/log/imldeployment-client
# keep last N logs per project
typeset -i iKeep=10
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 force 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"
hasfilechange=0
grep . $cfgdiff && hasfilechange=1
if [ $hasfilechange -eq 1 ]; then
echo "INFO: a config file was created or changed."
else
echo SKIP: No config file was changed.
fi
if [ $isupdate -ne 0 -o $hasfilechange -eq 1 ]; then
run_task "${profiledir}/tasks_postchange.sh"
fi
cd $( dirname $0 )
}
# delete old logfiles keeping the last N files
# param string name of project
function logdelete(){
local sProfile=$1
header "DELETE LOGS ${logdir}/${sProfile}__* ... keep $iKeep"
# order files by time
typeset -i local _iFiles=$( ls -1t ${logdir}/${sProfile}__*.log | wc -l )
typeset -i local _iStart=$iKeep+1
if [ $_iFiles -gt $iKeep ]; then
ls -1t ${logdir}/${sProfile}__*.log | sed -n "${_iStart},${_iFiles}p" | while read mylogfile
do
echo -n "deleting "
ls -l "${mylogfile}" && rm -f "${mylogfile}"
done
else
echo SKIP: deletion ... less than $iKeep files found
fi
}
# ----------------------------------------------------------------------
# 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
test -d "${logdir}" || mkdir -p "${logdir}"
for myprofile in $allprofiles
do
( deploy $myprofile; logdelete $myprofile ) 2>&1 | tee ${logdir}/${myprofile}__$(date +%Y-%m-%d__%H%M%S).log
profile=
done
rc=$?
profile=
header "All done."
echo exiting with statuscode $rc
exit $rc
# ----------------------------------------------------------------------
header "DONE :-)"
# ----------------------------------------------------------------------