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.12
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|zfs|overlay
] ]] ; then : ; else
106 echo "Your Docker installation is not using a supported filesystem if we were to proceed you may have a broken install."
107 echo "aufs is the recommended filesystem you should be using (zfs/btrfs and overlay may work as well)"
108 echo "You can tell what filesystem you are using by running \"docker info\" and looking at the driver"
110 echo "If you wish to continue anyway using your existing unsupported filesystem"
111 echo "read the source code of launcher and figure out how to bypass this."
115 # 3. running recommended docker version
116 test=($
($docker_path --version)) # Get docker version string
117 test=${test[2]//,/} # Get version alone and strip comma if exists
119 [[ "$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
121 # At least minimum version
122 if compare_version
"${docker_min_version}" "${test}"; then
123 echo "ERROR: Docker version ${test} not supported, please upgrade to at least ${docker_min_version}, or recommended ${docker_rec_version}"
127 # Recommend best version
128 if compare_version
"${docker_rec_version}" "${test}"; then
129 echo "WARNING: Docker version ${test} deprecated, recommend upgrade to ${docker_rec_version} or newer."
132 # 4. discourse docker image is downloaded
133 test=`$docker_path images | awk '{print $1 ":" $2 }' | grep "$image"`
135 if [ -z "$test" ]; then
137 echo "WARNING: We are about to start downloading the Discourse base image"
138 echo "This process may take anywhere between a few minutes to an hour, depending on your network speed"
140 echo "Please be patient"
145 # 5. able to attach stderr / out / tty
146 test=`$docker_path run $user_args -i --rm -a stdout -a stderr $image echo working`
147 if [[ "$test" =~
"working" ]] ; then : ; else
148 echo "Your Docker installation is not working correctly"
150 echo "See: https://meta.discourse.org/t/docker-error-on-bootstrap/13657/18?u=sam"
155 if [ "$opt" != "--skip-prereqs" ] ; then
159 if [ "$opt" == "--docker-args" ] ; then
166 local ${ssh_key_locations}
168 ~
/.ssh
/id_ed25519.pub
172 ~core
/.ssh
/authorized_keys
176 for keyfile
in "${ssh_key_locations[@]}"; do
177 if [[ -e ${keyfile} ]] ; then
178 ssh_pub_key
="$(cat ${keyfile})"
189 echo "Docker is not installed, make sure you are running on the 3.8 kernel"
190 echo "The best supported Docker release is Ubuntu 12.04.03 for it run the following"
192 echo "sudo apt-get update"
193 echo "sudo apt-get install linux-image-generic-lts-raring linux-headers-generic-lts-raring"
197 echo "sudo sh -c \"wget -qO- https://get.docker.io/gpg | apt-key add -\""
198 echo "sudo sh -c \"echo deb http://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list\""
199 echo "sudo apt-get update"
200 echo "sudo apt-get install lxc-docker"
206 read -r -d '' env_ruby
<< 'RUBY'
209 input
= STDIN.readlines.
join
210 yaml
= YAML.load
(input
)
212 if host_run
= yaml
['host_run']
213 params
= yaml
['params'] ||
{}
214 host_run.each
do |run|
216 run
= run.gsub
("$#{k}", v
)
218 STDOUT.
write "#{run}--SEP--"
223 host_run
=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e "$env_ruby"`
225 while [ "$host_run" ] ; do
226 iter
=${host_run%%--SEP--*}
228 echo "Host run: $iter"
231 host_run
="${host_run#*--SEP--}"
237 volumes
=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \
238 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['volumes'].map{|v| '-v ' << v['volume']['host'] << ':' << v['volume']['guest'] << ' '}.join"`
242 links
=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \
243 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['links'].map{|l| '--link ' << l['link']['name'] << ':' << l['link']['alias'] << ' '}.join"`
246 set_template_info
() {
248 templates
=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
249 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['templates']"`
252 arrTemplates
=(${templates// / })
253 config_data
=$
(cat $config_file)
258 for template
in "${arrTemplates[@]}"
260 [ ! -z $template ] && {
261 input
="$input _FILE_SEPERATOR_ $(cat $template)"
265 # we always want our config file last so it takes priority
266 input
="$input _FILE_SEPERATOR_ $config_data"
268 read -r -d '' env_ruby
<< 'RUBY'
271 input
=STDIN.readlines.
join
272 # default to UTF-8 for the dbs sake
273 env
= {'LANG' => 'en_US.UTF-8'}
274 input.
split('_FILE_SEPERATOR_').each
do |yml|
277 env.merge
!(YAML.load
(yml
)['env'] ||
{})
278 rescue Psych
::SyntaxError
=> e
286 puts env.map
{|k
,v|
"-e\n#{k}=#{v}" }.
join("\n")
289 raw
=`exec echo "$input" | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e "$env_ruby"`
294 if [ "$i" == "*ERROR." ]; then
296 elif [ -n "$i" ]; then
301 if [ "$ok" -ne 1 ]; then
303 echo "YAML syntax error. Please check your /var/discourse/containers/*.yml config files."
308 [ -z $docker_path ] && {
312 [ $command == "cleanup" ] && {
314 echo "The following command will"
315 echo "- Delete all docker images for old containers"
316 echo "- Delete all stopped and orphan containers"
318 read -p "Are you sure (Y/n): " -n 1 -r && echo
319 if [[ $REPLY =~ ^
[Yy
]$ ||
! $REPLY ]]
321 space
=$
(df
/var
/lib
/docker |
awk '{ print $4 }' |
grep -v Available
)
323 echo "Starting Cleanup"
325 if [[ ! -z `docker ps -aq` ]]; then
326 docker inspect
-f '{{.Id}},{{.State.Running}},{{.State.FinishedAt}}' $
(docker ps
-qa) | \
327 awk -F, 'BEGIN { TIME=strftime("%FT%T.000000000Z",systime()-60*60*24); } $2=="false" && $3 < TIME {print $1;}' | \
328 xargs --no-run-if-empty docker
rm >/dev
/null
2>/dev
/null
331 docker rmi
`docker images -a | grep '<none>' | awk '{print $3}'` 2> /dev
/null
333 let freed
=$space-$
(df
/var
/lib
/docker |
awk '{ print $4 }' |
grep -v Available
)
336 echo $
(df
/var
/lib
/docker |
awk '{ print $4 }' |
grep -v Available
)
339 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 }}}'
340 [ -z "$output" ] && {
341 [[ $freed > 0 ]] && { echo "./launcher cleanup cleared up $freed of disk space."; } ||
{ echo "./launcher cleanup has finished, no files were removed."; }
342 } ||
{ echo "./launcher cleanup cleared up $freed of disk space."; }
353 if [ ! -e $config_file ]
355 echo "Config file was not found, ensure $config_file exists"
357 echo "Available configs ( `cd containers && ls -dm *.yml | tr -s '\n' ' ' | awk '{ gsub(/\.yml/, ""); print }'`)"
362 docker_version
=($
($docker_path --version))
363 docker_version
=${test[2]//,/}
365 if compare_version
"1.2.0" "$docker_version"; then
366 echo "We recommend you upgrade docker, the version you are running has no restart policies, on reboot your container may not start up"
369 restart_policy
=${restart_policy:---restart=always}
374 if [ ! -e $config_file ]; then
375 echo "Config does not exist: $config_file" >&2
378 exec scripts
/mailtest
$config_file
381 set_existing_container
(){
382 existing
=`$docker_path ps -a | awk '{ print $1, $(NF) }' | grep " $config$" | awk '{ print $1 }'`
387 set_existing_container
389 if [ ! -z $existing ]
393 $docker_path stop
-t 10 $config
396 echo "$config was not started !"
403 existing
=`$docker_path ps | awk '{ print $1, $(NF) }' | grep " $config$" | awk '{ print $1 }'`
405 if [ ! -z $existing ]
407 echo "Nothing to do, your container has already started!"
411 existing
=`$docker_path ps -a | awk '{ print $1, $(NF) }' | grep " $config$" | awk '{ print $1 }'`
412 if [ ! -z $existing ]
414 echo "starting up existing container"
417 $docker_path start
$config
423 ports
=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \
424 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['expose'].map{|p| '-p ' << p.to_s << ' '}.join"`
433 $docker_path run
$user_args $links $attach_on_run $restart_policy "${env[@]}" -h "$hostname-$config" \
434 -e DOCKER_HOST_IP
=$docker_ip --name $config -t $ports $volumes $local_discourse/$config /sbin
/boot
447 # Is the image available?
448 # If not, pull it here so the user is aware what's happening.
449 $docker_path history $image >/dev
/null
2>&1 ||
$docker_path pull
$image
453 base_image
=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
454 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['base_image']"`
456 update_pups
=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
457 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['update_pups']"`
459 if [[ ! X
"" = X
"$base_image" ]]; then
468 run_command
="cd /pups &&"
469 if [[ ! "false" = $update_pups ]]; then
470 run_command
="$run_command git pull &&"
472 run_command
="$run_command /pups/bin/pups --stdin"
476 env
=("${env[@]}" "-e" "SSH_PUB_KEY=$ssh_pub_key")
478 (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 \
479 /bin
/bash
-c "$run_command") \
480 ||
($docker_path rm `cat $cidbootstrap` && rm $cidbootstrap)
482 [ ! -e $cidbootstrap ] && echo "FAILED TO BOOTSTRAP" && exit 1
486 $docker_path commit
`cat $cidbootstrap` $local_discourse/$config ||
echo 'FAILED TO COMMIT'
487 $docker_path rm `cat $cidbootstrap` && rm $cidbootstrap
493 echo "Successfully bootstrapped, to startup use ./launcher start $config"
503 exec $docker_path exec -it $config /bin
/bash
507 existing
=`$docker_path ps | awk '{ print $1, $(NF) }' | grep " $config$" | awk '{ print $1 }'`
509 if [[ ! -z $existing ]]; then
510 address
="`$docker_path port $config 22`"
511 split=(${address//:/ })
512 exec ssh -o StrictHostKeyChecking
=no root@
${split[0]} -p ${split[1]}
514 echo "$config is not running!"
526 $docker_path logs
$config
542 if [ "$(git symbolic-ref --short HEAD)" == "master" ]; then
543 echo "Ensuring discourse docker is up to date"
547 LOCAL
=$
(git rev-parse @
)
548 REMOTE
=$
(git rev-parse @
{u
})
549 BASE
=$
(git merge-base @ @
{u
})
551 if [ $LOCAL = $REMOTE ]; then
552 echo "Discourse Docker is up-to-date"
554 elif [ $LOCAL = $BASE ]; then
555 echo "Updating Discourse Docker"
556 git pull ||
(echo 'failed to update' && exit 1)
559 elif [ $REMOTE = $BASE ]; then
560 echo "Your version of Discourse Docker is ahead of origin"
563 echo "Discourse Docker has diverged source, this is only expected in Dev mode"
568 set_existing_container
570 if [ ! -z $existing ]
572 echo "Stopping old container"
575 $docker_path stop
-t 10 $config
581 if [ ! -z $existing ]
583 echo "Removing old container"
586 $docker_path rm $config
596 (set -x; $docker_path stop
-t 10 $config && $docker_path rm $config) ||
(echo "$config was not found" && exit 0)