schon/scripts/lib/utils.sh
Egor fureunoir Gorbunov 7efc19e081 feat(monitoring): automate Prometheus web config generation
Remove manual password hashing and web.yml setup in favor of automated generation. Add scripts for both Unix and Windows to create `monitoring/web.yml` using credentials from `.env`.

This improves maintainability and reduces manual intervention during setup and configuration.
2026-02-22 00:17:55 +03:00

258 lines
6.1 KiB
Bash

#!/usr/bin/env bash
# Shared utilities for Unix scripts
# Provides: colors, progress indicators, interactive detection
# Detect if running in interactive shell
is_interactive() {
[[ -t 0 && -t 1 ]]
}
# Color definitions (only used in interactive mode)
if is_interactive && [[ "${NO_COLOR:-}" != "1" ]]; then
COLOR_RESET='\033[0m'
COLOR_RED='\033[0;31m'
COLOR_GREEN='\033[0;32m'
COLOR_YELLOW='\033[0;33m'
COLOR_BLUE='\033[0;34m'
COLOR_MAGENTA='\033[0;35m'
COLOR_CYAN='\033[0;36m'
COLOR_GRAY='\033[0;90m'
COLOR_BOLD='\033[1m'
else
COLOR_RESET=''
COLOR_RED=''
COLOR_GREEN=''
COLOR_YELLOW=''
COLOR_BLUE=''
COLOR_MAGENTA=''
COLOR_CYAN=''
COLOR_GRAY=''
COLOR_BOLD=''
fi
# Logging functions
log_info() {
echo -e "${COLOR_BLUE}${1}${COLOR_RESET}"
}
log_success() {
echo -e "${COLOR_GREEN}${1}${COLOR_RESET}"
}
log_warning() {
echo -e "${COLOR_YELLOW}${1}${COLOR_RESET}"
}
log_error() {
echo -e "${COLOR_RED}${1}${COLOR_RESET}" >&2
}
log_step() {
echo -e "${COLOR_MAGENTA}${1}${COLOR_RESET}"
}
log_result() {
echo -e "${COLOR_CYAN}${1}${COLOR_RESET}"
}
# Spinner animation (only for interactive shells)
SPINNER_FRAMES=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏')
SPINNER_PID=""
start_spinner() {
local message="${1:-Processing...}"
if ! is_interactive; then
echo "$message"
return
fi
{
local i=0
while true; do
printf "\r${COLOR_CYAN}${SPINNER_FRAMES[$i]}${COLOR_RESET} %s" "$message"
i=$(( (i + 1) % ${#SPINNER_FRAMES[@]} ))
sleep 0.1
done
} &
SPINNER_PID=$!
# Ensure spinner is cleaned up on script exit
trap "stop_spinner" EXIT INT TERM
}
stop_spinner() {
if [[ -n "$SPINNER_PID" ]] && kill -0 "$SPINNER_PID" 2>/dev/null; then
kill "$SPINNER_PID" 2>/dev/null
wait "$SPINNER_PID" 2>/dev/null
SPINNER_PID=""
fi
if is_interactive; then
printf "\r\033[K" # Clear the spinner line
fi
}
update_spinner_message() {
local message="${1:-Processing...}"
if ! is_interactive; then
echo "$message"
return
fi
if [[ -n "$SPINNER_PID" ]] && kill -0 "$SPINNER_PID" 2>/dev/null; then
stop_spinner
start_spinner "$message"
fi
}
# Execute command with progress indicator
run_with_progress() {
local message="$1"
shift
local cmd=("$@")
if is_interactive; then
start_spinner "$message"
local output
local exit_code
# Capture output and exit code
output=$("${cmd[@]}" 2>&1)
exit_code=$?
stop_spinner
if [[ $exit_code -eq 0 ]]; then
log_success "$message - Done!"
else
log_error "$message - Failed!"
echo "$output"
fi
return $exit_code
else
# Non-interactive: just show message and run
echo "$message"
"${cmd[@]}"
local exit_code=$?
if [[ $exit_code -eq 0 ]]; then
echo "$message - Done!"
else
echo "$message - Failed!"
fi
return $exit_code
fi
}
# Check if command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
}
# Validate system requirements
check_system_requirements() {
local min_cpu="${1:-4}"
local min_ram_gb="${2:-6}"
local min_disk_gb="${3:-20}"
log_step "Checking system requirements..."
# Check CPU cores
local cpu_count
cpu_count=$(getconf _NPROCESSORS_ONLN)
if [[ "$cpu_count" -lt "$min_cpu" ]]; then
log_error "Insufficient CPU cores: $cpu_count (minimum: $min_cpu)"
return 1
fi
# Check RAM
local total_mem_gb
if [[ -f /proc/meminfo ]]; then
local mem_kb
mem_kb=$(grep MemTotal /proc/meminfo | awk '{print $2}')
total_mem_gb=$(awk "BEGIN {printf \"%.2f\", $mem_kb/1024/1024}")
else
local total_mem_bytes
total_mem_bytes=$(sysctl -n hw.memsize 2>/dev/null || echo "0")
total_mem_gb=$(awk "BEGIN {printf \"%.2f\", $total_mem_bytes/1024/1024/1024}")
fi
if ! awk "BEGIN {exit !($total_mem_gb >= $min_ram_gb)}"; then
log_error "Insufficient RAM: ${total_mem_gb}GB (minimum: ${min_ram_gb}GB)"
return 1
fi
# Check disk space
local avail_kb free_gb
avail_kb=$(df -k . | tail -1 | awk '{print $4}')
free_gb=$(awk "BEGIN {printf \"%.2f\", $avail_kb/1024/1024}")
if ! awk "BEGIN {exit !($free_gb >= $min_disk_gb)}"; then
log_error "Insufficient disk space: ${free_gb}GB (minimum: ${min_disk_gb}GB)"
return 1
fi
log_success "System requirements met: CPU cores=$cpu_count, RAM=${total_mem_gb}GB, FreeDisk=${free_gb}GB"
return 0
}
# Generate monitoring/web.yml from PROMETHEUS_USER and PROMETHEUS_PASSWORD in .env
generate_prometheus_web_config() {
if [ ! -f .env ]; then
return 0
fi
local prom_user prom_password
prom_user=$(grep '^PROMETHEUS_USER=' .env | head -1 | cut -d= -f2- | tr -d '"')
prom_password=$(grep '^PROMETHEUS_PASSWORD=' .env | head -1 | cut -d= -f2- | tr -d '"')
if [ -z "$prom_user" ] || [ -z "$prom_password" ]; then
log_warning "PROMETHEUS_USER or PROMETHEUS_PASSWORD not set in .env, skipping web.yml generation"
return 0
fi
local raw_hash hash
raw_hash=$(docker run --rm httpd:2-alpine htpasswd -nbBC 12 "" "$prom_password" 2>/dev/null)
if [ -z "$raw_hash" ]; then
log_warning "Failed to generate Prometheus password hash"
return 0
fi
# htpasswd outputs ":$2y$..." — strip leading colon and trailing whitespace
hash="${raw_hash#:}"
hash="${hash%%[[:space:]]*}"
cat > monitoring/web.yml <<EOF
basic_auth_users:
${prom_user}: ${hash}
EOF
log_success "Prometheus web config generated"
}
# Confirm action (returns 0 for yes, 1 for no)
confirm() {
local prompt="${1:-Are you sure?}"
local response
if ! is_interactive; then
# In non-interactive mode, assume yes
return 0
fi
read -r -p "$prompt [y/N]: " response
case "$response" in
[yY][eE][sS]|[yY])
return 0
;;
*)
return 1
;;
esac
}