#!/bin/echo 'This is function library. To use: source $OPENSHIFT_CARTRIDGE_SDK_BASH'; exit 1

# This is a library of functions for OpenShift cartridge authors.
# To use this library add 'source $OPENSHIFT_CARTRIDGE_SDK_BASH' to your bash script.

[ ${OO_BASH_SDK:-false} == true ] && return 0
OO_BASH_SDK=true

# report message on stderr and exit with provided exit status
function error {
    echo "$1" 1>&2
    exit "$2"
}

# report message on stderr
function warning {
    echo "$1" 1>&2
}

# report text to application developer
# Argument(s):
# - Text to be displayed on success
function client_result {
    client_out "CLIENT_RESULT" "$1"
}

# report text to application developer
# Argument(s):
# - Text will always be displayed. Used to notify application developer of a transient issue.
function client_message {
    client_out "CLIENT_MESSAGE" "$1"
}

# report text to application developer as error
# Argument(s):
# - Text will be displayed when there is an error
function client_error {
    client_out "CLIENT_ERROR" "$1"
}

# report text to application developer as error in your code
# Will be displayed...
function client_internal_error {
    client_out "CLIENT_INTERNAL_ERROR" "$1"
}

# report text to application developer as debugging information
function client_debug {
    client_out "CLIENT_DEBUG" "$1"
}

# format text for reporting to application developer
#
# Argument(s):
# - type of message, will be prefix for each line in text
# - text to be processed
function client_out() {
    local type=$1
    local output=$2
    local IFS_BAK=$IFS
IFS="
"
    if [ -z "$output" ]
    then
      echo "$type: "
    else
      for line in $output
      do
          echo "$type: $line"
      done
    fi
    IFS=$IFS_BAK
}

# set application information in Broker data store
# Argument(s):
# - name of attribute to add plus value
function set_app_info {
    echo "APP_INFO: $1"
}

# set cartridge attribute in Broker data store
# Argument(s):
# - name of attribute to add plus value
function send_attr {
    echo "ATTR: $1"
}

function add_domain_ssh_key {
    echo "SSH_KEY_ADD: $1"
}

function add_app_ssh_key {
    echo "APP_SSH_KEY_ADD: $1 $2"
}

# Add environment variable visible to all gears in a domain
# Argument(s):
# - name of environment variable to add plus value
function add_domain_env_var {
    echo "ENV_VAR_ADD: $1"
}

# remove environment variable visible to all gears in application
# Argument(s):
# - name of environment variable to remove
function app_remove_env_var {
    echo "APP_ENV_VAR_REMOVE: $1"
}

function add_broker_auth_key {
    echo "BROKER_AUTH_KEY_ADD: "
}

# add cartridge data in Broker data store
# Argument(s):
# - list of cartridge datums
function cart_data {
    echo "CART_DATA: $@"
}

# add cartridge properties in Broker data store
# Argument(s):
# - list of cartridge properties
function cart_props {
    echo "CART_PROPERTIES: $@"
}

# Sets the appropriate env variable files
# Arguments:
#  - Variable to set
#  - Value
#  - Target ENV directory
function set_env_var {
  local var=$1
  local val=$2
  local target=$3

  [[ -z $target || -z $var || -z $val ]] && \
    error "Must provide a variable name, value, and target directory for environment variables" 64
  [ ! -d $target ] && \
    error "Target directory must exist for environment variables" 64

  echo "$val" >"${target}/${var}"
}

