Recent Public Pastes

Alpine Linux automatic updates script (DRAFT)

Author: CRC

19d, 18h, 8m and 46s ago

URL: https://lamp.crc.id.au/paste/fa5D2dCa1be57f2CCA946Ee8bF (raw)

Pasted: 19d, 18h, 8m and 46s ago

Expires: 10d, 5h, 51m and 14s from now

Language: Bash

Author: CRC

#!/bin/bash
set -o pipefail

# Alpine Linux Automatic Update Script
# ====================================

# Configuration
LOG_FILE="/var/log/alpine-auto-update.log"          # Path to the update log file
BACKUP_DIR="/var/backups/pre-update"                # Directory for pre-update backups
ADMIN_EMAIL="admin@example.com"                     # Email address for update notifications
SMTP_SERVER="smtp://mail.server.com"                # SMTP server for sending notifications
REBOOT_AFTER_KERNEL_UPDATE=1                        # Set to 0 to skip rebooting after kernel updates
MAX_START_DELAY=1800                                # Maximum random delay in seconds before starting (0 to disable)

# Internal state
UPDATED_PACKAGES=""
REBOOT_REQUIRED=0

# Logging function
log_message() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}

# Create backup before update
create_backup() {
    local today
    today=$(date +%Y%m%d)

    log_message "Creating system backup before update"
    mkdir -p "$BACKUP_DIR/$today"

    # Backup critical configurations
    if ! tar -czf "$BACKUP_DIR/$today/etc-backup.tar.gz" /etc/ 2>/dev/null; then
        log_message "ERROR: Failed to backup /etc"
        return 1
    fi

    # Backup package list
    if ! apk info -v > "$BACKUP_DIR/$today/installed-packages.txt"; then
        log_message "ERROR: Failed to backup package list"
        return 1
    fi

    # Remove backups older than 30 days
    find "$BACKUP_DIR" -mindepth 1 -type d -mtime +30 -exec rm -rf {} \; 2>/dev/null
    log_message "Removed backups older than 30 days"

    log_message "Backup completed"
}

# Update repository lists
update_repositories() {
    log_message "Updating repository lists"

    if apk update; then
        log_message "Repository update successful"
        return 0
    else
        log_message "ERROR: Repository update failed"
        return 1
    fi
}

# Check for available updates
check_updates() {
    log_message "Checking for available updates"

    UPDATES=$(apk list -u 2>/dev/null | grep -c "upgradable")
    log_message "Found $UPDATES available updates"

    if [ "$UPDATES" -eq 0 ]then
        log_message "No updates available"
        return 1
    fi

    return 0
}

# Install updates
install_updates() {
    log_message "Installing available updates"

    # Capture the list of packages to be updated
    UPDATED_PACKAGES=$(apk list -u 2>/dev/null)
    echo "$UPDATED_PACKAGES" | tee -a "$LOG_FILE"

    if apk upgrade; then
        log_message "Updates installed successfully"

        # Check if reboot is required (kernel update)
        if echo "$UPDATED_PACKAGES" | grep -qE "linux-(virt|lts|edge|hardened)"; then
            log_message "Kernel update detected - reboot required"
            REBOOT_REQUIRED=1
        fi

        return 0
    else
        log_message "ERROR: Update installation failed"
        return 1
    fi
}

# Clean up old packages
cleanup_cache() {
    log_message "Cleaning up package cache"

    apk cache clean 2>/dev/null

    # Remove old backups (keep last 7 days)
    find "$BACKUP_DIR" -mindepth 1 -type d -mtime +7 -exec rm -rf {} \; 2>/dev/null

    log_message "Cleanup completed"
}

# Send notification email (requires s-nail, no local MTA needed)
send_notification() {
    local status="$1"
    local message="$2"

    if [ -n "$ADMIN_EMAIL" ]then
        if ! command -v s-nail >/dev/null 2>&1; then
            log_message "s-nail not found, installing"
            apk add s-nail
        fi
        if command -v s-nail >/dev/null 2>&1; then
            echo "$message" | s-nail -S smtp="$SMTP_SERVER" \
                -S smtp-use-starttls \
                -s "Alpine Auto-Update: $status" "$ADMIN_EMAIL"
        else
            log_message "ERROR: Failed to install s-nail, cannot send notification"
        fi
    fi
}

# Main update process
main() {
    # Random delay to stagger updates across multiple systems (only when run from cron)
    if [ "$MAX_START_DELAY" -gt 0 ] && ps -o comm= -p $PPID 2>/dev/null | grep -q crond; then
        local delay=$((RANDOM % MAX_START_DELAY))
        log_message "Delaying start by ${delay} seconds (max ${MAX_START_DELAY}s)"
        sleep "$delay"
    fi

    log_message "=== Starting automatic update process ==="

    # Create backup
    if ! create_backup; then
        log_message "ERROR: Backup failed, aborting update"
        send_notification "FAILED" "Pre-update backup failed, update aborted"
        exit 1
    fi

    # Update repositories (also serves as connectivity check)
    if ! update_repositories; then
        send_notification "FAILED" "Repository update failed (check network connectivity)"
        exit 1
    fi

    # Check for updates
    if ! check_updates; then
        log_message "No updates to install"
        exit 0
    fi

    # Install updates
    if install_updates; then
        cleanup_cache

        local email_body="Packages updated:
$UPDATED_PACKAGES

Updates installed successfully."

        # Reboot if kernel was updated and rebooting is enabled
        if [ "$REBOOT_REQUIRED" -eq 1 ]then
            if [ "$REBOOT_AFTER_KERNEL_UPDATE" -eq 1 ]then
                log_message "Rebooting system due to kernel update"
                email_body="$email_body

NOTE: System is rebooting due to kernel update."
            else
                log_message "Kernel update detected but REBOOT_AFTER_KERNEL_UPDATE is disabled, skipping reboot"
                email_body="$email_body

NOTE: Kernel was updated but automatic reboot is disabled. A manual reboot is required."
            fi
        fi

        send_notification "SUCCESS" "$email_body"
        log_message "=== Update process completed successfully ==="

        if [ "$REBOOT_REQUIRED" -eq 1 ] && [ "$REBOOT_AFTER_KERNEL_UPDATE" -eq 1 ]then
            sleep 30
            reboot
        fi
    else
        send_notification "FAILED" "Packages pending:
$UPDATED_PACKAGES

Update installation failed."
        log_message "=== Update process failed ==="
        exit 1
    fi
}

# Run main function
main "$@"