9 docker_min_version
='1.2.0'
10 docker_rec_version
='1.2.0'
12 config_file
=containers
/"$config".yml
13 cidfile
=cids
/"$config".cid
14 cidbootstrap
=cids
/"$config"_bootstrap.cid
15 local_discourse
=local_discourse
16 image
=samsaffron
/discourse
:1.0.7
17 docker_path
=`which docker.io || which docker`
19 if [ "${SUPERVISED}" = "true" ]; then
20 restart_policy
="--restart=no"
22 attach_on_run
="-a stdout -a stderr"
27 if [ -x "$(which ip 2>/dev/null)" ]; then
28 docker_ip
=`ip addr show docker0 | \
30 awk '{ split($2,a,"/"); print a[1] }';`
32 docker_ip
=`ifconfig | \
33 grep -B1 "inet addr" | \
34 awk '{ if ( $1 == "inet" ) { print $2 } else if ( $2 == "Link" ) { printf "%s:" ,$1 } }' | \
36 awk -F: '{ print $3 }';`
41 echo "Usage: launcher COMMAND CONFIG [--skip-prereqs]"
43 echo " start: Start/initialize a container"
44 echo " stop: Stop a running container"
45 echo " restart: Restart a container"
46 echo " destroy: Stop and remove a container"
47 echo " enter: Use nsenter to enter a container"
48 echo " ssh: Start a bash shell in a running container"
49 echo " logs: Docker logs for container"
50 echo " mailtest: Test the mail settings in a container"
51 echo " bootstrap: Bootstrap a container for the config based on a template"
52 echo " rebuild: Rebuild a container (destroy old, bootstrap, start new)"
53 echo " cleanup: Remove all containers that have stopped for > 24 hours"
56 echo " --skip-prereqs Don't check prerequisites"
57 echo " --docker-args Extra arguments to pass when running docker"
64 IFS
=.
read -a ver_a
<<< "$1"
65 IFS
=.
read -a ver_b
<<< "$2"
67 while [[ -n $ver_a ]]; do
68 if (( ver_a
> ver_b
)); then
70 elif (( ver_b
> ver_a
)); then
79 return 1 # They are equal
84 # 1. docker daemon running?
85 test=`$docker_path info >/dev/null`
87 if [[ $?
-ne 0 ]] ; then
88 echo "Cannot connect to the docker daemon - verify it is running and you have access"
92 # 2. running aufs or btrfs
93 test=`$docker_path info 2> /dev/null | grep 'Driver: '`
94 if [[ "$test" =~
[aufs|btrfs
] ]] ; then : ; else
95 echo "Your Docker installation is not using the recommended AuFS (union filesystem) and may be unstable."
96 echo "If you are unable to bootstrap / stop your image please report the issue at:"
97 echo "https://meta.discourse.org/t/discourse-docker-installation-without-aufs/15639"
99 read -p "Continue without proper filesystem? [yN]" yn
106 # 3. running recommended docker version
107 test=($
($docker_path --version)) # Get docker version string
108 test=${test[2]//,/} # Get version alone and strip comma if exists
110 [[ "$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
112 # At least minimum version
113 if compare_version
"${docker_min_version}" "${test}"; then
114 echo "ERROR: Docker version ${test} not supported, please upgrade to at least ${docker_min_version}, or recommended ${docker_rec_version}"
118 # Recommend best version
119 if compare_version
"${docker_rec_version}" "${test}"; then
120 echo "WARNING: Docker version ${test} deprecated, recommend upgrade to ${docker_rec_version} or newer."
123 # 4. able to attach stderr / out / tty
124 test=`$docker_path run $user_args -i --rm -a stdout -a stderr $image echo working`
125 if [[ "$test" =~
"working" ]] ; then : ; else
126 echo "Your Docker installation is not working correctly"
128 echo "See: https://meta.discourse.org/t/docker-error-on-bootstrap/13657/18?u=sam"
133 if [ "$opt" != "--skip-prereqs" ] ; then
137 if [ "$opt" == "--docker-args" ] ; then
144 local ${ssh_key_locations}
146 ~
/.ssh
/id_ed25519.pub
150 ~core
/.ssh
/authorized_keys
154 for keyfile
in "${ssh_key_locations[@]}"; do
155 if [[ -e ${keyfile} ]] ; then
156 ssh_pub_key
="$(cat ${keyfile})"
162 echo "This user has no SSH key, but a SSH key is required to access the Discourse Docker container."
163 read -p "Generate a SSH key? (Y/n) " -n 1 -r
164 if [[ $REPLY =~ ^
[Nn
]$
]] ; then
166 echo WARNING
: You may not be able to log
in to your container.
170 echo Generating SSH key
171 mkdir
-p ~
/.
ssh && ssh-keygen
-f ~
/.ssh
/id_rsa
-t rsa
-N ''
173 ssh_pub_key
="$(cat ~/.ssh/id_rsa.pub)"
184 echo "Docker is not installed, make sure you are running on the 3.8 kernel"
185 echo "The best supported Docker release is Ubuntu 12.04.03 for it run the following"
187 echo "sudo apt-get update"
188 echo "sudo apt-get install linux-image-generic-lts-raring linux-headers-generic-lts-raring"
192 echo "sudo sh -c \"wget -qO- https://get.docker.io/gpg | apt-key add -\""
193 echo "sudo sh -c \"echo deb http://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list\""
194 echo "sudo apt-get update"
195 echo "sudo apt-get install lxc-docker"
201 read -r -d '' env_ruby
<< 'RUBY'
204 input
= STDIN.readlines.
join
205 yaml
= YAML.load
(input
)
207 if host_run
= yaml
['host_run']
208 params
= yaml
['params'] ||
{}
209 host_run.each
do |run|
211 run
= run.gsub
("$#{k}", v
)
213 STDOUT.
write "#{run}--SEP--"
218 host_run
=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e "$env_ruby"`
220 while [ "$host_run" ] ; do
221 iter
=${host_run%%--SEP--*}
223 echo "Host run: $iter"
226 host_run
="${host_run#*--SEP--}"
232 volumes
=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \
233 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['volumes'].map{|v| '-v ' << v['volume']['host'] << ':' << v['volume']['guest'] << ' '}.join"`
237 links
=`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)['links'].map{|l| '--link ' << l['link']['name'] << ':' << l['link']['alias'] << ' '}.join"`
241 set_template_info
() {
243 templates
=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
244 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['templates']"`
247 arrTemplates
=(${templates// / })
248 config_data
=$
(cat $config_file)
253 for template
in "${arrTemplates[@]}"
255 [ ! -z $template ] && {
256 input
="$input _FILE_SEPERATOR_ $(cat $template)"
260 # we always want our config file last so it takes priority
261 input
="$input _FILE_SEPERATOR_ $config_data"
263 read -r -d '' env_ruby
<< 'RUBY'
266 input
=STDIN.readlines.
join
267 # default to UTF-8 for the dbs sake
268 env
= {'LANG' => 'en_US.UTF-8'}
269 input.
split('_FILE_SEPERATOR_').each
do |yml|
272 env.merge
!(YAML.load
(yml
)['env'] ||
{})
273 rescue Psych
::SyntaxError
=> e
281 puts env.map
{|k
,v|
"-e\n#{k}=#{v}" }.
join("\n")
284 raw
=`exec echo "$input" | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e "$env_ruby"`
289 if [ "$i" == "*ERROR." ]; then
291 elif [ -n "$i" ]; then
296 if [ "$ok" -ne 1 ]; then
298 echo "YAML syntax error. Please check your /var/discourse/containers/*.yml config files."
301 echo "Calculated ENV: ${env[@]}"
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 GetSpace
=$
(du
-sk |
awk '{print $1}');
318 echo "Starting Cleanup"
319 docker inspect
-f '{{.Id}},{{.State.Running}},{{.State.FinishedAt}}' $
(docker ps
-qa) | \
320 awk -F, 'BEGIN { TIME=strftime("%FT%T.000000000Z",systime()-60*60*24); } $2=="false" && $3 < TIME {print $1;}' | \
321 xargs --no-run-if-empty docker
rm >/dev
/null
2>/dev
/null
322 docker images
--no-trunc|
grep none |
awk '{print $3}' |
xargs -r docker rmi
323 let SpaceFreed
=$GetSpace-$
(du
-sk |
awk '{print $1}')
324 Output
="$SpaceFreed" |
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 }}}'
325 [ -z "$Output" ] && {
326 [[ $SpaceFreed > 0 ]] && { echo "./launcher cleanup cleared up $SpaceFreed Bytes of disk space."; } ||
{ echo "./launcher cleanup has finished, no files were removed."; }
327 } ||
{ echo "./launcher cleanup cleared up $Output of disk space."; }
338 if [ ! -e $config_file ]
340 echo "Config file was not found, ensure $config_file exists"
342 echo "Available configs ( `cd containers && ls -dm *.yml | tr -s '\n' ' ' | awk '{ gsub(/\.yml/, ""); print }'`)"
347 docker_version
=($
($docker_path --version))
348 docker_version
=${test[2]//,/}
350 if compare_version
"1.2.0" "$docker_version"; then
351 echo "We recommend you upgrade docker, the version you are running has no restart policies, on reboot your container may not start up"
354 restart_policy
=${restart_policy:---restart=always}
359 if [ ! -e $config_file ]; then
360 echo "Config does not exist: $config_file" >&2
363 exec scripts
/mailtest
$config_file
372 $docker_path stop
-t 10 `cat $cidfile`
382 echo "No cid found, creating a new container"
383 ports
=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \
384 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['expose'].map{|p| '-p ' << p.to_s << ' '}.join"`
390 existing
=`$docker_path ps -a | awk '{ print $1, $(NF) }' | grep "$config$" | awk '{ print $1 }'`
391 if [ ! -z $existing ]
393 echo "Found an existing container by its name, recovering cidfile, please rerun"
394 echo $existing > $cidfile
398 $docker_path run
$user_args $links $attach_on_run $restart_policy "${env[@]}" -h "`hostname`-$config" -e DOCKER_HOST_IP
=$docker_ip --name $config -t --cidfile $cidfile $ports \
399 $volumes $local_discourse/$config /sbin
/boot
407 echo "Detected empty cid file, deleting, please re-run"
412 found
=`$docker_path ps -q -a --no-trunc | grep $cid`
415 echo "Invalid cid file, deleting, please re-run"
420 echo "cid found, ensuring container is started"
421 $docker_path start
$attach_on_start `cat $cidfile`
433 # Is the image available?
434 # If not, pull it here so the user is aware what's happening.
435 $docker_path history $image >/dev
/null
2>&1 ||
$docker_path pull
$image
439 base_image
=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
440 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['base_image']"`
442 update_pups
=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
443 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['update_pups']"`
445 if [[ ! X
"" = X
"$base_image" ]]; then
454 run_command
="cd /pups &&"
455 if [[ ! "false" = $update_pups ]]; then
456 run_command
="$run_command git pull &&"
458 run_command
="$run_command /pups/bin/pups --stdin"
462 env
=("${env[@]}" "-e" "SSH_PUB_KEY=$ssh_pub_key")
464 (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 \
465 /bin
/bash
-c "$run_command") \
466 ||
($docker_path rm `cat $cidbootstrap` && rm $cidbootstrap)
468 [ ! -e $cidbootstrap ] && echo "FAILED TO BOOTSTRAP" && exit 1
472 $docker_path commit
`cat $cidbootstrap` $local_discourse/$config ||
echo 'FAILED TO COMMIT'
473 $docker_path rm `cat $cidbootstrap` && rm $cidbootstrap
479 echo "Successfully bootstrapped, to startup use ./launcher start $config"
496 if [ ! $UID -eq 0 ] ;
498 echo "enter command must run as root, will attempt to sudo"
502 if [ ! -e bin
/nsenter
]
504 echo "Downloading nsenter"
505 $docker_path pull samsaffron
/nsenter
506 ($docker_path run
$user_args --rm samsaffron
/nsenter
cat /nsenter
> bin
/nsenter1
) ||
exit 1
507 cp bin
/nsenter1 bin
/nsenter
511 PID
=$
($docker_path inspect
--format {{.State.Pid
}} `cat $cidfile`)
512 SHELL
=/bin
/bash sudo
-E bin
/nsenter
--target $PID --mount --uts --ipc --net --pid
524 address
="`$docker_path port $cid 22`"
525 split=(${address//:/ })
526 exec ssh -o StrictHostKeyChecking
=no root@
${split[0]} -p ${split[1]}
542 $docker_path logs
`cat $cidfile`
559 if [ "$(git symbolic-ref --short HEAD)" == "master" ]; then
560 echo "Updating discourse docker"
561 git pull ||
(echo 'failed to update' && exit 1)
565 echo "Stopping old container"
566 $docker_path stop
-t 10 `cat $cidfile`
573 $docker_path rm `cat $cidfile` && rm $cidfile
584 echo "destroying container $cidfile"
585 $docker_path stop
-t 10 `cat $cidfile`
586 $docker_path rm `cat $cidfile` && rm $cidfile
589 echo "nothing to destroy cidfile does not exist"