#!/usr/bin/env bash

set -e

if [ -n "${EZ_VERBOSE}" ]; then
    set -x
fi

USE_TASKSMAX=${USE_TASKSMAX:-true}

# Warning: This variable API is experimental so these variables may be subject
# to change in the future.
prefix=${prefix:=/usr}
unitdir_redhat=${unitdir:-/usr/lib/systemd/system}
unitdir_debian=${unitdir:-/lib/systemd/system}
defaultsdir_redhat=${defaultsdir:-/etc/sysconfig}
defaultsdir_debian=${defaultsdir:-/etc/default}
tmpfilesdir=${tmpfilesdir:=/usr/lib/tmpfiles.d}
datadir=${datadir:=${prefix}/share}
real_name=${real_name:=puppetdb}
projdatadir=${projdatadir:=${datadir}/${real_name}}
confdir=${confdir:=/etc}
projconfdir=${projconfdir:=${confdir}/puppetlabs/${real_name}}
rundir=${rundir:=/var/run/puppetlabs/${real_name}}
# Application specific bin directory
bindir=${bindir:=/opt/puppetlabs/server/apps/${real_name}/bin}
# User facing bin directory, expected to be added to interactive shell PATH
uxbindir=${uxbindir:=/opt/puppetlabs/bin}
# symlinks of server binaries
symbindir=${symbindir:=/opt/puppetlabs/server/bin}
app_prefix=${app_prefix:=/opt/puppetlabs/server/apps/${real_name}}
dest_apps_dir="${DESTDIR}${app_prefix}"
app_data=${app_data:=/opt/puppetlabs/server/data/${real_name}}
app_logdir=${app_logdir:=/var/log/puppetlabs/${real_name}}
system_config_dir=${system_config_dir:=${app_prefix}/config}


##################
# EZBake Vars    #
##################

if [ -n "${EZ_VERBOSE}" ]; then
    set +x
    echo "#-------------------------------------------------#"
    echo "The following variables are set: "
    echo
    env | sort

    echo
    echo "End of variable print."
    echo "#-------------------------------------------------#"
    set -x
fi

##################
# Task functions #
##################

# The below functions are exposed to the user to be able to be called from
# the command line directly.

# Catch all, to install the lot, with osdetection included etc.
function task_all {
    task service
    task termini
}

# Run installer, and automatically choose correct tasks using os detection.
function task_service {
    osdetection

    if [ "$OSFAMILY" = "RedHat" ]; then
        unitdir=${unitdir_redhat}
        defaultsdir=${defaultsdir_redhat}
        task install_source_rpm_systemd
    elif [ "$OSFAMILY" = "Debian" ]; then
        unitdir=${unitdir_debian}
        defaultsdir=${defaultsdir_debian}
        task install_source_deb_systemd
    else
        echo "Unsupported platform, exiting ..."
        exit 1
    fi
}

# Source based install for Redhat based + systemd setups
function task_install_source_rpm_systemd {
    task preinst_redhat
    task install_redhat
    task systemd_redhat
    task logrotate
    task postinst_redhat
    task postinst_permissions
}

# Source based install for Debian based + systemd setups
function task_install_source_deb_systemd {
    task preinst_deb
    task install_deb
    task systemd_deb
    task logrotate
    task postinst_deb
}

# install docs for debian based systems
function task_install_docs_deb {
  if [ -d ext/docs ]; then
    install -d "${DESTDIR}${app_prefix}/share/doc/puppetdb"
    cp -a ext/docs "${DESTDIR}${app_prefix}/share/doc/puppetdb"
  fi
}

# install docs for rpm based systems
function task_install_docs_rpm {
  if [ -d ext/docs ]; then
    install -d "${DESTDIR}${app_prefix}/share/doc/puppetdb-8.14.1"
    cp -a ext/docs "${DESTDIR}${app_prefix}/share/doc/puppetdb-8.14.1"
  fi
}

