-
Notifications
You must be signed in to change notification settings - Fork 0
/
run.sh
executable file
·255 lines (216 loc) · 8.2 KB
/
run.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
#!/usr/bin/env bash
set -mb
cd "$(dirname "$0")"
declare -ar CONFIG_PARAMS=(runas http_probe_headers python_virtualenv_params depends_grace_period system_packages package_manager_lock_wait probe_as_dependency probe_timeout probe_retries restart periodic_interval success_exit probe depends stop_signal reload_signal command probe_interval continous_probe probe_failure_action)
declare -A BACKGROUND_PIDS
for parameter in ${@}; do
shift
if [[ "$parameter" =~ (-d|--debug) ]]; then
export debug=1
else
set -- "$@" "$parameter"
fi
done
. _/defaults.config
. _/bash-init.config
. _/system.sh
. _/tools.sh
cleanup_bash_init
trap "exit" INT TERM
trap "exit_trap" EXIT
check_defaults
if [ $# -eq 0 ]; then
text error "Missing service name/s in attribute"
exit 1
fi
if command -v apk >/dev/null; then
text info "Found apk, assuming an Alpine environment"
export alpine=1
export runas_helper="su-exec"
elif command -v apt >/dev/null; then
text info "Found apt, assuming a Debian environment"
export debian=1
export runas_helper="gosu"
else
text info "Found neither apt nor apk, unknown environment"
fi
text info "Spawned bash-init with PID $$"
#---------------------------#
#--------- Stage 1 ---------#
#---------------------------#
for i in {1..2}; do
# On first loop, declare associative arrays
if [ $i -eq 1 ]; then
for raw_service in ${@}; do
service="$(trim_string "$raw_service")"
if ! is_regex_match "$service" '^[a-zA-Z0-9_]+$'; then
text error "Invalid service name: ${service}"
exit 1
fi
declare -A $service
done
else
# On second loop source services and process
. services.array
for raw_service in ${@}; do
service="$(trim_string "$raw_service")"
declare -n "s=$service"
if [ ${#s[@]} -eq 0 ]; then
text error "Service $service is not defined"
exit 1
elif [ ${#s[command]} -eq 0 ]; then
text error "Service $service has no command defined"
exit 1
fi
text info "$(stage info 1) Starting: $(text info $service color_only)"
> /tmp/bash-init-svc_${service}
for config in "${CONFIG_PARAMS[@]}"; do
user_config=0
for attr in "${!s[@]}"; do
if [[ "$attr" == "$config" ]]; then
user_config=1
printf '%s="%s"\n' "$attr" "${s[$attr]}" >> /tmp/bash-init-svc_${service}
fi
done
[ $user_config -eq 0 ] && printf '%s="%s"\n' "$config" "${!config}" >> /tmp/bash-init-svc_${service}
done
printf 'service_name="%s"\n' "$service" >> /tmp/bash-init-svc_${service}
env_file=/tmp/bash-init-svc_${service} bash _/task.sh &
pid=$!
BACKGROUND_PIDS[$service]=$pid
text success "$(stage success 1) Spawned service container $(text info $service color_only) with PID $pid"
done
fi
done
#---------------------------#
#--------- Stage 2 ---------#
#---------------------------#
declare -i pid
declare -i stage_2_loop=0
declare -A started_containers
declare -A awaiting_dependencies
while [ ${#started_containers[@]} -ne ${#BACKGROUND_PIDS[@]} ]; do
for key in "${!BACKGROUND_PIDS[@]}"; do
((stage_2_loop++))
service_dependency="$(env_ctrl "$key" "get" "depends")"
if [ ! -z $service_dependency ]; then
declare -n "sd=$service_dependency"
if [ ${#sd[@]} -eq 0 ]; then
text error \
"Dependency $(text info $service_dependency color_only) of service \
$(text info $key color_only) is not a defined service"
exit 1
elif [ "$service_dependency" == "$key" ]; then
text error "Service $(text info $key color_only) depends on itself"
exit 1
else
if [ $((stage_2_loop%3)) -eq 0 ]; then
text info "$(stage info 2) \
Trying service dependency $(text info $service_dependency color_only) of service $(text info $key color_only)"
fi
awaiting_dependencies[$key]=$service_dependency
declare -i depends_grace_period=0
depends_grace_period="$(env_ctrl "$key" "get" "depends_grace_period")"
# Retries exceeded
# OR Service is gone
# OR Service is about to be terminated
if [ $stage_2_loop -gt $depends_grace_period ] || \
[ -z "$(env_ctrl "$service_dependency" "get" "service_name")" ] || \
[ ! -z "$(env_ctrl "$service_dependency" "get" "pending_signal")" ]; then
text error "$(stage error 2) Service $(text info $key color_only) will be configured to stop \
after initialization due to unhealthy dependency $(text info $service_dependency color_only)"
# Remove dependency to stop looping over it
env_ctrl "$key" "set" "depends" ""
# Tell service container to self-destroy
env_ctrl "$key" "set" "pending_signal" "stop"
unset health_check_loop[$key]
unset awaiting_dependencies[$key]
continue
fi
if [ ${#started_containers[$service_dependency]} -eq 0 ]; then
if [ "${awaiting_dependencies[$service_dependency]}" == "$key" ]; then
text error "FATAL: Dependency loop: Service $(text info $key color_only) depends on \
$(text info $service_dependency color_only) which depends on $key"
exit 1
fi
else
if [ ! -z "$(env_ctrl "$service_dependency" "get" "probe")" ] && \
[ "$(env_ctrl "$service_dependency" "get" "active_probe_status")" != "1" ]; then
if [ $((health_check_loop[$key]%3)) -eq 0 ]; then
text info "$(stage info 2) Service $(text info $key color_only) is awaiting \
healthy state of service dependency $(text info $service_dependency color_only), delaying"
fi
else
unset awaiting_dependencies[$key]
fi
fi
fi
fi
#---------------------------#
#-------- Stage 2.5 --------#
#---------------------------#
# This condition check will only be tried when dependencies were met
# bash-init does now CONT the prepared service containers in stage 2
# Stage 3 will ultimately be triggered by run_command() which also fires the probe inside the service container
if [ ! -v awaiting_dependencies[$key] ] && [ ${#started_containers[$key]} -eq 0 ]; then
pid=${BACKGROUND_PIDS[$key]}
if await_stop $pid; then
started_containers[$key]=1
kill -CONT $pid
[[ -z "$(env_ctrl "$key" "get" "pending_signal")" ]] && \
text success "$(stage success 2) Service container $(text info $key color_only) was initialized"
else
text error "$(stage error 2) FATAL: Service container $(text info $key color_only) could not be initialized"
exit 1
fi
fi
done
delay 1
done
declare -i run_loop=0
while true; do
((run_loop++))
for key in ${!BACKGROUND_PIDS[@]}; do
pid=${BACKGROUND_PIDS[$key]}
if proc_exists $pid; then
if [ $((run_loop%emit_stats_interval)) -eq 0 ]; then
emit_service_stats $key
run_loop=0
fi
pending_signal="$(env_ctrl "$key" "get" "pending_signal")"
if [ ! -z "$pending_signal" ]; then
env_ctrl "$key" "del" "pending_signal"
stop_service "$key" "$pending_signal"
fi
else
if [ -s /tmp/bash-init-svc_${key} ]; then
while [ -s /tmp/env.lock ]; do
delay 0.1
done
env_file=/tmp/bash-init-svc_${key} bash _/task.sh &
_pid=$!
BACKGROUND_PIDS[$key]=$_pid
text warning "Restarting initialization of service container $(text info $key color_only) with PID $_pid"
if await_stop $_pid; then
kill -CONT $_pid
text success "Service container $(text info $key color_only) was re-initialized"
else
text error "$(stage error 2) Service container $(text info $key color_only) could not be \
re-initialized and will be removed"
>/tmp/bash-init-svc_${key}
fi
else
unset BACKGROUND_PIDS[$key]
kill -$pid 2>/dev/null
kill $pid 2>/dev/null
>/tmp/bash-init-svc_${key}
text info "Service $key has left the chat"
fi
fi
done
if [ ${#BACKGROUND_PIDS[@]} -eq 0 ]; then
text info "No more running services to monitor"
exit 0
fi
delay 1
done