#!/usr/bin/env sh
#set -e
#
# This script is meant for quick & easy install via:
#   curl -sSL https://get.onedata.org/onedatify.sh | sh -s onedatify \
#       --onezone-url <url> --registration-token <reg-token> --token <support-token> <import-option>
# or:
#   wget -qO- https://get.onedata.org/onedatify.sh | sh -s onedatify \
#       --onezone-url <url> --registration-token <reg-token> --token <support-token> <import-option>
#

# By default this script installs onedatify package version
DEFAULT_ONEDATIFY_PACKAGE_VERSION="21.02.8"
DEFAULT_ONEPROVIDER_VERSION="onedata/oneprovider:21.02.8"

#######################################
# Utility logging and colour functions
#######################################
colors() {
  if [ -t 1 ] ; then
    def_color=$(tput sgr0 || tput me )
    red=$(tput setaf 1; tput bold || tput md)
    green=$(tput setaf 2;tput bold || tput md)
    yellow=$(tput setaf 3; tput bold || tput md)
    blue=$(tput setaf 4; tput bold || tput md)
    magenta=$(tput setaf 5; tput bold || tput md)
    cyan=$(tput setaf 6; tput bold || tput md)
    grey=$(tput setaf 7;)
    grey_bold=$(tput setaf 7 ; tput bold || tput md )
  else
    def_color=''
    red=''
    green=''
    yellow=''
    blue=''
    magenta=''
    cyan=''
    grey=''
    grey_bold=''
  fi
}
colors

log() {
  printf '%s%s%s%s\n' "$1" "$2" "$3" "$def_color" >&2
}
_log() {
  printf '%s%s%s%s' "$1" "$2" "$3" "$def_color" >&2
}

message() {
  log "$grey_bold" "" "$@"
}
message_imp() {
  log "$red" "" "$@"
}

_message_imp() {
  _log "$red" "" "$@"
}
_message() {
  _log "$grey_bold" "" "$@"
}

command_exists() {
  command -v "$@" > /dev/null 2>&1
}

prompt_for_proceeding() {
    proceed_with_the_installation=""
    while [ "$proceed_with_the_installation" != "n" ] && [ "$proceed_with_the_installation" != "y" ] ; do
        _message "Are you ready to proceed with the installation (y|n)?: "
        read proceed_with_the_installation </dev/tty
    done
    if [ "$proceed_with_the_installation" = "n" ] ; then
        return 1
    fi
}

