#!/usr/bin/env bash
# AWS Reference Common Linux Tools
# authors: tonynv@amazon.com, andglenn@amazon.com
#

# Supported only while bootstrapping Amazon EC2:
#
# -Amazon Linux 2
# -Amazon Linux 2022
# -CentOS 7
# -SUSE Linux Enterprise Server 15
# -Ubuntu 20.04 & 22.04
#
#

# Configuration
#
PROGRAM='AWS Reference Linux Common Tools'

# Usage
#
# To use the functions defined here (source this file):
# Example:
#   load script into scripts
#   source quickstart-cfn-tools.source
#   # To print os type to std out
#   get_os-type
#   # to assign the os type to a variable OS
#   get_os-type OS
#


# Detects operating system type and return value
# If no variable is passed in function will print to std-out
#
qs_int_set_svc_executable() {
  if [[ $(which systemctl) ]]; then
    export qs_svc_executable="systemd"
  else
    export qs_svc_executable="sysvinit"
  fi
}

qs_int_is_svc_active() {
  case ${qs_svc_executable} in
    systemd)
      systemctl is-active --quiet ${1}.service
      ;;
    sysvinit)
      service ${1} status
      ;;
  esac
}

qs_int_service_restart() {
  case ${qs_svc_executable} in
    systemd)
      systemctl restart ${1}.service
      ;;
    sysvinit)
      service ${1} restart
      ;;
  esac
}

