9 docker_min_version
='1.2.0'
10 docker_rec_version
='1.2.0'
12 config_file
=containers
/"$config".yml
13 cidbootstrap
=cids
/"$config"_bootstrap.cid
14 local_discourse
=local_discourse
15 image
=samsaffron
/discourse
:1.0.8
16 docker_path
=`which docker.io || which docker`
18 if [ "${SUPERVISED}" = "true" ]; then
19 restart_policy
="--restart=no"
21 attach_on_run
="-a stdout -a stderr"
26 if [ -x "$(which ip 2>/dev/null)" ]; then
27 docker_ip
=`ip addr show docker0 | \
29 awk '{ split($2,a,"/"); print a[1] }';`
31 docker_ip
=`ifconfig | \
32 grep -B1 "inet addr" | \
33 awk '{ if ( $1 == "inet" ) { print $2 } else if ( $2 == "Link" ) { printf "%s:" ,$1 } }' | \
35 awk -F: '{ print $3 }';`
40 echo "Usage: launcher COMMAND CONFIG [--skip-prereqs]"
42 echo " start: Start/initialize a container"
43 echo " stop: Stop a running container"
44 echo " restart: Restart a container"
45 echo " destroy: Stop and remove a container"
46 echo " enter: Use nsenter to enter a container"
47 echo " ssh: Start a bash shell in a running container"
48 echo " logs: Docker logs for container"
49 echo " mailtest: Test the mail settings in a container"
50 echo " bootstrap: Bootstrap a container for the config based on a template"
51 echo " rebuild: Rebuild a container (destroy old, bootstrap, start new)"
52 echo " cleanup: Remove all containers that have stopped for > 24 hours"
55 echo " --skip-prereqs Don't check prerequisites"
56 echo " --docker-args Extra arguments to pass when running docker"
63 IFS
=.
read -a ver_a
<<< "$1"
64 IFS
=.
read -a ver_b
<<< "$2"
66 while [[ -n $ver_a ]]; do
67 if (( ver_a
> ver_b
)); then
69 elif (( ver_b
> ver_a
)); then
78 return 1 # They are equal
83 # 1. docker daemon running?
84 # we send stderr to /dev/null cause we don't care about warnings,
85 # it usually complains about swap which does not matter
86 test=`$docker_path info 2> /dev/null`
88 if [[ $?
-ne 0 ]] ; then
89 echo "Cannot connect to the docker daemon - verify it is running and you have access"
93 # 2. running aufs or btrfs
94 test=`$docker_path info 2> /dev/null | grep 'Driver: '`
95 if [[ "$test" =~
[aufs|btrfs
] ]] ; then : ; else
96 echo "Your Docker installation is not using the recommended AuFS (union filesystem) and may be unstable."
97 echo "If you are unable to bootstrap / stop your image please report the issue at:"
98 echo "https://meta.discourse.org/t/discourse-docker-installation-without-aufs/15639"
100 read -p "Continue without proper filesystem? [yN]" yn
107 # 3. running recommended docker version
108 test=($
($docker_path --version)) # Get docker version string
109 test=${test[2]//,/} # Get version alone and strip comma if exists
111 [[ "$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
113 # At least minimum version
114 if compare_version
"${docker_min_version}" "${test}"; then
115 echo "ERROR: Docker version ${test} not supported, please upgrade to at least ${docker_min_version}, or recommended ${docker_rec_version}"
119 # Recommend best version
120 if compare_version
"${docker_rec_version}" "${test}"; then
121 echo "WARNING: Docker version ${test} deprecated, recommend upgrade to ${docker_rec_version} or newer."
124 # 4. discourse docker image is downloaded
125 test=`$docker_path images | awk '{print $1 ":" $2 }' | grep "$image"`
127 if [ -z "$test" ]; then
129 echo "WARNING: We are about to start downloading the Discourse base image"
130 echo "This process may take anywhere between a few minutes to an hour, depending on your network speed"
132 echo "Please be patient"
139 # 5. able to attach stderr / out / tty
140 test=`$docker_path run $user_args -i --rm -a stdout -a stderr $image echo working`
141 if [[ "$test" =~
"working" ]] ; then : ; else
142 echo "Your Docker installation is not working correctly"
144 echo "See: https://meta.discourse.org/t/docker-error-on-bootstrap/13657/18?u=sam"
149 if [ "$opt" != "--skip-prereqs" ] ; then
153 if [ "$opt" == "--docker-args" ] ; then
160 local ${ssh_key_locations}
162 ~
/.ssh
/id_ed25519.pub
166 ~core
/.ssh
/authorized_keys
170 for keyfile
in "${ssh_key_locations[@]}"; do
171 if [[ -e ${keyfile} ]] ; then
172 ssh_pub_key
="$(cat ${keyfile})"
183 echo "Docker is not installed, make sure you are running on the 3.8 kernel"
184 echo "The best supported Docker release is Ubuntu 12.04.03 for it run the following"
186 echo "sudo apt-get update"
187 echo "sudo apt-get install linux-image-generic-lts-raring linux-headers-generic-lts-raring"
191 echo "sudo sh -c \"wget -qO- https://get.docker.io/gpg | apt-key add -\""
192 echo "sudo sh -c \"echo deb http://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list\""
193 echo "sudo apt-get update"
194 echo "sudo apt-get install lxc-docker"
200 read -r -d '' env_ruby
<< 'RUBY'
203 input
= STDIN.readlines.
join
204 yaml
= YAML.load
(input
)
206 if host_run
= yaml
['host_run']
207 params
= yaml
['params'] ||
{}
208 host_run.each
do |run|
210 run
= run.gsub
("$#{k}", v
)
212 STDOUT.
write "#{run}--SEP--"
217 host_run
=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e "$env_ruby"`
219 while [ "$host_run" ] ; do
220 iter
=${host_run%%--SEP--*}
222 echo "Host run: $iter"
225 host_run
="${host_run#*--SEP--}"
231 volumes
=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \
232 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['volumes'].map{|v| '-v ' << v['volume']['host'] << ':' << v['volume']['guest'] << ' '}.join"`
236 links
=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \
237 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['links'].map{|l| '--link ' << l['link']['name'] << ':' << l['link']['alias'] << ' '}.join"`
240 set_template_info
() {
242 templates
=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
243 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['templates']"`
246 arrTemplates
=(${templates// / })
247 config_data
=$
(cat $config_file)
252 for template
in "${arrTemplates[@]}"
254 [ ! -z $template ] && {
255 input
="$input _FILE_SEPERATOR_ $(cat $template)"
259 # we always want our config file last so it takes priority
260 input
="$input _FILE_SEPERATOR_ $config_data"
262 read -r -d '' env_ruby
<< 'RUBY'
265 input
=STDIN.readlines.
join
266 # default to UTF-8 for the dbs sake
267 env
= {'LANG' => 'en_US.UTF-8'}
268 input.
split('_FILE_SEPERATOR_').each
do |yml|
271 env.merge
!(YAML.load
(yml
)['env'] ||
{})
272 rescue Psych
::SyntaxError
=> e
280 puts env.map
{|k
,v|
"-e\n#{k}=#{v}" }.
join("\n")
283 raw
=`exec echo "$input" | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e "$env_ruby"`
288 if [ "$i" == "*ERROR." ]; then
290 elif [ -n "$i" ]; then
295 if [ "$ok" -ne 1 ]; then
297 echo "YAML syntax error. Please check your /var/discourse/containers/*.yml config files."
302 [ -z $docker_path ] && {
306 [ $command == "cleanup" ] && {
308 echo "The following command will"
309 echo "- Delete all docker images for old containers"
310 echo "- Delete all stopped and orphan containers"
312 read -p "Are you sure (Y/n): " -n 1 -r && echo
313 if [[ $REPLY =~ ^
[Yy
]$ ||
! $REPLY ]]
315 space
=$
(df
/var
/lib
/docker |
awk '{ print $4 }' |
grep -v Available
)
317 echo "Starting Cleanup"
319 if [[ ! -z `docker ps -aq` ]]; then
320 docker inspect
-f '{{.Id}},{{.State.Running}},{{.State.FinishedAt}}' $
(docker ps
-qa) | \
321 awk -F, 'BEGIN { TIME=strftime("%FT%T.000000000Z",systime()-60*60*24); } $2=="false" && $3 < TIME {print $1;}' | \
322 xargs --no-run-if-empty docker
rm >/dev
/null
2>/dev
/null
325 docker rmi
`docker images -a | grep '<none>' | awk '{print $3}'` 2> /dev
/null
327 let freed
=$space-$
(df
/var
/lib
/docker |
awk '{ print $4 }' |
grep -v Available
)
330 echo $
(df
/var
/lib
/docker |
awk '{ print $4 }' |
grep -v Available
)
333 output
="$freed" |
awk '{sum=$1;hum[1024**3]="GB"; hum[1024**2]="MB"; hum[1024]="KB"; for (x=1024**3;x>=1024; x/=1024){ if (sum>=x) { printf "%.2f %s\n",sum/x,hum[x];break }}}'
334 [ -z "$output" ] && {
335 [[ $freed > 0 ]] && { echo "./launcher cleanup cleared up $freed of disk space."; } ||
{ echo "./launcher cleanup has finished, no files were removed."; }
336 } ||
{ echo "./launcher cleanup cleared up $freed of disk space."; }
347 if [ ! -e $config_file ]
349 echo "Config file was not found, ensure $config_file exists"
351 echo "Available configs ( `cd containers && ls -dm *.yml | tr -s '\n' ' ' | awk '{ gsub(/\.yml/, ""); print }'`)"
356 docker_version
=($
($docker_path --version))
357 docker_version
=${test[2]//,/}
359 if compare_version
"1.2.0" "$docker_version"; then
360 echo "We recommend you upgrade docker, the version you are running has no restart policies, on reboot your container may not start up"
363 restart_policy
=${restart_policy:---restart=always}
368 if [ ! -e $config_file ]; then
369 echo "Config does not exist: $config_file" >&2
372 exec scripts
/mailtest
$config_file
375 set_existing_container
(){
376 existing
=`$docker_path ps -a | awk '{ print $1, $(NF) }' | grep " $config$" | awk '{ print $1 }'`
381 set_existing_container
383 if [ ! -z $existing ]
387 $docker_path stop
-t 10 $config
390 echo "$config was not started !"
397 existing
=`$docker_path ps | awk '{ print $1, $(NF) }' | grep " $config$" | awk '{ print $1 }'`
399 if [ ! -z $existing ]
401 echo "Nothing to do, your container has already started!"
405 existing
=`$docker_path ps -a | awk '{ print $1, $(NF) }' | grep " $config$" | awk '{ print $1 }'`
406 if [ ! -z $existing ]
408 echo "starting up existing container"
411 $docker_path start
$config
417 ports
=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \
418 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['expose'].map{|p| '-p ' << p.to_s << ' '}.join"`
427 $docker_path run
$user_args $links $attach_on_run $restart_policy "${env[@]}" -h "$hostname-$config" \
428 -e DOCKER_HOST_IP
=$docker_ip --name $config -t $ports $volumes $local_discourse/$config /sbin
/boot
441 # Is the image available?
442 # If not, pull it here so the user is aware what's happening.
443 $docker_path history $image >/dev
/null
2>&1 ||
$docker_path pull
$image
447 base_image
=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
448 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['base_image']"`
450 update_pups
=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
451 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['update_pups']"`
453 if [[ ! X
"" = X
"$base_image" ]]; then
462 run_command
="cd /pups &&"
463 if [[ ! "false" = $update_pups ]]; then
464 run_command
="$run_command git pull &&"
466 run_command
="$run_command /pups/bin/pups --stdin"
470 env
=("${env[@]}" "-e" "SSH_PUB_KEY=$ssh_pub_key")
472 (exec echo "$input" |
$docker_path run
$user_args $links "${env[@]}" -e DOCKER_HOST_IP
=$docker_ip --cidfile $cidbootstrap -i -a stdin
-a stdout
-a stderr
$volumes $image \
473 /bin
/bash
-c "$run_command") \
474 ||
($docker_path rm `cat $cidbootstrap` && rm $cidbootstrap)
476 [ ! -e $cidbootstrap ] && echo "FAILED TO BOOTSTRAP" && exit 1
480 $docker_path commit
`cat $cidbootstrap` $local_discourse/$config ||
echo 'FAILED TO COMMIT'
481 $docker_path rm `cat $cidbootstrap` && rm $cidbootstrap
487 echo "Successfully bootstrapped, to startup use ./launcher start $config"
497 exec $docker_path exec -it $config /bin
/bash
501 existing
=`$docker_path ps | awk '{ print $1, $(NF) }' | grep " $config$" | awk '{ print $1 }'`
503 if [[ ! -z $existing ]]; then
504 address
="`$docker_path port $config 22`"
505 split=(${address//:/ })
506 exec ssh -o StrictHostKeyChecking
=no root@
${split[0]} -p ${split[1]}
508 echo "$config is not running!"
520 $docker_path logs
$config
536 if [ "$(git symbolic-ref --short HEAD)" == "master" ]; then
537 echo "Ensuring discourse docker is up to date"
541 LOCAL
=$
(git rev-parse @
)
542 REMOTE
=$
(git rev-parse @
{u
})
543 BASE
=$
(git merge-base @ @
{u
})
545 if [ $LOCAL = $REMOTE ]; then
546 echo "Discourse Docker is up-to-date"
548 elif [ $LOCAL = $BASE ]; then
549 echo "Updating Discourse Docker"
550 git pull ||
(echo 'failed to update' && exit 1)
553 elif [ $REMOTE = $BASE ]; then
554 echo "Your version of Discourse Docker is ahead of origin"
557 echo "Discourse Docker has diverged source, this is only expected in Dev mode"
562 set_existing_container
564 if [ ! -z $existing ]
566 echo "Stopping old container"
569 $docker_path stop
-t 10 $config
575 if [ ! -z $existing ]
577 echo "Removing old container"
580 $docker_path rm $config
590 (set -x; $docker_path stop
-t 10 $config && $docker_path rm $config) ||
(echo "$config was not found" && exit 0)