# Pad a string with random characters
# Arguments:
#  - String to pad
#  - Desired length
#  - Pattern to pad with (optional)
function pad_string {
  local str=$1
  local len=$2
  local pattern=$3

  local remain=$(( $len - ${#str} ))
  if [ "$remain" -ge 1 ]
  then
    local rnstr=$(random_string $remain $pattern)
    str="${str}${rnstr}"
  fi
  echo $str
}

# Generate a password
# Arguments:
#  - Desired length (optional)
#  - Character space (optional)
#  - Ignore pattern (optional)
function generate_password {
  local DEFAULT_LEN=12
  local DEFAULT_CHAR="a-np-zA-NP-Z1-9-_" #Dash, underscore, Alphanumeric except o,O,0
  local DEFAULT_IGNORE="^-"

  echo $(random_string ${1-$DEFAULT_LEN} ${2-$DEFAULT_CHAR} ${3-$DEFAULT_IGNORE})
}

# Generate a username and pad it to a certain length
# Arguments:
#  - Username (optional)
#  - Desired length (optional)
#  - Pad characters (optional)
function generate_username {
  local DEFAULT_USERNAME='admin'
  local DEFAULT_LEN=12
  local DEFAULT_CHAR="a-np-zA-NP-Z1-9" #Alphanumeric except o,O,0

  echo $(pad_string ${1-$DEFAULT_USERNAME} ${2-$DEFAULT_LEN} ${3-$DEFAULT_CHAR})
}

function web_gears {
  oo-gear-registry web
}

function proxy_gears {
  oo-gear-registry proxy
}

function all_gears {
  oo-gear-registry all
}

function wait_for_pid_file {
  [ -f "$1" ] && return 0
  for i in {1..20}; do
    sleep .5
    [ -f "$1" ] && break;
  done
}

# wait up to 30 seconds for given process to stop
# Argument(s):
#  - process id to wait on
function wait_for_stop {
    local pid=$1
    for i in {1..60}
    do
        if `ps --pid $pid > /dev/null 2>&1`
        then
            echo "Waiting for stop to finish"
            sleep .5
        else
            break
        fi
    done
}

# report processing running for given user uid.
# Argument(s):
# - user uid    Do not use user login name, all numeric user login names will break ps
function print_user_running_processes {
    local userid=$1
    echo ""
    echo "Running Processes:"
    echo ""
    ps -FCvx -U "${userid}"
    echo ""
}

# Check is a process is running
# Arguments:
#  - Process name
#  - Pidfile
#  - UID to check (optional)
function process_running {
  local process=$1
  local pidfile=$2
  local uid=${3-`id -u`}

  # Check the pidfile for a running process
  {
    if [ -f $pidfile ]; then
      local error=$(pgrep -F $pidfile 2>&1)
      # pgrep returns 0 with an invalid pidfile, so we need to check the output
      [ $? ] && ! [[ "${error}" =~ 'pidfile not valid' ]] && return 0
    fi
  }

  # Check pgrep for the process name and user id
  {
    $(pgrep -x ${process} -u ${uid} > /dev/null 2>&1) && return 0
  }
  return 1
}

function pid_is_httpd() {
    ps -p "$1" 2> /dev/null | grep httpd > /dev/null
}

function killall_matching_httpds() {
   [ -z "$1" ]  &&  return 1
   ps -u `id -u` -o pid,command | grep "$1" | grep -v "grep" |    \
       awk '{ print $1 }' |  xargs kill -9  > /dev/null 2>&1  || :
}

# Attempt to resurrect the Apache PID file if its corrupt.
#  Caution: there may be multiple Apache processes on the gear.
function ensure_valid_httpd_pid_file() {
    local pid_file="$1"
    local cfg_file="$2"

    local force_rebuild=""
    local pid_contents=""
    local pid_regex='^[1-9][0-9]+$'
    local real_pid=""

    if [ -e "$pid_file" ]
    then
        # Is it a file, owned by me and readable?
        if ! [ -f "$pid_file" -a -O "$pid_file" -a -r "$pid_file" ]
        then
            force_rebuild="true"
        else
            # The pid file must contain one and only one number, nothing else or be blank.
            pid_contents=$(cat "$pid_file" 2>/dev/null || :)
            if ! [[ "$pid_contents" =~ $pid_regex ]]
            then
                force_rebuild="true"
            fi
        fi
    fi

    # If we are very lucky, we can find a valid PID to replace corrupt data.
    if [ -n "$force_rebuild" ]
    then
        rm -rf "$pid_file"  # Could be a corrupt inode, a dir, symlink, context or ownership problems, etc...
        if [ "$cfg_file" ]
        then
            real_pid=$(pgrep -o -u `id -u` -f -- "-f $cfg_file" )
            if [ -n "$real_pid" ]
            then
                echo "$real_pid" > "$pid_file"
            fi
        fi
    fi
}

function ensure_valid_httpd_process() {
    # $1 == pidfile.
    # $2 == httpd config file.
    ensure_valid_httpd_pid_file "$1" "$2"
    [ -n "$1" ]  &&  [ -f "$1" ]  &&  pid_is_httpd `cat "$1"`  &&  return 0
    [ -n "$1" ]  &&  rm -f "$1"
    killall_matching_httpds "$2"
}

# application developer has requested a hot deploy, 0 == true, 1 == false
function hot_deploy_marker_is_present() {
    hot_deploy_enabled_for_latest_deployment
}

# return 'start' instead of restart if the HTTPD pid file is corrupted and the
# HTTPD is not running
function ensure_httpd_restart_succeed() {
    local pid_file="$1"
    local cfg_file="$2"

    if process_running 'httpd' $pid_file; then
      ensure_valid_httpd_pid_file "$pid_file" "$cfg_file"
    else
      rm -f $pid_file 2>/dev/null
    fi
}

# report the primary cartridge name for this gear
function primary_cartridge_name() {
  awk -F: '{printf "%s-%s-%s", $1, $2, $3}' $OPENSHIFT_PRIMARY_CARTRIDGE_DIR/env/OPENSHIFT_*_IDENT
}

# Returns 0 if the named marker $1 exists, otherwise 1.
function marker_present() {
  [ -f "${OPENSHIFT_REPO_DIR}/.openshift/markers/$1" ]
}

# Add element(s) to end of path
#
# $1 path
# $2 element(s) to add
# return modified path
function path_append {
    local canon="$(path_remove $1 $2)"
    echo -n "${canon:+"$canon:"}$2"
}

# Add element(s) to front of path
#
# $1 path
# $2 element(s) to add
# return modified path
function path_prepend {
    local canon="$(path_remove $1 $2)"
    echo -n "$2${canon:+":$canon"}"
}

# Remove element(s) from path
#
# $1 path
# $2 element(s) to remove
# return modified path
function path_remove {
  local results="$1"
  for e in $(echo -n "$2" | sed -e 's/:/ /g'); do
    results=$(echo -n "$results" | awk -v RS=: -v ORS=: '$0 != "'$e'"' | sed 's/:$//')
  done
  echo -n "$results"
}

# Update the PassEnv directives in the httpd configuration file
#
# $1 full path to httpd.conf file
function update_httpd_passenv {
  [ -f $1 ] || (client_error "HTTP Configuration for PassEnv failed: $1 missing" && return)

  updated=$(mktemp)
  ( sed <$1 -e '/^PassEnv /d'
    for key in $(env |cut -d= -f1)
    do
      # Exclude these three variables as Apache unset them
      [[ $key == 'SHELL' ]] && continue
      [[ $key == 'USER' ]] && continue
      [[ $key == 'LOGNAME' ]] && continue
      if [[ $key =~ ^[A-Z].* ]]
      then
        echo PassEnv $key
      fi
    done ) >$updated

  if [ -s $updated ]
  then
    cat $updated >$1
  else
    client_error "HTTP Configuration for PassEnv failed: update corrupted for $1, using old configuration"
  fi
  rm -f $updated
}

# Write the PassEnv directives into the httpd configuration file
#
# $1 full path to PassEnv.conf file
function write_httpd_passenv {
  ( for key in $(env |cut -d= -f1)
    do
      if [[ $key =~ ^[A-Z].* ]]
      then
        echo PassEnv $key
      fi
    done ) >$1
}

# Returns 1 if we have a web_proxy cartridge installed
function has_web_proxy() {
    has_web_proxy_cart=0
    for manifest in ${HOME}/*/metadata/manifest.yml; do
        check_for_web_proxy=$(awk '/:$/ {section=$1} /^- web_proxy/ {if (section == "Categories:") print 1}' $manifest)
        if  [ "$check_for_web_proxy" == "1" ]; then
            return 0
        fi
    done
    return 1
}


# Wrapper for the OpenShift Ruby SDK
#
function ruby_sdk() {
  oo-ruby -I/usr/lib/openshift/cartridge_sdk -rruby/sdk -e "include OpenShift::CartridgeSdk; puts $1"
}

function primary_cartridge_short_name() {
  ruby_sdk "primary_cartridge_manifest['Cartridge-Short-Name']"
}

function primary_cartridge_private_ip_name() {
  ruby_sdk 'primary_cartridge_manifest["Endpoints"][0]["Private-IP-Name"]'
}

function primary_cartridge_private_port_name() {
  ruby_sdk 'primary_cartridge_manifest["Endpoints"][0]["Private-Port-Name"]'
}

function app_web_to_proxy_ratio_and_colocated_gears() {
  ruby_sdk 'app_web_to_proxy_ratio_and_colocated_gears'
}

function hot_deploy_enabled_for_latest_deployment() {
  enabled=$(ruby_sdk 'latest_deployment_metadata.hot_deploy')

  if [ "$enabled" == "true" ]; then
    return 0
  else
    return 1
  fi
}

function force_clean_build_enabled_for_latest_deployment() {
  enabled=$(ruby_sdk 'latest_deployment_metadata.force_clean_build')

  if [ "$enabled" == "true" ]; then
    return 0
  else
    return 1
  fi
}

# Generate a random string from /dev/urandom
# Arguments:
#  - Desired length (optional)
#  - Possible character space (optional)
#  - Patterns to omit (optional)
function random_string {
  local DEFAULT_SPACE="a-zA-Z0-9"
  local DEFAULT_LEN=12

  local len=${1-$DEFAULT_LEN}
  local space=${2-"${DEFAULT_SPACE}"}
  local omit=${3-""}

  local rnd=$(head -n 50 /dev/urandom | tr -dc $space | fold -w $len)
  [ -n "${omit}" ] && rnd=$(echo "${rnd}" | grep -v "${omit}")
  echo $(echo "${rnd}" | head -n1)

}
