-
Notifications
You must be signed in to change notification settings - Fork 14
/
launcher
executable file
·436 lines (362 loc) · 12.1 KB
/
launcher
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
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
#!/usr/bin/env bash
# Launcher script for Kontalk Docker server
# Inspired by the Discourse launcher
set -e
usage () {
echo "Usage: launcher COMMAND"
echo "Commands:"
echo " start: Start/initialize containers"
echo " stop: Stop running containers"
echo " restart: Restart containers"
echo " destroy: Stop and remove containers"
echo " enter: Open a shell to run commands inside the xmpp container"
echo " logs: View the Docker logs for containers"
echo " bootstrap: Bootstrap containers for the config"
echo " rebuild: Rebuild containers (destroy old, bootstrap, start new)"
echo " backup: Initiate server backup procedure"
echo " restore: Restore a previously generated backup"
echo
echo "Any other command will be passed through to docker-compose, including all arguments."
exit 1
}
command=$1
if [ -z "$command" ]; then
usage
fi
cd "$(dirname "$0")"
docker_path=`which docker.io || which docker`
dockercompose_path=`which docker-compose`
gpg_path=`which gpg2 || which gpg`
config_file=local.properties
# escape sequences for output
set +e
bold=$(tput bold 2>/dev/null || false)
normal=$(tput sgr0 2>/dev/null || false)
set -e
install_docker() {
echo "Docker is not installed, you will need to install Docker in order to run Kontalk"
echo "See https://docs.docker.com/installation/"
exit 1
}
install_dockercompose() {
echo "Docker Compose is not installed, you will need to install Docker Compose in order to run Kontalk"
echo "See https://docs.docker.com/compose/install/"
exit 1
}
run_setup_first() {
echo "Run \`./kontalk-setup\` first."
exit 1
}
check_prereqs() {
if [ -z ${docker_path} ]; then
install_docker
fi
if [ -z ${dockercompose_path} ]; then
install_dockercompose
fi
if [ ! -f ${config_file} ]; then
run_setup_first
fi
}
check_version() {
if [ ! -f .version ]; then
echo "Run \`./launcher bootstrap\` first."
exit 1
fi
}
# check prerequisites
check_prereqs
# load configuration
set -a
. local.properties
set +a
DATADIR=data
CONFDIR=config
SSL_TRUSTED=trusted.pem
TIGASE_CONF=init.properties.in
TIGASE_ENV=tigase.conf
HTTUPLOAD_CONF=config.yml.in
REPO_URL=https://github.com/kontalk/tigase-kontalk.git
docker_compose() {
check_version
(export IMAGE_VERSION=$(cat .version); set -x; ${dockercompose_path} -p ${INSTANCE_NAME} $*)
}
docker_compose_internal() {
check_version
(export IMAGE_VERSION=$(cat .version); ${dockercompose_path} -p ${INSTANCE_NAME} $*)
}
docker_compose_exec() {
check_version
export IMAGE_VERSION=$(cat .version); set -x; exec ${dockercompose_path} -p ${INSTANCE_NAME} $*
}
monitor_docker_build() {
image="$1"
base="$2"
shift 2
if [ -n "${DEBUG}" ]; then
${docker_path} build -t ${image} $* ${base}
else
${docker_path} build -t ${image} $* ${base} >/dev/null &
docker_pid=$!
trap "kill ${docker_pid} 2>/dev/null" EXIT
spin[0]="-"
spin[1]="\\"
spin[2]="|"
spin[3]="/"
echo -n "${image} ${spin[0]}"
while kill -0 ${docker_pid} 2>/dev/null
do
for i in "${spin[@]}"
do
echo -ne "\b$i"
sleep 0.1
done
done
trap - EXIT
wait ${docker_pid} && echo -e "\bOK"
fi
}
run_bootstrap() {
# get information about what we are going to build
build=${VERSION}
serverbuild=${VERSION}
docker_tag=$(echo ${VERSION} | colrm 8)
refs=$(git ls-remote ${REPO_URL} ${VERSION})
commit=$(echo "${refs}" | cut -f 1 -)
ref=$(echo "${refs}" | cut -f 2 -)
echo ${ref} | grep heads >/dev/null &&
docker_tag=$(echo ${commit} | colrm 8) &&
build=${commit} &&
echo "${bold}Building images for branch ${VERSION} (${commit})${normal}"
echo ${ref} | grep tags >/dev/null &&
docker_tag=${build} &&
echo "${bold}Building images for version ${VERSION}${normal}"
# build images
echo "This could take several minutes."
docker pull kontalk/xmppserver:${docker_tag} || monitor_docker_build kontalk/xmppserver:${docker_tag} tigase --build-arg BRANCH=${build} --build-arg SERVER_BRANCH=${serverbuild}
monitor_docker_build kontalk/httpupload httpupload
echo
# ensure config directory exists
mkdir -p ${CONFDIR}
# check GPG key
if [ ! -f ${CONFDIR}/server-private.key ] || [ ! -f ${CONFDIR}/server-public.key ]; then
echo "${bold}Not using provided GPG server key, I'll generate one later automatically.${normal}"
echo "You can provide a GPG server key by exporting it into ${CONFDIR}/server-private.key and ${CONFDIR}/server-public.key"
echo "Please note that the private key MUST NOT be password-protected."
echo
fi
# check if private GPG key is not password-protected
if [ -f ${CONFDIR}/server-private.key ] && ${gpg_path} --list-packets ${CONFDIR}/server-private.key | grep protected &>/dev/null; then
echo "${bold}The provided GPG server key is password-protected.${normal}"
echo "That can't be used in an automated system. Please remove the passphrase from the private key using GnuPG and export it again."
exit 1
fi
# check certificate
if [ ! -f ${CONFDIR}/privatekey.pem ] || [ ! -f ${CONFDIR}/certificate.pem ]; then
echo "${bold}Not using provided X.509 certificate, I'll generate one later automatically.${normal}"
echo "If you want to provide an existing X.509 certificate for the server,"
echo "you can copy it into ${CONFDIR}/privatekey.pem and ${CONFDIR}/certificate.pem"
echo "An optional CA chain can be provided into ${CONFDIR}/cachain.pem"
echo
fi
# check trusted.pem
if [ ! -f ${CONFDIR}/${SSL_TRUSTED} ];
then
# copy default trusted certs bundle
echo "Using default trusted certs bundle"
cp ${DATADIR}/${SSL_TRUSTED}.dist ${CONFDIR}/${SSL_TRUSTED}
echo
fi
# check init.properties
if [ ! -f ${CONFDIR}/${TIGASE_CONF} ];
then
echo "Using default Tigase configuration"
cp ${DATADIR}/${TIGASE_CONF}.dist ${CONFDIR}/${TIGASE_CONF}
echo
fi
if [ ! -f ${CONFDIR}/${TIGASE_ENV} ];
then
echo "Using default Tigase environment"
cp ${DATADIR}/${TIGASE_ENV}.dist ${CONFDIR}/${TIGASE_ENV}
echo
fi
# check config.yml (httpupload)
if [ ! -f ${CONFDIR}/${HTTUPLOAD_CONF} ];
then
echo "Using default HTTP upload component configuration"
cp ${DATADIR}/${HTTUPLOAD_CONF}.dist ${CONFDIR}/${HTTUPLOAD_CONF}
echo
fi
# trusted keystore
rm -f ${CONFDIR}/truststore
if [ -d trustedcerts ]; then
for i in trustedcerts/*.cer; do
keytool -importcert -keystore ${CONFDIR}/truststore -storepass truststore -noprompt -alias $(basename ${i} .cer) -file ${i} &>/dev/null
done
fi
echo ${docker_tag} > .version
}
run_start() {
docker_compose up -d $*
}
run_stop() {
docker_compose stop -t 10 $*
}
run_backup() {
check_version
if [[ ! -d "${BACKUP_PATH}" ]]; then
echo "Backup path not configured or not a directory. Please configure an accessible directory in the BACKUP_PATH configuration key in local.properties."
exit 1
fi
# create temporary directory
OUTDIR="${BACKUP_PATH}/${INSTANCE_NAME}-`date +%Y-%m-%d_%H-%M-%S`"
mkdir -p "${OUTDIR}/db"
mkdir -p "${OUTDIR}/httpupload"
mkdir -p "${OUTDIR}/xmpp"
# dump the database
echo "${bold}Dumping database${normal}"
# TODO fallback to an emergency container if original fails
if ! docker_compose exec -T db /usr/bin/mysqldump \
--skip-opt \
--single-transaction \
--quick \
--create-options \
--extended-insert \
--routines \
--set-charset \
--no-autocommit \
--user=kontalk --password="${MYSQL_PASSWORD}" \
kontalk >"${OUTDIR}/db/kontalk.sql" ; then
echo
echo -e "${bold}Unable to backup database. Ensure the database container is running by launching: \`./launcher start db\`${normal}" &&
exit 1
fi
# copy httpupload files
# not really transactional, but we can live with it
echo "${bold}Dumping httpupload files${normal}"
docker run --rm -v "${INSTANCE_NAME}_httpupload_data:/out_data" \
busybox tar -C /out_data -cvf - . >"${OUTDIR}/httpupload/disk.tar"
# copy xmpp files
# TODO create kyoto cabinet tools image based on busybox so we can use kchashmgr copy
echo "${bold}Dumping keyring${normal}"
docker run --rm -v "${INSTANCE_NAME}_xmpp_data:/out_data" \
busybox cat /out_data/keyring.kch >"${OUTDIR}/xmpp/keyring.kch"
# create the backup file (intentionally manually crafted list of files)
echo "${bold}Creating backup archive${normal}"
OUTFILE="${OUTDIR}.tar.gz"
tar -C "${OUTDIR}" -cvzf "${OUTFILE}" \
"db/" \
"httpupload/" \
"xmpp/"
rm -fR "${OUTDIR}"
echo
echo "${bold}Backup created: ${OUTFILE}${normal}"
}
run_restore() {
check_version
RESTORE_FILE="$1"
# TODO if $1 is empty, take latest (by name) file in BACKUP_PATH/${INSTANCE_NAME}_*.tar.gz
if [[ "${RESTORE_FILE}" == "" ]]; then
echo "Usage: launcher restore <backup_file.tar.gz>"
exit 1
fi
if [[ ! -f "${RESTORE_FILE}" ]]; then
echo "Unable to read backup file ${RESTORE_FILE}"
exit 1
fi
# ask for confirmation
echo "${bold}You are about to restore the backup from:"
echo " ${RESTORE_FILE}"
echo
echo "Any existing containers and their data in instance '${INSTANCE_NAME}' WILL BE DESTROYED.${normal}"
echo
read -p "Type 'OK' to proceed: " restore_ack
[[ "${restore_ack}" == "OK" ]] || exit 1
echo
# take down the whole stack and re-create it
echo "${bold}Rebuilding all containers and data${normal}"
docker_compose down -v
docker_compose up --no-start
# restore database
echo
echo "${bold}Restoring database${normal}"
# wait for the database to spin up
docker_compose up -d db
while ! docker_compose exec db mysql -u kontalk -p"${MYSQL_PASSWORD}" kontalk -e 'status' &>/dev/null ; do
echo "Waiting for database connection..."
sleep 4
done
tar -xOzf "${RESTORE_FILE}" "db/kontalk.sql" |
docker exec -i $(docker_compose_internal ps -q db) \
mysql -u kontalk -p"${MYSQL_PASSWORD}" kontalk
# restore httpupload files
echo
echo "${bold}Restoring httpupload files${normal}"
docker_compose up -d httpupload
tar -xOzf "${RESTORE_FILE}" "httpupload/disk.tar" |
docker exec -i $(docker_compose_internal ps -q httpupload) \
tar -C /home/kontalk/disk -xf -
# restore xmpp data
echo
echo "${bold}Restoring keyring${normal}"
docker_compose up -d xmpp
tar -xOzf "${RESTORE_FILE}" "xmpp/keyring.kch" |
docker exec -i $(docker_compose_internal ps -q xmpp) \
tee /home/kontalk/data/keyring.kch >/dev/null
echo
echo "${bold}Server should now be up and running.${normal}"
}
case "$command" in
bootstrap)
run_bootstrap
echo "${bold}Successfully bootstrapped, to startup use \`./launcher start\`${normal}"
exit 0
;;
start)
shift 1
run_start $*
exit 0
;;
stop)
shift 1
run_stop $*
exit 0
;;
restart)
shift 1
run_stop $*
run_start $*
exit 0
;;
rebuild)
echo "${bold}Stopping and removing old containers${normal}"
docker_compose down
run_bootstrap
run_start
exit 0
;;
destroy)
docker_compose down
exit 0
;;
backup)
run_backup
exit 0
;;
restore)
shift 1
run_restore $*
exit 0
;;
enter)
docker_compose_exec exec xmpp /bin/bash --login
;;
root)
docker_compose_exec exec --privileged --user root xmpp /bin/bash --login
;;
*)
shift 1
docker_compose_exec ${command} $*
;;
esac
usage