Build script and bump up to version 1.0.8
[discourse_docker.git] / launcher
CommitLineData
ace450bd 1#!/bin/bash
7e738616
S
2
3command=$1
4config=$2
55d17203
RW
5opt=$3
6
7936ebaa 7cd "$(dirname "$0")"
55d17203 8
5701c085
S
9docker_min_version='1.2.0'
10docker_rec_version='1.2.0'
60668406 11
8dea575c 12config_file=containers/"$config".yml
7e738616 13cidfile=cids/"$config".cid
5201a40c 14cidbootstrap=cids/"$config"_bootstrap.cid
5efded6a 15local_discourse=local_discourse
8a9d1525 16image=samsaffron/discourse:1.0.7
4807b1b8 17docker_path=`which docker.io || which docker`
7e738616 18
e0fd1f5b
TB
19if [ "${SUPERVISED}" = "true" ]; then
20 restart_policy="--restart=no"
21 attach_on_start="-a"
22 attach_on_run="-a stdout -a stderr"
23else
24 attach_on_run="-d"
25fi
26
813fef38
MK
27if [ -x "$(which ip 2>/dev/null)" ]; then
28 docker_ip=`ip addr show docker0 | \
03bb0735
LG
29 grep 'inet ' | \
30 awk '{ split($2,a,"/"); print a[1] }';`
31else
813fef38 32 docker_ip=`ifconfig | \
03bb0735
LG
33 grep -B1 "inet addr" | \
34 awk '{ if ( $1 == "inet" ) { print $2 } else if ( $2 == "Link" ) { printf "%s:" ,$1 } }' | \
35 grep docker0 | \
36 awk -F: '{ print $3 }';`
37fi
80c11be3
SS
38
39
5f803fb4 40usage () {
55d17203 41 echo "Usage: launcher COMMAND CONFIG [--skip-prereqs]"
7e738616 42 echo "Commands:"
1acce9e4
SS
43 echo " start: Start/initialize a container"
44 echo " stop: Stop a running container"
45 echo " restart: Restart a container"
46 echo " destroy: Stop and remove a container"
2fc6ff36 47 echo " enter: Use nsenter to enter a container"
5f803fb4 48 echo " ssh: Start a bash shell in a running container"
1acce9e4 49 echo " logs: Docker logs for container"
7936ebaa 50 echo " mailtest: Test the mail settings in a container"
408a9c19 51 echo " bootstrap: Bootstrap a container for the config based on a template"
680dd4ea 52 echo " rebuild: Rebuild a container (destroy old, bootstrap, start new)"
a8b66f98 53 echo " cleanup: Remove all containers that have stopped for > 24 hours"
55d17203
RW
54 echo
55 echo "Options:"
56 echo " --skip-prereqs Don't check prerequisites"
e02c1511 57 echo " --docker-args Extra arguments to pass when running docker"
7e738616
S
58 exit 1
59}
60
60668406
DP
61compare_version() {
62 declare -a ver_a
63 declare -a ver_b
64 IFS=. read -a ver_a <<< "$1"
65 IFS=. read -a ver_b <<< "$2"
66
67 while [[ -n $ver_a ]]; do
68 if (( ver_a > ver_b )); then
69 return 0
70 elif (( ver_b > ver_a )); then
71 return 1
72 else
73 unset ver_a[0]
74 ver_a=("${ver_a[@]}")
75 unset ver_b[0]
76 ver_b=("${ver_b[@]}")
77 fi
78 done
79 return 1 # They are equal
80}
81
a3e18d95
S
82prereqs() {
83
e741295a 84 # 1. docker daemon running?
4807b1b8 85 test=`$docker_path info >/dev/null`
e741295a
MB
86
87 if [[ $? -ne 0 ]] ; then
88 echo "Cannot connect to the docker daemon - verify it is running and you have access"
89 exit 1
90 fi
91
185e78c6
S
92 # 2. running aufs or btrfs
93 test=`$docker_path info 2> /dev/null | grep 'Driver: '`
94 if [[ "$test" =~ [aufs|btrfs] ]] ; then : ; else
9717d9cb
JA
95 echo "Your Docker installation is not using the recommended AuFS (union filesystem) and may be unstable."
96 echo "If you are unable to bootstrap / stop your image please report the issue at:"
2d7d1501 97 echo "https://meta.discourse.org/t/discourse-docker-installation-without-aufs/15639"
7d87f85f 98 echo ""
99 read -p "Continue without proper filesystem? [yN]" yn
100 case $yn in
101 [Yy]* ) break;;
102 * ) exit 1;;
103 esac
a3e18d95
S
104 fi
105
60668406
DP
106 # 3. running recommended docker version
107 test=($($docker_path --version)) # Get docker version string
108 test=${test[2]//,/} # Get version alone and strip comma if exists
a3e18d95 109
cf00fce0
S
110 [[ "$test" =~ "0.12.0" ]] && echo "You are running a broken version of Docker, please upgrade ASAP. See: https://meta.discourse.org/t/the-installation-stopped-in-the-middle/16311/ for more details." && exit 1
111
60668406 112 # At least minimum version
cf00fce0 113 if compare_version "${docker_min_version}" "${test}"; then
60668406 114 echo "ERROR: Docker version ${test} not supported, please upgrade to at least ${docker_min_version}, or recommended ${docker_rec_version}"
a3e18d95
S
115 exit 1
116 fi
117
60668406
DP
118 # Recommend best version
119 if compare_version "${docker_rec_version}" "${test}"; then
120 echo "WARNING: Docker version ${test} deprecated, recommend upgrade to ${docker_rec_version} or newer."
121 fi
122
e741295a 123 # 4. able to attach stderr / out / tty
e02c1511 124 test=`$docker_path run $user_args -i --rm -a stdout -a stderr $image echo working`
a3e18d95
S
125 if [[ "$test" =~ "working" ]] ; then : ; else
126 echo "Your Docker installation is not working correctly"
127 echo
128 echo "See: https://meta.discourse.org/t/docker-error-on-bootstrap/13657/18?u=sam"
129 exit 1
130 fi
131}
132
55d17203
RW
133if [ "$opt" != "--skip-prereqs" ] ; then
134 prereqs
135fi
a3e18d95 136
e02c1511
S
137if [ "$opt" == "--docker-args" ] ; then
138 user_args=$4
139else
140 user_args=""
141fi
142
88126eba 143get_ssh_pub_key() {
c76db1ea
MK
144 local ${ssh_key_locations}
145 ssh_key_locations=(
841334d9
MK
146 ~/.ssh/id_ed25519.pub
147 ~/.ssh/id_ecdsa.pub
c76db1ea
MK
148 ~/.ssh/id_rsa.pub
149 ~/.ssh/id_dsa.pub
841334d9 150 ~core/.ssh/authorized_keys
c76db1ea
MK
151 )
152
153 local $keyfile
154 for keyfile in "${ssh_key_locations[@]}"; do
155 if [[ -e ${keyfile} ]] ; then
156 ssh_pub_key="$(cat ${keyfile})"
841334d9 157 return 0
c76db1ea
MK
158 fi
159 done
160
9d28af0e 161 if tty -s ; then
c76db1ea
MK
162 echo "This user has no SSH key, but a SSH key is required to access the Discourse Docker container."
163 read -p "Generate a SSH key? (Y/n) " -n 1 -r
164 if [[ $REPLY =~ ^[Nn]$ ]] ; then
165 echo
166 echo WARNING: You may not be able to log in to your container.
167 echo
168 else
169 echo
170 echo Generating SSH key
171 mkdir -p ~/.ssh && ssh-keygen -f ~/.ssh/id_rsa -t rsa -N ''
172 echo
173 ssh_pub_key="$(cat ~/.ssh/id_rsa.pub)"
841334d9 174 return 0
88126eba
S
175 fi
176 fi
177
841334d9 178 return 1
88126eba
S
179}
180
181
52388b87
SS
182install_docker() {
183
184 echo "Docker is not installed, make sure you are running on the 3.8 kernel"
185 echo "The best supported Docker release is Ubuntu 12.04.03 for it run the following"
186 echo
187 echo "sudo apt-get update"
188 echo "sudo apt-get install linux-image-generic-lts-raring linux-headers-generic-lts-raring"
189 echo "sudo reboot"
190 echo
191
192 echo "sudo sh -c \"wget -qO- https://get.docker.io/gpg | apt-key add -\""
193 echo "sudo sh -c \"echo deb http://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list\""
194 echo "sudo apt-get update"
195 echo "sudo apt-get install lxc-docker"
196
197 exit 1
198}
199
60f9f04c
S
200host_run() {
201 read -r -d '' env_ruby << 'RUBY'
202 require 'yaml'
203
204 input = STDIN.readlines.join
205 yaml = YAML.load(input)
206
207 if host_run = yaml['host_run']
208 params = yaml['params'] || {}
209 host_run.each do |run|
210 params.each do |k,v|
211 run = run.gsub("$#{k}", v)
212 end
213 STDOUT.write "#{run}--SEP--"
214 end
215 end
216RUBY
217
e02c1511 218 host_run=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e "$env_ruby"`
60f9f04c
S
219
220 while [ "$host_run" ] ; do
221 iter=${host_run%%--SEP--*}
222 echo
223 echo "Host run: $iter"
224 $iter || exit 1
225 echo
226 host_run="${host_run#*--SEP--}"
227 done
228}
229
230
d90671f3 231set_volumes() {
e02c1511 232 volumes=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \
d90671f3
SS
233 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['volumes'].map{|v| '-v ' << v['volume']['host'] << ':' << v['volume']['guest'] << ' '}.join"`
234}
235
41daa523 236set_links() {
e02c1511 237 links=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \
41daa523 238 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['links'].map{|l| '--link ' << l['link']['name'] << ':' << l['link']['alias'] << ' '}.join"`
239}
240
7f77c274
SS
241set_template_info() {
242
e02c1511 243 templates=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
7f77c274
SS
244 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['templates']"`
245
246
247 arrTemplates=(${templates// / })
248 config_data=$(cat $config_file)
249
250 input="hack: true"
251
252
253 for template in "${arrTemplates[@]}"
254 do
255 [ ! -z $template ] && {
256 input="$input _FILE_SEPERATOR_ $(cat $template)"
257 }
258 done
259
260 # we always want our config file last so it takes priority
261 input="$input _FILE_SEPERATOR_ $config_data"
262
263 read -r -d '' env_ruby << 'RUBY'
264 require 'yaml'
265
266 input=STDIN.readlines.join
3cb3d9c4
S
267 # default to UTF-8 for the dbs sake
268 env = {'LANG' => 'en_US.UTF-8'}
7f77c274
SS
269 input.split('_FILE_SEPERATOR_').each do |yml|
270 yml.strip!
271 begin
272 env.merge!(YAML.load(yml)['env'] || {})
f3824347 273 rescue Psych::SyntaxError => e
274 puts e
275 puts "*ERROR."
7f77c274
SS
276 rescue => e
277 puts yml
278 p e
279 end
280 end
4b563ee8 281 puts env.map{|k,v| "-e\n#{k}=#{v}" }.join("\n")
7f77c274
SS
282RUBY
283
e02c1511 284 raw=`exec echo "$input" | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e "$env_ruby"`
4b563ee8
SS
285
286 env=()
f3824347 287 ok=1
4b563ee8 288 while read i; do
f3824347 289 if [ "$i" == "*ERROR." ]; then
290 ok=0
291 elif [ -n "$i" ]; then
16f2d250
S
292 env[${#env[@]}]=$i
293 fi
4b563ee8
SS
294 done <<< "$raw"
295
f3824347 296 if [ "$ok" -ne 1 ]; then
297 echo "${env[@]}"
d6d1fb6e 298 echo "YAML syntax error. Please check your /var/discourse/containers/*.yml config files."
f3824347 299 exit 1
300 fi
4b563ee8 301 echo "Calculated ENV: ${env[@]}"
7f77c274
SS
302}
303
52388b87
SS
304[ -z $docker_path ] && {
305 install_docker
306}
307
a8b66f98 308[ $command == "cleanup" ] && {
c692f743
L
309 echo
310 echo "The following command will"
311 echo "- Delete all docker images for old containers"
312 echo "- Delete all stopped and orphan containers"
313 echo
314 read -p "Are you sure (Y/n): " -n 1 -r && echo
315 if [[ $REPLY =~ ^[Yy]$ || ! $REPLY ]]
316 then
317 GetSpace=$(du -sk | awk '{print $1}');
318 echo "Starting Cleanup"
319 docker inspect -f '{{.Id}},{{.State.Running}},{{.State.FinishedAt}}' $(docker ps -qa) | \
320 awk -F, 'BEGIN { TIME=strftime("%FT%T.000000000Z",systime()-60*60*24); } $2=="false" && $3 < TIME {print $1;}' | \
321 xargs --no-run-if-empty docker rm >/dev/null 2>/dev/null
322 docker images --no-trunc| grep none | awk '{print $3}' | xargs -r docker rmi
323 let SpaceFreed=$GetSpace-$(du -sk | awk '{print $1}')
324 Output="$SpaceFreed" | awk '{sum=$1;hum[1024**3]="GB"; hum[1024**2]="MB"; hum[1024]="KB"; for (x=1024**3;x>=1024; x/=1024){ if (sum>=x) { printf "%.2f %s\n",sum/x,hum[x];break }}}'
325 [ -z "$Output" ] && {
326 [[ $SpaceFreed > 0 ]] && { echo "./launcher cleanup cleared up $SpaceFreed Bytes of disk space."; } || { echo "./launcher cleanup has finished, no files were removed."; }
327 } || { echo "./launcher cleanup cleared up $Output of disk space."; }
328 else
329 echo "Aborting..."
330 fi
a8b66f98
L
331 exit 0
332}
5f803fb4 333
c1005add 334[ $# -lt 2 ] && {
5f803fb4
SS
335 usage
336}
337
7e738616
S
338if [ ! -e $config_file ]
339 then
340 echo "Config file was not found, ensure $config_file exists"
71680b16
S
341 echo ""
342 echo "Available configs ( `cd containers && ls -dm *.yml | tr -s '\n' ' ' | awk '{ gsub(/\.yml/, ""); print }'`)"
7e738616
S
343 exit 1
344fi
345
337a89aa 346
e2ed1fb6
S
347docker_version=($($docker_path --version))
348docker_version=${test[2]//,/}
349
350if compare_version "1.2.0" "$docker_version"; then
351 echo "We recommend you upgrade docker, the version you are running has no restart policies, on reboot your container may not start up"
352 restart_policy=""
353else
e0fd1f5b 354 restart_policy=${restart_policy:---restart=always}
e2ed1fb6
S
355fi
356
357
7936ebaa
MB
358run_mailtest(){
359 if [ ! -e $config_file ]; then
360 echo "Config does not exist: $config_file" >&2
361 exit 1
362 fi
363 exec scripts/mailtest $config_file
364}
365
337a89aa
S
366run_stop(){
367 if [ ! -e $cidfile ]
368 then
369 echo "No cid found"
370 exit 1
371 else
4807b1b8 372 $docker_path stop -t 10 `cat $cidfile`
337a89aa
S
373 fi
374}
375
376run_start(){
377
60f9f04c
S
378 host_run
379
337a89aa
S
380 if [ ! -e $cidfile ]
381 then
382 echo "No cid found, creating a new container"
e02c1511 383 ports=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \
337a89aa
S
384 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['expose'].map{|p| '-p ' << p.to_s << ' '}.join"`
385
386 set_template_info
387 set_volumes
41daa523 388 set_links
337a89aa 389
4807b1b8 390 existing=`$docker_path ps -a | awk '{ print $1, $(NF) }' | grep "$config$" | awk '{ print $1 }'`
337a89aa
S
391 if [ ! -z $existing ]
392 then
393 echo "Found an existing container by its name, recovering cidfile, please rerun"
394 echo $existing > $cidfile
395 exit 1
396 fi
397
e02c1511 398 $docker_path run $user_args $links $attach_on_run $restart_policy "${env[@]}" -h "`hostname`-$config" -e DOCKER_HOST_IP=$docker_ip --name $config -t --cidfile $cidfile $ports \
074f2b6a 399 $volumes $local_discourse/$config /sbin/boot
337a89aa
S
400
401 exit 0
402 else
403 cid=`cat $cidfile`
404
405 if [ -z $cid ]
406 then
407 echo "Detected empty cid file, deleting, please re-run"
408 rm $cidfile
409 exit 1
410 fi
411
4807b1b8 412 found=`$docker_path ps -q -a --no-trunc | grep $cid`
337a89aa
S
413 if [ -z $found ]
414 then
415 echo "Invalid cid file, deleting, please re-run"
416 rm $cidfile
417 exit 1
418 fi
419
420 echo "cid found, ensuring container is started"
e0fd1f5b 421 $docker_path start $attach_on_start `cat $cidfile`
337a89aa
S
422 exit 0
423 fi
424
425}
426
680dd4ea 427run_bootstrap(){
60f9f04c
S
428
429 host_run
430
680dd4ea 431 get_ssh_pub_key
88126eba 432
680dd4ea
S
433 # Is the image available?
434 # If not, pull it here so the user is aware what's happening.
4807b1b8 435 $docker_path history $image >/dev/null 2>&1 || $docker_path pull $image
88126eba 436
680dd4ea 437 set_template_info
93149421 438
e02c1511 439 base_image=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
680dd4ea 440 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['base_image']"`
93149421 441
e02c1511 442 update_pups=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
680dd4ea 443 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['update_pups']"`
b9c7b50e 444
680dd4ea
S
445 if [[ ! X"" = X"$base_image" ]]; then
446 image=$base_image
447 fi
b9c7b50e 448
680dd4ea 449 set_volumes
41daa523 450 set_links
b9c7b50e 451
680dd4ea 452 rm -f $cidbootstrap
d90671f3 453
680dd4ea
S
454 run_command="cd /pups &&"
455 if [[ ! "false" = $update_pups ]]; then
456 run_command="$run_command git pull &&"
457 fi
458 run_command="$run_command /pups/bin/pups --stdin"
2162f1d4 459
680dd4ea 460 echo $run_command
b9c7b50e 461
680dd4ea 462 env=("${env[@]}" "-e" "SSH_PUB_KEY=$ssh_pub_key")
c4498636 463
e02c1511 464 (exec echo "$input" | $docker_path run $user_args $links "${env[@]}" -e DOCKER_HOST_IP=$docker_ip --cidfile $cidbootstrap -i -a stdin -a stdout -a stderr $volumes $image \
680dd4ea 465 /bin/bash -c "$run_command") \
4807b1b8 466 || ($docker_path rm `cat $cidbootstrap` && rm $cidbootstrap)
88126eba 467
680dd4ea 468 [ ! -e $cidbootstrap ] && echo "FAILED TO BOOTSTRAP" && exit 1
9fb5f2d3 469
680dd4ea 470 sleep 5
2162f1d4 471
4807b1b8
MB
472 $docker_path commit `cat $cidbootstrap` $local_discourse/$config || echo 'FAILED TO COMMIT'
473 $docker_path rm `cat $cidbootstrap` && rm $cidbootstrap
680dd4ea 474}
9fb5f2d3 475
680dd4ea
S
476case "$command" in
477 bootstrap)
680dd4ea 478 run_bootstrap
2dd2e330 479 echo "Successfully bootstrapped, to startup use ./launcher start $config"
4b3aebe1 480 exit 0
5f803fb4 481 ;;
1acce9e4 482
7936ebaa
MB
483 mailtest)
484 run_mailtest
485 exit 0
486 ;;
487
2fc6ff36
S
488 enter)
489
490 if [ ! -e $cidfile ]
491 then
492 echo "No cid found"
493 exit 1
494 fi
495
496 if [ ! $UID -eq 0 ] ;
497 then
498 echo "enter command must run as root, will attempt to sudo"
499 echo
500 fi
501
502 if [ ! -e bin/nsenter ]
503 then
504 echo "Downloading nsenter"
505 $docker_path pull samsaffron/nsenter
e02c1511 506 ($docker_path run $user_args --rm samsaffron/nsenter cat /nsenter > bin/nsenter1) || exit 1
2fc6ff36
S
507 cp bin/nsenter1 bin/nsenter
508 chmod +x bin/nsenter
509 fi
510
511 PID=$($docker_path inspect --format {{.State.Pid}} `cat $cidfile`)
512 SHELL=/bin/bash sudo -E bin/nsenter --target $PID --mount --uts --ipc --net --pid
513
514 exit 0;
515 ;;
516
5f803fb4
SS
517 ssh)
518 if [ ! -e $cidfile ]
519 then
520 echo "No cid found"
521 exit 1
522 else
523 cid="`cat $cidfile`"
4807b1b8 524 address="`$docker_path port $cid 22`"
5f803fb4 525 split=(${address//:/ })
38c09f5e 526 exec ssh -o StrictHostKeyChecking=no root@${split[0]} -p ${split[1]}
5f803fb4
SS
527 fi
528 ;;
7e738616 529
5f803fb4 530 stop)
337a89aa
S
531 run_stop
532 exit 0
5f803fb4 533 ;;
7e738616 534
5f803fb4 535 logs)
7e738616 536
5f803fb4
SS
537 if [ ! -e $cidfile ]
538 then
539 echo "No cid found"
540 exit 1
541 else
4807b1b8 542 $docker_path logs `cat $cidfile`
5f803fb4
SS
543 exit 0
544 fi
545 ;;
7e738616 546
337a89aa
S
547 restart)
548 run_stop
549 run_start
550 exit 0
551 ;;
80c11be3 552
337a89aa
S
553 start)
554 run_start
555 exit 0
5f803fb4 556 ;;
7e738616 557
680dd4ea 558 rebuild)
4b6456ef
MB
559 if [ "$(git symbolic-ref --short HEAD)" == "master" ]; then
560 echo "Updating discourse docker"
561 git pull || (echo 'failed to update' && exit 1)
562 fi
680dd4ea
S
563 if [ -e $cidfile ]
564 then
565 echo "Stopping old container"
4807b1b8 566 $docker_path stop -t 10 `cat $cidfile`
680dd4ea
S
567 fi
568
569 run_bootstrap
570
571 if [ -e $cidfile ]
572 then
4807b1b8 573 $docker_path rm `cat $cidfile` && rm $cidfile
680dd4ea
S
574 fi
575
576 run_start
577 exit 0
578 ;;
579
7e738616 580
5f803fb4
SS
581 destroy)
582 if [ -e $cidfile ]
583 then
584 echo "destroying container $cidfile"
4807b1b8
MB
585 $docker_path stop -t 10 `cat $cidfile`
586 $docker_path rm `cat $cidfile` && rm $cidfile
5f803fb4
SS
587 exit 0
588 else
589 echo "nothing to destroy cidfile does not exist"
590 exit 1
591 fi
592 ;;
593esac
7e738616 594
5f803fb4 595usage