# shellcheck disable=SC1117
preflight_checks() {

cat <<EOF

  ______                             __              __      __   ______           
 /      \                           /  |            /  |    /  | /      \          
/\$\$\$\$\$\$  | _______    ______    ____\$\$ |  ______   _\$\$ |_   \$\$/ /\$\$\$\$\$\$  |__    __ 
\$\$ |  \$\$ |/       \  /      \  /    \$\$ | /      \ / \$\$   |  /  |\$\$ |_ \$\$//  |  /  |
\$\$ |  \$\$ |\$\$\$\$\$\$\$  |/\$\$\$\$\$\$  |/\$\$\$\$\$\$\$ | \$\$\$\$\$\$  |\$\$\$\$\$\$/   \$\$ |\$\$   |   \$\$ |  \$\$ |
\$\$ |  \$\$ |\$\$ |  \$\$ |\$\$    \$\$ |\$\$ |  \$\$ | /    \$\$ |  \$\$ | __ \$\$ |\$\$\$\$/    \$\$ |  \$\$ |
\$\$ \__\$\$ |\$\$ |  \$\$ |\$\$\$\$\$\$\$\$/ \$\$ \__\$\$ |/\$\$\$\$\$\$\$ |  \$\$ |/  |\$\$ |\$\$ |     \$\$ \__\$\$ |
\$\$    \$\$/ \$\$ |  \$\$ |\$\$       |\$\$    \$\$ |\$\$    \$\$ |  \$\$  \$\$/ \$\$ |\$\$ |     \$\$    \$\$ |
 \$\$\$\$\$\$/  \$\$/   \$\$/  \$\$\$\$\$\$\$/  \$\$\$\$\$\$\$/  \$\$\$\$\$\$\$/    \$\$\$\$/  \$\$/ \$\$/       \$\$\$\$\$\$\$ |
                                                                         /  \__\$\$ |
                                                                         \$\$    \$\$/ 
                                                                          \$\$\$\$\$\$/  

The Onedatify script allows easy installation of a Oneprovider service
with a preconfigured support for a data space, optionally importing
a pre-existing data collection into the space.

Before proceeding, make sure this machine has been prepared according to these recomendations:
  https://onedata.org/#/home/documentation/topic/latest/oneprovider-prerequisites

The deployment will be based on **docker compose** and the script will install Docker if it's not present.

EOF

if ! prompt_for_proceeding; then
    message "Make sure all prerequisites are met and rerun the script."
    exit 1
fi

cat <<EOF

To ensure secure connections to the Oneprovider server, valid SSL certificates
are strongly recommended. You can provide your own certificates
or the Onedatify script will help you generate Let's Encrypt certificates.

If you wish to expose an existing data collection via a Onedata space, the storage device hosting
the data must be accessible from this machine. For network filesystems, it is enough for the machine
to be interconnected with the storage. For POSIX storage backends, the data must be mounted locally
to this machine's filesystem (e.g. via an NFS mount point).

If you expect this Oneprovider to transfer data to/from other Oneproviders,
make sure that this machine has a direct connection (public IP or dedicated routing) to them
and the port 6665 is open in both directions.

EOF
    if ! prompt_for_proceeding; then
        exit 1
    fi

    if ! command_exists systemctl ; then
        message_imp "Your system does not support systemd, the installation cannot continue."
        exit 1
    fi
}

install_docker() {
    message "Starting docker installation..."
    #echo "$sh_c $curl"
    $sh_c "$curl https://get.docker.com/ | sh"
}

