PostgreSQL 12 (#462)
[discourse_docker.git] / discourse-setup
index 3945a9b32cf78afcb4c9a6f003d77a9a3bebcff9..ec1adaffbd3d4334ca972fb7b4b6d26c3767423a 100755 (executable)
@@ -1,4 +1,6 @@
-#!/bin/bash
+#!/usr/bin/env bash
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+cd $DIR
 
 ##
 ## Make sure only root can run our script
 
 ##
 ## Make sure only root can run our script
@@ -10,13 +12,138 @@ check_root() {
   fi
 }
 
   fi
 }
 
+##
+## Check whether a connection to HOSTNAME ($1) on PORT ($2) is possible
+##
+connect_to_port () {
+  HOST="$1"
+  PORT="$2"
+  VERIFY=$(date +%s | sha256sum | base64 | head -c 20)
+  if ! [ -x "$(command -v nc)" ]; then
+    echo "In order to check the connection to $HOST:$PORT we need to open a socket using netcat."
+    echo However netcat is not installed on your system. You can continue without this check
+    echo or abort the setup, install netcat and try again.
+    while true; do
+      read -p "Would you like to continue without this check? [yn] " yn
+      case $yn in
+      [Yy]*) return 2 ;;
+      [Nn]*) exit ;;
+      *) echo "Please answer y or n." ;;
+      esac
+    done
+  else
+    echo -e "HTTP/1.1 200 OK\n\n $VERIFY" | nc -w 4 -l -p $PORT >/dev/null 2>&1 &
+    if curl --proto =http -s $HOST:$PORT --connect-timeout 3 | grep $VERIFY >/dev/null 2>&1; then
+      return 0
+    else
+      curl --proto =http -s localhost:$PORT >/dev/null 2>&1
+      return 1
+    fi
+  fi
+}
+
+check_IP_match() {
+  HOST="$1"
+  echo
+  echo Checking your domain name . . .
+  connect_to_port $HOST 443; ec=$?
+  case $ec in
+    0)
+      echo "Connection to $HOST succeeded."
+      ;;
+    1)
+      echo "WARNING: Port 443 of computer does not appear to be accessible using hostname:  $HOST."
+      if connect_to_port $HOST 80; then
+        echo
+        echo SUCCESS: A connection to port 80 succeeds!
+        echo This suggests that your DNS settings are correct,
+        echo but something is keeping traffic to port 443 from getting to your server.
+        echo Check your networking configuration to see that connections to port 443 are allowed.
+      else
+        echo "WARNING: Connection to http://$HOST (port 80) also fails."
+        echo
+        echo "This suggests that $HOST resolves to some IP address that does not reach this "
+        echo machine where you are installing discourse.
+      fi
+      echo
+      echo "The first thing to do is confirm that $HOST resolves to the IP address of this server."
+      echo You usually do this at the same place you purchased the domain.
+      echo
+      echo If you are sure that the IP address resolves correctly, it could be a firewall issue.
+      echo A web search for  \"open ports YOUR CLOUD SERVICE\" might help.
+      echo
+      echo This tool is designed only for the most standard installations. If you cannot resolve
+      echo the issue above, you will need to edit containers/app.yml yourself and then type
+      echo
+      echo                   ./launcher rebuild app
+      echo
+      exit 1
+      ;;
+    2)
+      echo "Continuing without port check."
+      ;;
+  esac
+}
+
+##
+## Do we have docker?
+##
+check_and_install_docker () {
+  docker_path=`which docker.io || which docker`
+  if [ -z $docker_path ]; then
+    read  -p "Docker not installed. Enter to install from https://get.docker.com/ or Ctrl+C to exit"
+    curl https://get.docker.com/ | sh
+  fi
+  docker_path=`which docker.io || which docker`
+  if [ -z $docker_path ]; then
+    echo Docker install failed. Quitting.
+    exit
+  fi
+}
+
+##
+## What are we running on
+##
+check_OS() {
+  echo `uname -s`
+}
+
+##
+## OS X available memory
+##
+check_osx_memory() {
+  echo `free -m | awk '/Mem:/ {print $2}'`
+}
+
+##
+## Linux available memory
+##
+check_linux_memory() {
+  ## some VMs report just under 1GB of RAM, so
+  ## make an exception and allow those with more
+  ## than 989MB
+  mem=`free -m --si | awk ' /Mem:/ {print $2}'`
+  if [ "$mem" -ge 990 -a "$mem" -lt 1000 ]; then
+    echo 1
+  else
+    echo `free -g --si | awk ' /Mem:/  {print $2} '`
+  fi
+}
+
 ##
 ## Do we have enough memory and disk space for Discourse?
 ##
 check_disk_and_memory() {
 ##
 ## Do we have enough memory and disk space for Discourse?
 ##
 check_disk_and_memory() {
-  
-  avail_mem="$(LANG=C free -m | grep '^Mem:' | awk '{print $2}')"
-  if [ "$avail_mem" -lt 900 ]; then
+
+  os_type=$(check_OS)
+  avail_mem=0
+  if [ "$os_type" == "Darwin" ]; then
+    avail_mem=$(check_osx_memory)
+  else
+    avail_mem=$(check_linux_memory)
+  fi
+
+  if [ "$avail_mem" -lt 1 ]; then
     echo "WARNING: Discourse requires 1GB RAM to run. This system does not appear"
     echo "to have sufficient memory."
     echo
     echo "WARNING: Discourse requires 1GB RAM to run. This system does not appear"
     echo "to have sufficient memory."
     echo
@@ -24,38 +151,41 @@ check_disk_and_memory() {
     echo "complete successfully."
     exit 1
   fi
     echo "complete successfully."
     exit 1
   fi
-    
-  if [ "$avail_mem" -lt 1800 ]; then
-    total_swap="$(LANG=C free -m | grep ^Swap: | awk '{print $2}')"
-    if [ "$total_swap" -lt 1000 ]; then
-      echo "WARNING: Discourse requires at least 2GB of swap when running with less "
-      echo "than 2GB of RAM. This system does not appear to have sufficient swap space."
+
+  if [ "$avail_mem" -le 2 ]; then
+    total_swap=`free -g --si | awk ' /Swap:/  {print $2} '`
+
+    if [ "$total_swap" -lt 2 ]; then
+      echo "WARNING: Discourse requires at least 2GB of swap when running with 2GB of RAM"
+      echo "or less. This system does not appear to have sufficient swap space."
       echo
       echo "Without sufficient swap space, your site may not work properly, and future"
       echo "upgrades of Discourse may not complete successfully."
       echo
       echo
       echo "Without sufficient swap space, your site may not work properly, and future"
       echo "upgrades of Discourse may not complete successfully."
       echo
-      read -p "ENTER to create a 2GB swapfile now, or Ctrl+C to exit"
-      
+      echo "Ctrl+C to exit or wait 5 seconds to have a 2GB swapfile created."
+      sleep 5
+
       ##
       ## derived from https://meta.discourse.org/t/13880
       ##
       ## derived from https://meta.discourse.org/t/13880
-      ## 
+      ##
       install -o root -g root -m 0600 /dev/null /swapfile
       install -o root -g root -m 0600 /dev/null /swapfile
-      dd if=/dev/zero of=/swapfile bs=1k count=2048k
+      fallocate -l 2G /swapfile
       mkswap /swapfile
       swapon /swapfile
       echo "/swapfile       swap    swap    auto      0       0" | tee -a /etc/fstab
       sysctl -w vm.swappiness=10
       mkswap /swapfile
       swapon /swapfile
       echo "/swapfile       swap    swap    auto      0       0" | tee -a /etc/fstab
       sysctl -w vm.swappiness=10
-      echo vm.swappiness = 10 | tee -a /etc/sysctl.conf
+      echo 'vm.swappiness = 10' > /etc/sysctl.d/30-discourse-swap.conf
 
 
-      total_swap="$(LANG=C free -m | grep ^Swap: | awk '{print $2}')"
-      if [ "$total_swap" -lt 1000 ]; then
-        echo "Failed to create swap, sorry!"
+      total_swap=`free -g --si | awk ' /Swap:/ {print $2} '`
+      if [ "$total_swap" -lt 2 ]; then
+        echo "Failed to create swap: are you root? Are you running on real hardware, or a fully virtualized server?"
         exit 1
       fi
         exit 1
       fi
-      
+
     fi
   fi
 
     fi
   fi
 
+
   free_disk="$(df /var | tail -n 1 | awk '{print $4}')"
   if [ "$free_disk" -lt 5000 ]; then
     echo "WARNING: Discourse requires at least 5GB free disk space. This system"
   free_disk="$(df /var | tail -n 1 | awk '{print $4}')"
   if [ "$free_disk" -lt 5000 ]; then
     echo "WARNING: Discourse requires at least 5GB free disk space. This system"
@@ -81,9 +211,16 @@ scale_ram_and_cpu() {
 
   local changelog=/tmp/changelog.$PPID
   # grab info about total system ram and physical (NOT LOGICAL!) CPU cores
 
   local changelog=/tmp/changelog.$PPID
   # 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}'`
+  avail_gb=0
+  avail_cores=0
+  os_type=$(check_OS)
+  if [ "$os_type" == "Darwin" ]; then
+    avail_gb=$(check_osx_memory)
+    avail_cores=`sysctl hw.ncpu | awk '/hw.ncpu:/ {print $2}'`
+  else
+    avail_gb=$(check_linux_memory)
+    avail_cores=$((`awk '/cpu cores/ {print $4;exit}' /proc/cpuinfo`*`sort /proc/cpuinfo | uniq | grep -c "physical id"`))
+  fi
   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
   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
@@ -100,7 +237,7 @@ scale_ram_and_cpu() {
   fi
   db_shared_buffers=$(( db_shared_buffers < 4096 ? db_shared_buffers : 4096 ))
 
   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
+  sed -i -e "s/^  #\?db_shared_buffers:.*/  db_shared_buffers: \"${db_shared_buffers}MB\"/w $changelog" $data_file
   if [ -s $changelog ]
   then
     echo "setting db_shared_buffers = ${db_shared_buffers}MB"
   if [ -s $changelog ]
   then
     echo "setting db_shared_buffers = ${db_shared_buffers}MB"
@@ -116,17 +253,18 @@ scale_ram_and_cpu() {
   fi
   unicorn_workers=$(( unicorn_workers < 8 ? unicorn_workers : 8 ))
 
   fi
   unicorn_workers=$(( unicorn_workers < 8 ? unicorn_workers : 8 ))
 
-  sed -i -e "s/^  #UNICORN_WORKERS:.*/  UNICORN_WORKERS: ${unicorn_workers}/w $changelog" $config_file
+  sed -i -e "s/^  #\?UNICORN_WORKERS:.*/  UNICORN_WORKERS: ${unicorn_workers}/w $changelog" $web_file
   if [ -s $changelog ]
   then
       echo "setting UNICORN_WORKERS = ${unicorn_workers}"
       rm $changelog
   fi
 
   if [ -s $changelog ]
   then
       echo "setting UNICORN_WORKERS = ${unicorn_workers}"
       rm $changelog
   fi
 
+  echo $data_file memory parameters updated.
 }
 
 
 }
 
 
-## 
+##
 ## standard http / https ports must not be occupied
 ##
 check_ports() {
 ## standard http / https ports must not be occupied
 ##
 check_ports() {
@@ -140,117 +278,177 @@ check_ports() {
 ## check a port to see if it is already in use
 ##
 check_port() {
 ## check a port to see if it is already in use
 ##
 check_port() {
-  
+
   local valid=$(netstat -tln | awk '{print $4}' | grep ":${1}\$")
 
   if [ -n "$valid" ]; then
     echo "Port ${1} appears to already be in use."
     echo
   local valid=$(netstat -tln | awk '{print $4}' | grep ":${1}\$")
 
   if [ -n "$valid" ]; then
     echo "Port ${1} appears to already be in use."
     echo
+    echo "This will show you what command is using port ${1}"
+    lsof -i tcp:${1} -s tcp:listen
+    echo
     echo "If you are trying to run Discourse simultaneously with another web"
     echo "server like Apache or nginx, you will need to bind to a different port"
     echo "If you are trying to run Discourse simultaneously with another web"
     echo "server like Apache or nginx, you will need to bind to a different port"
-    echo 
+    echo
     echo "See https://meta.discourse.org/t/17247"
     echo "See https://meta.discourse.org/t/17247"
+    echo
+    echo "If you are reconfiguring an already-configured Discourse, use "
+    echo
+    echo "./launcher stop app"
+    echo
+    echo "to stop Discourse before you reconfigure it and try again."
     exit 1
   fi
 }
 
     exit 1
   fi
 }
 
+##
+## read a variable from the config file
+##
+read_config() {
+  config_line=`egrep "^  #?$1:" $web_file`
+  read_config_result=`echo $config_line | awk  -F":" '{print $2}'`
+  read_config_result=`echo $read_config_result | sed "s/^\([\"']\)\(.*\)\1\$/\2/g"`
+}
+
+read_default() {
+  config_line=`egrep "^  #?$1:" samples/standalone.yml`
+  read_default_result=`echo $config_line | awk  -F":" '{print $2}'`
+  read_default_result=`echo $read_config_result | sed "s/^\([\"']\)\(.*\)\1\$/\2/g"`
+}
+
 ##
 ## prompt user for typical Discourse config file values
 ##
 ask_user_for_config() {
 ##
 ## prompt user for typical Discourse config file values
 ##
 ask_user_for_config() {
-  
+
+  # NOTE: Defaults now come from standalone.yml
+
   local changelog=/tmp/changelog.$PPID
   local changelog=/tmp/changelog.$PPID
-  local hostname="discourse.example.com"
-  local developer_emails="me@example.com,you@example.com"
-  local smtp_address="smtp.example.com"
-  local smtp_port="587"
-  local smtp_user_name="postmaster@discourse.example.com"  
-  local smtp_password=""
-  local letsencrypt_account_email="me@example.com"
-  local letsencrypt_status="ENTER to skip"
+  read_config "DISCOURSE_SMTP_ADDRESS"
+  local smtp_address=$read_config_result
+  # NOTE: if there are spaces between emails, this breaks, but a human should be paying attention
+  read_config "DISCOURSE_DEVELOPER_EMAILS"
+  local developer_emails=$read_config_result
+  read_config "DISCOURSE_SMTP_PASSWORD"
+  local smtp_password=$read_config_result
+  read_config "DISCOURSE_SMTP_PORT"
+  local smtp_port=$read_config_result
+  read_config "DISCOURSE_SMTP_USER_NAME"
+  local smtp_user_name=$read_config_result
+  if [ "$smtp_password" = "pa$$word" ]
+  then
+      smtp_password = ""
+  fi
+  read_config "LETSENCRYPT_ACCOUNT_EMAIL"
+  local letsencrypt_account_email=$read_config_result
+  if [ -z $letsencrypt_account_email ]
+  then
+      letsencrypt_account_email="me@example.com"
+  fi
+  if [ "$letsencrypt_account_email" = "me@example.com" ]
+  then
+      local letsencrypt_status="ENTER to skip"
+  else
+    local letsencrypt_status="Enter 'OFF' to disable."
+  fi
+
+  read_config "DISCOURSE_HOSTNAME"
+  hostname=$read_config_result
 
   local new_value=""
   local config_ok="n"
   local update_ok="y"
 
   local new_value=""
   local config_ok="n"
   local update_ok="y"
-  
+
   echo ""
 
   while [[ "$config_ok" == "n" ]]
   do
   echo ""
 
   while [[ "$config_ok" == "n" ]]
   do
-    if [ ! -z $hostname ]
+    if [ ! -z "$hostname" ]
     then
       read -p "Hostname for your Discourse? [$hostname]: " new_value
     then
       read -p "Hostname for your Discourse? [$hostname]: " new_value
-      if [ ! -z $new_value ]
+      if [ ! -z "$new_value" ]
       then
       then
-          hostname=$new_value
+          hostname="$new_value"
+      fi
+      if [[ $hostname =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]
+      then
+        echo
+        echo "Discourse requires a DNS hostname. IP addresses are unsupported and will not work."
+        echo
+        hostname="discourse.example.com"
       fi
     fi
       fi
     fi
-    
-    if [ ! -z $developer_emails ]
+
+    check_IP_match $hostname
+
+    if [ ! -z "$developer_emails" ]
     then
     then
-      read -p "Email address for admin account? [$developer_emails]: " new_value
-      if [ ! -z $new_value ]
+      read -p "Email address for admin account(s)? [$developer_emails]: " new_value
+      if [ ! -z "$new_value" ]
       then
       then
-          developer_emails=$new_value
+          developer_emails="$new_value"
       fi
     fi
       fi
     fi
-    
-    if [ ! -z $smtp_address ]
+
+    if [ ! -z "$smtp_address" ]
     then
       read -p "SMTP server address? [$smtp_address]: " new_value
     then
       read -p "SMTP server address? [$smtp_address]: " new_value
-      if [ ! -z $new_value ]
+      if [ ! -z "$new_value" ]
       then
       then
-        smtp_address=$new_value
+        smtp_address="$new_value"
       fi
     fi
       fi
     fi
-    
-    if [ ! -z $smtp_port ]
+
+    if [ ! -z "$smtp_port" ]
     then
       read -p "SMTP port? [$smtp_port]: " new_value
     then
       read -p "SMTP port? [$smtp_port]: " new_value
-      if [ ! -z $new_value ]
+      if [ ! -z "$new_value" ]
       then
       then
-        smtp_port=$new_value
+        smtp_port="$new_value"
       fi
     fi
       fi
     fi
-    
+
     ##
     ##
-    ## automatically set correct user name based on common mail providers
+    ## automatically set correct user name based on common mail providers unless it's been set
     ##
     ##
-    if [ "$smtp_address" == "smtp.sparkpostmail.com" ]
+    if [ "$smtp_user_name" == "user@example.com" ]
     then
     then
-       smtp_user_name="SMTP_Injection"
-    fi    
-    if [ "$smtp_address" == "smtp.sendgrid.net" ]
-    then
-           smtp_user_name="apikey"
-    fi
-    if [ "$smtp_address" == "smtp.mailgun.org" ]
-    then
-           smtp_user_name="postmaster@$hostname"
+      if [ "$smtp_address" == "smtp.sparkpostmail.com" ]
+      then
+       smtp_user_name="SMTP_Injection"
+      fi
+      if [ "$smtp_address" == "smtp.sendgrid.net" ]
+      then
+           smtp_user_name="apikey"
+      fi
+      if [ "$smtp_address" == "smtp.mailgun.org" ]
+      then
+           smtp_user_name="postmaster@$hostname"
+      fi
     fi
     fi
-    
-    if [ ! -z $smtp_user_name ]
+
+    if [ ! -z "$smtp_user_name" ]
     then
       read -p "SMTP user name? [$smtp_user_name]: " new_value
     then
       read -p "SMTP user name? [$smtp_user_name]: " new_value
-      if [ ! -z $new_value ]
+      if [ ! -z "$new_value" ]
       then
       then
-          smtp_user_name=$new_value
+          smtp_user_name="$new_value"
       fi
     fi
       fi
     fi
-    
+
     read -p "SMTP password? [$smtp_password]: " new_value
     read -p "SMTP password? [$smtp_password]: " new_value
-    if [ ! -z $new_value ]
+    if [ ! -z "$new_value" ]
     then
     then
-        smtp_password=$new_value
+        smtp_password="$new_value"
     fi
     fi
-    
+
     if [ ! -z $letsencrypt_account_email ]
     then
     if [ ! -z $letsencrypt_account_email ]
     then
-      read -p "Let's Encrypt account email? ($letsencrypt_status) [$letsencrypt_account_email]: " new_value
-      if [ ! -z $new_value ]
+      read -p "Optional email address for Let's Encrypt warnings? ($letsencrypt_status) [$letsencrypt_account_email]: " new_value
+      if [ ! -z "$new_value" ]
       then
       then
-          letsencrypt_account_email=$new_value
-          if [ "$new_value" == "off" ]
+          letsencrypt_account_email="$new_value"
+          if [ "${new_value,,}" = "off" ]
           then
             letsencrypt_status="ENTER to skip"
           else
           then
             letsencrypt_status="ENTER to skip"
           else
@@ -266,17 +464,18 @@ ask_user_for_config() {
     echo "SMTP port     : $smtp_port"
     echo "SMTP username : $smtp_user_name"
     echo "SMTP password : $smtp_password"
     echo "SMTP port     : $smtp_port"
     echo "SMTP username : $smtp_user_name"
     echo "SMTP password : $smtp_password"
-    
+
     if [ "$letsencrypt_status" == "Enter 'OFF' to disable." ]
     then
       echo "Let's Encrypt : $letsencrypt_account_email"
     fi
     if [ "$letsencrypt_status" == "Enter 'OFF' to disable." ]
     then
       echo "Let's Encrypt : $letsencrypt_account_email"
     fi
-    
+
+
     echo ""
     read -p "ENTER to continue, 'n' to try again, Ctrl+C to exit: " config_ok
   done
 
     echo ""
     read -p "ENTER to continue, 'n' to try again, Ctrl+C to exit: " config_ok
   done
 
-  sed -i -e "s/^  DISCOURSE_HOSTNAME:.*/  DISCOURSE_HOSTNAME: $hostname/w $changelog" $config_file
+  sed -i -e "s/^  DISCOURSE_HOSTNAME:.*/  DISCOURSE_HOSTNAME: $hostname/w $changelog" $web_file
   if [ -s $changelog ]
   then
     rm $changelog
   if [ -s $changelog ]
   then
     rm $changelog
@@ -285,7 +484,7 @@ ask_user_for_config() {
     update_ok="n"
   fi
 
     update_ok="n"
   fi
 
-  sed -i -e "s/^  DISCOURSE_DEVELOPER_EMAILS:.*/  DISCOURSE_DEVELOPER_EMAILS: \'$developer_emails\'/w $changelog" $config_file
+  sed -i -e "s/^  DISCOURSE_DEVELOPER_EMAILS:.*/  DISCOURSE_DEVELOPER_EMAILS: \'$developer_emails\'/w $changelog" $web_file
   if [ -s $changelog ]
   then
     rm $changelog
   if [ -s $changelog ]
   then
     rm $changelog
@@ -294,7 +493,7 @@ ask_user_for_config() {
     update_ok="n"
   fi
 
     update_ok="n"
   fi
 
-  sed -i -e "s/^  DISCOURSE_SMTP_ADDRESS:.*/  DISCOURSE_SMTP_ADDRESS: $smtp_address/w $changelog" $config_file
+  sed -i -e "s/^  DISCOURSE_SMTP_ADDRESS:.*/  DISCOURSE_SMTP_ADDRESS: $smtp_address/w $changelog" $web_file
   if [ -s $changelog ]
   then
     rm $changelog
   if [ -s $changelog ]
   then
     rm $changelog
@@ -303,7 +502,7 @@ ask_user_for_config() {
     update_ok="n"
   fi
 
     update_ok="n"
   fi
 
-  sed -i -e "s/^  #DISCOURSE_SMTP_PORT:.*/  DISCOURSE_SMTP_PORT: $smtp_port/w $changelog" $config_file
+  sed -i -e "s/^  #\?DISCOURSE_SMTP_PORT:.*/  DISCOURSE_SMTP_PORT: $smtp_port/w $changelog" $web_file
   if [ -s $changelog ]
   then
     rm $changelog
   if [ -s $changelog ]
   then
     rm $changelog
@@ -312,7 +511,7 @@ ask_user_for_config() {
     update_ok="n"
   fi
 
     update_ok="n"
   fi
 
-  sed -i -e "s/^  #DISCOURSE_SMTP_USER_NAME:.*/  DISCOURSE_SMTP_USER_NAME: $smtp_user_name/w $changelog" $config_file
+  sed -i -e "s/^  #\?DISCOURSE_SMTP_USER_NAME:.*/  DISCOURSE_SMTP_USER_NAME: $smtp_user_name/w $changelog" $web_file
   if [ -s $changelog ]
   then
     rm $changelog
   if [ -s $changelog ]
   then
     rm $changelog
@@ -321,53 +520,85 @@ ask_user_for_config() {
     update_ok="n"
   fi
 
     update_ok="n"
   fi
 
-  sed -i -e "s/^  #DISCOURSE_SMTP_PASSWORD:.*/  DISCOURSE_SMTP_PASSWORD: $smtp_password/w $changelog" $config_file
-  if [ -s $changelog ]
+  if [[ "$smtp_password" == *"\""* ]]
   then
   then
-      rm $changelog
-  else
-    echo "DISCOURSE_SMTP_PASSWORD change failed."
+    SLASH="BROKEN"
+    echo "========================================"
+    echo "WARNING!!!"
+    echo "Your password contains a quote (\")"
+    echo "Your SMTP Password will not be set. You will need to edit app.yml to enter it."
+    echo "========================================"
     update_ok="n"
     update_ok="n"
+  else
+    SLASH="|"
+    if [[ "$smtp_password" == *"$SLASH"* ]]
+    then SLASH="+"
+      if [[ "$smtp_password" == *"$SLASH"* ]]
+      then
+        SLASH="Q"
+        if [[ "$smtp_password" == *"$SLASH"* ]]
+       then
+         SLASH="BROKEN"
+          echo "========================================"
+          echo "WARNING!!!"
+          echo "Your password contains all available delimiters (+, |, and Q). "
+          echo "Your SMTP Password will not be set. You will need to edit app.yml to enter it."
+          echo "========================================"
+          update_ok="n"
+         fi
+       fi
+    fi
   fi
   fi
-
-  if [ "$letsencrypt_status" != "ENTER to skip" ]
+  if [[ "$SLASH" != "BROKEN" ]]
   then
   then
-      sed -i -e "s/^  #LETSENCRYPT_ACCOUNT_EMAIL:.*/  LETSENCRYPT_ACCOUNT_EMAIL: $letsencrypt_account_email/w $changelog" $config_file
-      if [ -s $changelog ]
-      then
-        rm $changelog
-      else
-        echo "LETSENCRYPT_ACCOUNT_EMAIL change failed."
-        update_ok="n"
-      fi
-      local src='^  #- "templates\/web.ssl.template.yml"'
-      local dst='  \- "templates\/web.ssl.template.yml"'
-      sed -i -e "s/$src/$dst/w $changelog" $config_file
-      if [ -s $changelog ]
-      then
-         echo "web.ssl.template.yml enabled"
-      else
-        update_ok="n"
-        echo "web.ssl.template.yml NOT ENABLED--was it on already?"
-      fi
-      local src='^  #- "templates\/web.letsencrypt.ssl.template.yml"'
-      local dst='  - "templates\/web.letsencrypt.ssl.template.yml"'
+    sed -i -e "s${SLASH}^  #\?DISCOURSE_SMTP_PASSWORD:.*${SLASH}  DISCOURSE_SMTP_PASSWORD: \"${smtp_password}\"${SLASH}w $changelog" $web_file
 
 
-      sed -i -e "s/$src/$dst/w $changelog" $config_file
-      if [ -s $changelog ]
-      then
-             echo "letsencrypt.ssl.template.yml enabled"
-      else
-        update_ok="n"
-        echo "letsencrypt.ssl.template.yml NOT ENABLED -- was it on already?"
-      fi
-  fi 
+    if [ -s $changelog ]
+    then
+      rm $changelog
+    else
+      echo "DISCOURSE_SMTP_PASSWORD change failed."
+      update_ok="n"
+    fi
+  fi
+
+  echo "Enabling Let's Encrypt"
+    sed -i -e "s/^  #\?LETSENCRYPT_ACCOUNT_EMAIL:.*/  LETSENCRYPT_ACCOUNT_EMAIL: $letsencrypt_account_email/w $changelog" $web_file
+    if [ -s $changelog ]
+    then
+      rm $changelog
+    else
+      echo "LETSENCRYPT_ACCOUNT_EMAIL change failed."
+      update_ok="n"
+    fi
+    local src='^  #\?- "templates\/web.ssl.template.yml"'
+    local dst='  \- "templates\/web.ssl.template.yml"'
+    sed -i -e "s/$src/$dst/w $changelog" $web_file
+    if [ -s $changelog ]
+    then
+  echo "web.ssl.template.yml enabled"
+    else
+      update_ok="n"
+      echo "web.ssl.template.yml NOT ENABLED--was it on already?"
+    fi
+    local src='^  #\?- "templates\/web.letsencrypt.ssl.template.yml"'
+    local dst='  - "templates\/web.letsencrypt.ssl.template.yml"'
+
+    sed -i -e "s/$src/$dst/w $changelog" $web_file
+    if [ -s $changelog ]
+    then
+      echo "letsencrypt.ssl.template.yml enabled"
+    else
+      update_ok="n"
+      echo "letsencrypt.ssl.template.yml NOT ENABLED -- was it on already?"
+    fi
 
   if [ "$update_ok" == "y" ]
   then
     echo -e "\nConfiguration file at $config_file updated successfully!\n"
   else
     echo -e "\nUnfortunately, there was an error changing $config_file\n"
 
   if [ "$update_ok" == "y" ]
   then
     echo -e "\nConfiguration file at $config_file updated successfully!\n"
   else
     echo -e "\nUnfortunately, there was an error changing $config_file\n"
+    echo -d "This may happen if you have made unexpected changes."
     exit 1
   fi
 }
     exit 1
   fi
 }
@@ -378,25 +609,26 @@ ask_user_for_config() {
 validate_config() {
 
   valid_config="y"
 validate_config() {
 
   valid_config="y"
-  
+
   for x in DISCOURSE_SMTP_ADDRESS DISCOURSE_SMTP_USER_NAME DISCOURSE_SMTP_PASSWORD \
            DISCOURSE_DEVELOPER_EMAILS DISCOURSE_HOSTNAME
   do
   for x in DISCOURSE_SMTP_ADDRESS DISCOURSE_SMTP_USER_NAME DISCOURSE_SMTP_PASSWORD \
            DISCOURSE_DEVELOPER_EMAILS DISCOURSE_HOSTNAME
   do
-    config_line=`grep "^  $x:" $config_file` 
-    local result=$?
-    local default="example.com"
+    read_config $x
+    local result=$read_config_result
+    read_default $x
+    local default=$read_default_result
 
 
-    if (( result == 0 ))
+    if [ ! -z "$result" ]
     then
     then
-      if [[ $config_line = *"$default"* ]]
+      if [[ "$config_line" = *"$default"* ]]
       then
       then
-        echo "$x left at incorrect default of example.com"
+        echo "$x left at incorrect default of $default"
         valid_config="n"
       fi
       config_val=`echo $config_line | awk '{print $2}'`
       if [ -z $config_val ]
       then
         valid_config="n"
       fi
       config_val=`echo $config_line | awk '{print $2}'`
       if [ -z $config_val ]
       then
-        echo "$x was left blank"
+        echo "$x was not configured"
         valid_config="n"
       fi
     else
         valid_config="n"
       fi
     else
@@ -404,10 +636,10 @@ validate_config() {
       valid_config="n"
     fi
   done
       valid_config="n"
     fi
   done
-  
+
   if [ "$valid_config" != "y" ]; then
   if [ "$valid_config" != "y" ]; then
-    echo -e "\nSorry, these $config_file settings aren't valid -- can't continue!"
-    echo "If you have unusual requirements, edit $config_file and then: "
+    echo -e "\nSorry, these $web_file settings aren't valid -- can't continue!"
+    echo "If you have unusual requirements, edit $web_file and then: "
     echo "./launcher bootstrap $app_name"
     exit 1
   fi
     echo "./launcher bootstrap $app_name"
     exit 1
   fi
@@ -417,30 +649,75 @@ validate_config() {
 ##
 ## template file names
 ##
 ##
 ## template file names
 ##
-app_name=app
-template_path=samples/standalone.yml
-config_file=containers/$app_name.yml
-changelog=/tmp/changelog
+
+if [ "$1" == "2container" ]
+then
+    app_name=web_only
+    data_name=data
+    web_template=samples/web_only.yml
+    data_template=samples/data.yml
+    web_file=containers/$app_name.yml
+    data_file=containers/$data_name.yml
+else
+  app_name=app
+  data_name=app
+  web_template=samples/standalone.yml
+  data_template=""
+  web_file=containers/$app_name.yml
+  data_file=containers/$app_name.yml
+fi
+    changelog=/tmp/changelog
 
 ##
 ## Check requirements before creating a copy of a config file we won't edit
 ##
 check_root
 
 ##
 ## Check requirements before creating a copy of a config file we won't edit
 ##
 check_root
+check_and_install_docker
 check_disk_and_memory
 check_disk_and_memory
-check_ports
 
 
-##
-## make a copy of the simple standalone config file
-##
-if [ -a $config_file ]
+if [ -a "$web_file" ]
 then
 then
-  echo "The configuration file $config_file already exists!"
-  echo ""
-  echo "If you want to delete your old configuration file and start over:"
-  echo "rm $config_file"
-  exit 1
+  echo "The configuration file $web_file already exists!"
+  echo
+  echo ". . . reconfiguring . . ."
+  echo
+  echo
+  DATE=`date +"%Y-%m-%d-%H%M%S"`
+  BACKUP=$app_name.yml.$DATE.bak
+  echo Saving old file as $BACKUP
+  cp $web_file containers/$BACKUP
+  echo "Stopping existing container in 5 seconds or Control-C to cancel."
+  sleep 5
+  ./launcher stop app
+  echo
 else
 else
-  cp $template_path $config_file
+  check_ports
+  cp -v $web_template $web_file
+  if [ "$data_name" == "data" ]
+  then
+      echo "--------------------------------------------------"
+      echo "This two container setup is currently unsupported. Use at your own risk!"
+      echo "--------------------------------------------------"
+      DISCOURSE_DB_PASSWORD=`date +%s | sha256sum | base64 | head -c 20`
+
+     sed -i -e "s/DISCOURSE_DB_PASSWORD: SOME_SECRET/DISCOURSE_DB_PASSWORD: $DISCOURSE_DB_PASSWORD/w $changelog" $web_file
+     if  [ -s $changelog ]
+     then
+        rm $changelog
+     else
+       echo "Problem changing DISCOURSE_DB_PASSWORD" in $web_file
+     fi
+
+     cp -v $data_template $data_file
+     quote=\'
+     sed -i -e "s/password ${quote}SOME_SECRET${quote}/password '$DISCOURSE_DB_PASSWORD'/w $changelog" $data_file
+     if  [ -s $changelog ]
+     then
+        rm $changelog
+     else
+       echo "Problem changing DISCOURSE_DB_PASSWORD" in $data_file
+     fi
+  fi
 fi
 
 scale_ram_and_cpu
 fi
 
 scale_ram_and_cpu
@@ -449,5 +726,17 @@ validate_config
 
 ##
 ## if we reach this point without exiting, OK to proceed
 
 ##
 ## if we reach this point without exiting, OK to proceed
+## rebuild won't fail if there's nothing to rebuild and does the restart
 ##
 ##
-./launcher bootstrap $app_name && ./launcher start $app_name
+echo "Updates successful. Rebuilding in 5 seconds."
+sleep 5 # Just a chance to ^C in case they were too fast on the draw
+if [ "$data_name" == "$app_name" ]
+then
+    echo Building $app_name
+    ./launcher rebuild $app_name
+else
+    echo Building $data_name now . . .
+    ./launcher rebuild $data_name
+    echo Building $app_name now . . .
+    ./launcher rebuild $app_name
+fi