All checks were successful
Check scripts syntax / check-scripts-syntax (push) Successful in 36s
301 lines
8.7 KiB
Bash
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 "$@" |