do_install() {
    package="$1"
    url="$2"
    onedatify_package_version="$3"

   if [ "$onedatify_package_version" != "" ]; then
     apt_package_package="=$onedatify_package_version"
     yum_package_version="-$onedatify_package_version"
   fi

    apt_url="${url}/apt"
    yum_url="${url}/yum"

    user="$(id -un 2>/dev/null || true)"

    sh_c='sh -c'
    if [ "$user" != 'root' ]; then
        if command_exists sudo; then
            sh_c='sudo -E sh -c'
        elif command_exists su; then
            sh_c='su -c'
        else
cat >&2 <<EOF
Error: this installer needs the ability to run commands as root.
Neither the 'sudo' nor 'su' command was found on the host, interrupting.
EOF
        exit 1
        fi
    fi

    curl=''
    if command_exists curl; then
        curl='curl -sSL'
    elif command_exists wget; then
        curl='wget -qO-'
    else
cat >&2 <<EOF
Neither the 'curl' nor 'wget' command was found on the host, interrupting.
EOF
        exit 1
    fi

    message ""
    message "########################################"
    message "Onedatify installation process started..."
    message ""

    if ! command_exists docker ; then
        install_docker=""
        while [ "$install_docker" != "n" ] && [ "$install_docker" != "y" ] ; do
            _message "You don't seem to have docker installed. Would you like to install it now (y|n)?: "
            read install_docker </dev/tty
        done
        if [ "$install_docker" = "y" ] ; then
            install_docker
            if ! command_exists docker ; then
                message "Docker installation appears to have failed."
                exit 1
            fi
        fi
        if [ "$install_docker" = "n" ] ; then
            message "Docker is required to continue, interrupting."
            exit 1
        fi
    else 
        message "Existing docker installation found."
    fi

    if ! docker compose version; then
        message_imp "The currently installed docker does not support the compose subcommand."
        message_imp "The cause can be that the installed docker is too old or docker compose plugin is missing."
        message_imp "Please fix this manually and rerun the onedatify script."
        exit 1
    fi

    message "Installing onedatify package:"
    # Temporary hack
    if command_exists apt; then
        if dpkg-query -W "$package" 2>/dev/null 2>&1; then
            $sh_c "apt remove -y $package"
        fi
        deb_package_name="onedatify_${onedatify_package_version}.systemd_amd64.deb"
        deb_file_name=$($sh_c "mktemp /tmp/${deb_package_name%.deb}.XXXXXXX.deb")
        $sh_c "curl -k --show-error https://packages.onedata.org/$deb_package_name > $deb_file_name"
        $sh_c "dpkg -i $deb_file_name"
        $sh_c "apt-mark hold $package"
    else
        if yum list installed "$package" 2>/dev/null 2>&1; then
            $sh_c "yum remove -y $package"
        fi
        rpm_package_name="onedatify-${onedatify_package_version}.systemd-1.x86_64.rpm"
        rpm_file_name=$($sh_c "mktemp /tmp/${rpm_package_name%.rpm}.XXXXXXX.rpm")
        $sh_c "curl -k --show-error --silent https://packages.onedata.org/$rpm_package_name > $rpm_file_name"
        $sh_c "yum localinstall -y $rpm_file_name"
    fi
    return 0

    # perform some very rudimentary platform detection
    lsb_dist=''
    dist_version=''
    if command_exists lsb_release; then
        lsb_dist="$(lsb_release -si)"
    fi
    if [ -z "$lsb_dist" ] && [ -r /etc/lsb-release ]; then
        lsb_dist="$(. /etc/lsb-release && echo "$DISTRIB_ID")"
    fi
    if [ -z "$lsb_dist" ] && [ -r /etc/debian_version ]; then
        lsb_dist='debian'
    fi
    if [ -z "$lsb_dist" ] && [ -r /etc/fedora-release ]; then
        lsb_dist='fedora'
    fi
    if [ -z "$lsb_dist" ] && [ -r /etc/oracle-release ]; then
        lsb_dist='oracleserver'
    fi
    if [ -z "$lsb_dist" ] && [ -r /etc/centos-release ]; then
        lsb_dist='centos'
    fi
    if [ -z "$lsb_dist" ] && [ -r /etc/redhat-release ]; then
        lsb_dist='redhat'
    fi
    if [ -z "$lsb_dist" ] && [ -r /etc/photon-release ]; then
        lsb_dist='photon'
    fi
    if [ -z "$lsb_dist" ] && [ -r /etc/os-release ]; then
        lsb_dist="$(. /etc/os-release && echo "$ID")"
    fi

    lsb_dist="$(echo "$lsb_dist" | tr '[:upper:]' '[:lower:]')"

    # Special case redhatenterpriseserver
    if [ "${lsb_dist}" = "redhatenterpriseserver" ]; then
            # Set it to redhat, it will be changed to centos below anyways
            lsb_dist='redhat'
    fi

    case "$lsb_dist" in
        ubuntu)
            if command_exists lsb_release; then
                dist_version="$(lsb_release --codename | cut -f2)"
            fi
            if [ -z "$dist_version" ] && [ -r /etc/lsb-release ]; then
                dist_version="$(. /etc/lsb-release && echo "$DISTRIB_CODENAME")"
            fi
        ;;
        debian|raspbian)
            dist_version="$(cat /etc/debian_version | sed 's/\/.*//' | sed 's/\..*//')"
            case "$dist_version" in
                9)
                    dist_version="stretch"
                ;;
                8)
                    dist_version="jessie"
                ;;
                7)
                    dist_version="wheezy"
                ;;
            esac
        ;;
        oracleserver)
            # need to switch lsb_dist to match yum repo URL
            lsb_dist="oraclelinux"
            dist_version="$(rpm -q --whatprovides redhat-release --queryformat "%{VERSION}\n" | sed 's/\/.*//' | sed 's/\..*//' | sed 's/Server*//')"
        ;;

        fedora|centos|redhat)
            dist_version="$(rpm -q --whatprovides ${lsb_dist}-release --queryformat "%{VERSION}\n" | sed 's/\/.*//' | sed 's/\..*//' | sed 's/Server*//' | sort | tail -1)"
        ;;

        "vmware photon")
            lsb_dist="photon"
            # shellcheck disable=SC1091
            dist_version="$(. /etc/os-release && echo "$VERSION_ID")"
        ;;

        *)
            if command_exists lsb_release; then
                dist_version="$(lsb_release --codename | cut -f2)"
            fi
            if [ -z "$dist_version" ] && [ -r /etc/os-release ]; then
            # shellcheck disable=SC1091
                dist_version="$(. /etc/os-release && echo "$VERSION_ID")"
            fi
        ;;
    esac

    # Run setup for each distro accordingly
    case "$lsb_dist" in
        ubuntu|debian|raspbian)
            export DEBIAN_FRONTEND=noninteractive
            (
            set -x
            $sh_c "$curl ${apt_url}/onedata.gpg.key | apt-key add -"
            $sh_c "mkdir -p /etc/apt/sources.list.d"
            $sh_c "echo deb \[arch=$(dpkg --print-architecture)\] ${apt_url}/${lsb_dist}/${dist_version} ${dist_version} ${repo} > /etc/apt/sources.list.d/onedata.list"
            $sh_c "echo deb-src \[arch=$(dpkg --print-architecture)\] ${apt_url}/${lsb_dist}/${dist_version} ${dist_version} ${repo} >> /etc/apt/sources.list.d/onedata.list"
            $sh_c "sleep 3 ; apt-get update ; apt-get remove -y -q ${package}"
            $sh_c "sleep 3 ; apt-get update ; apt-get install --allow-unauthenticated -y -q ${package}=${apt_package_package}"
            $sh_c "sleep 3 ; apt-mark hold ${package}"
            )
            return 0
            ;;

        fedora|centos|redhat|oraclelinux|photon)
            if [ "${lsb_dist}" = "redhat" ]; then
                # we use the centos repository for both redhat and centos releases
                lsb_dist='centos'
            fi
            if [ "${lsb_dist}" = "centos" ]; then
                # we use the centos repository for both redhat and centos releases
                dist_version=${dist_version}x
            fi
            if [ "${lsb_dist}" = "scientific" ]; then
                # we use the centos repository for both redhat and centos releases
                dist_version=${dist_version}x
            fi
            reponame="onedata-${repo}"
