X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=launcher;h=27b6a8d6e1e6ece0863a74f4a89b75375b4ee180;hb=955d360c6d016a601134f0fbe992a9dda19c854d;hp=8708d5ea5bb9efd1928e116a7b3e3dc1b053e19d;hpb=a752d8eaaf9c18f40516dd617dcf9467b5d24035;p=discourse_docker.git diff --git a/launcher b/launcher index 8708d5e..27b6a8d 100755 --- a/launcher +++ b/launcher @@ -1,14 +1,50 @@ #!/bin/bash +usage () { + 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 get a shell into a 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 " --docker-args Extra arguments to pass when running docker" + exit 1 +} + command=$1 config=$2 -opt=$3 +user_args="" + +while [ ${#} -gt 0 ]; do + case "${1}" in + --debug) + DEBUG="1" + ;; + --skip-prereqs) + SKIP_PREREQS="1" + ;; + --docker-args) + user_args="$2" + shift + ;; + esac + + shift 1 +done # Docker doesn't like uppercase characters, spaces or special characters, catch it now before we build everything out and then find out re='[A-Z/ !@#$%^&*()+~`=]' if [[ $config =~ $re ]]; then - echo + echo echo "ERROR: Config name must not contain upper case characters, spaces or special characters. Correct config name and rerun $0." echo exit 1 @@ -16,14 +52,17 @@ fi cd "$(dirname "$0")" -docker_min_version='1.2.0' -docker_rec_version='1.2.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=samsaffron/discourse:1.0.12 +image=discourse/discourse:1.3.2 docker_path=`which docker.io || which docker` +git_path=`which git` if [ "${SUPERVISED}" = "true" ]; then restart_policy="--restart=no" @@ -47,27 +86,6 @@ else awk -F: '{ print $3 }';` fi - -usage () { - 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 " 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 prerequisites" - echo " --docker-args Extra arguments to pass when running docker" - exit 1 -} - compare_version() { declare -a ver_a declare -a ver_b @@ -89,13 +107,23 @@ compare_version() { return 1 # They are equal } -prereqs() { + +install_docker() { + 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 +} + +check_prereqs() { + + if [ -z $docker_path ]; then + install_docker + fi # 1. docker daemon running? # we send stderr to /dev/null cause we don't care about warnings, # it usually complains about swap which does not matter test=`$docker_path info 2> /dev/null` - if [[ $? -ne 0 ]] ; then echo "Cannot connect to the docker daemon - verify it is running and you have access" exit 1 @@ -107,8 +135,8 @@ prereqs() { echo "Your Docker installation is not using a supported filesystem if we were to proceed you may have a broken install." echo "aufs is the recommended filesystem you should be using (zfs/btrfs and overlay may work as well)" echo "You can tell what filesystem you are using by running \"docker info\" and looking at the driver" - echo "" - echo "If you wish to continue anyway using your existing unsupported filesystem" + echo + echo "If you wish to continue anyway using your existing unsupported filesystem, " echo "read the source code of launcher and figure out how to bypass this." exit 1 fi @@ -117,15 +145,13 @@ prereqs() { 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 + # 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 @@ -140,112 +166,39 @@ prereqs() { echo echo "Please be patient" echo - fi - # 5. able to attach stderr / out / tty - test=`$docker_path run $user_args -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" + # 5. running recommended git version + test=($($git_path --version)) # Get git version string + test=${test[2]//,/} # Get version alone and strip comma if exists + + # At least minimum version + if compare_version "${git_min_version}" "${test}"; then + echo "ERROR: Git version ${test} not supported, please upgrade to at least ${git_min_version}, or recommended ${git_rec_version}" exit 1 fi - # 6. sufficient available resources - # 6a. 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 - resources="insufficient" - total_swap="$(LANG=C free -m | grep ^Swap: | awk '{print $2}')" - if [ "$total_swap" -lt 1000 ]; then - 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 + # Recommend best version + if compare_version "${git_rec_version}" "${test}"; then + echo "WARNING: Git version ${test} deprecated, recommend upgrade to ${git_rec_version} or newer." fi - # 6b. 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." + # 6. able to attach stderr / out / tty + test=`$docker_path run $user_args -i --rm -a stdout -a stderr $image echo working` + if [[ "$test" =~ "working" ]] ; then : ; else + echo "Your Docker installation is not working correctly" echo - echo "Please free up some space, or expand your disk, before continuing." + echo "See: https://meta.discourse.org/t/docker-error-on-bootstrap/13657/18?u=sam" 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 } -if [ "$opt" != "--skip-prereqs" ] ; then - prereqs -fi -if [ "$opt" == "--docker-args" ] ; then - user_args=$4 -else - user_args="" +if [ -z "$SKIP_PREREQS" ] ; then + check_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 - - return 0 -} - - -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 Ubuntu Trusty or later, you can try the following:" - echo - - echo "sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D" - echo "sudo sh -c \"echo deb https://apt.dockerproject.org/repo ubuntu-$(lsb_release -sc) main > /etc/apt/sources.list.d/docker.list\"" - echo "sudo apt-get update" - echo "sudo apt-get install docker-engine" - - exit 1 -} - host_run() { read -r -d '' env_ruby << 'RUBY' require 'yaml' @@ -292,13 +245,11 @@ set_template_info() { templates=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \ "require 'yaml'; puts YAML.load(STDIN.readlines.join)['templates']"` - arrTemplates=(${templates// / }) config_data=$(cat $config_file) input="hack: true" - for template in "${arrTemplates[@]}" do [ ! -z $template ] && { @@ -344,14 +295,14 @@ RUBY 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 } -[ -z $docker_path ] && { +if [ -z $docker_path ]; then install_docker -} +fi [ "$command" == "cleanup" ] && { echo @@ -376,34 +327,28 @@ RUBY exit 0 } -[ $# -lt 2 ] && { +if [ -z "$command" -a -z "$config" ]; then usage -} +fi -if [ ! -e $config_file ] - then +if [ ! "$command" == "setup" ]; then + if [[ ! -e $config_file ]]; then echo "Config file was not found, ensure $config_file exists" - echo "" + 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)) docker_version=${test[2]//,/} - -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 +restart_policy=${restart_policy:---restart=always} set_existing_container(){ existing=`$docker_path ps -a | awk '{ print $1, $(NF) }' | grep " $config$" | awk '{ print $1 }'` } -run_stop(){ +run_stop() { set_existing_container @@ -419,14 +364,38 @@ run_stop(){ fi } -run_start(){ +set_run_image() { + run_image=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \ + "require 'yaml'; puts YAML.load(STDIN.readlines.join)['run_image']"` + + if [ -z "$run_image" ]; then + run_image="$local_discourse/$config" + fi +} + +set_boot_command() { + boot_command=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \ + "require 'yaml'; puts YAML.load(STDIN.readlines.join)['boot_command']"` + + if [ -z "$boot_command" ]; then + + no_boot_command=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \ + "require 'yaml'; puts YAML.load(STDIN.readlines.join)['no_boot_command']"` + + if [ -z "$no_boot_command" ]; then + boot_command="/sbin/boot" + fi + fi +} + +run_start() { existing=`$docker_path ps | awk '{ print $1, $(NF) }' | grep " $config$" | awk '{ print $1 }'` echo $existing if [ ! -z $existing ] then echo "Nothing to do, your container has already started!" - exit 1 + exit 0 fi existing=`$docker_path ps -a | awk '{ print $1, $(NF) }' | grep " $config$" | awk '{ print $1 }'` @@ -441,8 +410,9 @@ run_start(){ fi host_run + 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.to_s << ' '}.join"` + "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']"` @@ -450,24 +420,51 @@ run_start(){ set_template_info set_volumes set_links + set_run_image + set_boot_command + + # get hostname and settings from container configuration + for envar in "${env[@]}" + do + if [[ $envar == DOCKER_USE_HOSTNAME* ]] || [[ $envar == DISCOURSE_HOSTNAME* ]] + then + # use as environment variable + eval $envar + fi + done ( - hostname=`hostname` + hostname=`hostname -s` + # overwrite hostname + if [ "$DOCKER_USE_HOSTNAME" = "true" ] + then + hostname=$DISCOURSE_HOSTNAME + else + hostname=$hostname-$config + fi + + # 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//_/-} + + 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/'")" + set -x - $docker_path run $user_args $links $attach_on_run $restart_policy "${env[@]}" -h "$hostname-$config" \ - -e DOCKER_HOST_IP=$docker_ip --name $config -t $ports $volumes $docker_args $local_discourse/$config \ - /sbin/boot + $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 $mac_address $docker_args \ + $run_image $boot_command ) exit 0 } -run_bootstrap(){ - host_run +run_bootstrap() { - get_ssh_pub_key + # I got no frigging clue what this does, ask Sam Saffron. It RUNS STUFF ON THE HOST I GUESS? + host_run # Is the image available? # If not, pull it here so the user is aware what's happening. @@ -498,13 +495,21 @@ run_bootstrap(){ echo $run_command - env=("${env[@]}" "-e" "SSH_PUB_KEY=$ssh_pub_key") - + unset FAILED (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") || FAILED="TRUE" - [ ! -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 @@ -512,6 +517,8 @@ run_bootstrap(){ $docker_path rm `cat $cidbootstrap` && rm $cidbootstrap } + + case "$command" in bootstrap) run_bootstrap @@ -520,20 +527,7 @@ case "$command" in ;; enter) - exec $docker_path exec -it $config /bin/bash - ;; - - ssh) - existing=`$docker_path ps | awk '{ print $1, $(NF) }' | grep " $config$" | awk '{ print $1 }'` - - if [[ ! -z $existing ]]; then - address="`$docker_path port $config 22`" - split=(${address//:/ }) - exec ssh -o StrictHostKeyChecking=no root@${split[0]} -p ${split[1]} - else - echo "$config is not running!" - exit 1 - fi + exec $docker_path exec -it $config /bin/bash --login ;; stop) @@ -560,7 +554,7 @@ case "$command" in 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 @@ -569,18 +563,18 @@ case "$command" in 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 $@ 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