f17fc7ca743f39b69147af87f407bb5b1982a605
[discourse_docker.git] / launcher
1 #!/bin/bash
2
3 command=$1
4 config=$2
5 opt=$3
6
7 cd "$(dirname "$0")"
8
9 docker_min_version='0.9.1'
10 docker_rec_version='0.11.1'
11
12 config_file=containers/"$config".yml
13 cidfile=cids/"$config".cid
14 cidbootstrap=cids/"$config"_boostrap.cid
15 local_discourse=local_discourse
16 image=samsaffron/discourse:0.2.4
17 docker_path=`which docker.io || which docker`
18
19 docker_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
26 usage () {
27 echo "Usage: launcher COMMAND CONFIG [--skip-prereqs]"
28 echo "Commands:"
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"
33 echo " ssh: Start a bash shell in a running container"
34 echo " logs: Docker logs for container"
35 echo " mailtest: Test the mail settings in a container"
36 echo " bootstrap: Bootstrap a container for the config based on a template"
37 echo " rebuild: Rebuild a container (destroy old, bootstrap, start new)"
38 echo
39 echo "Options:"
40 echo " --skip-prereqs Don't check prerequisites"
41 exit 1
42 }
43
44 compare_version() {
45 declare -a ver_a
46 declare -a ver_b
47 IFS=. read -a ver_a <<< "$1"
48 IFS=. read -a ver_b <<< "$2"
49
50 while [[ -n $ver_a ]]; do
51 if (( ver_a > ver_b )); then
52 return 0
53 elif (( ver_b > ver_a )); then
54 return 1
55 else
56 unset ver_a[0]
57 ver_a=("${ver_a[@]}")
58 unset ver_b[0]
59 ver_b=("${ver_b[@]}")
60 fi
61 done
62 return 1 # They are equal
63 }
64
65 prereqs() {
66
67 # 1. docker daemon running?
68 test=`$docker_path info >/dev/null`
69
70 if [[ $? -ne 0 ]] ; then
71 echo "Cannot connect to the docker daemon - verify it is running and you have access"
72 exit 1
73 fi
74
75 # 2. running aufs
76 test=`$docker_path info 2> /dev/null | grep 'Driver: aufs'`
77 if [[ "$test" =~ "aufs" ]] ; then : ; else
78 echo "Your Docker installation is not using aufs, in the past we have had issues with it"
79 echo "If you are unable to bootstrap your image (or stop it) please report the issue at:"
80 echo "https://meta.discourse.org/t/discourse-docker-installation-without-aufs/15639"
81 fi
82
83 # 3. running recommended docker version
84 test=($($docker_path --version)) # Get docker version string
85 test=${test[2]//,/} # Get version alone and strip comma if exists
86
87 [[ "$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
88
89 # At least minimum version
90 if compare_version "${docker_min_version}" "${test}"; then
91 echo "ERROR: Docker version ${test} not supported, please upgrade to at least ${docker_min_version}, or recommended ${docker_rec_version}"
92 exit 1
93 fi
94
95 # Recommend best version
96 if compare_version "${docker_rec_version}" "${test}"; then
97 echo "WARNING: Docker version ${test} deprecated, recommend upgrade to ${docker_rec_version} or newer."
98 fi
99
100 # 4. able to attach stderr / out / tty
101 test=`$docker_path run -i --rm -a stdout -a stderr $image echo working`
102 if [[ "$test" =~ "working" ]] ; then : ; else
103 echo "Your Docker installation is not working correctly"
104 echo
105 echo "See: https://meta.discourse.org/t/docker-error-on-bootstrap/13657/18?u=sam"
106 exit 1
107 fi
108 }
109
110 if [ "$opt" != "--skip-prereqs" ] ; then
111 prereqs
112 fi
113
114 get_ssh_pub_key() {
115 if tty -s ; then
116 if [[ ! -e ~/.ssh/id_rsa.pub && ! -e ~/.ssh/id_dsa.pub ]] ; then
117 echo "This user has no SSH key, but a SSH key is required to access the Discourse Docker container."
118 read -p "Generate a SSH key? (Y/n) " -n 1 -r
119 if [[ $REPLY =~ ^[Nn]$ ]] ; then
120 echo
121 echo WARNING: You may not be able to log in to your container.
122 echo
123 else
124 echo
125 echo Generating SSH key
126 mkdir -p ~/.ssh && ssh-keygen -f ~/.ssh/id_rsa -t rsa -N ''
127 echo
128 fi
129 fi
130 fi
131
132 ssh_pub_key="$(cat ~/.ssh/id_rsa.pub 2>/dev/null || cat ~/.ssh/id_dsa.pub)"
133 }
134
135
136 install_docker() {
137
138 echo "Docker is not installed, make sure you are running on the 3.8 kernel"
139 echo "The best supported Docker release is Ubuntu 12.04.03 for it run the following"
140 echo
141 echo "sudo apt-get update"
142 echo "sudo apt-get install linux-image-generic-lts-raring linux-headers-generic-lts-raring"
143 echo "sudo reboot"
144 echo
145
146 echo "sudo sh -c \"wget -qO- https://get.docker.io/gpg | apt-key add -\""
147 echo "sudo sh -c \"echo deb http://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list\""
148 echo "sudo apt-get update"
149 echo "sudo apt-get install lxc-docker"
150
151 exit 1
152 }
153
154 set_volumes() {
155 volumes=`cat $config_file | $docker_path run --rm -i -a stdout -a stdin $image ruby -e \
156 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['volumes'].map{|v| '-v ' << v['volume']['host'] << ':' << v['volume']['guest'] << ' '}.join"`
157 }
158
159 set_template_info() {
160
161 templates=`cat $config_file | $docker_path run --rm -i -a stdin -a stdout $image ruby -e \
162 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['templates']"`
163
164
165 arrTemplates=(${templates// / })
166 config_data=$(cat $config_file)
167
168 input="hack: true"
169
170
171 for template in "${arrTemplates[@]}"
172 do
173 [ ! -z $template ] && {
174 input="$input _FILE_SEPERATOR_ $(cat $template)"
175 }
176 done
177
178 # we always want our config file last so it takes priority
179 input="$input _FILE_SEPERATOR_ $config_data"
180
181 read -r -d '' env_ruby << 'RUBY'
182 require 'yaml'
183
184 input=STDIN.readlines.join
185 env = {}
186 input.split('_FILE_SEPERATOR_').each do |yml|
187 yml.strip!
188 begin
189 env.merge!(YAML.load(yml)['env'] || {})
190 rescue Psych::SyntaxError => e
191 puts e
192 puts "*ERROR."
193 rescue => e
194 puts yml
195 p e
196 end
197 end
198 puts env.map{|k,v| "-e\n#{k}=#{v}" }.join("\n")
199 RUBY
200
201 raw=`exec echo "$input" | $docker_path run --rm -i -a stdin -a stdout $image ruby -e "$env_ruby"`
202
203 env=()
204 ok=1
205 while read i; do
206 if [ "$i" == "*ERROR." ]; then
207 ok=0
208 elif [ -n "$i" ]; then
209 env[${#env[@]}]=$i
210 fi
211 done <<< "$raw"
212
213 if [ "$ok" -ne 1 ]; then
214 echo "${env[@]}"
215 echo "YAML syntax error. Please check your /var/docker/containers/*.yml config files."
216 exit 1
217 fi
218 echo "Calculated ENV: ${env[@]}"
219 }
220
221 [ -z $docker_path ] && {
222 install_docker
223 }
224
225
226 [ $# -lt 2 ] && {
227 usage
228 }
229
230 if [ ! -e $config_file ]
231 then
232 echo "Config file was not found, ensure $config_file exists"
233 echo ""
234 echo "Available configs ( `cd containers && ls -dm *.yml | tr -s '\n' ' ' | awk '{ gsub(/\.yml/, ""); print }'`)"
235 exit 1
236 fi
237
238
239 run_mailtest(){
240 if [ ! -e $config_file ]; then
241 echo "Config does not exist: $config_file" >&2
242 exit 1
243 fi
244 exec scripts/mailtest $config_file
245 }
246
247 run_stop(){
248 if [ ! -e $cidfile ]
249 then
250 echo "No cid found"
251 exit 1
252 else
253 $docker_path stop -t 10 `cat $cidfile`
254 fi
255 }
256
257 run_start(){
258
259 if [ ! -e $cidfile ]
260 then
261 echo "No cid found, creating a new container"
262 ports=`cat $config_file | $docker_path run --rm -i -a stdout -a stdin $image ruby -e \
263 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['expose'].map{|p| '-p ' << p.to_s << ' '}.join"`
264
265 set_template_info
266 set_volumes
267
268 existing=`$docker_path ps -a | awk '{ print $1, $(NF) }' | grep "$config$" | awk '{ print $1 }'`
269 if [ ! -z $existing ]
270 then
271 echo "Found an existing container by its name, recovering cidfile, please rerun"
272 echo $existing > $cidfile
273 exit 1
274 fi
275
276 $docker_path run "${env[@]}" -h "`hostname`-$config" -e DOCKER_HOST_IP=$docker_ip --name $config -t --cidfile $cidfile $ports \
277 -d $volumes $local_discourse/$config /sbin/runit
278
279 exit 0
280 else
281 cid=`cat $cidfile`
282
283 if [ -z $cid ]
284 then
285 echo "Detected empty cid file, deleting, please re-run"
286 rm $cidfile
287 exit 1
288 fi
289
290 found=`$docker_path ps -q -a --no-trunc | grep $cid`
291 if [ -z $found ]
292 then
293 echo "Invalid cid file, deleting, please re-run"
294 rm $cidfile
295 exit 1
296 fi
297
298 echo "cid found, ensuring container is started"
299 $docker_path start `cat $cidfile`
300 exit 0
301 fi
302
303 }
304
305 run_bootstrap(){
306 get_ssh_pub_key
307
308 # Is the image available?
309 # If not, pull it here so the user is aware what's happening.
310 $docker_path history $image >/dev/null 2>&1 || $docker_path pull $image
311
312 set_template_info
313
314 base_image=`cat $config_file | $docker_path run --rm -i -a stdin -a stdout $image ruby -e \
315 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['base_image']"`
316
317 update_pups=`cat $config_file | $docker_path run --rm -i -a stdin -a stdout $image ruby -e \
318 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['update_pups']"`
319
320 if [[ ! X"" = X"$base_image" ]]; then
321 image=$base_image
322 fi
323
324 set_volumes
325
326 rm -f $cidbootstrap
327
328 run_command="cd /pups &&"
329 if [[ ! "false" = $update_pups ]]; then
330 run_command="$run_command git pull &&"
331 fi
332 run_command="$run_command /pups/bin/pups --stdin"
333
334 echo $run_command
335
336 env=("${env[@]}" "-e" "SSH_PUB_KEY=$ssh_pub_key")
337
338 (exec echo "$input" | $docker_path run "${env[@]}" -e DOCKER_HOST_IP=$docker_ip --cidfile $cidbootstrap -i -a stdin -a stdout -a stderr $volumes $image \
339 /bin/bash -c "$run_command") \
340 || ($docker_path rm `cat $cidbootstrap` && rm $cidbootstrap)
341
342 [ ! -e $cidbootstrap ] && echo "FAILED TO BOOTSTRAP" && exit 1
343
344 sleep 5
345
346 $docker_path commit `cat $cidbootstrap` $local_discourse/$config || echo 'FAILED TO COMMIT'
347 $docker_path rm `cat $cidbootstrap` && rm $cidbootstrap
348 }
349
350 case "$command" in
351 bootstrap)
352 run_bootstrap
353 echo "Successfully bootstrapped, to startup use ./launcher start $config"
354 exit 0
355 ;;
356
357 mailtest)
358 run_mailtest
359 exit 0
360 ;;
361
362 ssh)
363 if [ ! -e $cidfile ]
364 then
365 echo "No cid found"
366 exit 1
367 else
368 cid="`cat $cidfile`"
369 address="`$docker_path port $cid 22`"
370 split=(${address//:/ })
371 exec ssh -o StrictHostKeyChecking=no root@${split[0]} -p ${split[1]}
372 fi
373 ;;
374
375 stop)
376 run_stop
377 exit 0
378 ;;
379
380 logs)
381
382 if [ ! -e $cidfile ]
383 then
384 echo "No cid found"
385 exit 1
386 else
387 $docker_path logs `cat $cidfile`
388 exit 0
389 fi
390 ;;
391
392 restart)
393 run_stop
394 run_start
395 exit 0
396 ;;
397
398 start)
399 run_start
400 exit 0
401 ;;
402
403 rebuild)
404 if [ -e $cidfile ]
405 then
406 echo "Stopping old container"
407 $docker_path stop -t 10 `cat $cidfile`
408 fi
409
410 run_bootstrap
411
412 if [ -e $cidfile ]
413 then
414 $docker_path rm `cat $cidfile` && rm $cidfile
415 fi
416
417 run_start
418 exit 0
419 ;;
420
421
422 destroy)
423 if [ -e $cidfile ]
424 then
425 echo "destroying container $cidfile"
426 $docker_path stop -t 10 `cat $cidfile`
427 $docker_path rm `cat $cidfile` && rm $cidfile
428 exit 0
429 else
430 echo "nothing to destroy cidfile does not exist"
431 exit 1
432 fi
433 ;;
434 esac
435
436 usage