# Install the ezbake package software. This step is used during RPM &
# Debian packaging during the 'install' phases.
function task_install {
    install -d -m 0755 "${dest_apps_dir}"
    install -d -m 0770 "${DESTDIR}${app_data}"
    install -m 0644 puppetdb.jar "${dest_apps_dir}"
    install -m 0755 ext/ezbake-functions.sh "${dest_apps_dir}"
    install -m 0644 ext/ezbake.manifest "${dest_apps_dir}"
    install -d -m 0755 "${DESTDIR}${projconfdir}/conf.d"

  

    install -m 0644 ext/config/request-logging.xml "${DESTDIR}${projconfdir}/request-logging.xml"
    install -m 0644 ext/config/bootstrap.cfg "${DESTDIR}${projconfdir}/bootstrap.cfg"
    install -m 0644 ext/config/logback.xml "${DESTDIR}${projconfdir}/logback.xml"
    install -m 0644 ext/config/conf.d/database.ini "${DESTDIR}${projconfdir}/conf.d/database.ini"
    install -m 0644 ext/config/conf.d/jetty.ini "${DESTDIR}${projconfdir}/conf.d/jetty.ini"
    install -m 0644 ext/config/conf.d/repl.ini "${DESTDIR}${projconfdir}/conf.d/repl.ini"
    install -m 0644 ext/config/conf.d/auth.conf "${DESTDIR}${projconfdir}/conf.d/auth.conf"
    install -m 0644 ext/config/conf.d/config.ini "${DESTDIR}${projconfdir}/conf.d/config.ini"

    install -d -m 0755 "${dest_apps_dir}/scripts"
    install -m 0755 install.sh "${dest_apps_dir}/scripts"

    install -d -m 0755 "${dest_apps_dir}/cli"
    install -d -m 0755 "${dest_apps_dir}/cli/apps"
    install -d -m 0755 "${DESTDIR}${bindir}"
    install -m 0755 "ext/bin/${real_name}" "${DESTDIR}${bindir}/${real_name}"
    install -d -m 0755 "${DESTDIR}${symbindir}"
    ln -s "../apps/${real_name}/bin/${real_name}" "${DESTDIR}${symbindir}/${real_name}"
    install -d -m 0755 "${DESTDIR}${uxbindir}"
    ln -s "../server/apps/${real_name}/bin/${real_name}" "${DESTDIR}${uxbindir}/${real_name}"
    install -m 0755 ext/cli/delete-reports "${dest_apps_dir}/cli/apps/delete-reports"
    install -m 0755 ext/cli/config-migration "${dest_apps_dir}/cli/apps/config-migration"
    install -m 0755 ext/cli/stop "${dest_apps_dir}/cli/apps/stop"
    install -m 0755 ext/cli/reload "${dest_apps_dir}/cli/apps/reload"
    install -m 0755 ext/cli/foreground "${dest_apps_dir}/cli/apps/foreground"
    install -m 0755 ext/cli/anonymize "${dest_apps_dir}/cli/apps/anonymize"
    install -m 0755 ext/cli/start "${dest_apps_dir}/cli/apps/start"
    install -m 0755 ext/cli/ssl-setup "${dest_apps_dir}/cli/apps/ssl-setup"
    install -m 0755 ext/cli/upgrade "${dest_apps_dir}/cli/apps/upgrade"

if [ -e "ext/cli_defaults/cli-defaults.sh" ]; then
    install -m 0755 ext/cli_defaults/cli-defaults.sh "${dest_apps_dir}/cli/"
fi

    install -d -m 0755 "${DESTDIR}${rundir}"
    install -d -m 700 "${DESTDIR}${app_logdir}"
}

function task_install_redhat {
    task install
    task install_docs_rpm
}

function task_install_deb {
    task install
    task install_docs_deb
}


