FIX: have "rebuild" update discourse docker
[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
60668406 9docker_min_version='0.9.1'
68f0b9dc 10docker_rec_version='0.11.1'
60668406 11
8dea575c 12config_file=containers/"$config".yml
7e738616 13cidfile=cids/"$config".cid
1acce9e4 14cidbootstrap=cids/"$config"_boostrap.cid
5efded6a 15local_discourse=local_discourse
d9c1b419 16image=samsaffron/discourse:0.2.5
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
7936ebaa
MB
241run_mailtest(){
242 if [ ! -e $config_file ]; then
243 echo "Config does not exist: $config_file" >&2
244 exit 1
245 fi
246 exec scripts/mailtest $config_file
247}
248
337a89aa
S
249run_stop(){
250 if [ ! -e $cidfile ]
251 then
252 echo "No cid found"
253 exit 1
254 else
4807b1b8 255 $docker_path stop -t 10 `cat $cidfile`
337a89aa
S
256 fi
257}
258
259run_start(){
260
261 if [ ! -e $cidfile ]
262 then
263 echo "No cid found, creating a new container"
4807b1b8 264 ports=`cat $config_file | $docker_path run --rm -i -a stdout -a stdin $image ruby -e \
337a89aa
S
265 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['expose'].map{|p| '-p ' << p.to_s << ' '}.join"`
266
267 set_template_info
268 set_volumes
269
4807b1b8 270 existing=`$docker_path ps -a | awk '{ print $1, $(NF) }' | grep "$config$" | awk '{ print $1 }'`
337a89aa
S
271 if [ ! -z $existing ]
272 then
273 echo "Found an existing container by its name, recovering cidfile, please rerun"
274 echo $existing > $cidfile
275 exit 1
276 fi
277
4807b1b8 278 $docker_path run "${env[@]}" -h "`hostname`-$config" -e DOCKER_HOST_IP=$docker_ip --name $config -t --cidfile $cidfile $ports \
23022c1d 279 -d $volumes $local_discourse/$config /sbin/runit
337a89aa
S
280
281 exit 0
282 else
283 cid=`cat $cidfile`
284
285 if [ -z $cid ]
286 then
287 echo "Detected empty cid file, deleting, please re-run"
288 rm $cidfile
289 exit 1
290 fi
291
4807b1b8 292 found=`$docker_path ps -q -a --no-trunc | grep $cid`
337a89aa
S
293 if [ -z $found ]
294 then
295 echo "Invalid cid file, deleting, please re-run"
296 rm $cidfile
297 exit 1
298 fi
299
300 echo "cid found, ensuring container is started"
4807b1b8 301 $docker_path start `cat $cidfile`
337a89aa
S
302 exit 0
303 fi
304
305}
306
680dd4ea
S
307run_bootstrap(){
308 get_ssh_pub_key
88126eba 309
680dd4ea
S
310 # Is the image available?
311 # If not, pull it here so the user is aware what's happening.
4807b1b8 312 $docker_path history $image >/dev/null 2>&1 || $docker_path pull $image
88126eba 313
680dd4ea 314 set_template_info
93149421 315
4807b1b8 316 base_image=`cat $config_file | $docker_path run --rm -i -a stdin -a stdout $image ruby -e \
680dd4ea 317 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['base_image']"`
93149421 318
4807b1b8 319 update_pups=`cat $config_file | $docker_path run --rm -i -a stdin -a stdout $image ruby -e \
680dd4ea 320 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['update_pups']"`
b9c7b50e 321
680dd4ea
S
322 if [[ ! X"" = X"$base_image" ]]; then
323 image=$base_image
324 fi
b9c7b50e 325
680dd4ea 326 set_volumes
b9c7b50e 327
680dd4ea 328 rm -f $cidbootstrap
d90671f3 329
680dd4ea
S
330 run_command="cd /pups &&"
331 if [[ ! "false" = $update_pups ]]; then
332 run_command="$run_command git pull &&"
333 fi
334 run_command="$run_command /pups/bin/pups --stdin"
2162f1d4 335
680dd4ea 336 echo $run_command
b9c7b50e 337
680dd4ea 338 env=("${env[@]}" "-e" "SSH_PUB_KEY=$ssh_pub_key")
c4498636 339
4807b1b8 340 (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 341 /bin/bash -c "$run_command") \
4807b1b8 342 || ($docker_path rm `cat $cidbootstrap` && rm $cidbootstrap)
88126eba 343
680dd4ea 344 [ ! -e $cidbootstrap ] && echo "FAILED TO BOOTSTRAP" && exit 1
9fb5f2d3 345
680dd4ea 346 sleep 5
2162f1d4 347
4807b1b8
MB
348 $docker_path commit `cat $cidbootstrap` $local_discourse/$config || echo 'FAILED TO COMMIT'
349 $docker_path rm `cat $cidbootstrap` && rm $cidbootstrap
680dd4ea 350}
9fb5f2d3 351
680dd4ea
S
352case "$command" in
353 bootstrap)
680dd4ea 354 run_bootstrap
2dd2e330 355 echo "Successfully bootstrapped, to startup use ./launcher start $config"
4b3aebe1 356 exit 0
5f803fb4 357 ;;
1acce9e4 358
7936ebaa
MB
359 mailtest)
360 run_mailtest
361 exit 0
362 ;;
363
2fc6ff36
S
364 enter)
365
366 if [ ! -e $cidfile ]
367 then
368 echo "No cid found"
369 exit 1
370 fi
371
372 if [ ! $UID -eq 0 ] ;
373 then
374 echo "enter command must run as root, will attempt to sudo"
375 echo
376 fi
377
378 if [ ! -e bin/nsenter ]
379 then
380 echo "Downloading nsenter"
381 $docker_path pull samsaffron/nsenter
382 ($docker_path run --rm samsaffron/nsenter cat /nsenter > bin/nsenter1) || exit 1
383 cp bin/nsenter1 bin/nsenter
384 chmod +x bin/nsenter
385 fi
386
387 PID=$($docker_path inspect --format {{.State.Pid}} `cat $cidfile`)
388 SHELL=/bin/bash sudo -E bin/nsenter --target $PID --mount --uts --ipc --net --pid
389
390 exit 0;
391 ;;
392
5f803fb4
SS
393 ssh)
394 if [ ! -e $cidfile ]
395 then
396 echo "No cid found"
397 exit 1
398 else
399 cid="`cat $cidfile`"
4807b1b8 400 address="`$docker_path port $cid 22`"
5f803fb4 401 split=(${address//:/ })
38c09f5e 402 exec ssh -o StrictHostKeyChecking=no root@${split[0]} -p ${split[1]}
5f803fb4
SS
403 fi
404 ;;
7e738616 405
5f803fb4 406 stop)
337a89aa
S
407 run_stop
408 exit 0
5f803fb4 409 ;;
7e738616 410
5f803fb4 411 logs)
7e738616 412
5f803fb4
SS
413 if [ ! -e $cidfile ]
414 then
415 echo "No cid found"
416 exit 1
417 else
4807b1b8 418 $docker_path logs `cat $cidfile`
5f803fb4
SS
419 exit 0
420 fi
421 ;;
7e738616 422
337a89aa
S
423 restart)
424 run_stop
425 run_start
426 exit 0
427 ;;
80c11be3 428
337a89aa
S
429 start)
430 run_start
431 exit 0
5f803fb4 432 ;;
7e738616 433
680dd4ea 434 rebuild)
21d7b075
S
435 echo "Updating discourse docker"
436 git pull || (echo 'failed to update' && exit 1)
680dd4ea
S
437 if [ -e $cidfile ]
438 then
439 echo "Stopping old container"
4807b1b8 440 $docker_path stop -t 10 `cat $cidfile`
680dd4ea
S
441 fi
442
443 run_bootstrap
444
445 if [ -e $cidfile ]
446 then
4807b1b8 447 $docker_path rm `cat $cidfile` && rm $cidfile
680dd4ea
S
448 fi
449
450 run_start
451 exit 0
452 ;;
453
7e738616 454
5f803fb4
SS
455 destroy)
456 if [ -e $cidfile ]
457 then
458 echo "destroying container $cidfile"
4807b1b8
MB
459 $docker_path stop -t 10 `cat $cidfile`
460 $docker_path rm `cat $cidfile` && rm $cidfile
5f803fb4
SS
461 exit 0
462 else
463 echo "nothing to destroy cidfile does not exist"
464 exit 1
465 fi
466 ;;
467esac
7e738616 468
5f803fb4 469usage