#!/bin/bash
# ======================================================================
#
# haproxy functions
# parse haproxy.cfg - as good I can do with bash
#
# ----------------------------------------------------------------------
# 2021-12-14  v1.0  <axel.hahn@iml.unibe.ch>  init
# 2022-10-21  v1.1  <axel.hahn@unibe.ch>      remove grep: warning: stray \ before white space
# 2023-06-09  v1.2  <axel.hahn@unibe.ch>      take first haproxy stats port on multiple binds
# 2023-07-28  v1.3  <axel.hahn@unibe.ch>      check existance of file $HAPROXYcfgfile
# 2025-02-20  v1.4  <axel.hahn@unibe.ch>      cfgrewriter returns exitcode 0 - haproxy check do not abort when sourcing on non haproxy systems
# ======================================================================

# full path to haproxy.cfg
HAPROXYcfgfile=/etc/haproxy/haproxy.cfg

# rewritten config as tmp file to parse data
HAPROXYdumpfile=/tmp/haproxy.config

# enable caching; value 0 = off - it recreates tmp file on each run
HAPROXYdoCache=0


# TESTING ONLY
# HAPROXYcfgfile=./haproxy-01.cfg
# /TESTING


# ----------------------------------------------------------------------
#
# functions
#
# ----------------------------------------------------------------------


# ----------------------------------------------------------------------
# CONFIG REWRITER
# ----------------------------------------------------------------------


# rewrite the ha proxy config file to parse it by section
# it creates output with
# 
#    section + ":" + variable + "=" + value

# i.e.:
#    # new section: global
#    global:chroot=/var/lib/haproxy
#    global:daemon=
#    global:user=haproxy
#    global:group=haproxy
#    global:maxconn=4000
#    global:pidfile=/var/run/haproxy.pid
#    ...
function cfgrewriter(){
  local _section=
  IFS=''
  test -f $HAPROXYcfgfile && while read line
  do
    echo $line | grep "^[a-z]" >/dev/null
    if [ $? -eq 0 ]; then
        _section=$line
        echo "# new section: $_section"
    fi
    echo $line | grep "^  [a-z]*" >/dev/null
    if [ $? -eq 0 ]; then
        echo -n "$_section:"
        echo $line | sed -e "s#^ *\([a-z\-]*\) *#\1=#g"
    fi
  done < $HAPROXYcfgfile
  return 0
}

# ------------------------------------------------------------
# dump Cfg data
# ------------------------------------------------------------

# dump rewritten config
function _dumpCfg(){
  cat $HAPROXYdumpfile
}

# dump rewritten config and filter by a given section
# param  string  name of section
function _dumpSection(){
  local _section=$1
  _dumpCfg | grep "^${_section}:"
}

# ------------------------------------------------------------
# parsing functions
# ------------------------------------------------------------

# get a value from a given section
# param  string  name of variable
# param  string  name of section
function _getCfgVarFromSection(){
  local _var=$1
  local _section=$2
  _dumpSection "${_section}" | grep ":${_var}=" | cut -f 2 -d "="
}

# get sections where a given var exists
# param  string  name of variable
function getCfgSectionsOfVar(){
  local _var="$1"
  _dumpCfg | grep ":${_var}=" | cut -f 1 -d ":" | sort -u
}

# get a value from config - with autoscan of sections defaults and globals
# param  string  name of variable
# param  string  name of section
function getCfgVar(){
  local _var=$1
  local _section=$2
  test -z "$_section" && _section='.*'

  _getCfgVarFromSection      "${_var}" "${_section}"                  \
    || _getCfgVarFromSection "${_var}" "defaults"                     \
    || _getCfgVarFromSection "${_var}" "global"

}

# ----------------------------------------------------------------------
# High level functions
# ----------------------------------------------------------------------

# detect the section where variables named "stats" are defined
# to generate urls for health check and status
# WARNING: this function is UNSAFE ... it is heavy to parse
# haproxy.cfg with a bash script :-/
function detectStatsSection(){
    getCfgSectionsOfVar "stats" | tail -1
}

# get a string with base url for stats and health check
function getStatsBaseUrl(){
    # default url parts
    local proto=http
    local auth=
    local host=localhost
    local port=80
    local mysection=$( detectStatsSection )

    # read config
    port=$( getCfgVar "bind"  "${mysection}" | cut -f 2 -d ":" | head -1 )
    auth=$( getCfgVar "stats" "${mysection}" | grep "^auth" | cut -f 2- -d " " | head -1 )

    # build url
    url="$proto://"
    test -z "$auth" || url="${url}${auth}@"
    url="${url}${host}:${port}"

    echo $url
}

# get a string with a health url
function getHealthUri(){
    local uri=$( getCfgVar "monitor-uri" )
    test -z "$uri" || echo $( getStatsBaseUrl )${uri}
}

# get a string with a status page
function getStatusUri(){
    local mysection=$( detectStatsSection )
    local uri=$( getCfgVar "stats" "${mysection}" | grep "^uri " | cut -f 2- -d " " )
    test -z "$uri" || echo $( getStatsBaseUrl )${uri}
}

# ----------------------------------------------------------------------

# INIT
if [ $HAPROXYdoCache = 0 -o $HAPROXYcfgfile -nt $HAPROXYdumpfile ]; then
  cfgrewriter >$HAPROXYdumpfile
fi