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. able to attach stderr / out / tty
125 test=`$docker_path run $user_args -i --rm -a stdout -a stderr $image echo working`
126 if [[ "$test" =~
"working" ]] ; then : ; else
127 echo "Your Docker installation is not working correctly"
129 echo "See: https://meta.discourse.org/t/docker-error-on-bootstrap/13657/18?u=sam"
134 if [ "$opt" != "--skip-prereqs" ] ; then
138 if [ "$opt" == "--docker-args" ] ; then
145 local ${ssh_key_locations}
147 ~
/.ssh
/id_ed25519.pub
151 ~core
/.ssh
/authorized_keys
155 for keyfile
in "${ssh_key_locations[@]}"; do
156 if [[ -e ${keyfile} ]] ; then
157 ssh_pub_key
="$(cat ${keyfile})"
163 echo "This user has no SSH key, but a SSH key is required to access the Discourse Docker container."
164 read -p "Generate a SSH key? (Y/n) " -n 1 -r
165 if [[ $REPLY =~ ^
[Nn
]$
]] ; then
167 echo WARNING
: You may not be able to log
in to your container.
171 echo Generating SSH key
172 mkdir
-p ~
/.
ssh && ssh-keygen
-f ~
/.ssh
/id_rsa
-t rsa
-N ''
174 ssh_pub_key
="$(cat ~/.ssh/id_rsa.pub)"
185 echo "Docker is not installed, make sure you are running on the 3.8 kernel"
186 echo "The best supported Docker release is Ubuntu 12.04.03 for it run the following"
188 echo "sudo apt-get update"
189 echo "sudo apt-get install linux-image-generic-lts-raring linux-headers-generic-lts-raring"
193 echo "sudo sh -c \"wget -qO- https://get.docker.io/gpg | apt-key add -\""
194 echo "sudo sh -c \"echo deb http://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list\""
195 echo "sudo apt-get update"
196 echo "sudo apt-get install lxc-docker"
202 read -r -d '' env_ruby
<< 'RUBY'
205 input
= STDIN.readlines.
join
206 yaml
= YAML.load
(input
)
208 if host_run
= yaml
['host_run']
209 params
= yaml
['params'] ||
{}
210 host_run.each
do |run|
212 run
= run.gsub
("$#{k}", v
)
214 STDOUT.
write "#{run}--SEP--"
219 host_run
=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e "$env_ruby"`
221 while [ "$host_run" ] ; do
222 iter
=${host_run%%--SEP--*}
224 echo "Host run: $iter"
227 host_run
="${host_run#*--SEP--}"
233 volumes
=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \
234 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['volumes'].map{|v| '-v ' << v['volume']['host'] << ':' << v['volume']['guest'] << ' '}.join"`
238 links
=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \
239 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['links'].map{|l| '--link ' << l['link']['name'] << ':' << l['link']['alias'] << ' '}.join"`
242 set_template_info
() {
244 templates
=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
245 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['templates']"`
248 arrTemplates
=(${templates// / })
249 config_data
=$
(cat $config_file)
254 for template
in "${arrTemplates[@]}"
256 [ ! -z $template ] && {
257 input
="$input _FILE_SEPERATOR_ $(cat $template)"
261 # we always want our config file last so it takes priority
262 input
="$input _FILE_SEPERATOR_ $config_data"
264 read -r -d '' env_ruby
<< 'RUBY'
267 input
=STDIN.readlines.
join
268 # default to UTF-8 for the dbs sake
269 env
= {'LANG' => 'en_US.UTF-8'}
270 input.
split('_FILE_SEPERATOR_').each
do |yml|
273 env.merge
!(YAML.load
(yml
)['env'] ||
{})
274 rescue Psych
::SyntaxError
=> e
282 puts env.map
{|k
,v|
"-e\n#{k}=#{v}" }.
join("\n")
285 raw
=`exec echo "$input" | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e "$env_ruby"`
290 if [ "$i" == "*ERROR." ]; then
292 elif [ -n "$i" ]; then
297 if [ "$ok" -ne 1 ]; then
299 echo "YAML syntax error. Please check your /var/discourse/containers/*.yml config files."
304 [ -z $docker_path ] && {
308 [ $command == "cleanup" ] && {
310 echo "The following command will"
311 echo "- Delete all docker images for old containers"
312 echo "- Delete all stopped and orphan containers"
314 read -p "Are you sure (Y/n): " -n 1 -r && echo
315 if [[ $REPLY =~ ^
[Yy
]$ ||
! $REPLY ]]
317 space
=$
(df
/var
/lib
/docker |
awk '{ print $4 }' |
grep -v Available
)
319 echo "Starting Cleanup"
321 if [[ ! -z `docker ps -aq` ]]; then
322 docker inspect
-f '{{.Id}},{{.State.Running}},{{.State.FinishedAt}}' $
(docker ps
-qa) | \
323 awk -F, 'BEGIN { TIME=strftime("%FT%T.000000000Z",systime()-60*60*24); } $2=="false" && $3 < TIME {print $1;}' | \
324 xargs --no-run-if-empty docker
rm >/dev
/null
2>/dev
/null
327 docker rmi
`docker images -a | grep '<none>' | awk '{print $3}'` 2> /dev
/null
329 let freed
=$space-$
(df
/var
/lib
/docker |
awk '{ print $4 }' |
grep -v Available
)
332 echo $
(df
/var
/lib
/docker |
awk '{ print $4 }' |
grep -v Available
)
335 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 }}}'
336 [ -z "$output" ] && {
337 [[ $freed > 0 ]] && { echo "./launcher cleanup cleared up $freed of disk space."; } ||
{ echo "./launcher cleanup has finished, no files were removed."; }
338 } ||
{ echo "./launcher cleanup cleared up $freed of disk space."; }
349 if [ ! -e $config_file ]
351 echo "Config file was not found, ensure $config_file exists"
353 echo "Available configs ( `cd containers && ls -dm *.yml | tr -s '\n' ' ' | awk '{ gsub(/\.yml/, ""); print }'`)"
358 docker_version
=($
($docker_path --version))
359 docker_version
=${test[2]//,/}
361 if compare_version
"1.2.0" "$docker_version"; then
362 echo "We recommend you upgrade docker, the version you are running has no restart policies, on reboot your container may not start up"
365 restart_policy
=${restart_policy:---restart=always}
370 if [ ! -e $config_file ]; then
371 echo "Config does not exist: $config_file" >&2
374 exec scripts
/mailtest
$config_file
377 set_existing_container
(){
378 existing
=`$docker_path ps -a | awk '{ print $1, $(NF) }' | grep "$config$" | awk '{ print $1 }'`
383 set_existing_container
385 if [ ! -z $existing ]
389 $docker_path stop
-t 10 $config
392 echo "$config was not started !"
399 existing
=`$docker_path ps | awk '{ print $1, $(NF) }' | grep "$config$" | awk '{ print $1 }'`
400 if [ ! -z $existing ]
402 echo "Nothing to do, your container has already started!"
406 existing
=`$docker_path ps -a | awk '{ print $1, $(NF) }' | grep "$config$" | awk '{ print $1 }'`
407 if [ ! -z $existing ]
409 echo "starting up existing container"
412 $docker_path start
$config
418 ports
=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \
419 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['expose'].map{|p| '-p ' << p.to_s << ' '}.join"`
428 $docker_path run
$user_args $links $attach_on_run $restart_policy "${env[@]}" -h "$hostname-$config" \
429 -e DOCKER_HOST_IP
=$docker_ip --name $config -t $ports $volumes $local_discourse/$config /sbin
/boot
442 # Is the image available?
443 # If not, pull it here so the user is aware what's happening.
444 $docker_path history $image >/dev
/null
2>&1 ||
$docker_path pull
$image
448 base_image
=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
449 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['base_image']"`
451 update_pups
=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
452 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['update_pups']"`
454 if [[ ! X
"" = X
"$base_image" ]]; then
463 run_command
="cd /pups &&"
464 if [[ ! "false" = $update_pups ]]; then
465 run_command
="$run_command git pull &&"
467 run_command
="$run_command /pups/bin/pups --stdin"
471 env
=("${env[@]}" "-e" "SSH_PUB_KEY=$ssh_pub_key")
473 (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 \
474 /bin
/bash
-c "$run_command") \
475 ||
($docker_path rm `cat $cidbootstrap` && rm $cidbootstrap)
477 [ ! -e $cidbootstrap ] && echo "FAILED TO BOOTSTRAP" && exit 1
481 $docker_path commit
`cat $cidbootstrap` $local_discourse/$config ||
echo 'FAILED TO COMMIT'
482 $docker_path rm `cat $cidbootstrap` && rm $cidbootstrap
488 echo "Successfully bootstrapped, to startup use ./launcher start $config"
498 exec $docker_path exec -it $config /bin
/bash
502 existing
=`$docker_path ps | awk '{ print $1, $(NF) }' | grep "$config$" | awk '{ print $1 }'`
504 if [[ ! -z $existing ]]; then
505 address
="`$docker_path port $config 22`"
506 split=(${address//:/ })
507 exec ssh -o StrictHostKeyChecking
=no root@
${split[0]} -p ${split[1]}
509 echo "$config is not running!"
521 $docker_path logs
$config
537 if [ "$(git symbolic-ref --short HEAD)" == "master" ]; then
538 echo "Ensuring discourse docker is up to date"
542 LOCAL
=$
(git rev-parse @
)
543 REMOTE
=$
(git rev-parse @
{u
})
544 BASE
=$
(git merge-base @ @
{u
})
546 if [ $LOCAL = $REMOTE ]; then
547 echo "Discourse Docker is up-to-date"
549 elif [ $LOCAL = $BASE ]; then
550 echo "Updating Discourse Docker"
551 git pull ||
(echo 'failed to update' && exit 1)
554 elif [ $REMOTE = $BASE ]; then
555 echo "Your version of Discourse Docker is ahead of origin"
558 echo "Discourse Docker has diverged source, this is only expected in Dev mode"
563 set_existing_container
565 if [ ! -z $existing ]
567 echo "Stopping old container"
570 $docker_path stop
-t 10 $config
576 if [ ! -z $existing ]
578 echo "Removing old container"
581 $docker_path rm $config
591 (set -x; $docker_path stop
-t 10 $config && $docker_path rm $config) ||
(echo "$config was not found" && exit 0)