#!/bin/bash
set -euo pipefail
 
# =============================================================================
# Setup HAProxy for Kubernetes API
# Mode: TCP passthrough with SNI routing
# No TLS termination, no certs required
# Stats available on HTTP port 8443
# =============================================================================
 
SCRIPT_NAME=$(basename "$0")
 
usage() {
    cat << EOF
Usage: $SCRIPT_NAME [OPTIONS]
 
Setup HAProxy to load balance kube-apiserver in TCP passthrough mode.
Uses SNI to route traffic for the given hostname.
 
Options:
    --hostname HOSTNAME       DNS name for SNI (e.g. apiserver.my-cluster.dapp.test.un.sbt)
    --port PORT               kube-apiserver port (default: 6443)
    --stats-port PORT         HAProxy stats port (default: 8443)
    --controlplane LIST       Comma-separated list of control-plane IPs
    --cluster-name NAME       Cluster name (used in server names)
    --help                    Show this help
 
Example:
    sudo $SCRIPT_NAME \\
      --hostname apiserver.my-cluster.dapp.test.un.sbt \\
      --port 6443 \\
      --stats-port 8443 \\
      --controlplane <controlplane1-ip>,<controlplane2-ip>,<controlplane3-ip> \\
      --cluster-name my-cluster
EOF
}
 
# =============================================================================
# Parse arguments
# =============================================================================
 
HOSTNAME=""
API_PORT="6443"
STATS_PORT="8443"
CONTROLPLANE=""
CLUSTER_NAME=""
 
while [[ $# -gt 0 ]]; do
    case $1 in
        --hostname)
            HOSTNAME="$2"
            shift; shift
            ;;
        --port)
            API_PORT="$2"
            shift; shift
            ;;
        --stats-port)
            STATS_PORT="$2"
            shift; shift
            ;;
        --controlplane)
            CONTROLPLANE="$2"
            shift; shift
            ;;
        --cluster-name)
            CLUSTER_NAME="$2"
            shift; shift
            ;;
        --help)
            usage
            exit 0
            ;;
        *)
            echo "Error: Unknown argument $1"
            usage
            exit 1
            ;;
    esac
done
 
# Validate required args
if [[ -z "$HOSTNAME" ]]; then
    echo "Error: --hostname is required"
    usage
    exit 1
fi
 
if [[ -z "$CONTROLPLANE" ]]; then
    echo "Error: --controlplane is required"
    usage
    exit 1
fi
 
if [[ -z "$CLUSTER_NAME" ]]; then
    echo "Error: --cluster-name is required"
    usage
    exit 1
fi
 