# Install the termini packaged with the ezbake software
function task_termini {
    # We do this here, instead of at the top of the file, to avoid the need
    # for ruby unless completely necessary.
    #
    # This also allows us to prefer PE ruby over system ruby, so this will work
    # with PE as well
    local rubylibdir=${rubylibdir:=/opt/puppetlabs/puppet/lib/ruby/vendor_ruby}
    install -Dm 0644 puppet/indirector/resource/puppetdb.rb "${DESTDIR}${rubylibdir}/puppet/indirector/resource/puppetdb.rb"
    install -Dm 0644 puppet/indirector/facts/puppetdb.rb "${DESTDIR}${rubylibdir}/puppet/indirector/facts/puppetdb.rb"
    install -Dm 0644 puppet/indirector/facts/puppetdb_apply.rb "${DESTDIR}${rubylibdir}/puppet/indirector/facts/puppetdb_apply.rb"
    install -Dm 0644 puppet/indirector/node/puppetdb.rb "${DESTDIR}${rubylibdir}/puppet/indirector/node/puppetdb.rb"
    install -Dm 0644 puppet/indirector/catalog/puppetdb.rb "${DESTDIR}${rubylibdir}/puppet/indirector/catalog/puppetdb.rb"
    install -Dm 0644 puppet/functions/puppetdb_query.rb "${DESTDIR}${rubylibdir}/puppet/functions/puppetdb_query.rb"
    install -Dm 0644 puppet/util/puppetdb/atom.rb "${DESTDIR}${rubylibdir}/puppet/util/puppetdb/atom.rb"
    install -Dm 0644 puppet/util/puppetdb/char_encoding.rb "${DESTDIR}${rubylibdir}/puppet/util/puppetdb/char_encoding.rb"
    install -Dm 0644 puppet/util/puppetdb/command_names.rb "${DESTDIR}${rubylibdir}/puppet/util/puppetdb/command_names.rb"
    install -Dm 0644 puppet/util/puppetdb/http.rb "${DESTDIR}${rubylibdir}/puppet/util/puppetdb/http.rb"
    install -Dm 0644 puppet/util/puppetdb/command.rb "${DESTDIR}${rubylibdir}/puppet/util/puppetdb/command.rb"
    install -Dm 0644 puppet/util/puppetdb/config.rb "${DESTDIR}${rubylibdir}/puppet/util/puppetdb/config.rb"
    install -Dm 0644 puppet/util/puppetdb.rb "${DESTDIR}${rubylibdir}/puppet/util/puppetdb.rb"
    install -Dm 0644 puppet/face/node/deactivate.rb "${DESTDIR}${rubylibdir}/puppet/face/node/deactivate.rb"
    install -Dm 0644 puppet/face/node/status.rb "${DESTDIR}${rubylibdir}/puppet/face/node/status.rb"
    install -Dm 0644 puppet/reports/puppetdb.rb "${DESTDIR}${rubylibdir}/puppet/reports/puppetdb.rb"
}

function task_defaults_redhat {
    install -d -m 0755 "${DESTDIR}${defaultsdir_redhat}"
    install -m 0644 ext/default "${DESTDIR}${defaultsdir_redhat}/puppetdb"
}

function task_defaults_deb {
    install -d -m 0755 "${DESTDIR}${defaultsdir_debian}"
    install -m 0644 ext/debian/puppetdb.default_file "${DESTDIR}${defaultsdir_debian}/puppetdb"
}

# Install the systemd and defaults configuration for Redhat.
function task_systemd_redhat {
    task defaults_redhat
    install -d -m 0755 "${DESTDIR}${unitdir_redhat}"
    install -m 0644 ext/redhat/puppetdb.service "${DESTDIR}${unitdir_redhat}/puppetdb.service"
    install -d -m 0755 "${DESTDIR}${tmpfilesdir}"
    install -m 0644 ext/puppetdb.tmpfiles.conf "${DESTDIR}${tmpfilesdir}/puppetdb.conf"
}

# Install the systemd and defaults configuration for Debian.
function task_systemd_deb {
    task defaults_deb
    install -d -m 0755 "${DESTDIR}${unitdir_debian}"
    install -m 0644 ext/debian/puppetdb.service_file "${DESTDIR}${unitdir_debian}/puppetdb.service"
    install -d -m 0755 "${DESTDIR}${tmpfilesdir}"
    install -m 0644 ext/puppetdb.tmpfiles.conf "${DESTDIR}${tmpfilesdir}/puppetdb.conf"
}

function task_service_account {
    # Add puppetdb group
    getent group puppetdb > /dev/null || \
        groupadd -r puppetdb || :
    # Add or update puppetdb user
    if getent passwd puppetdb > /dev/null; then
        usermod --gid puppetdb --home "${app_data}" \
            --comment "puppetdb daemon" puppetdb || :
    else
        useradd -r --gid puppetdb --home "${app_data}" --shell $(which nologin) \
            --comment "puppetdb daemon"  puppetdb || :
    fi
}

# RPM based pre-installation tasks.
# Note: Any changes to this section may require synchronisation with the
# packaging, due to the fact that we can't access this script from the pre
# section of an rpm/deb.
function task_preinst_redhat {
    task service_account
}

