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 [ -n "$DOCKER_HOST" ]; then
37 docker_ip
=`sed -e 's/^tcp:\/\/\(.*\):.*$/\1/' <<< "$DOCKER_HOST"`
38 elif [ -x "$(which ip 2>/dev/null)" ]; then
39 docker_ip
=`ip addr show docker0 | \
41 awk '{ split($2,a,"/"); print a[1] }';`
43 docker_ip
=`ifconfig | \
44 grep -B1 "inet addr" | \
45 awk '{ if ( $1 == "inet" ) { print $2 } else if ( $2 == "Link" ) { printf "%s:" ,$1 } }' | \
47 awk -F: '{ print $3 }';`
52 echo "Usage: launcher COMMAND CONFIG [--skip-prereqs]"
54 echo " start: Start/initialize a container"
55 echo " stop: Stop a running container"
56 echo " restart: Restart a container"
57 echo " destroy: Stop and remove a container"
58 echo " enter: Use nsenter to enter a container"
59 echo " ssh: Start a bash shell in a running container"
60 echo " logs: Docker logs for container"
61 echo " bootstrap: Bootstrap a container for the config based on a template"
62 echo " rebuild: Rebuild a container (destroy old, bootstrap, start new)"
63 echo " cleanup: Remove all containers that have stopped for > 24 hours"
66 echo " --skip-prereqs Don't check prerequisites"
67 echo " --docker-args Extra arguments to pass when running docker"
74 IFS
=.
read -a ver_a
<<< "$1"
75 IFS
=.
read -a ver_b
<<< "$2"
77 while [[ -n $ver_a ]]; do
78 if (( ver_a
> ver_b
)); then
80 elif (( ver_b
> ver_a
)); then
89 return 1 # They are equal
94 # 1. docker daemon running?
95 # we send stderr to /dev/null cause we don't care about warnings,
96 # it usually complains about swap which does not matter
97 test=`$docker_path info 2> /dev/null`
99 if [[ $?
-ne 0 ]] ; then
100 echo "Cannot connect to the docker daemon - verify it is running and you have access"
104 # 2. running aufs or btrfs
105 test=`$docker_path info 2> /dev/null | grep 'Driver: '`
106 if [[ "$test" =~
[aufs|btrfs|zfs|overlay
] ]] ; then : ; else
107 echo "Your Docker installation is not using a supported filesystem if we were to proceed you may have a broken install."
108 echo "aufs is the recommended filesystem you should be using (zfs/btrfs and overlay may work as well)"
109 echo "You can tell what filesystem you are using by running \"docker info\" and looking at the driver"
111 echo "If you wish to continue anyway using your existing unsupported filesystem"
112 echo "read the source code of launcher and figure out how to bypass this."
116 # 3. running recommended docker version
117 test=($
($docker_path --version)) # Get docker version string
118 test=${test[2]//,/} # Get version alone and strip comma if exists
120 [[ "$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
122 # At least minimum version
123 if compare_version
"${docker_min_version}" "${test}"; then
124 echo "ERROR: Docker version ${test} not supported, please upgrade to at least ${docker_min_version}, or recommended ${docker_rec_version}"
128 # Recommend best version
129 if compare_version
"${docker_rec_version}" "${test}"; then
130 echo "WARNING: Docker version ${test} deprecated, recommend upgrade to ${docker_rec_version} or newer."
133 # 4. discourse docker image is downloaded
134 test=`$docker_path images | awk '{print $1 ":" $2 }' | grep "$image"`
136 if [ -z "$test" ]; then
138 echo "WARNING: We are about to start downloading the Discourse base image"
139 echo "This process may take anywhere between a few minutes to an hour, depending on your network speed"
141 echo "Please be patient"
146 # 5. able to attach stderr / out / tty
147 test=`$docker_path run $user_args -i --rm -a stdout -a stderr $image echo working`
148 if [[ "$test" =~
"working" ]] ; then : ; else
149 echo "Your Docker installation is not working correctly"
151 echo "See: https://meta.discourse.org/t/docker-error-on-bootstrap/13657/18?u=sam"
155 # 6. sufficient available resources
159 avail_mem
="$(LANG=C free -m | grep '^Mem:' | awk '{print $2}')"
160 if [ "$avail_mem" -lt 900 ]; then
161 resources
="insufficient"
162 echo "WARNING: You do not appear to have sufficient memory to run Discourse."
164 echo "Your system may not work properly, or future upgrades of Discourse may"
165 echo "not complete successfully."
167 echo "See https://github.com/discourse/discourse/blob/master/docs/INSTALL-cloud.md#create-new-cloud-server"
168 elif [ "$avail_mem" -lt 1800 ]; then
169 resources
="insufficient"
170 total_swap
="$(LANG=C free -m | grep ^Swap: | awk '{print $2}')"
171 if [ "$total_swap" -lt 1000 ]; then
172 echo "WARNING: You must have at least 1GB of swap when running with less"
173 echo "than 2GB of RAM."
175 echo "Your system may not work properly, or future upgrades of Discourse may"
176 echo "not complete successfully."
178 echo "See https://github.com/discourse/discourse/blob/master/docs/INSTALL-cloud.md#set-up-swap-if-needed"
183 free_disk
="$(df /var | tail -n 1 | awk '{print $4}')"
184 if [ "$free_disk" -lt 5000 ]; then
185 resources
="insufficient"
186 echo "WARNING: You must have at least 5GB of *free* disk space to run Discourse."
188 echo "Insufficient disk space may result in problems running your site, and may"
189 echo "not even allow Discourse installation to complete successfully."
191 echo "Please free up some space, or expand your disk, before continuing."
195 if [ -t 0 ] && [ "$resources" != "ok" ]; then
197 read -p "Press ENTER to continue, or Ctrl-C to exit and give your system more resources"
201 if [ "$opt" != "--skip-prereqs" ] ; then
205 if [ "$opt" == "--docker-args" ] ; then
212 local ${ssh_key_locations}
214 ~
/.ssh
/id_ed25519.pub
218 ~core
/.ssh
/authorized_keys
222 for keyfile
in "${ssh_key_locations[@]}"; do
223 if [[ -e ${keyfile} ]] ; then
224 ssh_pub_key
="$(cat ${keyfile})"
235 echo "Docker is not installed, you will need to install Docker in order to run Discourse"
236 echo "Please visit https://docs.docker.com/installation/ for instructions on how to do this for your system"
238 echo "If you are running Ubuntu Trusty or later, you can try the following:"
241 echo "sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D"
242 echo "sudo sh -c \"echo deb https://apt.dockerproject.org/repo ubuntu-$(lsb_release -sc) main > /etc/apt/sources.list.d/docker.list\""
243 echo "sudo apt-get update"
244 echo "sudo apt-get install docker-engine"
250 read -r -d '' env_ruby
<< 'RUBY'
253 input
= STDIN.readlines.
join
254 yaml
= YAML.load
(input
)
256 if host_run
= yaml
['host_run']
257 params
= yaml
['params'] ||
{}
258 host_run.each
do |run|
260 run
= run.gsub
("$#{k}", v
)
262 STDOUT.
write "#{run}--SEP--"
267 host_run
=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e "$env_ruby"`
269 while [ "$host_run" ] ; do
270 iter
=${host_run%%--SEP--*}
272 echo "Host run: $iter"
275 host_run
="${host_run#*--SEP--}"
281 volumes
=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \
282 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['volumes'].map{|v| '-v ' << v['volume']['host'] << ':' << v['volume']['guest'] << ' '}.join"`
286 links
=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \
287 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['links'].map{|l| '--link ' << l['link']['name'] << ':' << l['link']['alias'] << ' '}.join"`
290 set_template_info
() {
292 templates
=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
293 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['templates']"`
296 arrTemplates
=(${templates// / })
297 config_data
=$
(cat $config_file)
302 for template
in "${arrTemplates[@]}"
304 [ ! -z $template ] && {
305 input
="$input _FILE_SEPERATOR_ $(cat $template)"
309 # we always want our config file last so it takes priority
310 input
="$input _FILE_SEPERATOR_ $config_data"
312 read -r -d '' env_ruby
<< 'RUBY'
315 input
=STDIN.readlines.
join
316 # default to UTF-8 for the dbs sake
317 env
= {'LANG' => 'en_US.UTF-8'}
318 input.
split('_FILE_SEPERATOR_').each
do |yml|
321 env.merge
!(YAML.load
(yml
)['env'] ||
{})
322 rescue Psych
::SyntaxError
=> e
330 puts env.map
{|k
,v|
"-e\n#{k}=#{v}" }.
join("\n")
333 raw
=`exec echo "$input" | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e "$env_ruby"`
338 if [ "$i" == "*ERROR." ]; then
340 elif [ -n "$i" ]; then
345 if [ "$ok" -ne 1 ]; then
347 echo "YAML syntax error. Please check your /var/discourse/containers/*.yml config files."
352 [ -z $docker_path ] && {
356 [ "$command" == "cleanup" ] && {
358 echo "The following command will"
359 echo "- Delete all docker images for old containers"
360 echo "- Delete all stopped and orphan containers"
362 read -p "Are you sure (Y/n): " -n 1 -r && echo
363 if [[ $REPLY =~ ^
[Yy
]$ ||
! $REPLY ]]
365 space
=$
(df
/var
/lib
/docker |
awk '{ print $4 }' |
grep -v Available
)
366 echo "Starting Cleanup (bytes free $space)"
368 STATE_DIR
=.
/.gc-state
scripts
/docker-gc
370 space
=$
(df
/var
/lib
/docker |
awk '{ print $4 }' |
grep -v Available
)
371 echo "Finished Cleanup (bytes free $space)"
383 if [ ! -e $config_file ]
385 echo "Config file was not found, ensure $config_file exists"
387 echo "Available configs ( `cd containers && ls -dm *.yml | tr -s '\n' ' ' | awk '{ gsub(/\.yml/, ""); print }'`)"
392 docker_version
=($
($docker_path --version))
393 docker_version
=${test[2]//,/}
395 if compare_version
"1.2.0" "$docker_version"; then
396 echo "We recommend you upgrade docker, the version you are running has no restart policies, on reboot your container may not start up"
399 restart_policy
=${restart_policy:---restart=always}
402 set_existing_container
(){
403 existing
=`$docker_path ps -a | awk '{ print $1, $(NF) }' | grep " $config$" | awk '{ print $1 }'`
408 set_existing_container
410 if [ ! -z $existing ]
414 $docker_path stop
-t 10 $config
417 echo "$config was not started !"
424 existing
=`$docker_path ps | awk '{ print $1, $(NF) }' | grep " $config$" | awk '{ print $1 }'`
426 if [ ! -z $existing ]
428 echo "Nothing to do, your container has already started!"
432 existing
=`$docker_path ps -a | awk '{ print $1, $(NF) }' | grep " $config$" | awk '{ print $1 }'`
433 if [ ! -z $existing ]
435 echo "starting up existing container"
438 $docker_path start
$config
444 ports
=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \
445 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['expose'].map{|p| '-p ' << p.to_s << ' '}.join"`
447 docker_args
=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \
448 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['docker_args']"`
457 $docker_path run
$user_args $links $attach_on_run $restart_policy "${env[@]}" -h "$hostname-$config" \
458 -e DOCKER_HOST_IP
=$docker_ip --name $config -t $ports $volumes $docker_args $local_discourse/$config \
472 # Is the image available?
473 # If not, pull it here so the user is aware what's happening.
474 $docker_path history $image >/dev
/null
2>&1 ||
$docker_path pull
$image
478 base_image
=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
479 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['base_image']"`
481 update_pups
=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
482 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['update_pups']"`
484 if [[ ! X
"" = X
"$base_image" ]]; then
493 run_command
="cd /pups &&"
494 if [[ ! "false" = $update_pups ]]; then
495 run_command
="$run_command git pull &&"
497 run_command
="$run_command /pups/bin/pups --stdin"
501 env
=("${env[@]}" "-e" "SSH_PUB_KEY=$ssh_pub_key")
503 (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 \
504 /bin
/bash
-c "$run_command") \
505 ||
($docker_path rm `cat $cidbootstrap` && rm $cidbootstrap)
507 [ ! -e $cidbootstrap ] && echo "** FAILED TO BOOTSTRAP ** please scroll up and look for earlier error messages, there may be more than one" && exit 1
511 $docker_path commit
`cat $cidbootstrap` $local_discourse/$config ||
echo 'FAILED TO COMMIT'
512 $docker_path rm `cat $cidbootstrap` && rm $cidbootstrap
518 echo "Successfully bootstrapped, to startup use ./launcher start $config"
523 exec $docker_path exec -it $config /bin
/bash
527 existing
=`$docker_path ps | awk '{ print $1, $(NF) }' | grep " $config$" | awk '{ print $1 }'`
529 if [[ ! -z $existing ]]; then
530 address
="`$docker_path port $config 22`"
531 split=(${address//:/ })
532 exec ssh -o StrictHostKeyChecking
=no root@
${split[0]} -p ${split[1]}
534 echo "$config is not running!"
546 $docker_path logs
$config
562 if [ "$(git symbolic-ref --short HEAD)" == "master" ]; then
563 echo "Ensuring discourse docker is up to date"
567 LOCAL
=$
(git rev-parse @
)
568 REMOTE
=$
(git rev-parse @
{u
})
569 BASE
=$
(git merge-base @ @
{u
})
571 if [ $LOCAL = $REMOTE ]; then
572 echo "Discourse Docker is up-to-date"
574 elif [ $LOCAL = $BASE ]; then
575 echo "Updating Discourse Docker"
576 git pull ||
(echo 'failed to update' && exit 1)
579 elif [ $REMOTE = $BASE ]; then
580 echo "Your version of Discourse Docker is ahead of origin"
583 echo "Discourse Docker has diverged source, this is only expected in Dev mode"
588 set_existing_container
590 if [ ! -z $existing ]
592 echo "Stopping old container"
595 $docker_path stop
-t 10 $config
601 if [ ! -z $existing ]
603 echo "Removing old container"
606 $docker_path rm $config
616 (set -x; $docker_path stop
-t 10 $config && $docker_path rm $config) ||
(echo "$config was not found" && exit 0)