qs_get-ostype() {
  local __return=$1
  DETECTION_STRING="/etc/*-release"
  if [[ $(ls ${DETECTION_STRING}) ]]; then
    OS=$( cat /etc/*-release |
      grep ^ID= |awk -F= '{print $2}' |
      tr -cd [:alpha:])

    if [ "${OS}" == "ol" ]; then
      OS="rhel"
    fi
    if [ $? -eq 0 ] && [ "$__return" ]; then
      eval $__return="${OS}"
      return 0
    elif [ $OS ]; then
      echo $OS
      return 0;
    else
      echo "Unknown"
    fi
  else
    if [ "$__return" ]; then
      __return="Unknown"
      return 1
    else
      echo "Unknown"
      return 1;
    fi
  fi
}

# Returns operating system version or return 1
# If no variable is passed in function will print to std-out
#
qs_get-osversion () {
  local __return=$1
  DETECTION_STRING="/etc/*-release"
  if [[ $(ls ${DETECTION_STRING})  ]]; then
    OSLEVEL=$(cat ${DETECTION_STRING} |
      grep VERSION_ID |
      tr -d \" |
      awk -F= '{print $2}')

    if [ $? -eq 0 ] && [ "$__return" ]; then
      eval $__return="${OSLEVEL}"
      return 0
    elif [ $OS ]; then
      echo $OSLEVEL
      return 0;
    else
      echo "Unknown"
    fi
  else
    if [ "$__return" ]; then
      __return="Unknown"
      return 1
    else
      echo "Unknown"
      return 1;
    fi
  fi
}

# If python is install returns default python path
# If no variable is passed in function will print to std-out
#
qs_get-python-path() {
  local __return=$1
  # Set PYTHON_EXECUTEABLE to default python version
  if command -v python > /dev/null 2>&1; then
    PYTHON_EXECUTEABLE=$(which python)
  else
    PYTHON_EXECUTEABLE=$(which python3)
  fi

  #Return python path or return code (1)
  if [ $PYTHON_EXECUTEABLE ] && [ "$__return" ]; then
    eval $__return="${PYTHON_EXECUTEABLE}"
    return 0
  elif [ $PYTHON_EXECUTEABLE ]; then
    echo $PYTHON_EXECUTEABLE
    return 0;
  else
    echo "Python Not installed"
    return 1
  fi
}

# Relax require tty
#
qs_notty() {
  qs_get-ostype INSTANCE_OSTYPE
  qs_get-osversion INSTANCE_OSVERSION
  echo "[INFO] Relax tty requirement"
  if [ "$INSTANCE_OSTYPE" == "centos" ]; then
    sed -i -e "s/Defaults    requiretty/Defaults    \!requiretty/" /etc/sudoers
  fi
}

# Installs pip from bootstrap.pypa
#
qs_bootstrap_pip() {
  qs_notty
  echo "[INFO] Check for python/pip"
  qs_get-python-path PYTHON_EXECUTEABLE
  if [ $? -eq 0 ] ;then
    command -v pip > /dev/null 2&>1
    if [ $? -eq 1 ]; then
      curl -sS --retry 5 https://bootstrap.pypa.io/pip/2.7/get-pip.py |
        $PYTHON_EXECUTEABLE
    fi
  else
    echo $PYTHON_EXECUTEABLE
    exit 1
  fi
}

# Installs and configures cloudwatch
# Then adds /var/log/syslog to log collection
#
qs_cloudwatch_tracklog() {
  local -r __log="$@"
  cat cloudwatch_logs.stub | sed s,__LOG__,$__log,g >> /var/awslogs/etc/awslogs.conf
  qs_int_service_restart awslogs
}

# Added EPEL enabler
#
qs_enable_epel() {
  qs_get-ostype INSTANCE_OSTYPE
  qs_get-osversion INSTANCE_OSVERSION
  echo "[INFO] Enable epel-release-latest-7"
  if [ "$INSTANCE_OSTYPE" == "centos" ]; then
    yum install -y epel-release
  else
    exit 1
  fi
}

# Updates supported operating systems to latest
# or
# exit with code (1)
#
# If no variable is passed in function will print to std-out
#
qs_update-os() {
  # Assigns values to INSTANCE_OSTYPE
  qs_get-ostype INSTANCE_OSTYPE
  qs_get-osversion INSTANCE_OSVERSION

  echo "[INFO] Start OS Updates"
  if [ "$INSTANCE_OSTYPE" == "amzn" ]; then
    yum update -y
  elif [ "$INSTANCE_OSTYPE" == "ubuntu" ]; then
    apt-get update -y
  elif [ "$INSTANCE_OSTYPE" == "centos" ]; then
    yum update -y
  elif [ "$INSTANCE_OSTYPE" == "sles" ]; then
    zypper -n refresh && zypper -n update
  else
    exit 1
  fi
  echo "[INFO] Finished OS Updates"
}

# Install aws-cfn-bootstrap tools
#
qs_aws-cfn-bootstrap() {
  # Assigns values to INSTANCE_OSTYPE
  qs_get-ostype INSTANCE_OSTYPE
  qs_get-osversion INSTANCE_OSVERSION

  echo "[INSTALL aws-cfn-bootstrap tools]"
  if [[ "$INSTANCE_OSTYPE" == "amzn" && ( "$INSTANCE_OSVERSION" == "2" || "$INSTANCE_OSVERSION" == "2022" ) ]]; then
    cp scripts/opt-aws.sh /etc/profile.d/
    ln -s /opt/aws/bin/cfn-* /usr/bin/
    export PATH=$PATH:/opt/aws/bin
    yum install -y python3-pip
    if [ "$INSTANCE_OSVERSION" == "2" ]; then
      alternatives --set python /usr/bin/python3
    fi
  elif [ "$INSTANCE_OSTYPE" == "ubuntu" ]; then
    apt-get -y update
    pip3 install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz
  elif [ "$INSTANCE_OSTYPE" == "centos" ]; then
    yum update -y
    qs_bootstrap_pip
    pip install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-latest.tar.gz
  elif [ "$INSTANCE_OSTYPE" == "sles" ]; then
    zypper -n refresh && zypper -n update
    pip install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz
  else
    exit 1
  fi

  if [ $(which cfn-signal) ];then
    echo "[FOUND] (cfn-signal)"
  else
    echo "[ERROR] (cfn-signal) not installed!!"
    exit 1
  fi
}

qs_err() {
  touch /var/tmp/stack_failed
  echo "[FAILED] @ $1" >>/var/tmp/stack_failed
  echo "[FAILED] @ $1"
}

qs_status() {
  if [ -f /var/tmp/stack_failed ]; then
    printf 1;
    return 1
  else
    printf 0
    return 0;
  fi
}

qs_status.clean() {
  if [ -f /var/tmp/stack_failed ]; then
    echo "clean failed state"
    rm /var/tmp/stack_failed
  else
    echo "failed state not active"
  fi
}

available_functions() {
  echo "--------------------------------"
  echo "Available quickstart_functions:
    #qs_err
    #qs_status
    #qs_get-ostype
    #qs_get-osversion
    #qs_get-python-path
    #qs_bootstrap_pip
    #qs_update-os
    #qs_enable_epel
    #qs_notty
    #qs_aws-cfn-bootstrap
    #qs_cloudwatch_tracklog
    #qs_retry_command"
  echo "--------------------------------"
}

# Install dependencies
# Assigns values to INSTANCE_
#
install_dependancies() {
  qs_get-ostype INSTANCE_OSTYPE
  qs_get-osversion INSTANCE_OSVERSION


  check_cmd() {
    if hash $1 &>/dev/null; then
      echo "[INFO] Dependencies met!"
      return 0
    else
      echo "[INFO] Installing dependencies"
      return 1
    fi
  }

  if [ "$INSTANCE_OSTYPE" == "amzn" ]; then
    check_cmd curl
    [[ $? -eq 1 ]] && yum clean all && yum install -y curl || return 0

  elif [ "$INSTANCE_OSTYPE" == "ubuntu" ]; then
    check_cmd curl
    [[ $? -eq 1 ]] && apt update && apt install -y curl || return 0

  elif [ "$INSTANCE_OSTYPE" == "centos" ]; then
    check_cmd curl
    [[ $? -eq 1 ]] && yum clean && yum install -y curl || return 0

  elif [ "$INSTANCE_OSTYPE" == "sles" ]; then
    check_cmd curl
    [[ $? -eq 1 ]] && zypper -n refresh && zypper -n install curl || return 0
  else
    echo "[FAIL] : Dependencies not satisfied!"
    exit 1
  fi
}

# $1 = NumberOfRetries $2 = Command
# qs_retry_command 10 some_command.sh
# Command will retry with linear back-off
#
qs_retry_command() {
  local -r __tries="$1"; shift
  local -r __run="$@"
  local -i __backoff_delay=2

  until $__run
  do
    if (( __current_try == __tries ))
    then
      echo "Tried $__current_try times and failed!"
      return 1
    else
      echo "Retrying ...."
      sleep $((((__backoff_delay++)) + ((__current_try++))))
    fi
  done
}

# start exec
available_functions
install_dependancies
# end exec
