#!/bin/bash
usage () {
- echo "Usage: launcher COMMAND CONFIG [--skip-prereqs] [--skip-discourse-prereqs] [--docker-args STRING]"
+ echo "Usage: launcher COMMAND CONFIG [--skip-prereqs] [--docker-args STRING]"
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 " logs: Docker logs for container"
+ echo " enter: Open a shell to run commands inside the container"
+ echo " logs: View the Docker logs for 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 " cleanup: Remove all containers that have stopped for > 24 hours"
echo
echo "Options:"
echo " --skip-prereqs Don't check launcher prerequisites"
- echo " --skip-discourse-prereqs Don't check prerequisites specifiy to Discourse"
echo " --docker-args Extra arguments to pass when running docker"
+ echo " --skip-mac-address Don't assign a mac address"
exit 1
}
-[ $# -lt 2 ] && {
- usage
-}
-
command=$1
config=$2
-shift 2
user_args=""
while [ ${#} -gt 0 ]; do
- case "#{1}" in
-
+ case "${1}" in
+ --debug)
+ DEBUG="1"
+ ;;
--skip-prereqs)
- SKIP_PREREQ="1"
+ SKIP_PREREQS="1"
;;
- --skip-discourse-prereqs)
- SKIP_DISCOURSE_PREREQS="1"
+ --skip-mac-address)
+ SKIP_MAC_ADDRESS="1"
;;
--docker-args)
user_args="$2"
shift
;;
- *)
- echo "Unknown options '${1}'"
- usage
- ;;
esac
shift 1
cd "$(dirname "$0")"
-docker_min_version='1.6.0'
-docker_rec_version='1.6.0'
+docker_min_version='1.8.0'
+docker_rec_version='1.8.0'
git_min_version='1.8.0'
git_rec_version='1.8.0'
config_file=containers/"$config".yml
cidbootstrap=cids/"$config"_bootstrap.cid
local_discourse=local_discourse
-image=discourse/discourse:1.0.17
+image=discourse/discourse:1.3.9
docker_path=`which docker.io || which docker`
git_path=`which git`
-template_path=samples/standalone.yml
-changelog=/tmp/changelog # used to test whether sed did anything
if [ "${SUPERVISED}" = "true" ]; then
restart_policy="--restart=no"
install_docker() {
-
- echo "Docker is not installed, you will need to install Docker in order to run Discourse"
- echo "Please visit https://docs.docker.com/installation/ for instructions on how to do this for your system"
- echo
- echo "If you are running a recent Ubuntu Server, try the following:"
- echo "sudo apt-get install docker-engine"
-
+ echo "Docker is not installed, you will need to install Docker in order to run Launcher"
+ echo "See https://docs.docker.com/installation/"
exit 1
}
-prereqs() {
+check_prereqs() {
if [ -z $docker_path ]; then
install_docker
test=($($docker_path --version)) # Get docker version string
test=${test[2]//,/} # Get version alone and strip comma if exists
- # At least minimum version
+ # At least minimum docker 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
+ # Recommend newer docker version
if compare_version "${docker_rec_version}" "${test}"; then
echo "WARNING: Docker version ${test} deprecated, recommend upgrade to ${docker_rec_version} or newer."
fi
echo
echo "Please be patient"
echo
-
fi
# 5. running recommended git version
}
-
-check_resources() {
- # Memory
- resources="ok"
- avail_mem="$(LANG=C free -m | grep '^Mem:' | awk '{print $2}')"
- if [ "$avail_mem" -lt 900 ]; then
- resources="insufficient"
- echo "WARNING: You do not appear to have sufficient memory to run Discourse."
- echo
- echo "Your system may not work properly, or future upgrades of Discourse may"
- echo "not complete successfully."
- echo
- echo "See https://github.com/discourse/discourse/blob/master/docs/INSTALL-cloud.md#create-new-cloud-server"
- elif [ "$avail_mem" -lt 1800 ]; then
- total_swap="$(LANG=C free -m | grep ^Swap: | awk '{print $2}')"
- if [ "$total_swap" -lt 1000 ]; then
- resources="insufficient"
- echo "WARNING: You must have at least 1GB of swap when running with less"
- echo "than 2GB of RAM."
- echo
- echo "Your system may not work properly, or future upgrades of Discourse may"
- echo "not complete successfully."
- echo
- echo "See https://github.com/discourse/discourse/blob/master/docs/INSTALL-cloud.md#set-up-swap-if-needed"
- fi
- fi
-
- # Disk space
- free_disk="$(df /var | tail -n 1 | awk '{print $4}')"
- if [ "$free_disk" -lt 5000 ]; then
- resources="insufficient"
- echo "WARNING: You must have at least 5GB of *free* disk space to run Discourse."
- echo
- echo "Insufficient disk space may result in problems running your site, and may"
- echo "not even allow Discourse installation to complete successfully."
- echo
- echo "Please free up some space, or expand your disk, before continuing."
- echo
- echo "Run \`apt-get autoremove && apt-get autoclean\` to clean up unused packages and \`./launcher cleanup\` to remove stale Docker containers."
- exit 1
- fi
-
- if [ -t 0 ] && [ "$resources" != "ok" ]; then
- echo
- read -p "Press ENTER to continue, or Ctrl-C to exit and give your system more resources"
- fi
-}
-
-check_ports() {
- local valid=$(netstat -tln | awk '{print $4}' | grep ":${1}\$")
-
- if [ -n "$valid" ]; then
- echo "Launcher has detected that port ${1} is in use."
- echo
- echo "If you are trying to run Discourse simultaneously with another web server like Apache or nginx, you will need to bind to a different port."
- echo "See https://meta.discourse.org/t/17247 for help."
- echo "To continue anyway, re-run Launcher with --skip-prereqs"
- exit 1
- fi
-}
-
if [ -z "$SKIP_PREREQS" ] ; then
- prereqs
+ check_prereqs
fi
host_run() {
"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_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
+find_templates() {
+ local templates=`cat $1 | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
"require 'yaml'; puts YAML.load(STDIN.readlines.join)['templates']"`
+ local arrTemplates=${templates// / }
+
+ if [ ! -z "$templates" ]; then
+ for template in "${arrTemplates[@]}"
+ do
+ local nested_templates=$(find_templates $template)
+
+ if [ ! -z "$nested_templates" ]; then
+ templates="$templates $nested_templates"
+ fi
+ done
+
+ echo $templates
+ else
+ echo ""
+ fi
+}
+
+set_template_info() {
+ templates=$(find_templates $config_file)
+
arrTemplates=(${templates// / })
config_data=$(cat $config_file)
if [ "$ok" -ne 1 ]; then
echo "${env[@]}"
- echo "YAML syntax error. Please check your /var/discourse/containers/*.yml config files."
+ echo "YAML syntax error. Please check your containers/*.yml config files."
+ exit 1
+ fi
+
+ read -r -d '' labels_ruby << 'RUBY'
+ require 'yaml'
+
+ input=STDIN.readlines.join
+ # default to UTF-8 for the dbs sake
+ labels = {}
+ input.split('_FILE_SEPERATOR_').each do |yml|
+ yml.strip!
+ begin
+ labels.merge!(YAML.load(yml)['labels'] || {})
+ rescue Psych::SyntaxError => e
+ puts e
+ puts "*ERROR."
+ rescue => e
+ puts yml
+ p e
+ end
+ end
+ puts labels.map{|k,v| "-l\n#{k}=#{v}" }.join("\n")
+RUBY
+
+ raw=`exec echo "$input" | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e "$labels_ruby"`
+
+ labels=()
+ ok=1
+ while read i; do
+ if [ "$i" == "*ERROR." ]; then
+ ok=0
+ elif [ -n "$i" ]; then
+ labels[${#labels[@]}]=$i
+ fi
+ done <<< "$raw"
+
+ if [ "$ok" -ne 1 ]; then
+ echo "${labels[@]}"
+ echo "YAML syntax error. Please check your containers/*.yml config files."
exit 1
fi
}
exit 0
}
-if [[ ! -e $config_file ]]
- then
+if [ -z "$command" -a -z "$config" ]; then
+ usage
+fi
+
+if [ ! "$command" == "setup" ]; then
+ 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
fi
docker_version=($($docker_path --version))
fi
}
-scale_ram_and_cpu() {
-
- # grab info about total system ram and physical (NOT LOGICAL!) CPU cores
- avail_mem="$(LANG=C free -m | grep '^Mem:' | awk '{print $2}')"
- avail_gb=$(( $avail_mem / 950 ))
- avail_cores=`cat /proc/cpuinfo | grep "cpu cores" | uniq | awk '{print $4}'`
- echo "Found ${avail_gb}GB of memory and $avail_cores physical CPU cores"
-
- # db_shared_buffers: 128MB for 1GB, 256MB for 2GB, or 256MB * GB, max 4096MB
- if [ "$avail_gb" -eq "1" ]
- then
- db_shared_buffers=128
- else
- if [ "$avail_gb" -eq "2" ]
- then
- db_shared_buffers=256
- else
- db_shared_buffers=$(( 256 * $avail_gb ))
- fi
- fi
- db_shared_buffers=$(( db_shared_buffers < 4096 ? db_shared_buffers : 4096 ))
-
- sed -i -e "s/^ #db_shared_buffers:.*/ db_shared_buffers: \"${db_shared_buffers}MB\"/w $changelog" $config_file
- if [ -s $changelog ]
- then
- echo "setting db_shared_buffers = ${db_shared_buffers}MB based on detected CPU/RAM"
- rm $changelog
- fi
-
-
- # UNICORN_WORKERS: 2 * GB for 2GB or less, or 2 * CPU, max 8
- if [ "$avail_gb" -le "2" ]
- then
- unicorn_workers=$(( 2 * $avail_gb ))
- else
- unicorn_workers=$(( 2 * $avail_cores ))
- fi
- unicorn_workers=$(( unicorn_workers < 8 ? unicorn_workers : 8 ))
-
- sed -i -e "s/^ #UNICORN_WORKERS:.*/ UNICORN_WORKERS: ${unicorn_workers}/w $changelog" $config_file
- if [ -s $changelog ]
- then
- echo "setting UNICORN_WORKERS = ${unicorn_workers} based on detected CPU/RAM"
- rm $changelog
- fi
-
-}
-
run_start() {
existing=`$docker_path ps | awk '{ print $1, $(NF) }' | grep " $config$" | awk '{ print $1 }'`
host_run
- if [ -z "$SKIP_DISCOURSE_PREREQS" ] ; then
- ports=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \
- "require 'yaml'; puts YAML.load(STDIN.readlines.join)['expose'].map{|p| \"-p #{p}\"}.join(' ')"`
-
- IFS='-p ' read -a array <<< "$ports"
- for element in "${array[@]}"
- do
- IFS=':' read -a args <<< "$element"
- if [ "${#args[@]}" == "2" ]; then
- check_ports "${args[0]}"
- elif [ "${#args[@]}" == "3" ]; then
- check_ports "${args[1]}"
- fi
- done
- fi
+ ports=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \
+ "require 'yaml'; puts YAML.load(STDIN.readlines.join)['expose'].map{|p| \"-p #{p}\"}.join(' ')"`
docker_args=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \
"require 'yaml'; puts YAML.load(STDIN.readlines.join)['docker_args']"`
# we got to normalize so we only have allowed strings, this is more comprehensive but lets see how bash does first
# hostname=`$docker_path run $user_args --rm $image ruby -e 'print ARGV[0].gsub(/[^a-zA-Z-]/, "-")' $hostname`
# docker added more hostname rules
- hostname=${hostname/_/-}
+ hostname=${hostname//_/-}
+
+
+ if [ -z "$SKIP_MAC_ADDRESS" ] ; then
+ mac_address="--mac-address $($docker_path run $user_args -i --rm -a stdout -a stderr $image /bin/sh -c "echo $hostname | md5sum | sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:\1:\2:\3:\4:\5/'")"
+ fi
set -x
- $docker_path run $user_args $links $attach_on_run $restart_policy "${env[@]}" -h "$hostname" \
- -e DOCKER_HOST_IP=$docker_ip --name $config -t $ports $volumes $docker_args $run_image $boot_command
+ $docker_path run $user_args $links $attach_on_run $restart_policy "${env[@]}" "${labels[@]}" -h "$hostname" \
+ -e DOCKER_HOST_IP=$docker_ip --name $config -t $ports $volumes $mac_address $docker_args \
+ $run_image $boot_command
)
exit 0
}
-valid_config_check() {
-
- valid_config="y"
- for x in DISCOURSE_SMTP_ADDRESS DISCOURSE_SMTP_USER_NAME DISCOURSE_SMTP_PASSWORD \
- DISCOURSE_DEVELOPER_EMAILS DISCOURSE_HOSTNAME
- do
- mail_var=`grep "^ $x:" $config_file`
- local result=$?
- local default="example.com"
- if (( result == 0 ))
- then
- if [[ $mail_var = *"$default"* ]]
- then
- echo "Warning: $x left at incorrect default of example.com"
- valid_config="n"
- fi
- else
- echo "Warning: $x not configured"
- valid_config="n"
- fi
- done
- if [ -t 0 ] && [ "$valid_config" != "y" ]; then
- echo
- read -p "Press Ctrl-C to exit and edit $config_file or ENTER to continue"
- fi
-}
run_bootstrap() {
- if [ -z "$SKIP_DISCOURSE_PREREQS" ] ; then
- # Does your system meet the minimum requirements?
- check_resources
-
- # is our configuration file valid?
- valid_config_check
-
- # make minor scaling adjustments for RAM and CPU
- scale_ram_and_cpu
- fi
# I got no frigging clue what this does, ask Sam Saffron. It RUNS STUFF ON THE HOST I GUESS?
host_run
echo $run_command
+ unset ERR
(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 \
- /bin/bash -c "$run_command") \
- || ($docker_path rm `cat $cidbootstrap` && rm $cidbootstrap)
+ /bin/bash -c "$run_command") || ERR=$?
+
+ unset FAILED
+ # magic exit code that indicates a retry
+ if [[ "$ERR" == 77 ]]; then
+ $docker_path rm `cat $cidbootstrap`
+ rm $cidbootstrap
+ exit 77
+ elif [[ "$ERR" > 0 ]]; then
+ FAILED=TRUE
+ fi
- [ ! -e $cidbootstrap ] && echo "** FAILED TO BOOTSTRAP ** please scroll up and look for earlier error messages, there may be more than one" && exit 1
+ if [[ $FAILED = "TRUE" ]]; then
+ if [[ ! -z "$DEBUG" ]]; then
+ $docker_path commit `cat $cidbootstrap` $local_discourse/$config-debug || echo 'FAILED TO COMMIT'
+ echo "** DEBUG ** Maintaining image for diagnostics $local_discourse/$config-debug"
+ fi
+
+ $docker_path rm `cat $cidbootstrap`
+ rm $cidbootstrap
+ echo "** FAILED TO BOOTSTRAP ** please scroll up and look for earlier error messages, there may be more than one"
+ exit 1
+ fi
sleep 5
rebuild)
if [ "$(git symbolic-ref --short HEAD)" == "master" ]; then
- echo "Ensuring discourse docker is up to date"
+ echo "Ensuring launcher is up to date"
git remote update
BASE=$(git merge-base @ @{u})
if [ $LOCAL = $REMOTE ]; then
- echo "Discourse Docker is up-to-date"
+ echo "Launcher is up-to-date"
elif [ $LOCAL = $BASE ]; then
- echo "Updating Discourse Docker"
+ echo "Updating Launcher"
git pull || (echo 'failed to update' && exit 1)
- exec /bin/bash $0 $@
+
+ for (( i=${#BASH_ARGV[@]}-1,j=0; i>=0,j<${#BASH_ARGV[@]}; i--,j++ ))
+ do
+ args[$j]=${BASH_ARGV[$i]}
+ done
+ exec /bin/bash $0 "${args[@]}" # $@ is empty, because of shift at the beginning. Use BASH_ARGV instead.
elif [ $REMOTE = $BASE ]; then
- echo "Your version of Discourse Docker is ahead of origin"
+ echo "Your version of Launcher is ahead of origin"
else
- echo "Discourse Docker has diverged source, this is only expected in Dev mode"
+ echo "Launcher has diverged source, this is only expected in Dev mode"
fi
fi