diff --git a/Dockerfile b/Dockerfile index eadb71e5..a547e96d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,14 +7,14 @@ RUN apk add bash rsync tzdata dos2unix jq # Copy all necessary files into the container (from /pkg in the repository to /app in the container) COPY pkg app -# Move the entrypoint script to the root directory for ease of access -RUN mv app/entry.sh /entry.sh +# Make the entire /app folder executable +RUN chmod +x /app -# Make the script executable -RUN chmod +x app/backup.sh && dos2unix app/backup.sh +# Make the all files in the /app folder Unix format +RUN find /app -type f -print0 | xargs -0 dos2unix -# Make the entry script executable -RUN chmod +x entry.sh && dos2unix entry.sh +# Move the entrypoint script to the root directory for ease of access +RUN mv app/entry.sh /entry.sh # Nautical Version (for example "v0.2.1") or "main" if not set ARG NAUTICAL_VERSION="main" @@ -32,9 +32,6 @@ ENV REPORT_FILE="true" # Run the backup immediately on start ENV BACKUP_ON_START="false" -# Log each rsync command to console before running (useful for debugging) -ENV LOG_RSYNC_COMMANDS="false" - # Use the default rsync args "-raq" (recursive, archive, quiet) ENV USE_DEFAULT_RSYNC_ARGS="true" @@ -44,5 +41,11 @@ ENV RSYNC_CUSTOM_ARGS="" # Require the Docker Label `nautical-backup.enable=true` to be present on each contianer or it will be skipped. ENV REQUIRE_LABEL="false" +# Set the default log level to INFO +ENV LOG_LEVEL="INFO" + +# Set the default log level for the repot file to INFO +ENV REPORT_FILE_LOG_LEVEL="INFO" + # Run the entry script and pass all variables to it ENTRYPOINT [ "bash", "-c", "exec ./entry.sh \"${@}\"", "--"] diff --git a/docs/arguments.md b/docs/arguments.md index 671770e8..dad78823 100644 --- a/docs/arguments.md +++ b/docs/arguments.md @@ -165,18 +165,27 @@ Will immediatly perform a backup when the container is started in addition to th BACKUP_ON_START=true ``` -## Log rsync Commands +## Console Log Level +Set the console log level for the container. -Log each `rsync` command to console before running (useful for debugging) +> **Default**: INFO -> **Default**: false +> **Options**: DEBUG, INFO, WARN, ERROR ```properties -LOG_RSYNC_COMMANDS=true +LOG_LEVEL=INFO ``` -You should see something like this in the Nautical contianer logs: -```console -rsync -ahq --exclude='*.log' --exclude='*.txt' /app/source/watchtower/ /app/destination/watchtower/ + +## Report Log Level +Set the log level for the generated report file. +Only used if the repot file is [enabled](#report-file). + +> **Default**: INFO + +> **Options**: DEBUG, INFO, WARN, ERROR + +```properties +REPORT_FILE_LOG_LEVEL=INFO ``` ## Use Default rsync Arguments diff --git a/docs/labels.md b/docs/labels.md index 6747516d..0b6718c4 100644 --- a/docs/labels.md +++ b/docs/labels.md @@ -13,6 +13,7 @@ Remember, these labels can be added to any container (other than Nautica labels: - "nautical-backup.enable=true" - "nautical-backup.stop-before-backup=true" + - "nautical-backup.rsync-custom-args= " # Disable custom rsync args ``` === "Docker Compose Example 2" @@ -204,7 +205,7 @@ nautical-backup.rsync-custom-args=--exclude='*.log' --exclude='*.txt' !!! note "This label will *override* the global setting applied through [Enviornment Variables](./arguments.md)" * *Any value* will override the global rsync arguemnts configured through [global settings](./arguments.md#custom-rsync-arguments). - * A (empty) value of `"nautical-backup.rsync-custom-args="` will ^^cancel^^ any [global setting](./arguments.md#custom-rsync-arguments) for this container only. + * A value of (space) `"nautical-backup.rsync-custom-args= "` will ^^cancel^^ any [global setting](./arguments.md#custom-rsync-arguments) for this container only. * Not setting the label value will use the [global setting](./arguments.md#custom-rsync-arguments). 🔄 Not setting a label is the same action as the [Custom rsync Arguments](./arguments.md#custom-rsync-arguments) variable, but applied only to this container. \ No newline at end of file diff --git a/pkg/backup.sh b/pkg/backup.sh index b3b49703..9b2b2066 100644 --- a/pkg/backup.sh +++ b/pkg/backup.sh @@ -1,6 +1,7 @@ #!/bin/bash -echo "Starting backup script..." +source /app/logger.sh # Use the logger script +logThis "Starting backup..." # Convert the string back to an array if [ ! -z "$CONTAINER_SKIP_LIST_STR" ]; then @@ -39,55 +40,31 @@ containers=$(docker ps --no-trunc --format="{{.ID}}:{{.Names}}") number_of_containers=$(echo "$containers" | wc -l) number_of_containers=$((number_of_containers - 1)) # Subtract 1 to exclude the container running the script -echo "Processing $number_of_containers containers..." +logThis "Processing $number_of_containers containers..." # Define the name for the report file report_file="Backup Report - $(date +'%Y-%m-%d').txt" -# Remove previous report files - -# Initialize the current report file with a header -if [ "$REPORT_FILE" = "true" ]; then - rm -f "$DEST_LOCATION/Backup Report - "*.txt - # Initialize the current report file with a header - echo "Backup Report - $(date)" >"$DEST_LOCATION/$report_file" -fi DEFAULT_RSYNC_ARGS="-ahq" default_rsync_args="-ahq" if [ "$USE_DEFAULT_RSYNC_ARGS" = "false" ]; then + logThis "Disabling default rsync arguments ($DEFAULT_RSYNC_ARGS)" "DEBUG" default_rsync_args="" fi # Global variables to hold rsync arguments custom_args="" if [ ! -z "$RSYNC_CUSTOM_ARGS" ]; then + logThis "Adding custom rsync arguments ($RSYNC_CUSTOM_ARGS)" "DEBUG" custom_args="$RSYNC_CUSTOM_ARGS" fi -# Get arguments: -# -s = skips -# -d = override DEST_LOCATION -while getopts ":s:d:" opt; do - case $opt in - s) currs+=("$OPTARG") ;; - d) DEST_LOCATION=$OPTARG ;; - esac -done # Merge the default skips with provided skips currs=("${currs[@]}" "${SKIP_CONTAINERS[@]}") containers_completed=0 -# Assumes the container name is the exact same as the directory name -log_entry() { - local message="$1" - echo "$message" - - if [ "$REPORT_FILE" = "true" ]; then - echo "$(date) - $message" >>"$DEST_LOCATION/$report_file" - fi -} BackupContainer() { local container=$1 @@ -96,7 +73,7 @@ BackupContainer() { for skip in "${SKIP_STOPPING[@]}"; do if [ "$skip" == "$container" ]; then skip_stopping=1 - log_entry "Skipping stopping of $container as it's in the SKIP_STOPPING list." + logThis "Skipping stopping of $container as it's in the SKIP_STOPPING list." "DEBUG" break fi done @@ -105,81 +82,81 @@ BackupContainer() { labels=$(docker inspect --format '{{json .Config.Labels}}' $id) if echo "$labels" | grep -q '"nautical-backup.stop-before-backup":"false"'; then - log_entry "Skipping stopping of $container because of label." + logThis "Skipping stopping of $container because of label." "DEBUG" skip_stopping=1 fi local src_dir="$SOURCE_LOCATION/$container" if [ ! -z "${override_source_dirs[$container]}" ]; then src_dir="$SOURCE_LOCATION/${override_source_dirs[$container]}" - log_entry "Overriding source directory for $container to ${override_source_dirs[$container]}" + logThis "Overriding source directory for $container to ${override_source_dirs[$container]}" "DEBUG" fi if echo "$labels" | grep -q '"nautical-backup.override-source-dir"'; then new_src_dir=$(echo "$labels" | jq -r '.["nautical-backup.override-source-dir"]') src_dir="$SOURCE_LOCATION/$new_src_dir" - log_entry "Overriding source directory for $container to $new_src_dir from label" + logThis "Overriding source directory for $container to $new_src_dir from label" "DEBUG" fi local dest_dir="$DEST_LOCATION/$container" if [ ! -z "${override_dest_dirs[$container]}" ]; then dest_dir="$DEST_LOCATION/${override_dest_dirs[$container]}" - log_entry "Overriding destination directory for $container to ${override_dest_dirs[$container]}" + logThis "Overriding destination directory for $container to ${override_dest_dirs[$container]}" "DEBUG" fi if echo "$labels" | grep -q '"nautical-backup.override-destination-dir"'; then new_destination_dir=$(echo "$labels" | jq -r '.["nautical-backup.override-destination-dir"]') dest_dir="$DEST_LOCATION/$new_destination_dir" - log_entry "Overriding destination directory for $container to $new_destination_dir from label" + logThis "Overriding destination directory for $container to $new_destination_dir from label" "DEBUG" fi if [ -d "$src_dir" ]; then if [ $skip_stopping -eq 0 ]; then - log_entry "Stopping $container..." + logThis "Stopping $container..." docker stop $container 2>&1 >/dev/null if [ $? -ne 0 ]; then - log_entry "Error stopping container $container. Skipping backup for this container." + logThis "Error stopping container $container. Skipping backup for this container." "ERROR" return fi fi if echo "$labels" | grep -q '"nautical-backup.use-default-rsync-args":"true"'; then - echo "Using default rsync args ($DEFAULT_RSYNC_ARGS) for $container" + logThis "Using default rsync args ($DEFAULT_RSYNC_ARGS) for $container" "DEBUG" default_rsync_args=$DEFAULT_RSYNC_ARGS elif echo "$labels" | grep -q '"nautical-backup.use-default-rsync-args":"false"'; then - echo "Not using default rsync args ($DEFAULT_RSYNC_ARGS) for $container" + logThis "Not using default rsync args ($DEFAULT_RSYNC_ARGS) for $container" "DEBUG" default_rsync_args="" fi if echo "$labels" | grep -q '"nautical-backup.rsync-custom-args"'; then new_custom_rsync_args=$(echo "$labels" | jq -r '.["nautical-backup.rsync-custom-args"]') custom_args=$new_custom_rsync_args - echo "Using custom rsync args for $container" + logThis "Using custom rsync args for $container" "DEBUG" fi - log_entry "Backing up $container data..." - if [ "$LOG_RSYNC_COMMANDS" = "true" ]; then - echo rsync $default_rsync_args $custom_args $src_dir/ $dest_dir/ - fi + logThis "Backing up $container data..." + logThis "RUNNING: 'rsync $default_rsync_args $custom_args $src_dir/ $dest_dir/'" "DEBUG" + + # Run rsync eval rsync $default_rsync_args $custom_args $src_dir/ $dest_dir/ if [ $? -ne 0 ]; then - log_entry "Error copying data for container $container. Skipping backup for this container." + logThis "Error copying data for container $container. Skipping backup for this container." "ERROR" fi if [ $skip_stopping -eq 0 ]; then - log_entry "Starting $container container..." + logThis "Starting $container container..." docker start $container 2>&1 >/dev/null if [ $? -ne 0 ]; then - log_entry "Error restarting container $container. Please check manually!" + logThis "Error restarting container $container. Please check manually!" "ERROR" return fi fi - log_entry "$container completed." + logThis "$container completed." ((containers_completed++)) else - log_entry "Directory $src_dir does not exist. Skipping" + logThis "Directory $src_dir does not exist. Skipping" "WARN" fi } @@ -198,21 +175,21 @@ for entry in $containers; do labels=$(docker inspect --format '{{json .Config.Labels}}' $id) if echo "$labels" | grep -q '"nautical-backup.enable":"true"'; then - echo "Enabling $name based on label." + logThis "Enabling $name based on label." "DEBUG" skip=0 # Do not skip the container elif echo "$labels" | grep -q '"nautical-backup.enable":"false"'; then - echo "Skipping $name based on label." + logThis "Skipping $name based on label." "DEBUG" skip=1 # Add the container to the skip list elif [ "$REQUIRE_LABEL" = "true" ]; then if [ "$id" != "$SELF_CONTAINER_ID" ]; then - echo "Skipping $name as 'nautical-backup.enable=true' was not found and REQUIRE_LABEL is true." + echo "Skipping $name as 'nautical-backup.enable=true' was not found and REQUIRE_LABEL is true." "DEBUG" fi fi for cur in "${SKIP_CONTAINERS[@]}"; do if [ "$cur" == "$name" ]; then skip=1 - echo "Skipping $name based on name." + echo "Skipping $name based on name." "DEBUG" break fi if [ "$cur" == "$id" ]; then @@ -220,7 +197,7 @@ for entry in $containers; do if [ "$cur" == "$SELF_CONTAINER_ID" ]; then break # Exclude self from logs fi - echo "Skipping $name based on ID $id." + echo "Skipping $name based on ID $id." "DEBUG" break fi done @@ -232,4 +209,4 @@ done containers_skipped=$((number_of_containers - containers_completed)) -echo "Success. $containers_completed containers backed up! $containers_skipped skipped." +logThis "Success. $containers_completed containers backed up! $containers_skipped skipped." "INFO" diff --git a/pkg/entry.sh b/pkg/entry.sh index 456d973b..642c47cf 100644 --- a/pkg/entry.sh +++ b/pkg/entry.sh @@ -1,9 +1,13 @@ #!/bin/bash +source /app/logger.sh # Use the logger script -echo "Nautical Backup Version: $NAUTICAL_VERSION" +create_new_report_file + +logThis "Nautical Backup Version: $NAUTICAL_VERSION" +logThis "Using log level: $LOG_LEVEL" "DEBUG" # Echo the CRON schedule for logging/debugging -echo "Installing CRON schedule: $CRON_SCHEDULE in TZ: $TZ" +logThis "Installing CRON schedule: $CRON_SCHEDULE in TZ: $TZ" "DEBUG" # Dump the current cron jobs to a temporary file crontab -l > tempcron @@ -23,24 +27,24 @@ export SOURCE_LOCATION=/app/source # Do not include a trailing slash export DEST_LOCATION=/app/destination # Do not include a trailing slash -echo "Verifying source directory..." +logThis "Verifying source directory..." "DEBUG" if [ ! -d "$SOURCE_LOCATION" ]; then - echo "Error: Source directory $SOURCE_LOCATION does not exist." + logThis "Error: Source directory $SOURCE_LOCATION does not exist." exit 1 elif [ ! -r "$SOURCE_LOCATION" ]; then - echo "Error: No read access to source directory $SOURCE_LOCATION." + logThis "Error: No read access to source directory $SOURCE_LOCATION." exit 1 fi -echo "Verifying destination directory..." +logThis "Verifying destination directory..." "DEBUG" if [ ! -d "$DEST_LOCATION" ]; then - echo "Error: Destination directory $DEST_LOCATION does not exist." + logThis "Error: Destination directory $DEST_LOCATION does not exist." "ERROR" exit 1 elif [ ! -r "$DEST_LOCATION" ]; then - echo "Error: No read access to destination directory $DEST_LOCATION." + logThis "Error: No read access to destination directory $DEST_LOCATION." "ERROR" exit 1 elif [ ! -w "$DEST_LOCATION" ]; then - echo "Error: No write access to destination directory $DEST_LOCATION." + logThis "Error: No write access to destination directory $DEST_LOCATION." "ERROR" exit 1 fi @@ -73,11 +77,11 @@ process_csv SKIP_STOPPING_LIST "$SKIP_STOPPING" if [ ! -z "$SKIP_CONTAINERS" ]; then - echo "SKIP_CONTAINERS: ${CONTAINER_SKIP_LIST[@]}" + logThis "SKIP_CONTAINERS: ${CONTAINER_SKIP_LIST[@]}" "DEBUG" fi if [ ! -z "$SKIP_STOPPING" ]; then - echo "SKIP_STOPPING: ${SKIP_STOPPING_LIST[@]}" + logThis "SKIP_STOPPING: ${SKIP_STOPPING_LIST[@]}" "DEBUG" fi # Get the container ID of the current container @@ -93,43 +97,43 @@ export SKIP_STOPPING_STR # Export the string # Assuming OVERRIDE_SOURCE_DIR is passed as an environment variable in the format "container1:dir1,container2:dir2,..." if [ ! -z "$OVERRIDE_SOURCE_DIR" ]; then - echo "OVERRIDE_SOURCE_DIR: ${OVERRIDE_SOURCE_DIR}" + logThis "OVERRIDE_SOURCE_DIR: ${OVERRIDE_SOURCE_DIR}" "DEBUG" fi export OVERRIDE_SOURCE_DIR # Assuming OVERRIDE_DEST_DIR is passed as an environment variable in the format "container1:dir1,container2:dir2,..." if [ ! -z "$OVERRIDE_DEST_DIR" ]; then - echo "OVERRIDE_DEST_DIR: ${OVERRIDE_DEST_DIR}" + logThis "OVERRIDE_DEST_DIR: ${OVERRIDE_DEST_DIR}" "DEBUG" fi export OVERRIDE_DEST_DIR if [ "$REPORT_FILE" = "false" ]; then - echo "REPORT_FILE: $REPORT_FILE" + logThis "REPORT_FILE: $REPORT_FILE" "DEBUG" fi # Set rsync custom arguments if specified if [ ! -z "$RSYNC_CUSTOM_ARGS" ]; then - echo "RSYNC_CUSTOM_ARGS: $RSYNC_CUSTOM_ARGS" + logThis "RSYNC_CUSTOM_ARGS: $RSYNC_CUSTOM_ARGS" "DEBUG" fi if [ "$LOG_RSYNC_COMMANDS" = "true" ]; then - echo "LOG_RSYNC_COMMANDS: $LOG_RSYNC_COMMANDS" + logThis "LOG_RSYNC_COMMANDS: $LOG_RSYNC_COMMANDS" "DEBUG" fi if [ "$USE_DEFAULT_RSYNC_ARGS" = "false" ]; then - echo "USE_DEFAULT_RSYNC_ARGS: $USE_DEFAULT_RSYNC_ARGS" + logThis "USE_DEFAULT_RSYNC_ARGS: $USE_DEFAULT_RSYNC_ARGS" "DEBUG" fi if [ "$REQUIRE_LABEL" = "true" ]; then - echo "REQUIRE_LABEL: $REQUIRE_LABEL" + logThis "REQUIRE_LABEL: $REQUIRE_LABEL" "DEBUG" fi if [ "$BACKUP_ON_START" = "true" ]; then - echo "BACKUP_ON_START: $BACKUP_ON_START" + logThis "BACKUP_ON_START: $BACKUP_ON_START" "DEBUG" bash ./app/backup.sh fi -echo "Initialization complete. Awaiting CRON schedule: $CRON_SCHEDULE" +logThis "Initialization complete. Awaiting CRON schedule: $CRON_SCHEDULE" diff --git a/pkg/logger.sh b/pkg/logger.sh new file mode 100644 index 00000000..e3bce629 --- /dev/null +++ b/pkg/logger.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +declare -A levels=([DEBUG]=0 [INFO]=1 [WARN]=2 [ERROR]=3) +script_logging_level=$LOG_LEVEL +report_file_logging_level=$REPORT_FILE_LOG_LEVEL + +report_file="Backup Report - $(date +'%Y-%m-%d').txt" + +create_new_report_file() { + if [ "$REPORT_FILE" = "true" ]; then + rm -f "$DEST_LOCATION/Backup Report - "*.txt + # Initialize the current report file with a header + echo "Backup Report - $(date)" >"$DEST_LOCATION/$report_file" + fi +} + +logThis() { + local log_message=$1 + local log_priority=${2:-INFO} + + # Check if level exists + [[ ${levels[$log_priority]} ]] || return 1 + + # Check if level is enough for console logging + if ((${levels[$log_priority]} >= ${levels[$script_logging_level]})); then + echo "${log_priority}: ${log_message}" + fi + + # Check if level is enough for report file logging + if [ "$REPORT_FILE" = "true" ] && ((${levels[$log_priority]} >= ${levels[$report_file_logging_level]})); then + echo "$(date) - ${log_priority}: ${log_message}" >>"$DEST_LOCATION/$report_file" + fi +}