command=$1
config=$2
+opt=$3
+
+cd "$(dirname "$0")"
+
+docker_min_version='1.2.0'
+docker_rec_version='1.2.0'
+
config_file=containers/"$config".yml
cidfile=cids/"$config".cid
cidbootstrap=cids/"$config"_boostrap.cid
local_discourse=local_discourse
-image=samsaffron/discourse
-docker_path=`which docker`
+image=samsaffron/discourse:1.0.5
+docker_path=`which docker.io || which docker`
+
+if [ "${SUPERVISED}" = "true" ]; then
+ restart_policy="--restart=no"
+ attach_on_start="-a"
+ attach_on_run="-a stdout -a stderr"
+else
+ attach_on_run="-d"
+fi
-docker_ip=`/sbin/ifconfig | \
- grep -B1 "inet addr" | \
- awk '{ if ( $1 == "inet" ) { print $2 } else if ( $2 == "Link" ) { printf "%s:" ,$1 } }' | \
- grep docker0 | \
- awk -F: '{ print $3 }';`
+if [ -x "$(which ip 2>/dev/null)" ]; then
+ docker_ip=`ip addr show docker0 | \
+ grep 'inet ' | \
+ awk '{ split($2,a,"/"); print a[1] }';`
+else
+ docker_ip=`ifconfig | \
+ grep -B1 "inet addr" | \
+ awk '{ if ( $1 == "inet" ) { print $2 } else if ( $2 == "Link" ) { printf "%s:" ,$1 } }' | \
+ grep docker0 | \
+ awk -F: '{ print $3 }';`
+fi
usage () {
- echo "Usage: launcher COMMAND CONFIG"
+ echo "Usage: launcher COMMAND CONFIG [--skip-prereqs]"
echo "Commands:"
echo " start: Start/initialize a container"
echo " stop: Stop a running container"
echo " restart: Restart a container"
echo " destroy: Stop and remove a container"
+ echo " enter: Use nsenter to enter a container"
echo " ssh: Start a bash shell in a running container"
echo " logs: Docker logs for container"
+ echo " mailtest: Test the mail settings in a container"
echo " bootstrap: Bootstrap a container for the config based on a template"
+ echo " rebuild: Rebuild a container (destroy old, bootstrap, start new)"
+ echo
+ echo "Options:"
+ echo " --skip-prereqs Don't check prerequisites"
exit 1
}
+compare_version() {
+ declare -a ver_a
+ declare -a ver_b
+ IFS=. read -a ver_a <<< "$1"
+ IFS=. read -a ver_b <<< "$2"
+
+ while [[ -n $ver_a ]]; do
+ if (( ver_a > ver_b )); then
+ return 0
+ elif (( ver_b > ver_a )); then
+ return 1
+ else
+ unset ver_a[0]
+ ver_a=("${ver_a[@]}")
+ unset ver_b[0]
+ ver_b=("${ver_b[@]}")
+ fi
+ done
+ return 1 # They are equal
+}
+
+prereqs() {
+
+ # 1. docker daemon running?
+ test=`$docker_path info >/dev/null`
+
+ if [[ $? -ne 0 ]] ; then
+ echo "Cannot connect to the docker daemon - verify it is running and you have access"
+ exit 1
+ fi
+
+ # 2. running aufs or btrfs
+ test=`$docker_path info 2> /dev/null | grep 'Driver: '`
+ if [[ "$test" =~ [aufs|btrfs] ]] ; then : ; else
+ echo "Your Docker installation is not using the recommended AuFS (union filesystem) and may be unstable."
+ echo "If you are unable to bootstrap / stop your image please report the issue at:"
+ echo "https://meta.discourse.org/t/discourse-docker-installation-without-aufs/15639"
+ fi
+
+ # 3. running recommended docker version
+ test=($($docker_path --version)) # Get docker version string
+ test=${test[2]//,/} # Get version alone and strip comma if exists
+
+ [[ "$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
+
+ # At least minimum version
+ if compare_version "${docker_min_version}" "${test}"; then
+ echo "ERROR: Docker version ${test} not supported, please upgrade to at least ${docker_min_version}, or recommended ${docker_rec_version}"
+ exit 1
+ fi
+
+ # Recommend best version
+ if compare_version "${docker_rec_version}" "${test}"; then
+ echo "WARNING: Docker version ${test} deprecated, recommend upgrade to ${docker_rec_version} or newer."
+ fi
+
+ # 4. able to attach stderr / out / tty
+ test=`$docker_path run -i --rm -a stdout -a stderr $image echo working`
+ if [[ "$test" =~ "working" ]] ; then : ; else
+ echo "Your Docker installation is not working correctly"
+ echo
+ echo "See: https://meta.discourse.org/t/docker-error-on-bootstrap/13657/18?u=sam"
+ exit 1
+ fi
+}
+
+if [ "$opt" != "--skip-prereqs" ] ; then
+ prereqs
+fi
+
+get_ssh_pub_key() {
+ local ${ssh_key_locations}
+ ssh_key_locations=(
+ ~/.ssh/id_ed25519.pub
+ ~/.ssh/id_ecdsa.pub
+ ~/.ssh/id_rsa.pub
+ ~/.ssh/id_dsa.pub
+ ~core/.ssh/authorized_keys
+ )
+
+ local $keyfile
+ for keyfile in "${ssh_key_locations[@]}"; do
+ if [[ -e ${keyfile} ]] ; then
+ ssh_pub_key="$(cat ${keyfile})"
+ return 0
+ fi
+ done
+
+ if tty -s ; then
+ echo "This user has no SSH key, but a SSH key is required to access the Discourse Docker container."
+ read -p "Generate a SSH key? (Y/n) " -n 1 -r
+ if [[ $REPLY =~ ^[Nn]$ ]] ; then
+ echo
+ echo WARNING: You may not be able to log in to your container.
+ echo
+ else
+ echo
+ echo Generating SSH key
+ mkdir -p ~/.ssh && ssh-keygen -f ~/.ssh/id_rsa -t rsa -N ''
+ echo
+ ssh_pub_key="$(cat ~/.ssh/id_rsa.pub)"
+ return 0
+ fi
+ fi
+
+ return 1
+}
+
+
install_docker() {
echo "Docker is not installed, make sure you are running on the 3.8 kernel"
exit 1
}
+host_run() {
+ read -r -d '' env_ruby << 'RUBY'
+ require 'yaml'
+
+ input = STDIN.readlines.join
+ yaml = YAML.load(input)
+
+ if host_run = yaml['host_run']
+ params = yaml['params'] || {}
+ host_run.each do |run|
+ params.each do |k,v|
+ run = run.gsub("$#{k}", v)
+ end
+ STDOUT.write "#{run}--SEP--"
+ end
+ end
+RUBY
+
+ host_run=`cat $config_file | $docker_path run --rm -i -a stdout -a stdin $image ruby -e "$env_ruby"`
+
+ while [ "$host_run" ] ; do
+ iter=${host_run%%--SEP--*}
+ echo
+ echo "Host run: $iter"
+ $iter || exit 1
+ echo
+ host_run="${host_run#*--SEP--}"
+ done
+}
+
+
set_volumes() {
- volumes=`cat $config_file | docker run -rm -i -a stdout -a stdin $image ruby -e \
+ volumes=`cat $config_file | $docker_path run --rm -i -a stdout -a stdin $image ruby -e \
"require 'yaml'; puts YAML.load(STDIN.readlines.join)['volumes'].map{|v| '-v ' << v['volume']['host'] << ':' << v['volume']['guest'] << ' '}.join"`
}
+set_links() {
+ links=`cat $config_file | $docker_path run --rm -i -a stdout -a stdin $image ruby -e \
+ "require 'yaml'; puts YAML.load(STDIN.readlines.join)['links'].map{|l| '--link ' << l['link']['name'] << ':' << l['link']['alias'] << ' '}.join"`
+}
+
set_template_info() {
- templates=`cat $config_file | docker run -rm -i -a stdin -a stdout $image ruby -e \
+ templates=`cat $config_file | $docker_path run --rm -i -a stdin -a stdout $image ruby -e \
"require 'yaml'; puts YAML.load(STDIN.readlines.join)['templates']"`
require 'yaml'
input=STDIN.readlines.join
- env = {}
+ # default to UTF-8 for the dbs sake
+ env = {'LANG' => 'en_US.UTF-8'}
input.split('_FILE_SEPERATOR_').each do |yml|
yml.strip!
begin
env.merge!(YAML.load(yml)['env'] || {})
+ rescue Psych::SyntaxError => e
+ puts e
+ puts "*ERROR."
rescue => e
puts yml
p e
end
end
- puts env.map{|k,v| "-e #{k}=#{v}" }.join(" ")
+ puts env.map{|k,v| "-e\n#{k}=#{v}" }.join("\n")
RUBY
- env=`exec echo "$input" | docker run -rm -i -a stdin -a stdout $image ruby -e "$env_ruby"`
- echo "Calculated ENV: $env"
+ raw=`exec echo "$input" | $docker_path run --rm -i -a stdin -a stdout $image ruby -e "$env_ruby"`
+
+ env=()
+ ok=1
+ while read i; do
+ if [ "$i" == "*ERROR." ]; then
+ ok=0
+ elif [ -n "$i" ]; then
+ env[${#env[@]}]=$i
+ fi
+ done <<< "$raw"
+
+ if [ "$ok" -ne 1 ]; then
+ echo "${env[@]}"
+ echo "YAML syntax error. Please check your /var/docker/containers/*.yml config files."
+ exit 1
+ fi
+ echo "Calculated ENV: ${env[@]}"
}
[ -z $docker_path ] && {
}
-[ $# -ne 2 ] && {
+[ $# -lt 2 ] && {
usage
}
if [ ! -e $config_file ]
then
echo "Config file was not found, ensure $config_file exists"
+ echo ""
+ echo "Available configs ( `cd containers && ls -dm *.yml | tr -s '\n' ' ' | awk '{ gsub(/\.yml/, ""); print }'`)"
exit 1
fi
-case "$command" in
- bootstrap)
- set_template_info
+docker_version=($($docker_path --version))
+docker_version=${test[2]//,/}
- base_image=`cat $config_file | docker run -rm -i -a stdin -a stdout $image ruby -e \
- "require 'yaml'; puts YAML.load(STDIN.readlines.join)['base_image']"`
+if compare_version "1.2.0" "$docker_version"; then
+ echo "We recommend you upgrade docker, the version you are running has no restart policies, on reboot your container may not start up"
+ restart_policy=""
+else
+ restart_policy=${restart_policy:---restart=always}
+fi
- update_pups=`cat $config_file | docker run -rm -i -a stdin -a stdout $image ruby -e \
- "require 'yaml'; puts YAML.load(STDIN.readlines.join)['update_pups']"`
- if [[ ! X"" = X"$base_image" ]]; then
- image=$base_image
- fi
+run_mailtest(){
+ if [ ! -e $config_file ]; then
+ echo "Config does not exist: $config_file" >&2
+ exit 1
+ fi
+ exec scripts/mailtest $config_file
+}
- set_volumes
+run_stop(){
+ if [ ! -e $cidfile ]
+ then
+ echo "No cid found"
+ exit 1
+ else
+ $docker_path stop -t 10 `cat $cidfile`
+ fi
+}
- rm -f $cidbootstrap
+run_start(){
+
+ host_run
+
+ if [ ! -e $cidfile ]
+ then
+ echo "No cid found, creating a new container"
+ ports=`cat $config_file | $docker_path run --rm -i -a stdout -a stdin $image ruby -e \
+ "require 'yaml'; puts YAML.load(STDIN.readlines.join)['expose'].map{|p| '-p ' << p.to_s << ' '}.join"`
+
+ set_template_info
+ set_volumes
+ set_links
+
+ existing=`$docker_path ps -a | awk '{ print $1, $(NF) }' | grep "$config$" | awk '{ print $1 }'`
+ if [ ! -z $existing ]
+ then
+ echo "Found an existing container by its name, recovering cidfile, please rerun"
+ echo $existing > $cidfile
+ exit 1
+ fi
+
+ $docker_path run $links $attach_on_run $restart_policy "${env[@]}" -h "`hostname`-$config" -e DOCKER_HOST_IP=$docker_ip --name $config -t --cidfile $cidfile $ports \
+ $volumes $local_discourse/$config /sbin/boot
+
+ exit 0
+ else
+ cid=`cat $cidfile`
+
+ if [ -z $cid ]
+ then
+ echo "Detected empty cid file, deleting, please re-run"
+ rm $cidfile
+ exit 1
+ fi
+
+ found=`$docker_path ps -q -a --no-trunc | grep $cid`
+ if [ -z $found ]
+ then
+ echo "Invalid cid file, deleting, please re-run"
+ rm $cidfile
+ exit 1
+ fi
+
+ echo "cid found, ensuring container is started"
+ $docker_path start $attach_on_start `cat $cidfile`
+ exit 0
+ fi
- run_command="cd /pups &&"
- if [[ ! "false" = $update_pups ]]; then
- run_command="$run_command git pull &&"
- fi
- run_command="$run_command /pups/bin/pups --stdin"
+}
+
+run_bootstrap(){
+
+ host_run
+
+ get_ssh_pub_key
+
+ # Is the image available?
+ # If not, pull it here so the user is aware what's happening.
+ $docker_path history $image >/dev/null 2>&1 || $docker_path pull $image
+
+ set_template_info
+
+ base_image=`cat $config_file | $docker_path run --rm -i -a stdin -a stdout $image ruby -e \
+ "require 'yaml'; puts YAML.load(STDIN.readlines.join)['base_image']"`
- (exec echo "$input" | docker run $env -e DOCKER_HOST_IP=$docker_ip -cidfile $cidbootstrap -i -a stdin -a stdout -a stderr $volumes $image \
- /bin/bash -c "$run_command") \
- || (docker rm `cat $cidbootstrap` && rm $cidbootstrap)
+ update_pups=`cat $config_file | $docker_path run --rm -i -a stdin -a stdout $image ruby -e \
+ "require 'yaml'; puts YAML.load(STDIN.readlines.join)['update_pups']"`
- [ ! -e $cidbootstrap ] && echo "FAILED TO BOOTSTRAP" && exit 1
+ if [[ ! X"" = X"$base_image" ]]; then
+ image=$base_image
+ fi
- sleep 5
+ set_volumes
+ set_links
- docker commit `cat $cidbootstrap` $local_discourse/$config || echo 'FAILED TO COMMIT'
- docker rm `cat $cidbootstrap` && rm $cidbootstrap
+ rm -f $cidbootstrap
- echo "Successfully bootstrappd, to starup use ./launcher start $config"
+ run_command="cd /pups &&"
+ if [[ ! "false" = $update_pups ]]; then
+ run_command="$run_command git pull &&"
+ fi
+ run_command="$run_command /pups/bin/pups --stdin"
+
+ echo $run_command
+
+ env=("${env[@]}" "-e" "SSH_PUB_KEY=$ssh_pub_key")
+
+ (exec echo "$input" | $docker_path run $links "${env[@]}" -e DOCKER_HOST_IP=$docker_ip --cidfile $cidbootstrap -i -a stdin -a stdout -a stderr $volumes $image \
+ /bin/bash -c "$run_command") \
+ || ($docker_path rm `cat $cidbootstrap` && rm $cidbootstrap)
+
+ [ ! -e $cidbootstrap ] && echo "FAILED TO BOOTSTRAP" && exit 1
+
+ sleep 5
+
+ $docker_path commit `cat $cidbootstrap` $local_discourse/$config || echo 'FAILED TO COMMIT'
+ $docker_path rm `cat $cidbootstrap` && rm $cidbootstrap
+}
+
+case "$command" in
+ bootstrap)
+ run_bootstrap
+ echo "Successfully bootstrapped, to startup use ./launcher start $config"
exit 0
;;
- ssh)
+ mailtest)
+ run_mailtest
+ exit 0
+ ;;
+
+ enter)
+
if [ ! -e $cidfile ]
then
echo "No cid found"
exit 1
- else
- cid="`cat $cidfile`"
- address="`docker port $cid 22`"
- split=(${address//:/ })
- exec ssh root@${split[0]} -p ${split[1]}
fi
+
+ if [ ! $UID -eq 0 ] ;
+ then
+ echo "enter command must run as root, will attempt to sudo"
+ echo
+ fi
+
+ if [ ! -e bin/nsenter ]
+ then
+ echo "Downloading nsenter"
+ $docker_path pull samsaffron/nsenter
+ ($docker_path run --rm samsaffron/nsenter cat /nsenter > bin/nsenter1) || exit 1
+ cp bin/nsenter1 bin/nsenter
+ chmod +x bin/nsenter
+ fi
+
+ PID=$($docker_path inspect --format {{.State.Pid}} `cat $cidfile`)
+ SHELL=/bin/bash sudo -E bin/nsenter --target $PID --mount --uts --ipc --net --pid
+
+ exit 0;
;;
- stop)
+ ssh)
if [ ! -e $cidfile ]
then
echo "No cid found"
exit 1
else
- docker stop -t 10 `cat $cidfile`
- exit 0
+ cid="`cat $cidfile`"
+ address="`$docker_path port $cid 22`"
+ split=(${address//:/ })
+ exec ssh -o StrictHostKeyChecking=no root@${split[0]} -p ${split[1]}
fi
;;
+ stop)
+ run_stop
+ exit 0
+ ;;
+
logs)
if [ ! -e $cidfile ]
echo "No cid found"
exit 1
else
- docker logs `cat $cidfile`
+ $docker_path logs `cat $cidfile`
exit 0
fi
;;
- start)
+ restart)
+ run_stop
+ run_start
+ exit 0
+ ;;
- if [ ! -e $cidfile ]
- then
- echo "No cid found, creating a new container"
- ports=`cat $config_file | docker run -rm -i -a stdout -a stdin $image ruby -e \
- "require 'yaml'; puts YAML.load(STDIN.readlines.join)['expose'].map{|p| '-p ' << p.to_s << ' '}.join"`
+ start)
+ run_start
+ exit 0
+ ;;
- set_template_info
- set_volumes
+ rebuild)
+ if [ "$(git symbolic-ref --short HEAD)" == "master" ]; then
+ echo "Updating discourse docker"
+ git pull || (echo 'failed to update' && exit 1)
+ fi
+ if [ -e $cidfile ]
+ then
+ echo "Stopping old container"
+ $docker_path stop -t 10 `cat $cidfile`
+ fi
- docker run $env -e DOCKER_HOST_IP=$docker_ip -name $config -cidfile $cidfile $ports \
- -d $volumes $local_discourse/$config /usr/bin/runsvdir -P /etc/service
+ run_bootstrap
- exit 0
- else
- echo "cid found, ensuring container is started"
- docker start `cat $cidfile`
- exit 0
+ if [ -e $cidfile ]
+ then
+ $docker_path rm `cat $cidfile` && rm $cidfile
fi
+
+ run_start
+ exit 0
;;
if [ -e $cidfile ]
then
echo "destroying container $cidfile"
- docker stop -t 10 `cat $cidfile`
- docker rm `cat $cidfile` && rm $cidfile
+ $docker_path stop -t 10 `cat $cidfile`
+ $docker_path rm `cat $cidfile` && rm $cidfile
exit 0
else
echo "nothing to destroy cidfile does not exist"