$sh_c "cat >/etc/yum.repos.d/onedata-${repo}.repo" <<EOF
[$reponame]
name=Onedata ${repo} Repository
baseurl=${yum_url}/${lsb_dist}/${dist_version}
enabled=0
gpgcheck=0
gpgkey=${yum_url}/gpg
EOF
            if [ "$lsb_dist" = "fedora" ] && [ "$dist_version" -ge "22" ]; then
                (
                    set -x
                    $sh_c "sleep 3; dnf -y -q install ${package}${yum_package_version}"
                )
            elif [ "$lsb_dist" = "photon" ]; then
                (
                    set -x
                    $sh_c "sleep 3; tdnf -y install ${package}${yum_package_version}"
                )
            else
                (
                    set -x
                    $sh_c "sleep 3; systemctl disable  onedatify.service"
                    $sh_c "sleep 3; yum remove -y -q ${package}"
                    $sh_c "sleep 3; yum -y -q  --disablerepo=* --enablerepo=$reponame install ${package}${yum_package_version}"
                )
            fi
            # Open ports on Centos and others
            $sh_c "firewall-cmd --zone=public --add-port=80/tcp --permanent"
            $sh_c "firewall-cmd --zone=public --add-port=443/tcp --permanent"
            $sh_c "firewall-cmd --zone=public --add-port=4443/tcp --permanent"
            $sh_c "firewall-cmd --zone=public --add-port=6665/tcp --permanent"
            $sh_c "firewall-cmd --zone=public --add-port=9443/tcp --permanent"

            return 0
            ;;
    esac

    # intentionally mixed spaces and tabs here -- tabs are stripped by "<<-'EOF'", spaces are kept in the output
    cat >&2 <<EOF
