Merge pull request #30 from Supermathie/fix-hang
[discourse_docker.git] / launcher
1 #!/bin/bash
2
3 command=$1
4 config=$2
5 opt=$3
6
7
8 config_file=containers/"$config".yml
9 cidfile=cids/"$config".cid
10 cidbootstrap=cids/"$config"_boostrap.cid
11 local_discourse=local_discourse
12 image=samsaffron/discourse:0.2.0
13 docker_path=`which docker`
14
15 docker_ip=`/sbin/ifconfig | \
16 grep -B1 "inet addr" | \
17 awk '{ if ( $1 == "inet" ) { print $2 } else if ( $2 == "Link" ) { printf "%s:" ,$1 } }' | \
18 grep docker0 | \
19 awk -F: '{ print $3 }';`
20
21
22 usage () {
23 echo "Usage: launcher COMMAND CONFIG [--skip-prereqs]"
24 echo "Commands:"
25 echo " start: Start/initialize a container"
26 echo " stop: Stop a running container"
27 echo " restart: Restart a container"
28 echo " destroy: Stop and remove a container"
29 echo " ssh: Start a bash shell in a running container"
30 echo " logs: Docker logs for container"
31 echo " bootstrap: Bootstrap a container for the config based on a template"
32 echo " rebuild: Rebuild a container (destroy old, bootstrap, start new)"
33 echo
34 echo "Options:"
35 echo " --skip-prereqs Don't check prerequisites"
36 exit 1
37 }
38
39 prereqs() {
40
41 # 1. docker daemon running?
42 test=`docker info >/dev/null`
43
44 if [[ $? -ne 0 ]] ; then
45 echo "Cannot connect to the docker daemon - verify it is running and you have access"
46 exit 1
47 fi
48
49 # 2. running aufs
50 test=`docker info 2> /dev/null | grep 'Driver: aufs'`
51 if [[ "$test" =~ "aufs" ]] ; then : ; else
52 echo "Your Docker installation is not using aufs"
53 echo "Device mapper and other experimental drivers are unstable"
54 echo
55 echo "Please ensure your kernel is running linux extras and aufs"
56 echo "Please follow the installation guide for Docker here: http://docs.docker.io/en/latest/installation/ubuntulinux/"
57 exit 1
58 fi
59
60 # 3. running docker 0.10+
61 test=`docker --version | grep 0.10`
62
63 if [[ "$test" =~ "0.10" ]] ; then : ; else
64 echo "Your Docker installation is old, please upgrade to 0.10.0 or up"
65 exit 1
66 fi
67
68 # 4. able to attach stderr / out / tty
69 test=`docker run -i --rm -a stdout -a stderr $image echo working`
70 if [[ "$test" =~ "working" ]] ; then : ; else
71 echo "Your Docker installation is not working correctly"
72 echo
73 echo "See: https://meta.discourse.org/t/docker-error-on-bootstrap/13657/18?u=sam"
74 exit 1
75 fi
76 }
77
78 if [ "$opt" != "--skip-prereqs" ] ; then
79 prereqs
80 fi
81
82 get_ssh_pub_key() {
83 if tty -s ; then
84 if [[ ! -e ~/.ssh/id_rsa.pub && ! -e ~/.ssh/id_dsa.pub ]] ; then
85 echo You have no SSH key associated to this profile
86 echo "(This will allow you ssh access into your container)"
87 read -p "Generate SSH key at ~/.ssh/id_rsa.pub? (y/N) " -n 1 -r
88 if [[ $REPLY =~ ^[Yy]$ ]] ; then
89 echo
90 echo Generating SSH key
91 mkdir -p ~/.ssh && ssh-keygen -f ~/.ssh/id_rsa -t rsa -N ''
92 else
93 echo
94 echo WARNING: You may not be able to log in to your container.
95 echo
96 fi
97 fi
98 fi
99
100 ssh_pub_key="$(cat ~/.ssh/id_rsa.pub 2>/dev/null || cat ~/.ssh/id_dsa.pub)"
101 }
102
103
104 install_docker() {
105
106 echo "Docker is not installed, make sure you are running on the 3.8 kernel"
107 echo "The best supported Docker release is Ubuntu 12.04.03 for it run the following"
108 echo
109 echo "sudo apt-get update"
110 echo "sudo apt-get install linux-image-generic-lts-raring linux-headers-generic-lts-raring"
111 echo "sudo reboot"
112 echo
113
114 echo "sudo sh -c \"wget -qO- https://get.docker.io/gpg | apt-key add -\""
115 echo "sudo sh -c \"echo deb http://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list\""
116 echo "sudo apt-get update"
117 echo "sudo apt-get install lxc-docker"
118
119 exit 1
120 }
121
122 set_volumes() {
123 volumes=`cat $config_file | docker run --rm -i -a stdout -a stdin $image ruby -e \
124 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['volumes'].map{|v| '-v ' << v['volume']['host'] << ':' << v['volume']['guest'] << ' '}.join"`
125 }
126
127 set_template_info() {
128
129 templates=`cat $config_file | docker run --rm -i -a stdin -a stdout $image ruby -e \
130 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['templates']"`
131
132
133 arrTemplates=(${templates// / })
134 config_data=$(cat $config_file)
135
136 input="hack: true"
137
138
139 for template in "${arrTemplates[@]}"
140 do
141 [ ! -z $template ] && {
142 input="$input _FILE_SEPERATOR_ $(cat $template)"
143 }
144 done
145
146 # we always want our config file last so it takes priority
147 input="$input _FILE_SEPERATOR_ $config_data"
148
149 read -r -d '' env_ruby << 'RUBY'
150 require 'yaml'
151
152 input=STDIN.readlines.join
153 env = {}
154 input.split('_FILE_SEPERATOR_').each do |yml|
155 yml.strip!
156 begin
157 env.merge!(YAML.load(yml)['env'] || {})
158 rescue => e
159 puts yml
160 p e
161 end
162 end
163 puts env.map{|k,v| "-e\n#{k}=#{v}" }.join("\n")
164 RUBY
165
166 raw=`exec echo "$input" | docker run --rm -i -a stdin -a stdout $image ruby -e "$env_ruby"`
167
168 env=()
169 while read i; do
170 env[${#env[@]}]=$i
171 done <<< "$raw"
172
173 echo "Calculated ENV: ${env[@]}"
174 }
175
176 [ -z $docker_path ] && {
177 install_docker
178 }
179
180
181 [ $# -lt 2 ] && {
182 usage
183 }
184
185 if [ ! -e $config_file ]
186 then
187 echo "Config file was not found, ensure $config_file exists"
188 exit 1
189 fi
190
191
192 run_stop(){
193 if [ ! -e $cidfile ]
194 then
195 echo "No cid found"
196 exit 1
197 else
198 docker stop -t 10 `cat $cidfile`
199 fi
200 }
201
202 run_start(){
203
204 if [ ! -e $cidfile ]
205 then
206 echo "No cid found, creating a new container"
207 ports=`cat $config_file | docker run --rm -i -a stdout -a stdin $image ruby -e \
208 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['expose'].map{|p| '-p ' << p.to_s << ' '}.join"`
209
210 set_template_info
211 set_volumes
212
213 existing=`docker ps -a | awk '{ print $1, $(NF) }' | grep "$config$" | awk '{ print $1 }'`
214 if [ ! -z $existing ]
215 then
216 echo "Found an existing container by its name, recovering cidfile, please rerun"
217 echo $existing > $cidfile
218 exit 1
219 fi
220
221 docker run "${env[@]}" -h "`hostname`-$config" -e DOCKER_HOST_IP=$docker_ip --name $config -t --cidfile $cidfile $ports \
222 -d $volumes $local_discourse/$config /usr/bin/runsvdir -P /etc/service
223
224 exit 0
225 else
226 cid=`cat $cidfile`
227
228 if [ -z $cid ]
229 then
230 echo "Detected empty cid file, deleting, please re-run"
231 rm $cidfile
232 exit 1
233 fi
234
235 found=`docker ps -q -a --no-trunc | grep $cid`
236 if [ -z $found ]
237 then
238 echo "Invalid cid file, deleting, please re-run"
239 rm $cidfile
240 exit 1
241 fi
242
243 echo "cid found, ensuring container is started"
244 docker start `cat $cidfile`
245 exit 0
246 fi
247
248 }
249
250 run_bootstrap(){
251 get_ssh_pub_key
252
253 # Is the image available?
254 # If not, pull it here so the user is aware what's happening.
255 docker history $image >/dev/null 2>&1 || docker pull $image
256
257 set_template_info
258
259 base_image=`cat $config_file | docker run --rm -i -a stdin -a stdout $image ruby -e \
260 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['base_image']"`
261
262 update_pups=`cat $config_file | docker run --rm -i -a stdin -a stdout $image ruby -e \
263 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['update_pups']"`
264
265 if [[ ! X"" = X"$base_image" ]]; then
266 image=$base_image
267 fi
268
269 set_volumes
270
271 rm -f $cidbootstrap
272
273 run_command="cd /pups &&"
274 if [[ ! "false" = $update_pups ]]; then
275 run_command="$run_command git pull &&"
276 fi
277 run_command="$run_command /pups/bin/pups --stdin"
278
279 echo $run_command
280
281 env=("${env[@]}" "-e" "SSH_PUB_KEY=$ssh_pub_key")
282
283 (exec echo "$input" | docker run "${env[@]}" -e DOCKER_HOST_IP=$docker_ip --cidfile $cidbootstrap -i -a stdin -a stdout -a stderr $volumes $image \
284 /bin/bash -c "$run_command") \
285 || (docker rm `cat $cidbootstrap` && rm $cidbootstrap)
286
287 [ ! -e $cidbootstrap ] && echo "FAILED TO BOOTSTRAP" && exit 1
288
289 sleep 5
290
291 docker commit `cat $cidbootstrap` $local_discourse/$config || echo 'FAILED TO COMMIT'
292 docker rm `cat $cidbootstrap` && rm $cidbootstrap
293 }
294
295 case "$command" in
296 bootstrap)
297
298 run_bootstrap
299 echo "Successfully bootstrapped, to startup use ./launcher start $config"
300 exit 0
301 ;;
302
303 ssh)
304 if [ ! -e $cidfile ]
305 then
306 echo "No cid found"
307 exit 1
308 else
309 cid="`cat $cidfile`"
310 address="`docker port $cid 22`"
311 split=(${address//:/ })
312 exec ssh -o StrictHostKeyChecking=no root@${split[0]} -p ${split[1]}
313 fi
314 ;;
315
316 stop)
317 run_stop
318 exit 0
319 ;;
320
321 logs)
322
323 if [ ! -e $cidfile ]
324 then
325 echo "No cid found"
326 exit 1
327 else
328 docker logs `cat $cidfile`
329 exit 0
330 fi
331 ;;
332
333 restart)
334 run_stop
335 run_start
336 exit 0
337 ;;
338
339 start)
340 run_start
341 exit 0
342 ;;
343
344 rebuild)
345 if [ -e $cidfile ]
346 then
347 echo "Stopping old container"
348 docker stop -t 10 `cat $cidfile`
349 fi
350
351 run_bootstrap
352
353 if [ -e $cidfile ]
354 then
355 docker rm `cat $cidfile` && rm $cidfile
356 fi
357
358 run_start
359 exit 0
360 ;;
361
362
363 destroy)
364 if [ -e $cidfile ]
365 then
366 echo "destroying container $cidfile"
367 docker stop -t 10 `cat $cidfile`
368 docker rm `cat $cidfile` && rm $cidfile
369 exit 0
370 else
371 echo "nothing to destroy cidfile does not exist"
372 exit 1
373 fi
374 ;;
375 esac
376
377 usage