default is wrong here, we really need an ssh key, its critical
[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 You have no SSH key associated to this profile
119 echo "(This will allow you ssh access into your container)"
120 read -p "Generate SSH key at ~/.ssh/id_rsa.pub? (Y/n) " -n 1 -r
121 if [[ $REPLY =~ ^[Nn]$ ]] ; then
122 echo
123 echo WARNING: You may not be able to log in to your container.
124 echo
125 else
126 echo
127 echo Generating SSH key
128 mkdir -p ~/.ssh && ssh-keygen -f ~/.ssh/id_rsa -t rsa -N ''
129 echo
130 fi
131 fi
132 fi
133
134 ssh_pub_key="$(cat ~/.ssh/id_rsa.pub 2>/dev/null || cat ~/.ssh/id_dsa.pub)"
135 }
136
137
138 install_docker() {
139
140 echo "Docker is not installed, make sure you are running on the 3.8 kernel"
141 echo "The best supported Docker release is Ubuntu 12.04.03 for it run the following"
142 echo
143 echo "sudo apt-get update"
144 echo "sudo apt-get install linux-image-generic-lts-raring linux-headers-generic-lts-raring"
145 echo "sudo reboot"
146 echo
147
148 echo "sudo sh -c \"wget -qO- https://get.docker.io/gpg | apt-key add -\""
149 echo "sudo sh -c \"echo deb http://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list\""
150 echo "sudo apt-get update"
151 echo "sudo apt-get install lxc-docker"
152
153 exit 1
154 }
155
156 set_volumes() {
157 volumes=`cat $config_file | $docker_path run --rm -i -a stdout -a stdin $image ruby -e \
158 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['volumes'].map{|v| '-v ' << v['volume']['host'] << ':' << v['volume']['guest'] << ' '}.join"`
159 }
160
161 set_template_info() {
162
163 templates=`cat $config_file | $docker_path run --rm -i -a stdin -a stdout $image ruby -e \
164 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['templates']"`
165
166
167 arrTemplates=(${templates// / })
168 config_data=$(cat $config_file)
169
170 input="hack: true"
171
172
173 for template in "${arrTemplates[@]}"
174 do
175 [ ! -z $template ] && {
176 input="$input _FILE_SEPERATOR_ $(cat $template)"
177 }
178 done
179
180 # we always want our config file last so it takes priority
181 input="$input _FILE_SEPERATOR_ $config_data"
182
183 read -r -d '' env_ruby << 'RUBY'
184 require 'yaml'
185
186 input=STDIN.readlines.join
187 env = {}
188 input.split('_FILE_SEPERATOR_').each do |yml|
189 yml.strip!
190 begin
191 env.merge!(YAML.load(yml)['env'] || {})
192 rescue => e
193 puts yml
194 p e
195 end
196 end
197 puts env.map{|k,v| "-e\n#{k}=#{v}" }.join("\n")
198 RUBY
199
200 raw=`exec echo "$input" | $docker_path run --rm -i -a stdin -a stdout $image ruby -e "$env_ruby"`
201
202 env=()
203 while read i; do
204 if [ -n "$i" ]; then
205 env[${#env[@]}]=$i
206 fi
207 done <<< "$raw"
208
209 echo "Calculated ENV: ${env[@]}"
210 }
211
212 [ -z $docker_path ] && {
213 install_docker
214 }
215
216
217 [ $# -lt 2 ] && {
218 usage
219 }
220
221 if [ ! -e $config_file ]
222 then
223 echo "Config file was not found, ensure $config_file exists"
224 exit 1
225 fi
226
227
228 run_mailtest(){
229 if [ ! -e $config_file ]; then
230 echo "Config does not exist: $config_file" >&2
231 exit 1
232 fi
233 exec scripts/mailtest $config_file
234 }
235
236 run_stop(){
237 if [ ! -e $cidfile ]
238 then
239 echo "No cid found"
240 exit 1
241 else
242 $docker_path stop -t 10 `cat $cidfile`
243 fi
244 }
245
246 run_start(){
247
248 if [ ! -e $cidfile ]
249 then
250 echo "No cid found, creating a new container"
251 ports=`cat $config_file | $docker_path run --rm -i -a stdout -a stdin $image ruby -e \
252 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['expose'].map{|p| '-p ' << p.to_s << ' '}.join"`
253
254 set_template_info
255 set_volumes
256
257 existing=`$docker_path ps -a | awk '{ print $1, $(NF) }' | grep "$config$" | awk '{ print $1 }'`
258 if [ ! -z $existing ]
259 then
260 echo "Found an existing container by its name, recovering cidfile, please rerun"
261 echo $existing > $cidfile
262 exit 1
263 fi
264
265 $docker_path run "${env[@]}" -h "`hostname`-$config" -e DOCKER_HOST_IP=$docker_ip --name $config -t --cidfile $cidfile $ports \
266 -d $volumes $local_discourse/$config /usr/bin/runsvdir -P /etc/service
267
268 exit 0
269 else
270 cid=`cat $cidfile`
271
272 if [ -z $cid ]
273 then
274 echo "Detected empty cid file, deleting, please re-run"
275 rm $cidfile
276 exit 1
277 fi
278
279 found=`$docker_path ps -q -a --no-trunc | grep $cid`
280 if [ -z $found ]
281 then
282 echo "Invalid cid file, deleting, please re-run"
283 rm $cidfile
284 exit 1
285 fi
286
287 echo "cid found, ensuring container is started"
288 $docker_path start `cat $cidfile`
289 exit 0
290 fi
291
292 }
293
294 run_bootstrap(){
295 get_ssh_pub_key
296
297 # Is the image available?
298 # If not, pull it here so the user is aware what's happening.
299 $docker_path history $image >/dev/null 2>&1 || $docker_path pull $image
300
301 set_template_info
302
303 base_image=`cat $config_file | $docker_path run --rm -i -a stdin -a stdout $image ruby -e \
304 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['base_image']"`
305
306 update_pups=`cat $config_file | $docker_path run --rm -i -a stdin -a stdout $image ruby -e \
307 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['update_pups']"`
308
309 if [[ ! X"" = X"$base_image" ]]; then
310 image=$base_image
311 fi
312
313 set_volumes
314
315 rm -f $cidbootstrap
316
317 run_command="cd /pups &&"
318 if [[ ! "false" = $update_pups ]]; then
319 run_command="$run_command git pull &&"
320 fi
321 run_command="$run_command /pups/bin/pups --stdin"
322
323 echo $run_command
324
325 env=("${env[@]}" "-e" "SSH_PUB_KEY=$ssh_pub_key")
326
327 (exec echo "$input" | $docker_path run "${env[@]}" -e DOCKER_HOST_IP=$docker_ip --cidfile $cidbootstrap -i -a stdin -a stdout -a stderr $volumes $image \
328 /bin/bash -c "$run_command") \
329 || ($docker_path rm `cat $cidbootstrap` && rm $cidbootstrap)
330
331 [ ! -e $cidbootstrap ] && echo "FAILED TO BOOTSTRAP" && exit 1
332
333 sleep 5
334
335 $docker_path commit `cat $cidbootstrap` $local_discourse/$config || echo 'FAILED TO COMMIT'
336 $docker_path rm `cat $cidbootstrap` && rm $cidbootstrap
337 }
338
339 case "$command" in
340 bootstrap)
341 run_bootstrap
342 echo "Successfully bootstrapped, to startup use ./launcher start $config"
343 exit 0
344 ;;
345
346 mailtest)
347 run_mailtest
348 exit 0
349 ;;
350
351 ssh)
352 if [ ! -e $cidfile ]
353 then
354 echo "No cid found"
355 exit 1
356 else
357 cid="`cat $cidfile`"
358 address="`$docker_path port $cid 22`"
359 split=(${address//:/ })
360 exec ssh -o StrictHostKeyChecking=no root@${split[0]} -p ${split[1]}
361 fi
362 ;;
363
364 stop)
365 run_stop
366 exit 0
367 ;;
368
369 logs)
370
371 if [ ! -e $cidfile ]
372 then
373 echo "No cid found"
374 exit 1
375 else
376 $docker_path logs `cat $cidfile`
377 exit 0
378 fi
379 ;;
380
381 restart)
382 run_stop
383 run_start
384 exit 0
385 ;;
386
387 start)
388 run_start
389 exit 0
390 ;;
391
392 rebuild)
393 if [ -e $cidfile ]
394 then
395 echo "Stopping old container"
396 $docker_path stop -t 10 `cat $cidfile`
397 fi
398
399 run_bootstrap
400
401 if [ -e $cidfile ]
402 then
403 $docker_path rm `cat $cidfile` && rm $cidfile
404 fi
405
406 run_start
407 exit 0
408 ;;
409
410
411 destroy)
412 if [ -e $cidfile ]
413 then
414 echo "destroying container $cidfile"
415 $docker_path stop -t 10 `cat $cidfile`
416 $docker_path rm `cat $cidfile` && rm $cidfile
417 exit 0
418 else
419 echo "nothing to destroy cidfile does not exist"
420 exit 1
421 fi
422 ;;
423 esac
424
425 usage