Update base image to Ruby 2.6.2 based image
[discourse_docker.git] / launcher
index ba05c88e30836137080849c197f6f50b3e959426..4e615927503187c33d970fc27142d7ad0238e080 100755 (executable)
--- a/launcher
+++ b/launcher
@@ -3,26 +3,34 @@
 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:      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 "    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:       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 "    run:         Run the given command with the config in the context of the last bootstrapped image"
+  echo "    rebuild:     Rebuild a container (destroy old, bootstrap, start new)"
+  echo "    cleanup:     Remove all containers that have stopped for > 24 hours"
+  echo "    start-cmd:   Generate docker command used to start container"
   echo
   echo "Options:"
   echo "    --skip-prereqs             Don't check launcher prerequisites"
   echo "    --docker-args              Extra arguments to pass when running docker"
   echo "    --skip-mac-address         Don't assign a mac address"
+  echo "    --run-image                Override the image used for running the container"
   exit 1
 }
 
 command=$1
 config=$2
 user_args=""
+user_run_image=""
+
+if [[ $command == "run" ]]; then
+  run_command=$3
+fi
 
 while [ ${#} -gt 0 ]; do
   case "${1}" in
@@ -39,17 +47,26 @@ while [ ${#} -gt 0 ]; do
     user_args="$2"
     shift
     ;;
+  --run-image)
+    user_run_image="$2"
+    shift
+    ;;
   esac
 
   shift 1
 done
 
+if [ -z "$command" -o -z "$config" -a "$command" != "cleanup" ]; then
+  usage
+  exit 1
+fi
+
 # 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/ !@#$%^&*()+~`=]'
+re='[[:upper:]/ !@#$%^&*()+~`=]'
 if [[ $config =~ $re ]];
   then
     echo
-    echo "ERROR: Config name must not contain upper case characters, spaces or special characters. Correct config name and rerun $0."
+    echo "ERROR: Config name '$config' must not contain upper case characters, spaces or special characters. Correct config name and rerun $0."
     echo
     exit 1
 fi
@@ -64,8 +81,8 @@ git_rec_version='1.8.0'
 config_file=containers/"$config".yml
 cidbootstrap=cids/"$config"_bootstrap.cid
 local_discourse=local_discourse
-image=discourse/base:2.0.20171008
-docker_path=`which docker.io || which docker`
+image="discourse/base:2.0.20190321-0122"
+docker_path=`which docker.io 2> /dev/null || which docker`
 git_path=`which git`
 
 if [ "${SUPERVISED}" = "true" ]; then
@@ -210,7 +227,7 @@ check_prereqs() {
     echo
     if [[ $REPLY =~ ^[Yy]$ ]]
     then
-      docker system prune
+      $docker_path system prune -af
       echo "If the cleanup was successful, you may try again now"
     fi
     exit 1
@@ -307,7 +324,7 @@ set_template_info() {
     require 'yaml'
 
     input=STDIN.readlines.join
-    # default to UTF-8 for the dbs sakeb
+    # default to UTF-8 for the dbs sake
     env = {'LANG' => 'en_US.UTF-8'}
     input.split('_FILE_SEPERATOR_').each do |yml|
        yml.strip!
@@ -324,7 +341,12 @@ set_template_info() {
     puts env.map{|k,v| "-e\n#{k}=#{v}" }.join("\n")
 RUBY
 
-    raw=`exec echo "$input" | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e "$env_ruby"`
+    tmp_input_file=$(mktemp)
+
+    echo "$input" > "$tmp_input_file"
+    raw=`exec cat "$tmp_input_file" | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e "$env_ruby"`
+
+    rm -f "$tmp_input_file"
 
     env=()
     ok=1
@@ -332,7 +354,7 @@ RUBY
       if [ "$i" == "*ERROR." ]; then
         ok=0
       elif [ -n "$i" ]; then
-        env[${#env[@]}]=$i
+        env[${#env[@]}]="${i//\{\{config\}\}/${config}}"
       fi
     done <<< "$raw"
 
@@ -342,11 +364,11 @@ RUBY
       exit 1
     fi
 
+    # labels
     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!
@@ -363,7 +385,12 @@ RUBY
     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"`
+    tmp_input_file=$(mktemp)
+
+    echo "$input" > "$tmp_input_file"
+    raw=`exec cat "$tmp_input_file" | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e "$labels_ruby"`
+
+    rm -f "$tmp_input_file"
 
     labels=()
     ok=1
@@ -371,7 +398,7 @@ RUBY
       if [ "$i" == "*ERROR." ]; then
         ok=0
       elif [ -n "$i" ]; then
-        labels[${#labels[@]}]=$(echo $i | sed s/{{config}}/${config}/)
+        labels[${#labels[@]}]=$(echo $i | sed s/{{config}}/${config}/g)
       fi
     done <<< "$raw"
 
@@ -380,6 +407,52 @@ RUBY
       echo "YAML syntax error. Please check your containers/*.yml config files."
       exit 1
     fi
+
+    # expose
+    read -r -d '' ports_ruby << 'RUBY'
+    require 'yaml'
+
+    input=STDIN.readlines.join
+    ports = []
+    input.split('_FILE_SEPERATOR_').each do |yml|
+       yml.strip!
+       begin
+         ports += (YAML.load(yml)['expose'] || [])
+       rescue Psych::SyntaxError => e
+        puts e
+        puts "*ERROR."
+       rescue => e
+        puts yml
+        p e
+       end
+    end
+    puts ports.map { |p| p.to_s.include?(':') ? "-p\n#{p}" : "--expose\n#{p}" }.join("\n")
+RUBY
+
+    tmp_input_file=$(mktemp)
+
+    echo "$input" > "$tmp_input_file"
+    raw=`exec cat "$tmp_input_file" | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e "$ports_ruby"`
+
+    rm -f "$tmp_input_file"
+
+    ports=()
+    ok=1
+    while read i; do
+      if [ "$i" == "*ERROR." ]; then
+        ok=0
+      elif [ -n "$i" ]; then
+        ports[${#ports[@]}]=$i
+      fi
+    done <<< "$raw"
+
+    if [ "$ok" -ne 1 ]; then
+      echo "${ports[@]}"
+      echo "YAML syntax error. Please check your containers/*.yml config files."
+      exit 1
+    fi
+
+   merge_user_args
 }
 
 if [ -z $docker_path ]; then
@@ -387,32 +460,24 @@ if [ -z $docker_path ]; then
 fi
 
 [ "$command" == "cleanup" ] && {
-  echo
-  echo "The following command will"
-  echo "- Delete all docker images for old containers"
-  echo "- Delete all stopped and orphan containers"
-  echo
-  read -p "Are you sure (Y/n): " -n 1 -r && echo
-  if [[ $REPLY =~ ^[Yy]$ || ! $REPLY ]]
-    then
-      space=$(df /var/lib/docker | awk '{ print $4 }' | grep -v Available)
-      echo "Starting Cleanup (bytes free $space)"
-
-      STATE_DIR=./.gc-state scripts/docker-gc
+  $docker_path system prune -a
 
-      space=$(df /var/lib/docker | awk '{ print $4 }' | grep -v Available)
-      echo "Finished Cleanup (bytes free $space)"
+  if [ -d /var/discourse/shared/standalone/postgres_data_old ]; then
+    echo
+    echo "Old PostgreSQL backup data cluster detected taking up $(du -hs /var/discourse/shared/standalone/postgres_data_old | awk '{print $1}') detected"
+    read -p "Would you like to remove it? (Y/n): " -n 1 -r && echo
 
+    if [[ $REPLY =~ ^[Yy]$ ]]; then
+      echo "removing old PostgreSQL data cluster at /var/discourse/shared/standalone/postgres_data_old..."
+      rm -rf /var/discourse/shared/standalone/postgres_data_old
     else
       exit 1
+    fi
   fi
+
   exit 0
 }
 
-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"
@@ -450,7 +515,9 @@ 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
+  if [ -n "$user_run_image" ]; then
+    run_image=$user_run_image
+  elif [ -z "$run_image" ]; then
     run_image="$local_discourse/$config"
   fi
 }
@@ -470,35 +537,43 @@ set_boot_command() {
   fi
 }
 
+merge_user_args() {
+  local docker_args
+
+  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']"`
+
+  if [[ -n "$docker_args" ]]; then
+    user_args="$user_args $docker_args"
+  fi
+}
+
 run_start() {
 
-   existing=`$docker_path ps | awk '{ print $1, $(NF) }' | grep " $config$" | awk '{ print $1 }'`
-   echo $existing
-   if [ ! -z $existing ]
+   if [ -z "$START_CMD_ONLY" ]
    then
-     echo "Nothing to do, your container has already started!"
-     exit 0
-   fi
+     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 0
+     fi
 
-   existing=`$docker_path ps -a | awk '{ print $1, $(NF) }' | grep " $config$" | awk '{ print $1 }'`
-   if [ ! -z $existing ]
-   then
-     echo "starting up existing container"
-     (
-       set -x
-       $docker_path start $config
-     )
-     exit 0
+     existing=`$docker_path ps -a | awk '{ print $1, $(NF) }' | grep " $config$" | awk '{ print $1 }'`
+     if [ ! -z $existing ]
+     then
+       echo "starting up existing container"
+       (
+         set -x
+         $docker_path start $config
+       )
+       exit 0
+     fi
    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}\"}.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']"`
-
    set_template_info
    set_volumes
    set_links
@@ -535,9 +610,14 @@ run_start() {
       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
 
+     if [ ! -z "$START_CMD_ONLY" ] ; then
+       docker_path="true"
+     fi
+
      set -x
-     $docker_path run $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 $user_args \
+
+     $docker_path run --shm-size=512m $links $attach_on_run $restart_policy "${env[@]}" "${labels[@]}" -h "$hostname" \
+        -e DOCKER_HOST_IP="$docker_ip" --name $config -t "${ports[@]}" $volumes $mac_address $user_args \
         $run_image $boot_command
 
    )
@@ -545,10 +625,22 @@ run_start() {
 
 }
 
+run_run() {
+  set_template_info
+  set_volumes
+  set_links
+  set_run_image
+
+  unset ERR
+  (exec $docker_path run --rm --shm-size=512m $user_args $links "${env[@]}" -e DOCKER_HOST_IP="$docker_ip" -i -a stdin -a stdout -a stderr $volumes $run_image \
+    /bin/bash -c "$run_command") || ERR=$?
 
-run_bootstrap() {
+  if [[ $ERR > 0 ]]; then
+    exit 1
+  fi
+}
 
-  # I got no frigging clue what this does, ask Sam Saffron. It RUNS STUFF ON THE HOST I GUESS?
+run_bootstrap() {
   host_run
 
   # Is the image available?
@@ -581,9 +673,15 @@ run_bootstrap() {
   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 \
+
+  tmp_input_file=$(mktemp)
+
+  echo "$input" > "$tmp_input_file"
+  (exec cat "$tmp_input_file" | $docker_path run --shm-size=512m $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") || ERR=$?
 
+  rm -f "$tmp_input_file"
+
   unset FAILED
   # magic exit code that indicates a retry
   if [[ "$ERR" == 77 ]]; then
@@ -612,8 +710,6 @@ run_bootstrap() {
   $docker_path rm `cat $cidbootstrap` && rm $cidbootstrap
 }
 
-
-
 case "$command" in
   bootstrap)
       run_bootstrap
@@ -621,6 +717,11 @@ case "$command" in
       exit 0
       ;;
 
+  run)
+      run_run
+      exit 0
+      ;;
+
   enter)
       exec $docker_path exec -it $config /bin/bash --login
       ;;
@@ -642,6 +743,12 @@ case "$command" in
       exit 0
       ;;
 
+  start-cmd)
+    START_CMD_ONLY="1"
+    run_start
+    exit 0;
+    ;;
+
   start)
       run_start
       exit 0