Skip to content
Snippets Groups Projects
Select Git revision
  • cb4cae8c0481dae6a1da54e1bf90f593f414d78b
  • master default protected
2 results

deploy_app.sh

Blame
  • 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 :-)"
    
    
    # ----------------------------------------------------------------------