#!/bin/bash # Refactored data commands with type-specific functions and standardized labels # Format: _ .... # where: # - command: create, destroy, backup, restore # - type: file, path, volume # - label: standardized identifier (e.g., "config", "data", "logs") # - location: for files - full file path. # for paths - full directory path # for volumes - volume label MYID=$(id -u) MYGRP=$(id -g) # ============================================================================ # FILE OPERATIONS # ============================================================================ create_file() { local label="$1" local filepath="$2" echo "[create_file] label=$label filepath=$filepath" local file_parent file_parent=$(dirname "${filepath}") local file_name file_name=$(basename "${filepath}") if [ ! -d "${file_parent}" ]; then echo "Creating parent directory ${file_parent}" mkdir -p "${file_parent}" fi if [ ! -f "${filepath}" ]; then echo "Creating file ${filepath}" touch "${filepath}" else echo "File ${filepath} already exists - unchanged" fi } destroy_file() { local label="$1" local filepath="$2" echo "[destroy_file] label=$label filepath=$filepath" if [ -f "${filepath}" ]; then echo "Removing file ${filepath}" rm -f "${filepath}" else echo "File ${filepath} does not exist - nothing to destroy" fi } backup_file() { local label="$1" local filepath="$2" local backup_folder="$3" echo "[backup_file] label=$label filepath=$filepath backup_folder=$backup_folder" # Create subfolder using the label local target_dir="${backup_folder}/file_${label}" mkdir -p "${target_dir}" if [ -f "${filepath}" ]; then local file_parent file_parent=$(dirname "${filepath}") local file_name file_name=$(basename "${filepath}") # Store metadata about the file echo "${filepath}" > "${target_dir}/original_path.txt" # Copy the file - save as the label name, not original filename docker run --rm \ -v "${file_parent}":/source \ -v "${target_dir}":/backup \ debian bash -c "cp /source/${file_name} /backup/${label}.data && chown -R $MYID:$MYGRP /backup" echo "File ${filepath} backed up successfully as ${label}.data" else echo "File ${filepath} does not exist - nothing to backup" fi } restore_file() { local label="$1" local filepath="$2" local backup_folder="$3" echo "[restore_file] label=$label filepath=$filepath backup_folder=$backup_folder" local source_dir="${backup_folder}/file_${label}" if [ ! -d "${source_dir}" ]; then echo "Backup for label '${label}' not found in ${backup_folder}" return 1 fi # File is backed up as ${label}.data, not original filename if [ -f "${source_dir}/${label}.data" ]; then # Ensure parent directory exists local file_parent file_parent=$(dirname "${filepath}") mkdir -p "${file_parent}" # Remove existing file if present rm -f "${filepath}" # Copy from backup - restore from label.data to target filepath cp "${source_dir}/${label}.data" "${filepath}" echo "File ${filepath} restored successfully from ${label}.data" else echo "Backup file ${source_dir}/${label}.data not found" return 1 fi } # ============================================================================ # PATH OPERATIONS # ============================================================================ create_path() { local label="$1" local path="$2" echo "[create_path] label=$label path=$path" if [ -d "${path}" ]; then echo "Path ${path} already exists - unchanged" else echo "Creating path ${path}" mkdir -p "${path}" fi } destroy_path() { local label="$1" local path="$2" echo "[destroy_path] label=$label path=$path" if [ -d "${path}" ]; then local path_parent path_parent=$(dirname "${path}") local path_child path_child=$(basename "${path}") echo "Destroying path ${path}" docker run --rm -v "${path_parent}":/volume debian bash -c "rm -rf /volume/${path_child}" else echo "Path ${path} does not exist - nothing to destroy" fi } backup_path() { local label="$1" local path="$2" local backup_folder="$3" echo "[backup_path] label=$label path=$path backup_folder=$backup_folder" # Create subfolder using the label local target_dir="${backup_folder}/path_${label}" mkdir -p "${target_dir}" if [ -d "${path}" ]; then # Store metadata about the path echo "${path}" > "${target_dir}/original_path.txt" # Create tar archive of the path contents docker run --rm \ -v "${path}":/source \ -v "${target_dir}":/backup \ debian bash -c "tar -czf /backup/data.tar.gz -C /source . && chown -R $MYID:$MYGRP /backup" echo "Path ${path} backed up successfully" else echo "Path ${path} does not exist - nothing to backup" fi } restore_path() { local label="$1" local path="$2" local backup_folder="$3" echo "[restore_path] label=$label path=$path backup_folder=$backup_folder" local source_dir="${backup_folder}/path_${label}" if [ ! -d "${source_dir}" ]; then echo "Backup for label '${label}' not found in ${backup_folder}" return 1 fi if [ -f "${source_dir}/data.tar.gz" ]; then # Ensure path exists mkdir -p "${path}" # Clear existing contents echo "Clearing existing contents of ${path}" docker run --rm -v "${path}":/target debian bash -c "rm -rf /target/{*,.*} 2>/dev/null || true" # Extract backup docker run --rm \ -v "${path}":/target \ -v "${source_dir}":/backup \ debian bash -c "tar -xzf /backup/data.tar.gz -C /target" echo "Path ${path} restored successfully" else echo "Backup file ${source_dir}/data.tar.gz not found" return 1 fi } # ============================================================================ # VOLUME OPERATIONS # ============================================================================ create_volume() { local label="$1" local volume_name="$2" echo "[create_volume] label=$label volume=$volume_name" if docker volume ls | grep -q "^local[[:space:]]*${volume_name}$"; then echo "Volume ${volume_name} already exists - unchanged" else echo "Creating volume ${volume_name}" docker volume create "${volume_name}" fi } destroy_volume() { local label="$1" local volume_name="$2" echo "[destroy_volume] label=$label volume=$volume_name" if docker volume ls | grep -q "^local[[:space:]]*${volume_name}$"; then echo "Destroying volume ${volume_name}" docker volume rm "${volume_name}" else echo "Volume ${volume_name} does not exist - nothing to destroy" fi } backup_volume() { local label="$1" local volume_name="$2" local backup_folder="$3" echo "[backup_volume] label=$label volume=$volume_name backup_folder=$backup_folder" # Create subfolder using the label local target_dir="${backup_folder}/volume_${label}" mkdir -p "${target_dir}" if docker volume ls | grep -q "^local[[:space:]]*${volume_name}$"; then # Store metadata about the volume echo "${volume_name}" > "${target_dir}/original_volume.txt" # Create tar archive of the volume contents docker run --rm \ -v "${volume_name}":/source \ -v "${target_dir}":/backup \ debian bash -c "tar -czf /backup/data.tar.gz -C /source . && chown -R $MYID:$MYGRP /backup" echo "Volume ${volume_name} backed up successfully" else echo "Volume ${volume_name} does not exist - nothing to backup" fi } restore_volume() { local label="$1" local volume_name="$2" local backup_folder="$3" echo "[restore_volume] label=$label volume=$volume_name backup_folder=$backup_folder" local source_dir="${backup_folder}/volume_${label}" if [ ! -d "${source_dir}" ]; then echo "Backup for label '${label}' not found in ${backup_folder}" return 1 fi if [ -f "${source_dir}/data.tar.gz" ]; then # Remove existing volume if present if docker volume ls | grep -q "^local[[:space:]]*${volume_name}$"; then echo "Removing existing volume ${volume_name}" docker volume rm "${volume_name}" fi # Create new volume docker volume create "${volume_name}" # Extract backup into volume docker run --rm \ -v "${volume_name}":/target \ -v "${source_dir}":/backup \ debian bash -c "tar -xzf /backup/data.tar.gz -C /target" echo "Volume ${volume_name} restored successfully" else echo "Backup file ${source_dir}/data.tar.gz not found" return 1 fi } # ============================================================================ # MAIN BACKUP/RESTORE ORCHESTRATION # ============================================================================ # Process multiple items for creation # Usage: create_items type1:label1:location1 type2:label2:location2 ... create_items() { echo "Starting create operations" # Process each item for item in "$@"; do IFS=':' read -r type label location <<< "$item" case "$type" in file) create_file "$label" "$location" ;; path) create_path "$label" "$location" ;; volume) create_volume "$label" "$location" ;; *) echo "Unknown type: $type (must be file, path, or volume)" ;; esac done echo "Create operations completed" } # Process multiple items for destruction # Usage: destroy_items type1:label1:location1 type2:label2:location2 ... destroy_items() { echo "Starting destroy operations" # Process each item for item in "$@"; do IFS=':' read -r type label location <<< "$item" case "$type" in file) destroy_file "$label" "$location" ;; path) destroy_path "$label" "$location" ;; volume) destroy_volume "$label" "$location" ;; *) echo "Unknown type: $type (must be file, path, or volume)" ;; esac done echo "Destroy operations completed" } # Process multiple items for backup # Usage: backup_items type1:label1:location1 type2:label2:location2 ... backup_items() { _check_required_env_vars "BACKUP_FILE" "TEMP_DIR" local backup_file="${BACKUP_FILE}" local temp_dir="${TEMP_DIR}" local backup_temp_path="${temp_dir}/backup" mkdir -p "$backup_temp_path" echo "Starting backup to ${backup_file}" # Process each item for item in "$@"; do IFS=':' read -r type label location <<< "$item" case "$type" in file) backup_file "$label" "$location" "$backup_temp_path" ;; path) backup_path "$label" "$location" "$backup_temp_path" ;; volume) backup_volume "$label" "$location" "$backup_temp_path" ;; *) echo "Unknown type: $type (must be file, path, or volume)" ;; esac done # Create final backup archive echo "Creating backup archive ${backup_file}" tar -czf "$backup_file" -C "$backup_temp_path" . # Clean up temp directory rm -rf "$backup_temp_path" echo "Backup completed successfully" } # Process multiple items for restore # Usage: restore_items type1:label1:location1 type2:label2:location2 ... restore_items() { _check_required_env_vars "BACKUP_FILE" "TEMP_DIR" local backup_file="${BACKUP_FILE}" local temp_dir="${TEMP_DIR}" if [ ! -f "$backup_file" ]; then echo "Backup file $backup_file not found" return 1 fi local restore_temp_path="${temp_dir}/restore" mkdir -p "$restore_temp_path" echo "Starting restore from ${backup_file}" # Extract backup archive echo "Extracting backup archive" tar -xzf "$backup_file" -C "$restore_temp_path" # Process each item for item in "$@"; do IFS=':' read -r type label location <<< "$item" case "$type" in file) restore_file "$label" "$location" "$restore_temp_path" ;; path) restore_path "$label" "$location" "$restore_temp_path" ;; volume) restore_volume "$label" "$location" "$restore_temp_path" ;; *) echo "Unknown type: $type (must be file, path, or volume)" ;; esac done # Clean up temp directory rm -rf "$restore_temp_path" echo "Restore completed successfully" } # # ============================================================================ # # LEGACY COMPATIBILITY WRAPPER (can be removed once migration is complete) # # ============================================================================ # # Convert old format to new format # # Input: key=value (e.g., "volume=myapp_data") # # Output: type:label:location (e.g., "volume:data:myapp_data") # convert_legacy_format() { # local pair="$1" # local key="${pair%%=*}" # local value="${pair#*=}" # # Generate a label from the value (simplified - you may want more sophisticated logic) # local label # label=$(echo "$value" | sed 's/[^a-zA-Z0-9_-]/_/g') # echo "${key}:${label}:${value}" # }