diff --git a/scripts/cleanup.sh b/scripts/cleanup.sh index fab95fe..612762a 100755 --- a/scripts/cleanup.sh +++ b/scripts/cleanup.sh @@ -40,6 +40,128 @@ log_error() { echo -e "${RED}[ERROR]${NC} $1" } +get_unused_images() { + # Get unused images (excluding Harbor images) with detailed information + # This includes both dangling images and tagged images not used by running containers + + # Get all images (excluding Harbor images) + local all_images=$(docker image ls --format "{{.Repository}}:{{.Tag}} ({{.ID}}) - Size: {{.Size}}, Created: {{.CreatedAt}}" | grep -v "goharbor" | grep -v "harbor" || true) + + if [ -n "$all_images" ]; then + echo "$all_images" | while read -r image; do + if [ -n "$image" ]; then + # Extract image ID from the format + local image_id=$(echo "$image" | sed 's/.*(\([a-f0-9]*\)).*/\1/') + + if [ -n "$image_id" ]; then + # Check if this image is used by any running containers + local used_by_containers=$(docker ps --format "{{.Image}}" | grep -q "$image_id" && echo "used" || echo "unused") + + # Check if this image is used by any stopped containers + local used_by_stopped=$(docker ps -a --format "{{.Image}}" | grep -q "$image_id" && echo "used" || echo "unused") + + # If image is not used by any containers, it's safe to remove + if [ "$used_by_containers" = "unused" ] && [ "$used_by_stopped" = "unused" ]; then + echo "$image (unused - safe to remove)" + fi + fi + fi + done + fi +} + +get_unused_volumes() { + # Get unused volumes (excluding Harbor volumes) - just IDs for cleanup + docker volume ls -q --filter "dangling=true" | grep -v "harbor" | grep -v "registry" || true +} + +get_unused_volumes_with_usage() { + # Get unused volumes (excluding Harbor volumes) with container usage info + local volumes=$(docker volume ls -q --filter "dangling=true" | grep -v "harbor" | grep -v "registry" || true) + + if [ -n "$volumes" ]; then + echo "$volumes" | while read -r volume; do + if [ -n "$volume" ]; then + # Get containers using this volume + local containers=$(docker ps -a --filter "volume=$volume" --format "{{.Names}}" 2>/dev/null || echo "") + # Get volume creation time + local created=$(docker volume inspect "$volume" --format "{{.CreatedAt}}" 2>/dev/null || echo "unknown") + # Get volume driver + local driver=$(docker volume inspect "$volume" --format "{{.Driver}}" 2>/dev/null || echo "local") + + # Format the output with volume ID and usage info + if [ -n "$containers" ]; then + echo "$volume (used by: $containers, created: $created, driver: $driver)" + else + echo "$volume (unused - safe to remove, created: $created, driver: $driver)" + fi + fi + done + fi +} + +get_unused_volumes_detailed() { + # Get unused volumes (excluding Harbor volumes) with detailed information for display + local volumes=$(docker volume ls -q --filter "dangling=true" | grep -v "harbor" | grep -v "registry" || true) + + if [ -n "$volumes" ]; then + echo "$volumes" | while read -r volume; do + if [ -n "$volume" ]; then + # Get containers using this volume + local containers=$(docker ps -a --filter "volume=$volume" --format "{{.Names}}" 2>/dev/null || echo "") + # Get volume creation time + local created=$(docker volume inspect "$volume" --format "{{.CreatedAt}}" 2>/dev/null || echo "unknown") + # Get volume driver + local driver=$(docker volume inspect "$volume" --format "{{.Driver}}" 2>/dev/null || echo "local") + + # Format the output with volume ID and usage info + if [ -n "$containers" ]; then + echo "$volume (used by: $containers, created: $created, driver: $driver)" + else + echo "$volume (unused - safe to remove, created: $created, driver: $driver)" + fi + fi + done + fi +} + +get_unused_networks() { + # Get unused networks (excluding Harbor networks) with detailed information + docker network ls --filter "type=custom" --format "{{.Name}} ({{.Driver}}) - Created: {{.CreatedAt}}" | grep -v "harbor" | grep -v "registry" || true +} + +get_protected_containers() { + # Get all critical containers that are running + local protected_containers="" + IFS=',' read -ra CONTAINERS <<< "$CRITICAL_CONTAINERS" + for container in "${CONTAINERS[@]}"; do + if docker ps --format "{{.Names}}" | grep -q "^${container}$"; then + if [ -n "$protected_containers" ]; then + protected_containers="$protected_containers + - $container (Harbor container)" + else + protected_containers=" - $container (Harbor container)" + fi + fi + done + echo "$protected_containers" +} + +get_protected_volumes() { + # Get Harbor volumes that would be protected + docker volume ls -q --filter "dangling=true" | grep -E "(harbor|registry)" || true +} + +get_protected_images() { + # Get Harbor images that would be protected + docker images --format "{{.Repository}}:{{.Tag}}" | grep -E "(goharbor|harbor)" || true +} + +get_protected_networks() { + # Get Harbor networks that would be protected + docker network ls --format "{{.Name}}" | grep -E "(harbor|registry)" || true +} + check_critical_infrastructure() { log_info "Checking critical infrastructure status..." @@ -97,6 +219,27 @@ label_critical_containers() { echo } +protect_harbor_volumes() { + log_info "Identifying and protecting Harbor volumes..." + + # Use the same logic as the dry-run section to find Harbor volumes + HARBOR_VOLUMES=$(docker volume ls -q --filter "dangling=true" | grep -E "(harbor|registry)" || true) + + if [ -n "$HARBOR_VOLUMES" ]; then + log_info "Found Harbor volumes:" + echo "$HARBOR_VOLUMES" | while read -r vol; do + if [ -n "$vol" ]; then + echo " - $vol (PROTECTED)" + fi + done + else + log_info "No Harbor volumes found" + fi + + log_success "Harbor volumes identified for protection" + echo +} + show_help() { cat << EOF Sharenet Cleanup Script @@ -123,32 +266,305 @@ EOF } cleanup_docker_resources() { - log_info "Cleaning up Docker resources..." - if [ "$DRY_RUN" = "true" ]; then - log_warning "DRY RUN MODE - No changes will be made" - echo "Would run: docker image prune -f (excluding critical infrastructure)" - echo "Would run: docker volume prune -f (excluding critical infrastructure)" - echo "Would run: docker network prune -f (excluding critical infrastructure)" + echo + echo "==================================================================================" + echo " 🚨 DRY RUN MODE 🚨" + echo " No resources will be deleted" + echo "==================================================================================" + echo + log_info "Checking what would be cleaned up..." + echo + + # Get lists using shared functions + local unused_images=$(get_unused_images) + local unused_volumes=$(get_unused_volumes_with_usage) + local unused_networks=$(get_unused_networks) + local protected_containers=$(get_protected_containers) + local protected_volumes=$(get_protected_volumes) + local protected_images=$(get_protected_images) + local protected_networks=$(get_protected_networks) + + # Section 1: ITEMS PROTECTED + echo "==================================================================================" + echo " 🛡️ ITEMS PROTECTED 🛡️" + echo "==================================================================================" + echo + + if [ -n "$protected_containers" ]; then + log_info "Protected Containers:" + echo "$protected_containers" + echo + else + log_info "Protected Containers: None found" + echo + fi + + if [ -n "$protected_volumes" ]; then + log_info "Protected Volumes:" + echo "$protected_volumes" | while read -r volume; do + echo " - $volume (Harbor volume)" + done + echo + else + log_info "Protected Volumes: None found" + echo + fi + + if [ -n "$protected_images" ]; then + log_info "Protected Images:" + echo "$protected_images" | while read -r image; do + echo " - $image (Harbor image)" + done + echo + else + log_info "Protected Images: None found" + echo + fi + + if [ -n "$protected_networks" ]; then + log_info "Protected Networks:" + echo "$protected_networks" | while read -r network; do + echo " - $network (Harbor network)" + done + echo + else + log_info "Protected Networks: None found" + echo + fi + + # Section 2: ITEMS REMOVED + echo "==================================================================================" + echo " 🗑️ ITEMS REMOVED 🗑️" + echo "==================================================================================" + echo + + # Clean up unused images + if [ -n "$unused_images" ]; then + log_info "Images removed:" + echo "$unused_images" | while read -r image; do + echo " - $image" + done + echo + + log_info "Removing unused images..." + echo "$unused_images" | while read -r image; do + # Extract image ID from the format "repo:tag (id) - Size: size, Created: date" + local image_id=$(echo "$image" | sed 's/.*(\([a-f0-9]*\)).*/\1/') + if [ -n "$image_id" ]; then + # First try to remove by image ID (this will remove all tags) + docker rmi "$image_id" 2>/dev/null || { + # If that fails, try to remove by repository:tag + local repo_tag=$(echo "$image" | cut -d' ' -f1) + if [ -n "$repo_tag" ]; then + docker rmi "$repo_tag" 2>/dev/null || log_warning "Failed to remove image: $repo_tag" + fi + } + fi + done + log_success "Unused images removed" + else + log_info "Images removed: None found" + log_info "No unused images to remove" + fi + + # Clean up unused volumes + if [ -n "$unused_volumes" ]; then + log_info "Volumes removed:" + local volumes_with_usage=$(get_unused_volumes_with_usage) + if [ -n "$volumes_with_usage" ]; then + echo "$volumes_with_usage" | while read -r volume_info; do + if [ -n "$volume_info" ]; then + echo " - $volume_info" + fi + done + fi + echo + + log_info "Removing unused volumes..." + # Now remove the volumes + echo "$unused_volumes" | while read -r volume; do + if [ -n "$volume" ]; then + docker volume rm "$volume" 2>/dev/null || log_warning "Failed to remove volume: $volume" + fi + done + log_success "Unused volumes removed" + else + log_info "Volumes removed: None found" + log_info "No unused volumes to remove" + fi + + # Clean up unused networks + if [ -n "$unused_networks" ]; then + log_info "Networks removed:" + echo "$unused_networks" | while read -r network; do + echo " - $network" + done + echo + + log_info "Removing unused networks..." + echo "$unused_networks" | while read -r network; do + # Extract network name from the format "name (driver) - Created: date" + local network_name=$(echo "$network" | cut -d' ' -f1) + if [ -n "$network_name" ]; then + docker network rm "$network_name" 2>/dev/null || log_warning "Failed to remove network: $network_name" + fi + done + log_success "Unused networks removed" + else + log_info "Networks removed: None found" + log_info "No unused networks to remove" + fi + + echo "==================================================================================" + log_success "Dry run completed. No resources were deleted." + echo "==================================================================================" return fi - # Remove unused images (excluding critical infrastructure) - log_info "Removing unused Docker images (excluding critical infrastructure)..." - # Use protection labels to exclude critical images - docker image prune -f --filter "label!=critical=infrastructure" --filter "label!=protected=true" - - # Remove unused volumes (excluding critical infrastructure) - log_info "Removing unused Docker volumes (excluding critical infrastructure)..." - # Use protection labels to exclude critical volumes - docker volume prune -f --filter "label!=critical=infrastructure" --filter "label!=protected=true" - - # Remove unused networks (excluding critical infrastructure) - log_info "Removing unused Docker networks (excluding critical infrastructure)..." - # Use protection labels to exclude critical networks - docker network prune -f --filter "label!=critical=infrastructure" --filter "label!=protected=true" - + # Get lists using shared functions + local unused_images=$(get_unused_images) + local unused_volumes=$(get_unused_volumes) + local unused_networks=$(get_unused_networks) + local protected_containers=$(get_protected_containers) + local protected_volumes=$(get_protected_volumes) + local protected_images=$(get_protected_images) + local protected_networks=$(get_protected_networks) + + # Section 1: ITEMS PROTECTED + echo "==================================================================================" + echo " 🛡️ ITEMS PROTECTED 🛡️" + echo "==================================================================================" + echo + + if [ -n "$protected_containers" ]; then + log_info "Protected Containers:" + echo "$protected_containers" + echo + else + log_info "Protected Containers: None found" + echo + fi + + if [ -n "$protected_volumes" ]; then + log_info "Protected Volumes:" + echo "$protected_volumes" | while read -r volume; do + echo " - $volume (Harbor volume)" + done + echo + else + log_info "Protected Volumes: None found" + echo + fi + + if [ -n "$protected_images" ]; then + log_info "Protected Images:" + echo "$protected_images" | while read -r image; do + echo " - $image (Harbor image)" + done + echo + else + log_info "Protected Images: None found" + echo + fi + + if [ -n "$protected_networks" ]; then + log_info "Protected Networks:" + echo "$protected_networks" | while read -r network; do + echo " - $network (Harbor network)" + done + echo + else + log_info "Protected Networks: None found" + echo + fi + + # Section 2: ITEMS REMOVED + echo "==================================================================================" + echo " 🗑️ ITEMS REMOVED 🗑️" + echo "==================================================================================" + echo + + # Clean up unused images + if [ -n "$unused_images" ]; then + log_info "Images removed:" + echo "$unused_images" | while read -r image; do + echo " - $image" + done + echo + + log_info "Removing unused images..." + echo "$unused_images" | while read -r image; do + # Extract image ID from the format "repo:tag (id) - Size: size, Created: date" + local image_id=$(echo "$image" | sed 's/.*(\([a-f0-9]*\)).*/\1/') + if [ -n "$image_id" ]; then + # First try to remove by image ID (this will remove all tags) + docker rmi "$image_id" 2>/dev/null || { + # If that fails, try to remove by repository:tag + local repo_tag=$(echo "$image" | cut -d' ' -f1) + if [ -n "$repo_tag" ]; then + docker rmi "$repo_tag" 2>/dev/null || log_warning "Failed to remove image: $repo_tag" + fi + } + fi + done + log_success "Unused images removed" + else + log_info "Images removed: None found" + log_info "No unused images to remove" + fi + + # Clean up unused volumes + if [ -n "$unused_volumes" ]; then + log_info "Volumes removed:" + local volumes_with_usage=$(get_unused_volumes_with_usage) + if [ -n "$volumes_with_usage" ]; then + echo "$volumes_with_usage" | while read -r volume_info; do + if [ -n "$volume_info" ]; then + echo " - $volume_info" + fi + done + fi + echo + + log_info "Removing unused volumes..." + # Now remove the volumes + echo "$unused_volumes" | while read -r volume; do + if [ -n "$volume" ]; then + docker volume rm "$volume" 2>/dev/null || log_warning "Failed to remove volume: $volume" + fi + done + log_success "Unused volumes removed" + else + log_info "Volumes removed: None found" + log_info "No unused volumes to remove" + fi + + # Clean up unused networks + if [ -n "$unused_networks" ]; then + log_info "Networks removed:" + echo "$unused_networks" | while read -r network; do + echo " - $network" + done + echo + + log_info "Removing unused networks..." + echo "$unused_networks" | while read -r network; do + # Extract network name from the format "name (driver) - Created: date" + local network_name=$(echo "$network" | cut -d' ' -f1) + if [ -n "$network_name" ]; then + docker network rm "$network_name" 2>/dev/null || log_warning "Failed to remove network: $network_name" + fi + done + log_success "Unused networks removed" + else + log_info "Networks removed: None found" + log_info "No unused networks to remove" + fi + + echo "==================================================================================" log_success "Docker resources cleanup completed (critical infrastructure protected)" + echo "==================================================================================" } cleanup_registry() { @@ -220,7 +636,10 @@ cleanup_ci_cd() { # Label critical containers for protection label_critical_containers - # Clean up Docker resources + # Protect Harbor volumes + protect_harbor_volumes + + # Clean up Docker resources (this will show the organized sections) cleanup_docker_resources # Clean up registry