7 # Docker doesn't like uppercase characters, spaces or special characters, catch it now before we build everything out and then find out
8 re
='[A-Z/ !@#$%^&*()+~`=]'
9 if [[ $config =~
$re ]];
12 echo "ERROR: Config name must not contain upper case characters, spaces or special characters. Correct config name and rerun $0."
19 docker_min_version
='1.2.0'
20 docker_rec_version
='1.2.0'
22 config_file
=containers
/"$config".yml
23 cidbootstrap
=cids
/"$config"_bootstrap.cid
24 local_discourse
=local_discourse
25 image
=samsaffron
/discourse
:1.0.11
26 docker_path
=`which docker.io || which docker`
28 if [ "${SUPERVISED}" = "true" ]; then
29 restart_policy
="--restart=no"
31 attach_on_run
="-a stdout -a stderr"
36 if [ -x "$(which ip 2>/dev/null)" ]; then
37 docker_ip
=`ip addr show docker0 | \
39 awk '{ split($2,a,"/"); print a[1] }';`
41 docker_ip
=`ifconfig | \
42 grep -B1 "inet addr" | \
43 awk '{ if ( $1 == "inet" ) { print $2 } else if ( $2 == "Link" ) { printf "%s:" ,$1 } }' | \
45 awk -F: '{ print $3 }';`
50 echo "Usage: launcher COMMAND CONFIG [--skip-prereqs]"
52 echo " start: Start/initialize a container"
53 echo " stop: Stop a running container"
54 echo " restart: Restart a container"
55 echo " destroy: Stop and remove a container"
56 echo " enter: Use nsenter to enter a container"
57 echo " ssh: Start a bash shell in a running container"
58 echo " logs: Docker logs for container"
59 echo " mailtest: Test the mail settings in a container"
60 echo " bootstrap: Bootstrap a container for the config based on a template"
61 echo " rebuild: Rebuild a container (destroy old, bootstrap, start new)"
62 echo " cleanup: Remove all containers that have stopped for > 24 hours"
65 echo " --skip-prereqs Don't check prerequisites"
66 echo " --docker-args Extra arguments to pass when running docker"
73 IFS
=.
read -a ver_a
<<< "$1"
74 IFS
=.
read -a ver_b
<<< "$2"
76 while [[ -n $ver_a ]]; do
77 if (( ver_a
> ver_b
)); then
79 elif (( ver_b
> ver_a
)); then
88 return 1 # They are equal
93 # 1. docker daemon running?
94 # we send stderr to /dev/null cause we don't care about warnings,
95 # it usually complains about swap which does not matter
96 test=`$docker_path info 2> /dev/null`
98 if [[ $?
-ne 0 ]] ; then
99 echo "Cannot connect to the docker daemon - verify it is running and you have access"
103 # 2. running aufs or btrfs
104 test=`$docker_path info 2> /dev/null | grep 'Driver: '`
105 if [[ "$test" =~
[aufs|btrfs
] ]] ; then : ; else
106 echo "Your Docker installation is not using the recommended AuFS (union filesystem) and may be unstable."
107 echo "If you are unable to bootstrap / stop your image please report the issue at:"
108 echo "https://meta.discourse.org/t/discourse-docker-installation-without-aufs/15639"
110 read -p "Continue without proper filesystem? [yN]" yn
117 # 3. running recommended docker version
118 test=($
($docker_path --version)) # Get docker version string
119 test=${test[2]//,/} # Get version alone and strip comma if exists
121 [[ "$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
123 # At least minimum version
124 if compare_version
"${docker_min_version}" "${test}"; then
125 echo "ERROR: Docker version ${test} not supported, please upgrade to at least ${docker_min_version}, or recommended ${docker_rec_version}"
129 # Recommend best version
130 if compare_version
"${docker_rec_version}" "${test}"; then
131 echo "WARNING: Docker version ${test} deprecated, recommend upgrade to ${docker_rec_version} or newer."
134 # 4. discourse docker image is downloaded
135 test=`$docker_path images | awk '{print $1 ":" $2 }' | grep "$image"`
137 if [ -z "$test" ]; then
139 echo "WARNING: We are about to start downloading the Discourse base image"
140 echo "This process may take anywhere between a few minutes to an hour, depending on your network speed"
142 echo "Please be patient"
147 # 5. able to attach stderr / out / tty
148 test=`$docker_path run $user_args -i --rm -a stdout -a stderr $image echo working`
149 if [[ "$test" =~
"working" ]] ; then : ; else
150 echo "Your Docker installation is not working correctly"
152 echo "See: https://meta.discourse.org/t/docker-error-on-bootstrap/13657/18?u=sam"
157 if [ "$opt" != "--skip-prereqs" ] ; then
161 if [ "$opt" == "--docker-args" ] ; then
168 local ${ssh_key_locations}
170 ~
/.ssh
/id_ed25519.pub
174 ~core
/.ssh
/authorized_keys
178 for keyfile
in "${ssh_key_locations[@]}"; do
179 if [[ -e ${keyfile} ]] ; then
180 ssh_pub_key
="$(cat ${keyfile})"
191 echo "Docker is not installed, make sure you are running on the 3.8 kernel"
192 echo "The best supported Docker release is Ubuntu 12.04.03 for it run the following"
194 echo "sudo apt-get update"
195 echo "sudo apt-get install linux-image-generic-lts-raring linux-headers-generic-lts-raring"
199 echo "sudo sh -c \"wget -qO- https://get.docker.io/gpg | apt-key add -\""
200 echo "sudo sh -c \"echo deb http://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list\""
201 echo "sudo apt-get update"
202 echo "sudo apt-get install lxc-docker"
208 read -r -d '' env_ruby
<< 'RUBY'
211 input
= STDIN.readlines.
join
212 yaml
= YAML.load
(input
)
214 if host_run
= yaml
['host_run']
215 params
= yaml
['params'] ||
{}
216 host_run.each
do |run|
218 run
= run.gsub
("$#{k}", v
)
220 STDOUT.
write "#{run}--SEP--"
225 host_run
=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e "$env_ruby"`
227 while [ "$host_run" ] ; do
228 iter
=${host_run%%--SEP--*}
230 echo "Host run: $iter"
233 host_run
="${host_run#*--SEP--}"
239 volumes
=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \
240 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['volumes'].map{|v| '-v ' << v['volume']['host'] << ':' << v['volume']['guest'] << ' '}.join"`
244 links
=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \
245 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['links'].map{|l| '--link ' << l['link']['name'] << ':' << l['link']['alias'] << ' '}.join"`
248 set_template_info
() {
250 templates
=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
251 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['templates']"`
254 arrTemplates
=(${templates// / })
255 config_data
=$
(cat $config_file)
260 for template
in "${arrTemplates[@]}"
262 [ ! -z $template ] && {
263 input
="$input _FILE_SEPERATOR_ $(cat $template)"
267 # we always want our config file last so it takes priority
268 input
="$input _FILE_SEPERATOR_ $config_data"
270 read -r -d '' env_ruby
<< 'RUBY'
273 input
=STDIN.readlines.
join
274 # default to UTF-8 for the dbs sake
275 env
= {'LANG' => 'en_US.UTF-8'}
276 input.
split('_FILE_SEPERATOR_').each
do |yml|
279 env.merge
!(YAML.load
(yml
)['env'] ||
{})
280 rescue Psych
::SyntaxError
=> e
288 puts env.map
{|k
,v|
"-e\n#{k}=#{v}" }.
join("\n")
291 raw
=`exec echo "$input" | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e "$env_ruby"`
296 if [ "$i" == "*ERROR." ]; then
298 elif [ -n "$i" ]; then
303 if [ "$ok" -ne 1 ]; then
305 echo "YAML syntax error. Please check your /var/discourse/containers/*.yml config files."
310 [ -z $docker_path ] && {
314 [ $command == "cleanup" ] && {
316 echo "The following command will"
317 echo "- Delete all docker images for old containers"
318 echo "- Delete all stopped and orphan containers"
320 read -p "Are you sure (Y/n): " -n 1 -r && echo
321 if [[ $REPLY =~ ^
[Yy
]$ ||
! $REPLY ]]
323 space
=$
(df
/var
/lib
/docker |
awk '{ print $4 }' |
grep -v Available
)
325 echo "Starting Cleanup"
327 if [[ ! -z `docker ps -aq` ]]; then
328 docker inspect
-f '{{.Id}},{{.State.Running}},{{.State.FinishedAt}}' $
(docker ps
-qa) | \
329 awk -F, 'BEGIN { TIME=strftime("%FT%T.000000000Z",systime()-60*60*24); } $2=="false" && $3 < TIME {print $1;}' | \
330 xargs --no-run-if-empty docker
rm >/dev
/null
2>/dev
/null
333 docker rmi
`docker images -a | grep '<none>' | awk '{print $3}'` 2> /dev
/null
335 let freed
=$space-$
(df
/var
/lib
/docker |
awk '{ print $4 }' |
grep -v Available
)
338 echo $
(df
/var
/lib
/docker |
awk '{ print $4 }' |
grep -v Available
)
341 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 }}}'
342 [ -z "$output" ] && {
343 [[ $freed > 0 ]] && { echo "./launcher cleanup cleared up $freed of disk space."; } ||
{ echo "./launcher cleanup has finished, no files were removed."; }
344 } ||
{ echo "./launcher cleanup cleared up $freed of disk space."; }
355 if [ ! -e $config_file ]
357 echo "Config file was not found, ensure $config_file exists"
359 echo "Available configs ( `cd containers && ls -dm *.yml | tr -s '\n' ' ' | awk '{ gsub(/\.yml/, ""); print }'`)"
364 docker_version
=($
($docker_path --version))
365 docker_version
=${test[2]//,/}
367 if compare_version
"1.2.0" "$docker_version"; then
368 echo "We recommend you upgrade docker, the version you are running has no restart policies, on reboot your container may not start up"
371 restart_policy
=${restart_policy:---restart=always}
376 if [ ! -e $config_file ]; then
377 echo "Config does not exist: $config_file" >&2
380 exec scripts
/mailtest
$config_file
383 set_existing_container
(){
384 existing
=`$docker_path ps -a | awk '{ print $1, $(NF) }' | grep " $config$" | awk '{ print $1 }'`
389 set_existing_container
391 if [ ! -z $existing ]
395 $docker_path stop
-t 10 $config
398 echo "$config was not started !"
405 existing
=`$docker_path ps | awk '{ print $1, $(NF) }' | grep " $config$" | awk '{ print $1 }'`
407 if [ ! -z $existing ]
409 echo "Nothing to do, your container has already started!"
413 existing
=`$docker_path ps -a | awk '{ print $1, $(NF) }' | grep " $config$" | awk '{ print $1 }'`
414 if [ ! -z $existing ]
416 echo "starting up existing container"
419 $docker_path start
$config
425 ports
=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \
426 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['expose'].map{|p| '-p ' << p.to_s << ' '}.join"`
435 $docker_path run
$user_args $links $attach_on_run $restart_policy "${env[@]}" -h "$hostname-$config" \
436 -e DOCKER_HOST_IP
=$docker_ip --name $config -t $ports $volumes $local_discourse/$config /sbin
/boot
449 # Is the image available?
450 # If not, pull it here so the user is aware what's happening.
451 $docker_path history $image >/dev
/null
2>&1 ||
$docker_path pull
$image
455 base_image
=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
456 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['base_image']"`
458 update_pups
=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
459 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['update_pups']"`
461 if [[ ! X
"" = X
"$base_image" ]]; then
470 run_command
="cd /pups &&"
471 if [[ ! "false" = $update_pups ]]; then
472 run_command
="$run_command git pull &&"
474 run_command
="$run_command /pups/bin/pups --stdin"
478 env
=("${env[@]}" "-e" "SSH_PUB_KEY=$ssh_pub_key")
480 (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 \
481 /bin
/bash
-c "$run_command") \
482 ||
($docker_path rm `cat $cidbootstrap` && rm $cidbootstrap)
484 [ ! -e $cidbootstrap ] && echo "FAILED TO BOOTSTRAP" && exit 1
488 $docker_path commit
`cat $cidbootstrap` $local_discourse/$config ||
echo 'FAILED TO COMMIT'
489 $docker_path rm `cat $cidbootstrap` && rm $cidbootstrap
495 echo "Successfully bootstrapped, to startup use ./launcher start $config"
505 exec $docker_path exec -it $config /bin
/bash
509 existing
=`$docker_path ps | awk '{ print $1, $(NF) }' | grep " $config$" | awk '{ print $1 }'`
511 if [[ ! -z $existing ]]; then
512 address
="`$docker_path port $config 22`"
513 split=(${address//:/ })
514 exec ssh -o StrictHostKeyChecking
=no root@
${split[0]} -p ${split[1]}
516 echo "$config is not running!"
528 $docker_path logs
$config
544 if [ "$(git symbolic-ref --short HEAD)" == "master" ]; then
545 echo "Ensuring discourse docker is up to date"
549 LOCAL
=$
(git rev-parse @
)
550 REMOTE
=$
(git rev-parse @
{u
})
551 BASE
=$
(git merge-base @ @
{u
})
553 if [ $LOCAL = $REMOTE ]; then
554 echo "Discourse Docker is up-to-date"
556 elif [ $LOCAL = $BASE ]; then
557 echo "Updating Discourse Docker"
558 git pull ||
(echo 'failed to update' && exit 1)
561 elif [ $REMOTE = $BASE ]; then
562 echo "Your version of Discourse Docker is ahead of origin"
565 echo "Discourse Docker has diverged source, this is only expected in Dev mode"
570 set_existing_container
572 if [ ! -z $existing ]
574 echo "Stopping old container"
577 $docker_path stop
-t 10 $config
583 if [ ! -z $existing ]
585 echo "Removing old container"
588 $docker_path rm $config
598 (set -x; $docker_path stop
-t 10 $config && $docker_path rm $config) ||
(echo "$config was not found" && exit 0)