#!/bin/bash # Common utility functions for K3s deployment scripts # Color codes for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Project directories SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" PROJECT_DIR="$(dirname "$SCRIPT_DIR")" STATE_FILE="$PROJECT_DIR/.deployment-state" LOG_FILE="$PROJECT_DIR/deployment.log" # Logging functions log() { local message="$1" local timestamp=$(date '+%Y-%m-%d %H:%M:%S') echo -e "${GREEN}[INFO]${NC} $message" echo "[$timestamp] [INFO] $message" >> "$LOG_FILE" } log_error() { local message="$1" local timestamp=$(date '+%Y-%m-%d %H:%M:%S') echo -e "${RED}[ERROR]${NC} $message" >&2 echo "[$timestamp] [ERROR] $message" >> "$LOG_FILE" } log_warn() { local message="$1" local timestamp=$(date '+%Y-%m-%d %H:%M:%S') echo -e "${YELLOW}[WARN]${NC} $message" echo "[$timestamp] [WARN] $message" >> "$LOG_FILE" } log_step() { local message="$1" local timestamp=$(date '+%Y-%m-%d %H:%M:%S') echo -e "${BLUE}[STEP]${NC} $message" echo "[$timestamp] [STEP] $message" >> "$LOG_FILE" } # State management functions mark_step_completed() { local step_name="$1" echo "$step_name" >> "$STATE_FILE" log "✓ Marked step as completed: $step_name" } is_step_completed() { local step_name="$1" if [ ! -f "$STATE_FILE" ]; then return 1 fi grep -q "^$step_name$" "$STATE_FILE" 2>/dev/null } reset_deployment_state() { if [ -f "$STATE_FILE" ]; then rm -f "$STATE_FILE" log "Deployment state reset" fi } # Tool checking functions check_tool() { local tool_name="$1" local install_cmd="$2" if command -v "$tool_name" &> /dev/null; then log "✓ Tool available: $tool_name" return 0 else log_warn "Tool not found: $tool_name" if [ -n "$install_cmd" ]; then log "Installing $tool_name..." if eval "$install_cmd"; then log "✓ Successfully installed: $tool_name" return 0 else log_error "Failed to install: $tool_name" return 1 fi else log_error "Please install $tool_name manually" return 1 fi fi } check_required_tools() { local all_ok=true log_step "Checking required tools..." check_tool "python3" "sudo apt update && sudo apt install -y python3" || all_ok=false check_tool "ansible" "sudo apt update && sudo apt install -y ansible" || all_ok=false check_tool "git" "sudo apt update && sudo apt install -y git" || all_ok=false if [ "$all_ok" = false ]; then log_error "Some required tools are missing" return 1 fi log "✓ All required tools are available" return 0 } # Network checking functions check_network() { local test_url="${1:-https://www.google.com}" local timeout="${2:-5}" if curl -s --max-time "$timeout" --head "$test_url" > /dev/null 2>&1; then return 0 else return 1 fi } check_network_with_retry() { local test_url="${1:-https://www.google.com}" local max_attempts="${2:-3}" local attempt=1 while [ $attempt -le $max_attempts ]; do if check_network "$test_url"; then log "✓ Network connection OK" return 0 fi log_warn "Network check failed (attempt $attempt/$max_attempts)" attempt=$((attempt + 1)) sleep 2 done log_error "Network connection failed after $max_attempts attempts" return 1 } # Retry mechanism retry() { local max_attempts="$1" local delay="$2" shift 2 local cmd="$@" local attempt=1 while [ $attempt -le $max_attempts ]; do if eval "$cmd"; then return 0 fi if [ $attempt -lt $max_attempts ]; then log_warn "Command failed (attempt $attempt/$max_attempts), retrying in ${delay}s..." sleep "$delay" fi attempt=$((attempt + 1)) done log_error "Command failed after $max_attempts attempts: $cmd" return 1 } # Configuration file validation check_config_file() { local config_file="${1:-$PROJECT_DIR/config/cluster-vars.yml}" if [ ! -f "$config_file" ]; then log_error "Configuration file not found: $config_file" log_error "Please copy config/cluster-vars.yml.example to config/cluster-vars.yml and configure it" return 1 fi log "✓ Configuration file exists: $config_file" # Check if yq is available for validation if command -v yq &> /dev/null; then if yq eval '.' "$config_file" > /dev/null 2>&1; then log "✓ Configuration file is valid YAML" else log_error "Configuration file has invalid YAML syntax" return 1 fi fi return 0 } # Kubernetes cluster checking check_kubectl() { if ! command -v kubectl &> /dev/null; then log_warn "kubectl not found, will be available after K3s installation" return 1 fi if ! kubectl cluster-info &> /dev/null; then log_warn "kubectl cannot connect to cluster" return 1 fi log "✓ kubectl is available and connected" return 0 } wait_for_pods() { local namespace="$1" local label="$2" local timeout="${3:-600}" log "Waiting for pods in namespace $namespace with label $label..." if kubectl wait --for=condition=ready pod \ -l "$label" \ -n "$namespace" \ --timeout="${timeout}s" 2>/dev/null; then log "✓ Pods are ready" return 0 else log_error "Pods failed to become ready within ${timeout}s" return 1 fi } wait_for_deployment() { local namespace="$1" local deployment="$2" local timeout="${3:-600}" log "Waiting for deployment $deployment in namespace $namespace..." if kubectl wait --for=condition=available \ --timeout="${timeout}s" \ deployment/"$deployment" \ -n "$namespace" 2>/dev/null; then log "✓ Deployment is available" return 0 else log_error "Deployment failed to become available within ${timeout}s" return 1 fi } # Download with retry download_file() { local url="$1" local output="$2" local max_attempts="${3:-3}" log "Downloading: $url" if retry "$max_attempts" 5 "curl -fsSL '$url' -o '$output'"; then log "✓ Downloaded successfully: $output" return 0 else log_error "Failed to download: $url" return 1 fi } # Install yq if not present ensure_yq() { if command -v yq &> /dev/null; then log "✓ yq is already installed" return 0 fi log "Installing yq..." local yq_url="https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64" local yq_path="/usr/local/bin/yq" if download_file "$yq_url" "/tmp/yq" 3; then sudo mv /tmp/yq "$yq_path" sudo chmod +x "$yq_path" log "✓ yq installed successfully" return 0 else log_error "Failed to install yq" return 1 fi } # Install htpasswd if not present ensure_htpasswd() { if command -v htpasswd &> /dev/null; then log "✓ htpasswd is already installed" return 0 fi log "Installing htpasswd (apache2-utils)..." if sudo apt update && sudo apt install -y apache2-utils; then log "✓ htpasswd installed successfully" return 0 else log_error "Failed to install htpasswd" return 1 fi } # Install helm if not present ensure_helm() { if command -v helm &> /dev/null; then log "✓ Helm is already installed" return 0 fi log "Installing Helm..." if retry 3 5 "curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash"; then log "✓ Helm installed successfully" return 0 else log_error "Failed to install Helm" return 1 fi } # Cleanup function for temporary files cleanup_temp_files() { local temp_dir="$1" if [ -n "$temp_dir" ] && [ -d "$temp_dir" ]; then rm -rf "$temp_dir" log "Cleaned up temporary directory: $temp_dir" fi } # Trap for cleanup on exit setup_cleanup_trap() { local temp_dir="$1" trap "cleanup_temp_files '$temp_dir'" EXIT INT TERM } # Print summary print_summary() { echo "" echo "==========================================" echo " Deployment Summary" echo "==========================================" echo "" } # Export functions for use in other scripts export -f log log_error log_warn log_step export -f mark_step_completed is_step_completed reset_deployment_state export -f check_tool check_required_tools export -f check_network check_network_with_retry export -f retry export -f check_config_file check_kubectl export -f wait_for_pods wait_for_deployment export -f download_file export -f ensure_yq ensure_htpasswd ensure_helm export -f cleanup_temp_files setup_cleanup_trap export -f print_summary