Either your platform is not easily detectable, is not supported by this
installer script, or does not yet have a package for ${package}.
Currently supported distributions are: Ubuntu, Centos and Fedora.

For detailed instructions go to https://onedata.org/
EOF
    exit 1
}

positional_usage() {
s=${0##*/}
margin=$(printf "%-${#s}s" " ")
#a=./abc.sh ; a="${a##*/}" ; echo "length=${#a}"
cat <<EOF
This script provides guided assistance to easily deploy a Oneprovider service
with a preconfigured support for a Onedata space:

${0##*/} -z <onezone url> -t <space support token> [ --interactive [ --install ] [--import ]]
${margin} [ --oneprovider-version <version> ] [ --onedatify-version <version> ] [ --help ]

Options:
  -h, --help                   display this help and exit
  -e, --interactive            enter interactive installation mode, where you will be asked a series
                               of questions to determine proper configuration of the Oneprovider service
  -t, --token                  space support token
  -r, --registration-token     Oneprovider registration token, obtained from the Onezone service
  -z, --onezone-url            Onezone url
  -p, --oneprovider-version    Oneprovider docker image tag to be deployed
  -i, --import                 when used with -e, includes the option to import an existing data into the space
  -o, --onedatify-version      Onedatify package version to be installed on your system
  -n, --install                automatically install a Oneprovider service using the configuration present on the host
  
EOF
exit 1
}

parse_positional() {

    if [ $(( $# )) -eq 0 ]; then
        positional_usage
        exit 0
    fi

    onedatify_onezone_url=""
    onedatify_onezone_registration_token=""
    onedatify_token=""
    onedatify_import=0
    onedatify_package_version="$DEFAULT_ONEDATIFY_PACKAGE_VERSION"
    onedatify_oneprovider_version="$DEFAULT_ONEPROVIDER_VERSION"
    onedatify_interactive=0
    onedatify_install=0

    while [ $(( $# )) -ne 0 ] ; do
        case $1 in
            -h|-\?|--help)   # Call a "usage" function to display a synopsis, then exit.
                positional_usage
                exit 0
                ;;
            -e|--interactive)
                onedatify_interactive=1
                ;;
            -n|--install)
                onedatify_install=1
                ;;
            -t|--token)
                onedatify_token="$2"
                shift
                ;;
            -r|--registration-token)
                onedatify_onezone_registration_token="$2"
                shift
                ;;
            -z|--onezone-url)
                onedatify_onezone_url=$2
                shift
                ;;
            -p|--oneprovider-version)
                onedatify_oneprovider_version="$2"
                shift
                ;;
            -i|--import)
                onedatify_import=1
                ;;
            -o|--onedatify-version)
                onedatify_package_version="$2"
                shift
                ;;
            -?*|*)
                printf 'WARN: Unknown option (ignored): %s\n' "$1" >&2
                exit 1
                ;;
        esac
        shift
    done

    if [ "$onedatify_onezone_url" = "" ]; then
        echo "Error: missing Onezone url. Please see -h."
        exit 1
    else
        onedatify_onezone_url="${onedatify_onezone_url#*//}"
    fi

    if [ "$onedatify_onezone_registration_token" = "" ] ; then
        echo "Error: missing Oneprovider registration token. Please see -h."
        exit 1
    fi

    if [ "$onedatify_token" = "" ] ; then
        echo "Error: missing space support token. Please see -h." 
        exit 1
    fi

}

