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