# Debian based pre-installation tasks.
# Note: Any changes to this section may require synchronisation with the
# packaging, due to the fact that we can't access this script from the pre
# section of an rpm/deb.
function task_preinst_deb {
    task service_account
}

# Debian based unconditional post-installation tasks.
function task_postinst_deb {
    task postinst_permissions
    /opt/puppetlabs/server/bin/puppetdb config-migration
    /opt/puppetlabs/server/bin/puppetdb ssl-setup
}

# Debian based install post-installation tasks.
function task_postinst_deb_install {
    : # Null command in case additional_postinst_install is empty
}

# RPM based unconditional post-installation tasks.
function task_postinst_redhat {
    : # Null command in case additional_postinst is empty
    /opt/puppetlabs/server/bin/puppetdb config-migration
    /opt/puppetlabs/server/bin/puppetdb ssl-setup
    
    # puppetdb changed its main namespace in 6.3 and the current rpm
    # upgrade strategy fails to restart the server correctly because
    # it ends up using the templated stop from the new package which 
    # refers to the wrong namespace for the older pdb that's still
    # running.  Kill the old pdb if found and start the new one.
    # These changes can be removed in PuppetDB version 8.
    (maybe_dead=''
     pid="$(pgrep -f "puppetdb.* -m puppetlabs.puppetdb.main")"
     if test $? -eq 0; then
         kill "$pid" 2>/dev/null
         for i in {1..75}; do  # Wait up to ~15s
             if kill -0 "$pid" 2>/dev/null; then
                 maybe_dead=1
                 break
             fi
             sleep 0.2
         done
         if test -z "$maybe_dead"; then
             echo 'Unable to kill puppetdb server cleanly; sending KILL' 1>&2
             kill -9 "$pid"
         fi
         /sbin/service puppetdb start > /dev/null 2>&1
     fi)
}

# RPM based install post-installation tasks.
function task_postinst_redhat_install {
    : # Null command in case additional_postinst_install is empty
}

# Global post installation permissions setup. Not to be used by Redhat
# during package based installation, as this is done by the RPM itself
# by the %files definitions
function task_postinst_permissions {
    chown puppetdb:puppetdb /var/log/puppetlabs/puppetdb
    chmod 700 /var/log/puppetlabs/puppetdb
    chown puppetdb:puppetdb $app_data
    chmod 770 $app_data
    chown puppetdb:puppetdb $projconfdir
    chmod 750 $projconfdir
    chown puppetdb:puppetdb $rundir
    chmod 0755 $rundir
}

# Install logrotate
function task_logrotate {
    install -d -m 0755 "${DESTDIR}${confdir}/logrotate.d"
    cp -pr ext/puppetdb.logrotate.conf "${DESTDIR}${confdir}/logrotate.d/puppetdb"
}

##################
# Misc functions #
##################

# Print output only if EZ_VERBOSE is set
function debug_echo {
    if [ -n "${EZ_VERBOSE}" ]; then
        echo $@
    fi
}

# Do basic OS detection using facter.
function osdetection {
    OSFAMILY=`facter osfamily`
    MAJREV=`facter operatingsystemmajrelease`
    CODENAME=`facter os.distro.codename`

    debug_echo "OS Detection results"
    debug_echo
    debug_echo "OSFAMILY: ${OSFAMILY}"
    debug_echo "MAJREV: ${MAJREV}"
    debug_echo "CODENAME: ${CODENAME}"
    debug_echo
}

# Run a task
# Accepts:
#   $1 = task to run
function task {
    local task=$1
    shift
    debug_echo "Running task ${task} ..."
    eval task_$task $@
}

# List available tasks
#
# Gathers a list of all functions starting with task_ so it can be displayed
# or used by other functions.
function available_tasks {
    declare -F | awk '{ print $3 }' | grep '^task_*' | cut -c 6-
}

# Dispatch a task from the CLI
# Accepts:
#   $1 = task to dispatch
function dispatch {
    local task=$1
    shift
    if [ -z "$task" ]; then
        echo "Starting full installation ..."
        echo
        task all
    elif [ "$1" = "-h" ]; then
        echo "Usage: $0 <task>"
        echo
        echo "Choose from one of the following tasks:"
        echo
        echo "$(available_tasks)"
        echo
        echo "Warning: this task system is still experimental and may be subject to change without notice"
        return 1
    else
        task $task $@
    fi
}

########
# Main #
########
dispatch $@