main() {
    preflight_checks
    parse_positional "$@"

    # check to see which repo they are trying to install from
    if [ -z "$repo" ]; then
        repo='main'
        repo_url="https://packages.onedata.org"
    elif [ "$repo" = "dev" ]; then
        repo_url="http://onedata-dev-packages.cloud.plgrid.pl"
        echo "Using $url"
    else
        echo "Repo \"$repo\" does not exist. Please use main or dev."
        exit 1
    fi

    do_install "onedatify" "$repo_url" "$onedatify_package_version"

    if command_exists onedatify ; then
        echo ""
        message "########################################"
        message "Onedatify package installation successful, version: $onedatify_package_version"

        if [ "$onedatify_interactive" = 1 ]; then
            onedatify config set zone_fqdn="$onedatify_onezone_url"
            onedatify config set -t zone_fqdn="$onedatify_onezone_url"
            onedatify config set version="$onedatify_oneprovider_version"
            onedatify config set -t version="$onedatify_oneprovider_version"
            onedatify config set onezone_registration_token="$onedatify_onezone_registration_token"
            onedatify config set -t onezone_registration_token="$onedatify_onezone_registration_token"
            exit_code=$? ; [ $exit_code -ne 0 ] && exit $exit_code
            
            onedatify config interactive -e onezone_fqdn
            exit_code=$? ; [ $exit_code -ne 0 ] && echo "Configuration exited with code: $exit_code" && exit $exit_code
            
            if [ "$onedatify_install" = 1 ]; then
                onedatify install interactive -p orzechpass
                exit_code=$? ; [ $exit_code -ne 0 ] && echo "Installation exited with code: $exit_code" && exit $exit_code

                if [ "$onedatify_import" = 1 ]; then
                    onedatify storage interactive-import
                else
                    onedatify storage interactive
                fi
                exit_code=$? ; [ $exit_code -ne 0 ] && echo "Storage support exited with code: $exit_code" && exit $exit_code

                detected_storage_size=""
                # check if storage added was posix
                if onedatify storage get -s name=onedatify | grep --quiet '"type": "posix"' ; then
                    posix_storage_path=$(onedatify storage get -s name=onedatify | grep mountPoint | cut -d ':' -f '2' | sed 's/^ "\(.*\)",$/\1/')
                    posix_storage_size=$($sh_c "docker exec onedatify-oneprovider-1 df -k --output=size \"$posix_storage_path\" | tail -n+2 | tr -d ' '")
                    detected_storage_size="${posix_storage_size}KiB"
                fi
                
                if [ "$detected_storage_size" != "" ] ; then
                    storage_support_size=$detected_storage_size
                else
                    storage_support_size=1073741824  # 1GiB
                fi
                
                if [ "$onedatify_import" = 1 ]; then
                    onedatify support interactive --token "$onedatify_token" \
                                                  --size "$storage_support_size" \
                                                  --storage-id 1 \
                                                  --import \
                                                  --update
                else
                    onedatify support interactive --token "$onedatify_token" \
                                                  --size "$storage_support_size" \
                                                  --storage-id 1
                fi
            else
              message ""
              message "Please use:"
              message "  onedatify install -p <your password>"
              message "to continue installing Oneprovider."
              message ""
              message "For more information how to use this script, please see:"
              message "  onedatify --help"
            fi
            # onedatify support posix --path /tmp --import
        else
            message ""
            message "Use commands:"
            message "  onedatify config interactive"
            message "  onedatify install -p <your password>"
            message "to configure and install your Oneprovider service."
            message ""
            message "For more information how to use this script, please see:"
            message "  onedatify --help"
        fi
    else
        echo ""
        message "'onedatify' is not available in your \$PATH, the installation appears to have failed."
    fi
}

# parse_command_line_options
# wrapped up in a function so that we have some protection against only getting
# half the file during "curl | sh"
main "$@"
