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