Skip to content

Instantly share code, notes, and snippets.

@stepw66
Forked from siteslave/backup.sh
Created November 6, 2024 13:28
Show Gist options
  • Select an option

  • Save stepw66/a63c857a1747be51cd6b57a139361ce5 to your computer and use it in GitHub Desktop.

Select an option

Save stepw66/a63c857a1747be51cd6b57a139361ce5 to your computer and use it in GitHub Desktop.

Revisions

  1. @siteslave siteslave revised this gist Nov 6, 2024. No changes.
  2. @siteslave siteslave created this gist Nov 6, 2024.
    211 changes: 211 additions & 0 deletions backup.sh
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,211 @@
    #!/bin/bash

    # Author: GitHub Copilot (adapted by Satit Rianpit)
    # Date: October 2024
    # Description: This script performs the dumping of HOSxP database tables.
    # Usage: ./backup.sh -f /path/to/config_file
    # Parameters:
    # - -f: Specifies the configuration file to use.
    # Returns: 0 if successful, 1 if an error occurs.
    # Notes: This script requires the 'zip', 'openssl' and 'mysqldump' commands to be installed.

    # Get configuration file path from command line argument
    # This script processes command-line options using getopts.
    # It currently supports the following options:
    # -f <file> : Specifies the configuration file to use.
    # Parse command line arguments

    # Example usage: ./backup.sh -f /path/to/config_file

    while getopts ":f:" opt; do
    case $opt in
    f)
    CONFIG_FILE="$OPTARG"
    ;;
    \?)
    echo "Invalid option: -$OPTARG" >&2
    exit 1
    ;;
    :)
    echo "Option -$OPTARG requires an argument." >&2
    exit 1
    ;;
    esac
    done

    # Check if the configuration file is provided
    if [ -z "$CONFIG_FILE" ]; then
    echo "Usage: $0 -f /path/to/config_file"
    exit 1
    fi

    # Source the configuration file
    source "$CONFIG_FILE"

    # Configuration variables for the backup script
    #
    # DB_HOST: The hostname of the database server. Default is "localhost".
    # DB_USER: The username to connect to the database. Default is "sa".
    # DB_NAME: The name of the database to back up. Default is "hos".
    # DB_PASS: The password to connect to the database. Default is "sa".
    # BACKUP_DIR: The directory where backups will be stored. Default is "/tmp/backup".
    # SKIP_LOG_TABLES: Whether to skip log tables during backup. Default is "Y".
    # THREADS: The number of threads to use for the backup process. Default is 8.
    # FAILED_TABLES_FILE: The file to log tables that failed to back up. Default is "failed_tables.log".
    # TELEGRAM_BOT_TOKEN: The token for the Telegram bot to send notifications. Default is "xxxxxx".
    # TELEGRAM_CHAT_ID: The chat ID for the Telegram bot to send notifications. Default is "-123456".
    #
    # Example configuration:
    # DB_HOST="db.example.com"
    # DB_USER="admin"
    # DB_NAME="production_db"
    # DB_PASS="securepassword"
    # BACKUP_DIR="/var/backups"
    # SKIP_LOG_TABLES="N"
    # THREADS=4
    # FAILED_TABLES_FILE="/var/log/failed_tables.log"
    # TELEGRAM_BOT_TOKEN="your_telegram_bot_token"
    # TELEGRAM_CHAT_ID="your_telegram_chat_id"

    : "${DB_HOST:-"localhost"}"
    : "${DB_USER:-"sa"}"
    : "${DB_NAME:-"hos"}"
    : "${DB_PASS:-"sa"}"
    : "${BACKUP_DIR:-"/tmp/backup"}"
    : "${SKIP_LOG_TABLES:-"Y"}"
    : "${THREADS:-8}"
    : "${FAILED_TABLES_FILE:-"failed_tables.log"}"
    : "${TELEGRAM_BOT_TOKEN:-"xxxxxx"}"
    : "${TELEGRAM_CHAT_ID:-"-123456"}"

    DATE=$(date +%Y%m%d_%H%M%S)
    TARGET_DIR="$BACKUP_DIR/dump_$DATE"

    # Generate a random password for the zip file
    ZIP_PASSWORD=$(openssl rand -base64 12 | tr -dc 'a-zA-Z0-9' | fold -w 12 | head -n 1)

    # Create backup directory
    mkdir -p "$TARGET_DIR"
    cd "$TARGET_DIR"

    # Get list of tables, optionally skipping log tables
    if [ "$SKIP_LOG_TABLES" = "Y" ]; then
    mysql -h$DB_HOST -u$DB_USER -p$DB_PASS $DB_NAME -N -e "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'" | awk '{print $1}' | grep -vE '_log$|^ksklog$' > tables.txt
    else
    mysql -h$DB_HOST -u$DB_USER -p$DB_PASS $DB_NAME -N -e "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'" | awk '{print $1}' > tables.txt
    fi

    # This script sets options for the mysqldump command used in the backup process.
    #
    # MYSQLDUMP_OPTS:
    # --single-transaction: Ensures a consistent backup by dumping all tables in a single transaction.
    # --quick: Fetches rows from the server one at a time, reducing memory usage.
    # --lock-tables=false: Prevents locking of tables during the dump process.
    # --add-drop-table: Adds a DROP TABLE statement before each CREATE TABLE statement in the dump.
    # --skip-events: Excludes events from the dump.
    # --skip-triggers: Excludes triggers from the dump.
    # --skip-routines: Excludes stored procedures and functions from the dump.
    MYSQLDUMP_OPTS="--single-transaction --quick --lock-tables=false --add-drop-table --skip-events --skip-triggers --skip-routines"

    # Function to dump and gzip a table
    dump_table() {
    local table=$1
    echo "Dumping table: $table"
    # This script performs a MySQL database table backup using mysqldump.
    # It connects to the MySQL server using the provided host, user, and password.
    # The backup is compressed using gzip and saved with the table name as the filename.
    # Variables:
    # DB_HOST - The hostname of the MySQL server.
    # DB_USER - The username to connect to the MySQL server.
    # DB_PASS - The password to connect to the MySQL server.
    # MYSQLDUMP_OPTS - Additional options for mysqldump.
    # DB_NAME - The name of the database to back up.
    # table - The name of the table to back up.
    mysqldump -h$DB_HOST -u$DB_USER -p$DB_PASS $MYSQLDUMP_OPTS $DB_NAME $table | gzip > "$table.sql.gz"
    # This script checks the exit status of the previous command.
    # If the exit status is 0 (indicating success), it prints a success message for the specified table.
    # If the exit status is non-zero (indicating failure), it prints an error message to stderr,
    # logs the failed table name to a specified file, and exits the script with a status of 1.
    if [ $? -eq 0 ]; then
    echo "Successfully dumped $table"
    else
    echo "Error dumping $table" >&2
    echo $table >> "$TARGET_DIR/$FAILED_TABLES_FILE"
    exit 1
    fi
    }

    # Export the dump_table function and necessary variables for parallel execution
    export -f dump_table
    export DB_HOST DB_USER DB_PASS DB_NAME TARGET_DIR FAILED_TABLES_FILE MYSQLDUMP_OPTS

    # Start time
    START_TIME=$(date +%s)
    START_TIME_FORMATTED=$(date +"%Y-%m-%d %H:%M:%S")

    # This script reads table names from the file 'tables.txt' and processes each table name in parallel.
    # The number of parallel processes is determined by the value of the THREADS variable.
    # For each table name, the script calls the 'dump_table' function.
    # The 'xargs' command is used to handle the parallel processing and argument passing.
    # -n1: Use one argument per command line.
    # -P$THREADS: Run up to $THREADS processes at a time.
    # -I{}: Replace occurrences of {} in the command with the argument read from 'tables.txt'.
    cat tables.txt | xargs -n1 -P$THREADS -I{} bash -c 'dump_table "$@"' _ {}

    # Check if dumps were successful
    if [ $? -eq 0 ]; then
    # Zip the backup folder and password protect it
    zip -r -e -P "$ZIP_PASSWORD" "$DB_NAME-$DATE.zip" .
    if [ $? -eq 0 ]; then
    echo "Successfully zipped and password protected the backup folder"
    rm -f *.sql.gz

    # End time
    END_TIME=$(date +%s)
    END_TIME_FORMATTED=$(date +"%Y-%m-%d %H:%M:%S")
    ELAPSED_TIME=$((END_TIME - START_TIME))

    # Format elapsed time in HH:MM:SS
    HOURS=$((ELAPSED_TIME / 3600))
    MINUTES=$(( (ELAPSED_TIME % 3600) / 60 ))
    SECONDS=$((ELAPSED_TIME % 60))
    printf "Backup completed successfully in %02d:%02d:%02d\n" $HOURS $MINUTES $SECONDS

    # Send telegram message
    if [ -n "$TELEGRAM_BOT_TOKEN" ] && [ -n "$TELEGRAM_CHAT_ID" ]; then
    # Get the size of the backup file in a human-readable format and store it in the FILE_SIZE variable.
    # The backup file is named using the database name and the current date, followed by a .zip extension.
    # The 'du -sh' command is used to get the size, and 'cut -f1' extracts the size value.
    FILE_SIZE=$(du -sh "$DB_NAME-$DATE.zip" | cut -f1)

    # Construct the message to be sent via Telegram
    # The message includes the start and end times of the backup, the duration, the backup file name,
    # the file size, any failed tables, and the zip password.
    MESSAGE="Backup started at $START_TIME_FORMATTED and finished at $END_TIME_FORMATTED. Completed successfully in $HOURS hours, $MINUTES minutes, and $SECONDS seconds \nBackup file: $DB_NAME-$DATE.zip \nFile size: $FILE_SIZE \nFailed tables: $(cat "$TARGET_DIR/$FAILED_TABLES_FILE" 2>/dev/null) \nZip password: $ZIP_PASSWORD"

    # This script sends a message to a specified Telegram chat using the Telegram Bot API.
    # It makes a POST request to the sendMessage endpoint with the chat ID and message text.
    # The TELEGRAM_BOT_TOKEN environment variable should contain the bot token.
    # The TELEGRAM_CHAT_ID environment variable should contain the chat ID.
    # The MESSAGE environment variable should contain the message text to be sent.
    # The output of the curl command is redirected to /dev/null to suppress any output.
    curl -s -X POST "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/sendMessage" \
    -H "Content-Type: application/json" \
    -d "{\"chat_id\": \"$TELEGRAM_CHAT_ID\", \"text\": \"$MESSAGE\"}" > /dev/null 2>&1

    # Check the exit status of the previous command.
    # If the exit status is not equal to 0 (indicating an error),
    # write the zip password to a file named "zip_password_<DB_NAME>_<DATE>.txt"
    # in the user's home directory.
    if [ $? -ne 0 ]; then
    echo "Zip password: $ZIP_PASSWORD" > "$HOME/zip_password_${DB_NAME}_$DATE.txt"
    fi
    fi
    else
    echo "Error zipping the backup folder" >&2
    exit 1
    fi
    else
    echo "Backup failed" >&2
    exit 1
    fi