Files
server-scripts/docker-updater.sh
Jose Henrique 1489062943
All checks were successful
Check scripts syntax / check-scripts-syntax (push) Successful in 36s
removing auto update
2025-08-24 20:59:49 -03:00

301 lines
8.7 KiB
Bash

#!/bin/bash
# Docker Container Updater
#
# Description: Automatically updates Docker containers and manages Docker images
# Features:
# - Updates all Docker Compose projects in /root/docker
# - Skips containers with .ignore file
# - Removes obsolete Docker Compose version attributes
# - Cleans up unused Docker images
# Author: ivanch
# Version: 2.0
set -euo pipefail # Exit on error, undefined vars, and pipe failures
HOSTNAME=$(cat /etc/hostname)
NOTIFY_URL_ERROR="http://notify.haven/template/notify/error"
NOTIFY_URL_UPDATE="http://notify.haven/template/notify/update"
send_error_notification() {
local message="$1"
local critical="$2"
curl -s -X POST "$NOTIFY_URL_ERROR" \
-H "Content-Type: application/json" \
-d "{\"caller\": \"$HOSTNAME\", \"message\": \"$message\", \"critical\": $critical}"
}
send_update_notification() {
local message="$1"
local script_time="$2"
curl -s -X POST "$NOTIFY_URL_UPDATE" \
-H "Content-Type: application/json" \
-d "{\"host\": \"$HOSTNAME\", \"asset\": \"Docker containers\", \"time\": $script_time}"
}
#==============================================================================
# CONFIGURATION
#==============================================================================
# Color definitions for output formatting
readonly NC='\033[0m'
readonly RED='\033[1;31m'
readonly GREEN='\033[1;32m'
readonly LIGHT_GREEN='\033[1;32m'
readonly LIGHT_BLUE='\033[1;34m'
readonly LIGHT_GREY='\033[0;37m'
readonly YELLOW='\033[1;33m'
# Script configuration
readonly DOCKER_FOLDER="/root/docker"
readonly COMPOSE_FILES=("docker-compose.yml" "docker-compose.yaml" "compose.yaml" "compose.yml")
# Auto-update configuration
readonly AUTO_UPDATE_ENABLED=true
#==============================================================================
# UTILITY FUNCTIONS
#==============================================================================
# Print formatted log messages
log_info() { echo -e "${LIGHT_GREY}[i] $1${NC}"; }
log_success() { echo -e "${LIGHT_GREEN}[✓] $1${NC}"; }
log_step() { echo -e "${LIGHT_BLUE}[i] $1${NC}"; }
log_container() { echo -e "${LIGHT_BLUE}[$1] $2${NC}"; }
log_warning() {
echo -e "${YELLOW}[!] $1${NC}";
send_error_notification "$1" false
}
log_error() {
echo -e "${RED}[x] $1${NC}" >&2;
send_error_notification "$1" true
}
# Exit with error message
die() {
log_error "$1"
exit 1
}
# Check if a command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
}
# Check if Docker and Docker Compose are available
check_docker_requirements() {
log_info "Checking Docker requirements..."
if ! command_exists docker; then
die "Docker is not installed or not in PATH"
fi
if ! docker compose version >/dev/null 2>&1; then
die "Docker Compose is not available"
fi
log_success "Docker requirements satisfied"
}
# Get SHA256 hash of a file
get_file_hash() {
local file="$1"
sha256sum "$file" 2>/dev/null | awk '{print $1}' || echo ""
}
# Get SHA256 hash from URL content
get_url_hash() {
local url="$1"
curl -s "$url" 2>/dev/null | sha256sum | awk '{print $1}' || echo ""
}
#==============================================================================
# DOCKER COMPOSE MANAGEMENT
#==============================================================================
# Find the active Docker Compose file in current directory
find_compose_file() {
for compose_file in "${COMPOSE_FILES[@]}"; do
if [[ -f "$compose_file" ]]; then
echo "$compose_file"
return 0
fi
done
return 1
}
# Remove obsolete version attribute from Docker Compose files
clean_compose_files() {
local container_name="$1"
for compose_file in "${COMPOSE_FILES[@]}"; do
if [[ -f "$compose_file" ]]; then
log_container "$container_name" "Cleaning obsolete version attribute from $compose_file"
sed -i '/^version:/d' "$compose_file" || log_warning "Failed to clean $compose_file"
fi
done
}
# Check if container should be skipped
should_skip_container() {
[[ -f ".ignore" ]]
}
# Check if any containers are running in current directory
has_running_containers() {
local running_containers
running_containers=$(docker compose ps -q 2>/dev/null || echo "")
[[ -n "$running_containers" ]]
}
# Update a single Docker Compose project
update_docker_project() {
local project_dir="$1"
local container_name
container_name=$(basename "$project_dir")
log_container "$container_name" "Checking for updates..."
# Change to project directory
cd "$project_dir" || {
log_error "Cannot access directory: $project_dir"
return 1
}
# Check if container should be skipped
if should_skip_container; then
log_container "$container_name" "Skipping (found .ignore file)"
return 0
fi
# Verify compose file exists
local compose_file
if ! compose_file=$(find_compose_file); then
log_container "$container_name" "No Docker Compose file found, skipping"
return 0
fi
# Clean compose files
clean_compose_files "$container_name"
# Check if containers are running
if ! has_running_containers; then
log_container "$container_name" "No running containers, skipping update"
return 0
fi
# Stop containers
log_container "$container_name" "Stopping containers..."
if ! docker compose down >/dev/null 2>&1; then
log_error "Failed to stop containers in $container_name"
return 1
fi
# Pull updated images
log_container "$container_name" "Pulling updated images..."
if ! docker compose pull -q >/dev/null 2>&1; then
log_warning "Failed to pull images for $container_name, attempting to restart anyway"
fi
# Start containers
log_container "$container_name" "Starting containers..."
if ! docker compose up -d >/dev/null 2>&1; then
log_error "Failed to start containers in $container_name"
return 1
fi
log_container "$container_name" "Update completed successfully!"
return 0
}
# Update all Docker Compose projects
update_all_docker_projects() {
log_step "Starting Docker container updates..."
# Check if Docker folder exists
if [[ ! -d "$DOCKER_FOLDER" ]]; then
die "Docker folder not found: $DOCKER_FOLDER"
fi
# Change to Docker folder
cd "$DOCKER_FOLDER" || die "Cannot access Docker folder: $DOCKER_FOLDER"
local updated_count=0
local failed_count=0
local skipped_count=0
# Process each subdirectory
for project_dir in */; do
if [[ -d "$project_dir" ]]; then
local project_path="$DOCKER_FOLDER/$project_dir"
if update_docker_project "$project_path"; then
if should_skip_container; then
((skipped_count++))
else
((updated_count++))
fi
else
((failed_count++))
fi
# Return to Docker folder for next iteration
cd "$DOCKER_FOLDER" || die "Cannot return to Docker folder"
fi
done
# Report results
log_success "Docker update summary:"
log_info " Updated: $updated_count projects"
log_info " Skipped: $skipped_count projects"
if [[ $failed_count -gt 0 ]]; then
log_warning " Failed: $failed_count projects"
fi
}
#==============================================================================
# DOCKER CLEANUP
#==============================================================================
# Clean up unused Docker resources
cleanup_docker_resources() {
log_step "Cleaning up unused Docker resources..."
# Remove unused images
log_info "Removing unused Docker images..."
if docker image prune -af >/dev/null 2>&1; then
log_success "Docker image cleanup completed"
else
log_warning "Docker image cleanup failed"
fi
}
#==============================================================================
# MAIN EXECUTION
#==============================================================================
main() {
START_TIME=$(date +%s)
log_step "Starting Docker Container Updater"
echo
# Check requirements
check_docker_requirements
# Update all Docker projects
update_all_docker_projects
# Clean up Docker resources
cleanup_docker_resources
echo
log_success "Docker container update process completed!"
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
log_info "Total duration: $DURATION seconds"
send_update_notification $DURATION
}
# Execute main function with all arguments
main "$@"