/
/
/
1#!/bin/bash
2# ==============================================================================
3# Docker Stop All Script
4# ==============================================================================
5#
6# Description: Stops all connectivity Docker services in proper order
7# Usage: ./docker-stop-all.sh [force|quiet|timeout]
8#
9# This script is automatically generated by Ansible - DO NOT EDIT MANUALLY
10# Template: docker-stop-all.sh.j2
11#
12# ==============================================================================
13
14set -euo pipefail
15
16# Configuration
17DOCKER_COMPOSE_DIR="{{ docker_base_path }}"
18LOG_FILE="/var/log/docker-stop-all.log"
19DEFAULT_TIMEOUT=30
20
21# Service shutdown order (reverse dependency order)
22SERVICES=(
23 "{{ wireguard_service_name }}"
24 "{{ nginx_proxy_service_name }}"
25 "{{ nginx_proxy_db_service_name }}"
26 "{{ pihole_service_name }}"
27 "{{ unbound_service_name }}"
28)
29
30# Logging function
31log() {
32 if [[ "${1:-}" != "quiet" ]]; then
33 echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "${LOG_FILE}"
34 else
35 echo "$(date '+%Y-%m-%d %H:%M:%S') - $2" >> "${LOG_FILE}"
36 fi
37}
38
39# Error handling function
40error_exit() {
41 log "ERROR: $1"
42 exit 1
43}
44
45# Check if container exists
46container_exists() {
47 docker inspect "$1" >/dev/null 2>&1
48}
49
50# Check if container is running
51container_running() {
52 docker inspect -f '{{.State.Running}}' "$1" 2>/dev/null | grep -q "true"
53}
54
55# Stop a specific service
56stop_service() {
57 local service="$1"
58 local timeout="${2:-${DEFAULT_TIMEOUT}}"
59 local force="${3:-false}"
60
61 local compose_file="${DOCKER_COMPOSE_DIR}/${service}/docker-compose.yml"
62
63 if ! container_exists "${service}"; then
64 log "${service} does not exist, skipping"
65 return 0
66 fi
67
68 if ! container_running "${service}"; then
69 log "${service} is not running, skipping"
70 return 0
71 fi
72
73 if [[ "${force}" == "true" ]]; then
74 log "Force stopping ${service}"
75 docker rm -f "${service}" 2>/dev/null || true
76 else
77 log "Stopping ${service} (timeout: ${timeout}s)"
78
79 if [[ -f "${compose_file}" ]]; then
80 docker-compose -f "${compose_file}" down --timeout ${timeout} 2>/dev/null || true
81 else
82 docker stop --time ${timeout} "${service}" 2>/dev/null || true
83 fi
84 fi
85
86 # Verify service is stopped
87 if container_running "${service}"; then
88 log "WARNING: ${service} is still running after stop attempt"
89 return 1
90 else
91 log "${service} stopped successfully"
92 return 0
93 fi
94}
95
96# Stop all services in order
97stop_all_services() {
98 local timeout="${1:-${DEFAULT_TIMEOUT}}"
99 local force="${2:-false}"
100 local quiet="${3:-false}"
101
102 log "Stopping all connectivity services"
103
104 local stopped_count=0
105 local skipped_count=0
106 local failed_count=0
107
108 for service in "${SERVICES[@]}"; do
109 if stop_service "${service}" "${timeout}" "${force}" "${quiet}"; then
110 stopped_count=$((stopped_count + 1))
111 else
112 failed_count=$((failed_count + 1))
113 fi
114 done
115
116 # Summary
117 log "Shutdown completed: ${stopped_count} services stopped, ${skipped_count} skipped, ${failed_count} failed"
118
119 if [[ ${failed_count} -gt 0 ]]; then
120 return 1
121 fi
122
123 return 0
124}
125
126# Force remove all containers (emergency cleanup)
127force_remove_all() {
128 log "Force removing all connectivity containers"
129
130 local removed_count=0
131
132 for service in "${SERVICES[@]}"; do
133 if container_exists "${service}"; then
134 docker rm -f "${service}" 2>/dev/null || true
135 removed_count=$((removed_count + 1))
136 log "Force removed: ${service}"
137 fi
138 done
139
140 log "Force removal completed: ${removed_count} containers removed"
141}
142
143# Show service status
144show_status() {
145 log "Current service status:"
146
147 local running_count=0
148 local stopped_count=0
149
150 for service in "${SERVICES[@]}"; do
151 if container_exists "${service}"; then
152 if container_running "${service}"; then
153 running_count=$((running_count + 1))
154 echo "${service}: RUNNING"
155 else
156 stopped_count=$((stopped_count + 1))
157 echo "${service}: STOPPED"
158 fi
159 else
160 echo "${service}: DOES NOT EXIST"
161 fi
162 done
163
164 echo ""
165 echo "Summary: ${running_count} running, ${stopped_count} stopped"
166}
167
168# Validate shutdown completeness
169validate_shutdown() {
170 log "Validating shutdown completeness"
171
172 local still_running=()
173
174 for service in "${SERVICES[@]}"; do
175 if container_running "${service}"; then
176 still_running+=("${service}")
177 fi
178 done
179
180 if [[ ${#still_running[@]} -eq 0 ]]; then
181 log "All services successfully stopped"
182 return 0
183 else
184 log "WARNING: Some services still running: ${still_running[*]}"
185 return 1
186 fi
187}
188
189# Show usage
190usage() {
191 cat << EOF
192Docker Stop All Script
193
194Usage: $0 [option]
195
196Options:
197 force Force remove all containers (emergency)
198 quiet Quiet mode (minimal output)
199 timeout N Custom timeout in seconds (default: ${DEFAULT_TIMEOUT})
200 status Show current service status
201 validate Validate shutdown completeness
202 help Show this help message
203
204Service Shutdown Order:
205 1. {{ wireguard_service_name }} (VPN)
206 2. {{ nginx_proxy_service_name }} (Reverse proxy)
207 3. {{ nginx_proxy_db_service_name }} (Database)
208 4. {{ pihole_service_name }} (DNS/ad-blocker)
209 5. {{ unbound_service_name }} (DNS resolver)
210
211Default timeout: ${DEFAULT_TIMEOUT} seconds per service
212EOF
213}
214
215# Main execution
216main() {
217 local option="${1:-}"
218 local value="${2:-}"
219
220 case "${option}" in
221 force)
222 force_remove_all
223 validate_shutdown
224 ;;
225 quiet)
226 stop_all_services "${DEFAULT_TIMEOUT}" "false" "quiet"
227 ;;
228 timeout)
229 if [[ -n "${value}" ]] && [[ "${value}" =~ ^[0-9]+$ ]]; then
230 stop_all_services "${value}" "false" "false"
231 validate_shutdown
232 else
233 error_exit "Invalid timeout value: ${value}"
234 fi
235 ;;
236 status)
237 show_status
238 ;;
239 validate)
240 validate_shutdown
241 ;;
242 help|*)
243 usage
244 ;;
245 esac
246}
247
248# Run main function with all arguments
249main "$@"