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.6.0'
20 docker_rec_version
='1.6.0'
22 config_file
=containers
/"$config".yml
23 cidbootstrap
=cids
/"$config"_bootstrap.cid
24 local_discourse
=local_discourse
25 image
=discourse
/discourse
:1.0.15
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"
160 avail_mem
="$(LANG=C free -m | grep '^Mem:' | awk '{print $2}')"
161 if [ "$avail_mem" -lt 900 ]; then
162 resources
="insufficient"
163 echo "WARNING: You do not appear to have sufficient memory to run Discourse."
165 echo "Your system may not work properly, or future upgrades of Discourse may"
166 echo "not complete successfully."
168 echo "See https://github.com/discourse/discourse/blob/master/docs/INSTALL-cloud.md#create-new-cloud-server"
169 elif [ "$avail_mem" -lt 1800 ]; then
170 total_swap
="$(LANG=C free -m | grep ^Swap: | awk '{print $2}')"
171 if [ "$total_swap" -lt 1000 ]; then
172 resources
="insufficient"
173 echo "WARNING: You must have at least 1GB of swap when running with less"
174 echo "than 2GB of RAM."
176 echo "Your system may not work properly, or future upgrades of Discourse may"
177 echo "not complete successfully."
179 echo "See https://github.com/discourse/discourse/blob/master/docs/INSTALL-cloud.md#set-up-swap-if-needed"
184 free_disk
="$(df /var | tail -n 1 | awk '{print $4}')"
185 if [ "$free_disk" -lt 5000 ]; then
186 resources
="insufficient"
187 echo "WARNING: You must have at least 5GB of *free* disk space to run Discourse."
189 echo "Insufficient disk space may result in problems running your site, and may"
190 echo "not even allow Discourse installation to complete successfully."
192 echo "Please free up some space, or expand your disk, before continuing."
196 if [ -t 0 ] && [ "$resources" != "ok" ]; then
198 read -p "Press ENTER to continue, or Ctrl-C to exit and give your system more resources"
202 if [ "$opt" != "--skip-prereqs" ] ; then
206 if [ "$opt" == "--docker-args" ] ; then
213 local ${ssh_key_locations}
215 ~
/.ssh
/id_ed25519.pub
219 ~core
/.ssh
/authorized_keys
223 for keyfile
in "${ssh_key_locations[@]}"; do
224 if [[ -e ${keyfile} ]] ; then
225 ssh_pub_key
="$(cat ${keyfile})"
236 echo "Docker is not installed, you will need to install Docker in order to run Discourse"
237 echo "Please visit https://docs.docker.com/installation/ for instructions on how to do this for your system"
239 echo "If you are running Ubuntu Trusty or later, you can try the following:"
242 echo "sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D"
243 echo "sudo sh -c \"echo deb https://apt.dockerproject.org/repo ubuntu-$(lsb_release -sc) main > /etc/apt/sources.list.d/docker.list\""
244 echo "sudo apt-get update"
245 echo "sudo apt-get install docker-engine"
251 read -r -d '' env_ruby
<< 'RUBY'
254 input
= STDIN.readlines.
join
255 yaml
= YAML.load
(input
)
257 if host_run
= yaml
['host_run']
258 params
= yaml
['params'] ||
{}
259 host_run.each
do |run|
261 run
= run.gsub
("$#{k}", v
)
263 STDOUT.
write "#{run}--SEP--"
268 host_run
=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e "$env_ruby"`
270 while [ "$host_run" ] ; do
271 iter
=${host_run%%--SEP--*}
273 echo "Host run: $iter"
276 host_run
="${host_run#*--SEP--}"
282 volumes
=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \
283 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['volumes'].map{|v| '-v ' << v['volume']['host'] << ':' << v['volume']['guest'] << ' '}.join"`
287 links
=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \
288 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['links'].map{|l| '--link ' << l['link']['name'] << ':' << l['link']['alias'] << ' '}.join"`
291 set_template_info
() {
293 templates
=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
294 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['templates']"`
297 arrTemplates
=(${templates// / })
298 config_data
=$
(cat $config_file)
303 for template
in "${arrTemplates[@]}"
305 [ ! -z $template ] && {
306 input
="$input _FILE_SEPERATOR_ $(cat $template)"
310 # we always want our config file last so it takes priority
311 input
="$input _FILE_SEPERATOR_ $config_data"
313 read -r -d '' env_ruby
<< 'RUBY'
316 input
=STDIN.readlines.
join
317 # default to UTF-8 for the dbs sake
318 env
= {'LANG' => 'en_US.UTF-8'}
319 input.
split('_FILE_SEPERATOR_').each
do |yml|
322 env.merge
!(YAML.load
(yml
)['env'] ||
{})
323 rescue Psych
::SyntaxError
=> e
331 puts env.map
{|k
,v|
"-e\n#{k}=#{v}" }.
join("\n")
334 raw
=`exec echo "$input" | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e "$env_ruby"`
339 if [ "$i" == "*ERROR." ]; then
341 elif [ -n "$i" ]; then
346 if [ "$ok" -ne 1 ]; then
348 echo "YAML syntax error. Please check your /var/discourse/containers/*.yml config files."
353 [ -z $docker_path ] && {
357 [ "$command" == "cleanup" ] && {
359 echo "The following command will"
360 echo "- Delete all docker images for old containers"
361 echo "- Delete all stopped and orphan containers"
363 read -p "Are you sure (Y/n): " -n 1 -r && echo
364 if [[ $REPLY =~ ^
[Yy
]$ ||
! $REPLY ]]
366 space
=$
(df
/var
/lib
/docker |
awk '{ print $4 }' |
grep -v Available
)
367 echo "Starting Cleanup (bytes free $space)"
369 STATE_DIR
=.
/.gc-state
scripts
/docker-gc
371 space
=$
(df
/var
/lib
/docker |
awk '{ print $4 }' |
grep -v Available
)
372 echo "Finished Cleanup (bytes free $space)"
384 if [ ! -e $config_file ]
386 echo "Config file was not found, ensure $config_file exists"
388 echo "Available configs ( `cd containers && ls -dm *.yml | tr -s '\n' ' ' | awk '{ gsub(/\.yml/, ""); print }'`)"
393 docker_version
=($
($docker_path --version))
394 docker_version
=${test[2]//,/}
396 if compare_version
"1.2.0" "$docker_version"; then
397 echo "We recommend you upgrade docker, the version you are running has no restart policies, on reboot your container may not start up"
400 restart_policy
=${restart_policy:---restart=always}
403 set_existing_container
(){
404 existing
=`$docker_path ps -a | awk '{ print $1, $(NF) }' | grep " $config$" | awk '{ print $1 }'`
409 set_existing_container
411 if [ ! -z $existing ]
415 $docker_path stop
-t 10 $config
418 echo "$config was not started !"
425 existing
=`$docker_path ps | awk '{ print $1, $(NF) }' | grep " $config$" | awk '{ print $1 }'`
427 if [ ! -z $existing ]
429 echo "Nothing to do, your container has already started!"
433 existing
=`$docker_path ps -a | awk '{ print $1, $(NF) }' | grep " $config$" | awk '{ print $1 }'`
434 if [ ! -z $existing ]
436 echo "starting up existing container"
439 $docker_path start
$config
445 ports
=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \
446 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['expose'].map{|p| '-p ' << p.to_s << ' '}.join"`
448 docker_args
=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \
449 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['docker_args']"`
458 $docker_path run
$user_args $links $attach_on_run $restart_policy "${env[@]}" -h "$hostname-$config" \
459 -e DOCKER_HOST_IP
=$docker_ip --name $config -t $ports $volumes $docker_args $local_discourse/$config \
475 # Is the image available?
476 # If not, pull it here so the user is aware what's happening.
477 $docker_path history $image >/dev
/null
2>&1 ||
$docker_path pull
$image
481 base_image
=`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)['base_image']"`
484 update_pups
=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
485 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['update_pups']"`
487 if [[ ! X
"" = X
"$base_image" ]]; then
496 run_command
="cd /pups &&"
497 if [[ ! "false" = $update_pups ]]; then
498 run_command
="$run_command git pull &&"
500 run_command
="$run_command /pups/bin/pups --stdin"
504 env
=("${env[@]}" "-e" "SSH_PUB_KEY=$ssh_pub_key")
506 (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 \
507 /bin
/bash
-c "$run_command") \
508 ||
($docker_path rm `cat $cidbootstrap` && rm $cidbootstrap)
510 [ ! -e $cidbootstrap ] && echo "** FAILED TO BOOTSTRAP ** please scroll up and look for earlier error messages, there may be more than one" && exit 1
514 $docker_path commit
`cat $cidbootstrap` $local_discourse/$config ||
echo 'FAILED TO COMMIT'
515 $docker_path rm `cat $cidbootstrap` && rm $cidbootstrap
521 echo "Successfully bootstrapped, to startup use ./launcher start $config"
526 exec $docker_path exec -it $config /bin
/bash
530 existing
=`$docker_path ps | awk '{ print $1, $(NF) }' | grep " $config$" | awk '{ print $1 }'`
532 if [[ ! -z $existing ]]; then
533 address
="`$docker_path port $config 22`"
534 split=(${address//:/ })
535 exec ssh -o StrictHostKeyChecking
=no root@
${split[0]} -p ${split[1]}
537 echo "$config is not running!"
549 $docker_path logs
$config
565 if [ "$(git symbolic-ref --short HEAD)" == "master" ]; then
566 echo "Ensuring discourse docker is up to date"
570 LOCAL
=$
(git rev-parse @
)
571 REMOTE
=$
(git rev-parse @
{u
})
572 BASE
=$
(git merge-base @ @
{u
})
574 if [ $LOCAL = $REMOTE ]; then
575 echo "Discourse Docker is up-to-date"
577 elif [ $LOCAL = $BASE ]; then
578 echo "Updating Discourse Docker"
579 git pull ||
(echo 'failed to update' && exit 1)
582 elif [ $REMOTE = $BASE ]; then
583 echo "Your version of Discourse Docker is ahead of origin"
586 echo "Discourse Docker has diverged source, this is only expected in Dev mode"
591 set_existing_container
593 if [ ! -z $existing ]
595 echo "Stopping old container"
598 $docker_path stop
-t 10 $config
604 if [ ! -z $existing ]
606 echo "Removing old container"
609 $docker_path rm $config
619 (set -x; $docker_path stop
-t 10 $config && $docker_path rm $config) ||
(echo "$config was not found" && exit 0)