Files
hops/lib/common.sh
T
Stephen Klein 721f7d7a75 Release HOPS v3.1.0 with major security and architecture improvements
🆕 New Features:
- Encrypted secret management with AES-256 encryption
- Privilege separation (root vs user operations)
- Comprehensive input validation and sanitization
- Pinned container versions for security
- Modular architecture with shared libraries

🔒 Security Enhancements:
- Encrypted .env file storage with master key management
- Input validation preventing injection attacks
- Secure password generation with complexity requirements
- Enhanced file permissions and ownership handling
- Security auditing capabilities

🏗️ Architecture Improvements:
- Shared library structure (lib/) for common functions
- Enhanced error handling with detailed context
- Improved service definitions with validation
- Standardized logging and UI components
- Better code organization and maintainability

📝 New Components:
- hops_install.sh: New secure installation wrapper
- hops_privileged_setup.sh: Root-only operations
- hops_user_operations.sh: User operations without sudo
- hops_service_definitions_improved.sh: Enhanced service generation
- lib/: Shared libraries for common functionality
- CLAUDE.md: Complete development documentation

🔧 User Experience:
- Multiple installation methods (new secure, manual, legacy)
- Better error messages and troubleshooting guidance
- Improved service management commands
- Enhanced documentation and help system

This release maintains backward compatibility while adding enterprise-grade security features.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-17 07:02:43 -04:00

251 lines
5.3 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# HOPS - Common Utility Functions
# Shared functions for logging, error handling, and UI
# Version: 3.1.0
# Color codes for output
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly BLUE='\033[0;34m'
readonly PURPLE='\033[0;35m'
readonly CYAN='\033[0;36m'
readonly WHITE='\033[1;37m'
readonly NC='\033[0m' # No Color
# Global variables (set by setup_logging)
LOG_DIR=""
LOG_FILE=""
# Initialize logging system
setup_logging() {
local log_prefix="$1"
if [[ -z "$log_prefix" ]]; then
echo "ERROR: setup_logging requires a log prefix" >&2
return 1
fi
LOG_DIR="/var/log/hops"
LOG_FILE="$LOG_DIR/${log_prefix}-$(date +%Y%m%d-%H%M%S).log"
if [[ $EUID -eq 0 ]]; then
mkdir -p "$LOG_DIR"
touch "$LOG_FILE"
else
echo "WARNING: Not running as root, logging to console only" >&2
fi
}
# Unified logging function
log() {
local message="$1"
local timestamp="$(date '+%Y-%m-%d %T')"
# Write to log file if available
if [[ -n "$LOG_FILE" && -w "$LOG_FILE" ]]; then
echo "$timestamp - $message" >> "$LOG_FILE"
fi
# Always output to console
echo -e "$message"
}
# Error handling with exit
error_exit() {
log "${RED}❌ ERROR: $1${NC}"
if [[ -n "$LOG_FILE" ]]; then
log "${RED}❌ Operation failed. Check logs at: $LOG_FILE${NC}"
fi
exit 1
}
# Warning function
warning() {
log "${YELLOW}⚠️ WARNING: $1${NC}"
}
# Success function
success() {
log "${GREEN}$1${NC}"
}
# Info function
info() {
log "${BLUE}$1${NC}"
}
# Debug function (only shows if DEBUG=1)
debug() {
if [[ "${DEBUG:-0}" == "1" ]]; then
log "${PURPLE}🐛 DEBUG: $1${NC}"
fi
}
# Show HOPS header
show_hops_header() {
local version="$1"
local subtitle="$2"
if [[ -z "$version" ]]; then
version="3.1.0"
fi
clear
cat << "EOF"
_ _ ____ ____ ____
| | | || _ \| _ \/ ___|
| |__| || |_) | |_) \___ \
| __ || __/| __/ ___) |
|_| |_||_| |_| |____/
EOF
echo -e "${CYAN}🚀 Homelab Orchestration Provisioning Script v${version}${NC}"
if [[ -n "$subtitle" ]]; then
echo -e "${WHITE}${subtitle}${NC}"
fi
echo -e "${WHITE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}\n"
}
# Progress indicator
show_progress() {
local current="$1"
local total="$2"
local message="$3"
local width=50
local percentage=$((current * 100 / total))
local filled=$((current * width / total))
local empty=$((width - filled))
printf "\r${BLUE}[${NC}"
printf "%${filled}s" | tr ' ' '='
printf "%${empty}s" | tr ' ' '-'
printf "${BLUE}] %3d%% %s${NC}" "$percentage" "$message"
if [[ $current -eq $total ]]; then
echo
fi
}
# Confirmation prompt
confirm() {
local message="$1"
local default="${2:-n}"
local prompt
case "$default" in
[Yy]|[Yy][Ee][Ss]) prompt="[Y/n]" ;;
[Nn]|[Nn][Oo]) prompt="[y/N]" ;;
*) prompt="[y/n]" ;;
esac
while true; do
read -r -p "${message} ${prompt}: " response
# Use default if empty response
if [[ -z "$response" ]]; then
response="$default"
fi
case "$response" in
[Yy]|[Yy][Ee][Ss]) return 0 ;;
[Nn]|[Nn][Oo]) return 1 ;;
*) echo "Please answer yes or no." ;;
esac
done
}
# Spinner for long operations
spinner() {
local pid=$1
local message="$2"
local spin='-\|/'
local i=0
while kill -0 "$pid" 2>/dev/null; do
printf "\r${BLUE}%s %s${NC}" "${spin:i++%${#spin}:1}" "$message"
sleep 0.1
done
printf "\r"
}
# Check if running as root
check_root() {
if [[ $EUID -ne 0 ]]; then
error_exit "This script must be run as root or with sudo."
fi
}
# Check if command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
}
# Wait for user input
pause() {
local message="${1:-Press any key to continue...}"
read -n 1 -s -r -p "$message"
echo
}
# Format bytes to human readable
format_bytes() {
local bytes=$1
local units=("B" "KB" "MB" "GB" "TB")
local unit=0
while [[ $bytes -gt 1024 && $unit -lt 4 ]]; do
bytes=$((bytes / 1024))
((unit++))
done
echo "${bytes}${units[$unit]}"
}
# Check if string is a valid IP address
is_valid_ip() {
local ip=$1
local regex="^([0-9]{1,3}\.){3}[0-9]{1,3}$"
if [[ $ip =~ $regex ]]; then
local IFS='.'
local -a octets=($ip)
for octet in "${octets[@]}"; do
if [[ $octet -gt 255 ]]; then
return 1
fi
done
return 0
fi
return 1
}
# Check if port is available
is_port_available() {
local port=$1
! ss -tuln | grep -q ":$port "
}
# Get available port starting from given port
get_available_port() {
local start_port=$1
local port=$start_port
while ! is_port_available "$port"; do
((port++))
if [[ $port -gt 65535 ]]; then
error_exit "No available ports found starting from $start_port"
fi
done
echo "$port"
}