UPDATE to Ruby 2.1.2
[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.0'
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.0
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"
79 echo "Device mapper and other experimental drivers are unstable"
80 echo
81 echo "Please ensure your kernel is running linux extras and aufs"
82 echo "Please follow the installation guide for Docker here: http://docs.docker.io/en/latest/installation/ubuntulinux/"
83 exit 1
84 fi
85
86 # 3. running recommended docker version
87 test=($($docker_path --version)) # Get docker version string
88 test=${test[2]//,/} # Get version alone and strip comma if exists
89
90 # At least minimum version
91 if compare_version "${socker_min_version}" "${test}"; then
92 echo "ERROR: Docker version ${test} not supported, please upgrade to at least ${docker_min_version}, or recommended ${docker_rec_version}"
93 exit 1
94 fi
95
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
101 # 4. able to attach stderr / out / tty
102 test=`$docker_path run -i --rm -a stdout -a stderr $image echo working`
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
111 if [ "$opt" != "--skip-prereqs" ] ; then
112 prereqs
113 fi
114
115 get_ssh_pub_key() {
116 if tty -s ; then
117 if [[ ! -e ~/.ssh/id_rsa.pub && ! -e ~/.ssh/id_dsa.pub ]] ; then
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
120 if [[ $REPLY =~ ^[Nn]$ ]] ; then
121 echo
122 echo WARNING: You may not be able to log in to your container.
123 echo
124 else
125 echo
126 echo Generating SSH key
127 mkdir -p ~/.ssh && ssh-keygen -f ~/.ssh/id_rsa -t rsa -N ''
128 echo
129 fi
130 fi
131 fi
132
133 ssh_pub_key="$(cat ~/.ssh/id_rsa.pub 2>/dev/null || cat ~/.ssh/id_dsa.pub)"
134 }
135
136
137 install_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
155 set_volumes() {
156 volumes=`cat $config_file | $docker_path run --rm -i -a stdout -a stdin $image ruby -e \
157 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['volumes'].map{|v| '-v ' << v['volume']['host'] << ':' << v['volume']['guest'] << ' '}.join"`
158 }
159
160 set_template_info() {
161
162 templates=`cat $config_file | $docker_path run --rm -i -a stdin -a stdout $image ruby -e \
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
186 env = {}
187 input.split('_FILE_SEPERATOR_').each do |yml|
188 yml.strip!
189 begin
190 env.merge!(YAML.load(yml)['env'] || {})
191 rescue => e
192 puts yml
193 p e
194 end
195 end
196 puts env.map{|k,v| "-e\n#{k}=#{v}" }.join("\n")
197 RUBY
198
199 raw=`exec echo "$input" | $docker_path run --rm -i -a stdin -a stdout $image ruby -e "$env_ruby"`
200
201 env=()
202 while read i; do
203 if [ -n "$i" ]; then
204 env[${#env[@]}]=$i
205 fi
206 done <<< "$raw"
207
208 echo "Calculated ENV: ${env[@]}"
209 }
210
211 [ -z $docker_path ] && {
212 install_docker
213 }
214
215
216 [ $# -lt 2 ] && {
217 usage
218 }
219
220 if [ ! -e $config_file ]
221 then
222 echo "Config file was not found, ensure $config_file exists"
223 exit 1
224 fi
225
226
227 run_mailtest(){
228 if [ ! -e $config_file ]; then
229 echo "Config does not exist: $config_file" >&2
230 exit 1
231 fi
232 exec scripts/mailtest $config_file
233 }
234
235 run_stop(){
236 if [ ! -e $cidfile ]
237 then
238 echo "No cid found"
239 exit 1
240 else
241 $docker_path stop -t 10 `cat $cidfile`
242 fi
243 }
244
245 run_start(){
246
247 if [ ! -e $cidfile ]
248 then
249 echo "No cid found, creating a new container"
250 ports=`cat $config_file | $docker_path run --rm -i -a stdout -a stdin $image ruby -e \
251 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['expose'].map{|p| '-p ' << p.to_s << ' '}.join"`
252
253 set_template_info
254 set_volumes
255
256 existing=`$docker_path ps -a | awk '{ print $1, $(NF) }' | grep "$config$" | awk '{ print $1 }'`
257 if [ ! -z $existing ]
258 then
259 echo "Found an existing container by its name, recovering cidfile, please rerun"
260 echo $existing > $cidfile
261 exit 1
262 fi
263
264 $docker_path run "${env[@]}" -h "`hostname`-$config" -e DOCKER_HOST_IP=$docker_ip --name $config -t --cidfile $cidfile $ports \
265 -d $volumes $local_discourse/$config /usr/bin/runsvdir -P /etc/service
266
267 exit 0
268 else
269 cid=`cat $cidfile`
270
271 if [ -z $cid ]
272 then
273 echo "Detected empty cid file, deleting, please re-run"
274 rm $cidfile
275 exit 1
276 fi
277
278 found=`$docker_path ps -q -a --no-trunc | grep $cid`
279 if [ -z $found ]
280 then
281 echo "Invalid cid file, deleting, please re-run"
282 rm $cidfile
283 exit 1
284 fi
285
286 echo "cid found, ensuring container is started"
287 $docker_path start `cat $cidfile`
288 exit 0
289 fi
290
291 }
292
293 run_bootstrap(){
294 get_ssh_pub_key
295
296 # Is the image available?
297 # If not, pull it here so the user is aware what's happening.
298 $docker_path history $image >/dev/null 2>&1 || $docker_path pull $image
299
300 set_template_info
301
302 base_image=`cat $config_file | $docker_path run --rm -i -a stdin -a stdout $image ruby -e \
303 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['base_image']"`
304
305 update_pups=`cat $config_file | $docker_path run --rm -i -a stdin -a stdout $image ruby -e \
306 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['update_pups']"`
307
308 if [[ ! X"" = X"$base_image" ]]; then
309 image=$base_image
310 fi
311
312 set_volumes
313
314 rm -f $cidbootstrap
315
316 run_command="cd /pups &&"
317 if [[ ! "false" = $update_pups ]]; then
318 run_command="$run_command git pull &&"
319 fi
320 run_command="$run_command /pups/bin/pups --stdin"
321
322 echo $run_command
323
324 env=("${env[@]}" "-e" "SSH_PUB_KEY=$ssh_pub_key")
325
326 (exec echo "$input" | $docker_path run "${env[@]}" -e DOCKER_HOST_IP=$docker_ip --cidfile $cidbootstrap -i -a stdin -a stdout -a stderr $volumes $image \
327 /bin/bash -c "$run_command") \
328 || ($docker_path rm `cat $cidbootstrap` && rm $cidbootstrap)
329
330 [ ! -e $cidbootstrap ] && echo "FAILED TO BOOTSTRAP" && exit 1
331
332 sleep 5
333
334 $docker_path commit `cat $cidbootstrap` $local_discourse/$config || echo 'FAILED TO COMMIT'
335 $docker_path rm `cat $cidbootstrap` && rm $cidbootstrap
336 }
337
338 case "$command" in
339 bootstrap)
340 run_bootstrap
341 echo "Successfully bootstrapped, to startup use ./launcher start $config"
342 exit 0
343 ;;
344
345 mailtest)
346 run_mailtest
347 exit 0
348 ;;
349
350 ssh)
351 if [ ! -e $cidfile ]
352 then
353 echo "No cid found"
354 exit 1
355 else
356 cid="`cat $cidfile`"
357 address="`$docker_path port $cid 22`"
358 split=(${address//:/ })
359 exec ssh -o StrictHostKeyChecking=no root@${split[0]} -p ${split[1]}
360 fi
361 ;;
362
363 stop)
364 run_stop
365 exit 0
366 ;;
367
368 logs)
369
370 if [ ! -e $cidfile ]
371 then
372 echo "No cid found"
373 exit 1
374 else
375 $docker_path logs `cat $cidfile`
376 exit 0
377 fi
378 ;;
379
380 restart)
381 run_stop
382 run_start
383 exit 0
384 ;;
385
386 start)
387 run_start
388 exit 0
389 ;;
390
391 rebuild)
392 if [ -e $cidfile ]
393 then
394 echo "Stopping old container"
395 $docker_path stop -t 10 `cat $cidfile`
396 fi
397
398 run_bootstrap
399
400 if [ -e $cidfile ]
401 then
402 $docker_path rm `cat $cidfile` && rm $cidfile
403 fi
404
405 run_start
406 exit 0
407 ;;
408
409
410 destroy)
411 if [ -e $cidfile ]
412 then
413 echo "destroying container $cidfile"
414 $docker_path stop -t 10 `cat $cidfile`
415 $docker_path rm `cat $cidfile` && rm $cidfile
416 exit 0
417 else
418 echo "nothing to destroy cidfile does not exist"
419 exit 1
420 fi
421 ;;
422 esac
423
424 usage