# Split IPs by comma
IFS=',' read -r -a CP_ARRAY <<< "$CONTROLPLANE"
if [[ ${#CP_ARRAY[@]} -eq 0 ]]; then
    echo "Error: No control-plane nodes specified"
    exit 1
fi
 
# Trim whitespace
for i in "${!CP_ARRAY[@]}"; do
    CP_ARRAY[i]=$(echo "${CP_ARRAY[i]}" | xargs)
done
 
# =============================================================================
# Check root privileges
# =============================================================================
 
if [[ $EUID -ne 0 ]]; then
    echo "Error: This script must be run as root (use sudo)"
    exit 1
fi
 
echo "=== Setting up HAProxy for Kubernetes API ==="
echo "Hostname: $HOSTNAME"
echo "API Port: $API_PORT"
echo "Stats Port: $STATS_PORT"
echo "Control-plane nodes: ${CP_ARRAY[*]}"
echo "Cluster Name: $CLUSTER_NAME"
echo "Mode: TCP passthrough (no TLS termination)"
echo
 
# =============================================================================
# 1. Install HAProxy
# =============================================================================
 
echo "=== Installing HAProxy ==="
if dnf list installed haproxy &>/dev/null; then
    echo "HAProxy already installed"
else
    dnf install -y haproxy
    echo "HAProxy installed"
fi
 
# =============================================================================
# 2. Generate HAProxy config
# =============================================================================
 
HAPROXY_CFG="/etc/haproxy/haproxy.cfg"
HAPROXY_CFG_BAK="/etc/haproxy/haproxy.cfg.bak"
 
if [[ -f "$HAPROXY_CFG" ]]; then
    if [[ ! -f "$HAPROXY_CFG_BAK" ]]; then
        cp "$HAPROXY_CFG" "$HAPROXY_CFG_BAK"
        echo "Backup saved: $HAPROXY_CFG_BAK"
    fi
fi
 
echo "=== Generating HAProxy configuration ==="
 
# Backend servers
BACKEND_SERVERS=""
for i in "${!CP_ARRAY[@]}"; do
    IP=$(echo "${CP_ARRAY[i]}" | xargs)
    if ! [[ "$IP" =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}$ ]]; then
        echo "Error: Invalid IP address: $IP"
        exit 1
    fi
    printf -v INDEX "%02d" $(($i + 1))
    SERVER_NAME="dapp-cp-${INDEX}-${CLUSTER_NAME}"
    BACKEND_SERVERS+="    server $SERVER_NAME $IP:$API_PORT check fall 3 rise 2\n"
done
 
# Generate config
cat > "$HAPROXY_CFG" << EOF
#---------------------------------------------------------------------
# Global settings
#---------------------------------------------------------------------
global
    log         <local2-ip> local2
    chroot      /var/lib/haproxy
    pidfile     /var/run/haproxy.pid
    maxconn     4000
    user        haproxy
    group       haproxy
    daemon
    stats socket /var/lib/haproxy/stats
 
#---------------------------------------------------------------------
# Common defaults
#---------------------------------------------------------------------
defaults
    mode                    tcp
    log                     global
    option                  tcplog
    option                  dontlognull
    timeout connect         5s
    timeout client          30s
    timeout server          30s
    timeout check           10s
    retries                 3
 
#---------------------------------------------------------------------
# Frontend: Kubernetes API with SNI
#---------------------------------------------------------------------
frontend k8s-api-frontend
    bind *:$API_PORT
    tcp-request inspect-delay 5s
    tcp-request content accept if { req.ssl_hello_type 1 }
    acl sni_ok req.ssl_sni -i $HOSTNAME
    tcp-request content reject if !sni_ok
    use_backend k8s-api-backend if sni_ok
 
#---------------------------------------------------------------------
# Backend: Control-plane nodes
#---------------------------------------------------------------------
backend k8s-api-backend
    balance roundrobin
    option tcp-check
$(echo -e "$BACKEND_SERVERS")
 
#---------------------------------------------------------------------
# Stats page (HTTP)
#---------------------------------------------------------------------
listen stats
    bind *:$STATS_PORT
    mode http
    stats enable
    stats uri /stats
    stats realm Platform\ V\ DropApp\ Cluster\ Statistics
    stats auth admin:dapp
    stats hide-version
    stats refresh 5s
EOF
 
echo "Configuration written to $HAPROXY_CFG"
 
# =============================================================================
# 3. Validate config
# =============================================================================
 
echo "=== Validating HAProxy config ==="
haproxy -c -f "$HAPROXY_CFG"
if [[ $? -ne 0 ]]; then
    echo "Error: HAProxy configuration is invalid"
    exit 1
fi
 
# =============================================================================
# 4. Configure firewalld
# =============================================================================
 
if systemctl is-active --quiet firewalld; then
    echo "=== Configuring firewalld ==="
    for PORT in "$API_PORT" "$STATS_PORT"; do
        if ! firewall-cmd --state &>/dev/null; then
            continue
        fi
        if ! firewall-cmd --list-ports | grep -q "\b$PORT/tcp"; then
            firewall-cmd --permanent --add-port="$PORT/tcp"
            echo "Port $PORT/tcp added to firewalld"
        else
            echo "Port $PORT/tcp already open"
        fi
    done
    firewall-cmd --reload
else
    echo "firewalld is not active — skipping"
fi
 
# =============================================================================
# 5. Configure SELinux
# =============================================================================
 
if command -v getenforce &>/dev/null && [[ $(getenforce) != "Disabled" ]]; then
    echo "=== Configuring SELinux ==="
    if ! command -v semanage &>/dev/null; then
        echo "Installing policycoreutils-python-utils..."
        dnf install -y policycoreutils-python-utils
    fi
 
    for PORT in "$API_PORT" "$STATS_PORT"; do
        if ! semanage port -l | grep -q "\b$PORT\b.*http_port_t"; then
            semanage port -a -t http_port_t -p tcp "$PORT"
            echo "SELinux: port $PORT added to http_port_t"
        else
            echo "SELinux: port $PORT already allowed"
        fi
    done
else
    echo "SELinux is disabled or not installed — skipping"
fi
 
# =============================================================================
# 6. Enable and start HAProxy
# =============================================================================
 
echo "=== Enabling and starting HAProxy ==="
systemctl enable haproxy --now
if ! systemctl is-active --quiet haproxy; then
    echo "Error: HAProxy failed to start"
    journalctl -u haproxy -n 20 --no-pager
    exit 1
fi
echo "HAProxy is now running and enabled on boot"
 
# =============================================================================
# Final message
# =============================================================================
 
echo
echo "SUCCESS: HAProxy setup complete!"
echo "Mode: TCP passthrough (no TLS termination)"
echo "API endpoint: $HOSTNAME:$API_PORT"
echo "Stats page: http://<IP>:$STATS_PORT/stats"
echo "Login: admin / dapp"
echo "Realm: Platform V DropApp Cluster Statistics"
echo "Control-plane nodes: ${CP_ARRAY[*]}"
echo "Server names: dapp-cp-01-$CLUSTER_NAME, dapp-cp-02-$CLUSTER_NAME, ..."
echo "Note: Ensure DNS resolves $HOSTNAME to this machine"
