FIX: on bootup ensure correct shared structure
[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
2d7d1501
S
79 echo "Your Docker installation is not using aufs, in the past we have had issues with it"
80 echo "If you are unable to bootstrap your image (or stop it) please report the issue at:"
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
d90671f3 155set_volumes() {
4807b1b8 156 volumes=`cat $config_file | $docker_path run --rm -i -a stdout -a stdin $image ruby -e \
d90671f3
SS
157 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['volumes'].map{|v| '-v ' << v['volume']['host'] << ':' << v['volume']['guest'] << ' '}.join"`
158}
159
7f77c274
SS
160set_template_info() {
161
4807b1b8 162 templates=`cat $config_file | $docker_path run --rm -i -a stdin -a stdout $image ruby -e \
7f77c274
SS
163 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['templates']"`
164
165
166 arrTemplates=(${templates// / })
167 config_data=$(cat $config_file)
168
169 input="hack: true"
170
171
172 for template in "${arrTemplates[@]}"
173 do
174 [ ! -z $template ] && {
175 input="$input _FILE_SEPERATOR_ $(cat $template)"
176 }
177 done
178
179 # we always want our config file last so it takes priority
180 input="$input _FILE_SEPERATOR_ $config_data"
181
182 read -r -d '' env_ruby << 'RUBY'
183 require 'yaml'
184
185 input=STDIN.readlines.join
3cb3d9c4
S
186 # default to UTF-8 for the dbs sake
187 env = {'LANG' => 'en_US.UTF-8'}
7f77c274
SS
188 input.split('_FILE_SEPERATOR_').each do |yml|
189 yml.strip!
190 begin
191 env.merge!(YAML.load(yml)['env'] || {})
f3824347 192 rescue Psych::SyntaxError => e
193 puts e
194 puts "*ERROR."
7f77c274
SS
195 rescue => e
196 puts yml
197 p e
198 end
199 end
4b563ee8 200 puts env.map{|k,v| "-e\n#{k}=#{v}" }.join("\n")
7f77c274
SS
201RUBY
202
4807b1b8 203 raw=`exec echo "$input" | $docker_path run --rm -i -a stdin -a stdout $image ruby -e "$env_ruby"`
4b563ee8
SS
204
205 env=()
f3824347 206 ok=1
4b563ee8 207 while read i; do
f3824347 208 if [ "$i" == "*ERROR." ]; then
209 ok=0
210 elif [ -n "$i" ]; then
16f2d250
S
211 env[${#env[@]}]=$i
212 fi
4b563ee8
SS
213 done <<< "$raw"
214
f3824347 215 if [ "$ok" -ne 1 ]; then
216 echo "${env[@]}"
a3322f2a 217 echo "YAML syntax error. Please check your /var/docker/containers/*.yml config files."
f3824347 218 exit 1
219 fi
4b563ee8 220 echo "Calculated ENV: ${env[@]}"
7f77c274
SS
221}
222
52388b87
SS
223[ -z $docker_path ] && {
224 install_docker
225}
226
5f803fb4 227
c1005add 228[ $# -lt 2 ] && {
5f803fb4
SS
229 usage
230}
231
7e738616
S
232if [ ! -e $config_file ]
233 then
234 echo "Config file was not found, ensure $config_file exists"
71680b16
S
235 echo ""
236 echo "Available configs ( `cd containers && ls -dm *.yml | tr -s '\n' ' ' | awk '{ gsub(/\.yml/, ""); print }'`)"
7e738616
S
237 exit 1
238fi
239
337a89aa 240
e2ed1fb6
S
241docker_version=($($docker_path --version))
242docker_version=${test[2]//,/}
243
244if compare_version "1.2.0" "$docker_version"; then
245 echo "We recommend you upgrade docker, the version you are running has no restart policies, on reboot your container may not start up"
246 restart_policy=""
247else
b6227eb0 248 restart_policy="--restart=always"
e2ed1fb6
S
249fi
250
251
7936ebaa
MB
252run_mailtest(){
253 if [ ! -e $config_file ]; then
254 echo "Config does not exist: $config_file" >&2
255 exit 1
256 fi
257 exec scripts/mailtest $config_file
258}
259
337a89aa
S
260run_stop(){
261 if [ ! -e $cidfile ]
262 then
263 echo "No cid found"
264 exit 1
265 else
4807b1b8 266 $docker_path stop -t 10 `cat $cidfile`
337a89aa
S
267 fi
268}
269
270run_start(){
271
272 if [ ! -e $cidfile ]
273 then
274 echo "No cid found, creating a new container"
4807b1b8 275 ports=`cat $config_file | $docker_path run --rm -i -a stdout -a stdin $image ruby -e \
337a89aa
S
276 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['expose'].map{|p| '-p ' << p.to_s << ' '}.join"`
277
278 set_template_info
279 set_volumes
280
4807b1b8 281 existing=`$docker_path ps -a | awk '{ print $1, $(NF) }' | grep "$config$" | awk '{ print $1 }'`
337a89aa
S
282 if [ ! -z $existing ]
283 then
284 echo "Found an existing container by its name, recovering cidfile, please rerun"
285 echo $existing > $cidfile
286 exit 1
287 fi
288
b6227eb0 289 $docker_path run $restart_policy "${env[@]}" -h "`hostname`-$config" -e DOCKER_HOST_IP=$docker_ip --name $config -t --cidfile $cidfile $ports \
23022c1d 290 -d $volumes $local_discourse/$config /sbin/runit
337a89aa
S
291
292 exit 0
293 else
294 cid=`cat $cidfile`
295
296 if [ -z $cid ]
297 then
298 echo "Detected empty cid file, deleting, please re-run"
299 rm $cidfile
300 exit 1
301 fi
302
4807b1b8 303 found=`$docker_path ps -q -a --no-trunc | grep $cid`
337a89aa
S
304 if [ -z $found ]
305 then
306 echo "Invalid cid file, deleting, please re-run"
307 rm $cidfile
308 exit 1
309 fi
310
311 echo "cid found, ensuring container is started"
4807b1b8 312 $docker_path start `cat $cidfile`
337a89aa
S
313 exit 0
314 fi
315
316}
317
680dd4ea
S
318run_bootstrap(){
319 get_ssh_pub_key
88126eba 320
680dd4ea
S
321 # Is the image available?
322 # If not, pull it here so the user is aware what's happening.
4807b1b8 323 $docker_path history $image >/dev/null 2>&1 || $docker_path pull $image
88126eba 324
680dd4ea 325 set_template_info
93149421 326
4807b1b8 327 base_image=`cat $config_file | $docker_path run --rm -i -a stdin -a stdout $image ruby -e \
680dd4ea 328 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['base_image']"`
93149421 329
4807b1b8 330 update_pups=`cat $config_file | $docker_path run --rm -i -a stdin -a stdout $image ruby -e \
680dd4ea 331 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['update_pups']"`
b9c7b50e 332
680dd4ea
S
333 if [[ ! X"" = X"$base_image" ]]; then
334 image=$base_image
335 fi
b9c7b50e 336
680dd4ea 337 set_volumes
b9c7b50e 338
680dd4ea 339 rm -f $cidbootstrap
d90671f3 340
680dd4ea
S
341 run_command="cd /pups &&"
342 if [[ ! "false" = $update_pups ]]; then
343 run_command="$run_command git pull &&"
344 fi
345 run_command="$run_command /pups/bin/pups --stdin"
2162f1d4 346
680dd4ea 347 echo $run_command
b9c7b50e 348
680dd4ea 349 env=("${env[@]}" "-e" "SSH_PUB_KEY=$ssh_pub_key")
c4498636 350
4807b1b8 351 (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 352 /bin/bash -c "$run_command") \
4807b1b8 353 || ($docker_path rm `cat $cidbootstrap` && rm $cidbootstrap)
88126eba 354
680dd4ea 355 [ ! -e $cidbootstrap ] && echo "FAILED TO BOOTSTRAP" && exit 1
9fb5f2d3 356
680dd4ea 357 sleep 5
2162f1d4 358
4807b1b8
MB
359 $docker_path commit `cat $cidbootstrap` $local_discourse/$config || echo 'FAILED TO COMMIT'
360 $docker_path rm `cat $cidbootstrap` && rm $cidbootstrap
680dd4ea 361}
9fb5f2d3 362
680dd4ea
S
363case "$command" in
364 bootstrap)
680dd4ea 365 run_bootstrap
2dd2e330 366 echo "Successfully bootstrapped, to startup use ./launcher start $config"
4b3aebe1 367 exit 0
5f803fb4 368 ;;
1acce9e4 369
7936ebaa
MB
370 mailtest)
371 run_mailtest
372 exit 0
373 ;;
374
2fc6ff36
S
375 enter)
376
377 if [ ! -e $cidfile ]
378 then
379 echo "No cid found"
380 exit 1
381 fi
382
383 if [ ! $UID -eq 0 ] ;
384 then
385 echo "enter command must run as root, will attempt to sudo"
386 echo
387 fi
388
389 if [ ! -e bin/nsenter ]
390 then
391 echo "Downloading nsenter"
392 $docker_path pull samsaffron/nsenter
393 ($docker_path run --rm samsaffron/nsenter cat /nsenter > bin/nsenter1) || exit 1
394 cp bin/nsenter1 bin/nsenter
395 chmod +x bin/nsenter
396 fi
397
398 PID=$($docker_path inspect --format {{.State.Pid}} `cat $cidfile`)
399 SHELL=/bin/bash sudo -E bin/nsenter --target $PID --mount --uts --ipc --net --pid
400
401 exit 0;
402 ;;
403
5f803fb4
SS
404 ssh)
405 if [ ! -e $cidfile ]
406 then
407 echo "No cid found"
408 exit 1
409 else
410 cid="`cat $cidfile`"
4807b1b8 411 address="`$docker_path port $cid 22`"
5f803fb4 412 split=(${address//:/ })
38c09f5e 413 exec ssh -o StrictHostKeyChecking=no root@${split[0]} -p ${split[1]}
5f803fb4
SS
414 fi
415 ;;
7e738616 416
5f803fb4 417 stop)
337a89aa
S
418 run_stop
419 exit 0
5f803fb4 420 ;;
7e738616 421
5f803fb4 422 logs)
7e738616 423
5f803fb4
SS
424 if [ ! -e $cidfile ]
425 then
426 echo "No cid found"
427 exit 1
428 else
4807b1b8 429 $docker_path logs `cat $cidfile`
5f803fb4
SS
430 exit 0
431 fi
432 ;;
7e738616 433
337a89aa
S
434 restart)
435 run_stop
436 run_start
437 exit 0
438 ;;
80c11be3 439
337a89aa
S
440 start)
441 run_start
442 exit 0
5f803fb4 443 ;;
7e738616 444
680dd4ea 445 rebuild)
4b6456ef
MB
446 if [ "$(git symbolic-ref --short HEAD)" == "master" ]; then
447 echo "Updating discourse docker"
448 git pull || (echo 'failed to update' && exit 1)
449 fi
680dd4ea
S
450 if [ -e $cidfile ]
451 then
452 echo "Stopping old container"
4807b1b8 453 $docker_path stop -t 10 `cat $cidfile`
680dd4ea
S
454 fi
455
456 run_bootstrap
457
458 if [ -e $cidfile ]
459 then
4807b1b8 460 $docker_path rm `cat $cidfile` && rm $cidfile
680dd4ea
S
461 fi
462
463 run_start
464 exit 0
465 ;;
466
7e738616 467
5f803fb4
SS
468 destroy)
469 if [ -e $cidfile ]
470 then
471 echo "destroying container $cidfile"
4807b1b8
MB
472 $docker_path stop -t 10 `cat $cidfile`
473 $docker_path rm `cat $cidfile` && rm $cidfile
5f803fb4
SS
474 exit 0
475 else
476 echo "nothing to destroy cidfile does not exist"
477 exit 1
478 fi
479 ;;
480esac
7e738616 481
5f803fb4 482usage