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