#!/usr/bin/env bash LOG_FILE="/tmp/discourse-debug.txt" WORKING_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" log() { if [ "$1" == "-e" ] then shift echo -e "$*" | tee -a "$LOG_FILE" else echo "$*" | tee -a "$LOG_FILE" fi } check_root() { if [[ $EUID -ne 0 ]]; then log "This script must be run as root. Please sudo or log in as root first." 1>&2 exit 1 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) 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 return 1 fi } check_ip_match() { HOST="$1" log log Checking your domain name . . . if connect_to_port $HOST 443 then log log "Connection to $HOST succeeded." else log WARNING:: This server does not appear to be accessible at $HOST:443. log if connect_to_port $HOST 80 then log A connection to port 80 succeeds, however. log This suggests that your DNS settings are correct, log but something is keeping traffic to port 443 from getting to your server. log Check your networking configuration to see that connections to port 443 are allowed. else log "A connection to http://$HOST (port 80) also fails." log log This suggests that $HOST resolves to the wrong IP address log or that traffic is not being routed to your server. fi log log Google: \"open ports YOUR CLOUD SERVICE\" for information for resolving this problem. log log This test might not work for all situations, log so if you can access Discourse at http://$HOST, this might not indicate a problem. sleep 3 fi } check_docker_is_installed() { log -e "\n==================== DOCKER INFO ====================" docker_path="$(which docker.io || which docker)" if [ -z $docker_path ]; then log "Docker is not installed. Have you installed Discourse at all?" log "Perhaps you're looking for ./discourse-setup ." log "There is no point in continuing." exit else log -e "DOCKER VERSION: $(docker --version)" log -e "\nDOCKER PROCESSES (docker ps -a)\n\n$(sudo docker ps -a)\n" fi } get_OS() { log -e "OS: $(uname -s)" } check_disk_and_memory() { log -e "\n\n==================== MEMORY INFORMATION ====================" os_type=$(get_OS) if [ "$os_type" == "Darwin" ]; then log -e "RAM: $( free -m | awk '/Mem:/ {print $2}' ) \n" else log -e "RAM (MB): $( free -m --si | awk ' /Mem:/ {print $2} ')\n" fi log "$(free -m)" log -e "\n==================== DISK SPACE CHECK ====================" log "---------- OS Disk Space ----------" log "$(df -h / /var/discourse /var/lib/docker /var/lib/docker/* | uniq)" if [ "$version" != "NOT FOUND" ] then log log "---------- Container Disk Space ----------" log "$(sudo docker exec -w /var/www/discourse -i $app_name df -h / /shared/ /shared/postgres_data /shared/redis_data /shared/backups /var/log | uniq)" fi log -e "\n==================== DISK INFORMATION ====================" log "$( fdisk -l )" log -e "\n==================== END DISK INFORMATION ====================" free_disk="$(df /var | tail -n 1 | awk '{print $4}')" # Arguably ./launcher is doing this so discourse-doctor does not need to . . . if [ "$free_disk" -lt 5000 ]; then log "\n\n==================== DISK SPACE PROBLEM ====================" log "WARNING: you appear to have very low disk space." log "This could be the cause of problems running your site." log "Please free up some space, or expand your disk, before continuing." log log "Run \'apt-get autoremove && apt-get autoclean\' to clean up unused" log "packages and \'./launcher cleanup\' to remove stale Docker containers." exit 1 fi } get_discourse_version() { version="" version=$(wget -q --timeout=3 https://$VERSION_HOSTNAME/privacy -O -|grep generator|head -1 |cut -d "=" -f 3|cut -d '-' -f 1 |cut -d '"' -f 2) &> /dev/null if ! echo $version | grep Discourse then version=$(wget -q --timeout=3 http://$VERSION_HOSTNAME/privacy -O -|grep generator|head -1 |cut -d "=" -f 3|cut -d '-' -f 1 |cut -d '"' -f 2) &> /dev/null fi if [ -z "$version" ] then version="NOT FOUND" fi log "Discourse version at $VERSION_HOSTNAME: $version" } check_if_hostname_resolves_here() { log "========================================" VERSION_HOSTNAME=$DISCOURSE_HOSTNAME get_discourse_version DISCOURSE_VERSION="$version" VERSION_HOSTNAME=localhost get_discourse_version LOCALHOST_VERSION="$version" if [ "$DISCOURSE_VERSION" != "$LOCALHOST_VERSION" ] then log "==================== DNS PROBLEM ====================" log "This server reports $LOCALHOST_VERSION, but $DISCOURSE_HOSTNAME reports $DISCOURSE_VERSION." log "This suggests that you have a DNS problem or that an intermediate proxy is to blame." log "If you are using Cloudflare, or a CDN, it may be improperly configured." fi } ## ## get discourse configuration values from YML file ## get_discourse_config() { log -e "\n==================== YML SETTINGS ====================" read_config "DISCOURSE_HOSTNAME" DISCOURSE_HOSTNAME=$read_config_result log DISCOURSE_HOSTNAME=$DISCOURSE_HOSTNAME read_config "DISCOURSE_SMTP_ADDRESS" SMTP_ADDRESS=$read_config_result log SMTP_ADDRESS=$SMTP_ADDRESS read_config "DISCOURSE_DEVELOPER_EMAILS" DEVELOPER_EMAILS=$read_config_result log DEVELOPER_EMAILS=$DEVELOPER_EMAILS read_config "DISCOURSE_SMTP_PASSWORD" SMTP_PASSWORD=$read_config_result log SMTP_PASSWORD=$read_config_result read_config "DISCOURSE_SMTP_PORT" SMTP_PORT=$read_config_result log SMTP_PORT=$read_config_result read_config "DISCOURSE_SMTP_USER_NAME" SMTP_USER_NAME=$read_config_result log SMTP_USER_NAME=$read_config_result read_config "LETSENCRYPT_ACCOUNT_EMAIL" letsencrypt_account_email=$read_config_result log "LETSENCRYPT_ACCOUNT_EMAIL=$letsencrypt_account_email" } check_plugins() { log -e "\n\n==================== PLUGINS ====================" log -e "$(grep 'git clone' containers/$app_name.yml)" grep git containers/$app_name.yml > /tmp/$PPID.grep if grep -cv "github.com/discourse" /tmp/$PPID.grep > /dev/null then log -e "\nWARNING:" log You have what appear to be non-official plugins. log "If you are having trouble, you should disable them and try rebuilding again." else log -e "\nNo non-official plugins detected." fi log -e "\nSee https://github.com/discourse/discourse/blob/master/lib/plugin/metadata.rb for the official list.\n" } dump_yaml() { log -e "\n\n==================== YML DUMP ====================" log Dumping $app_name.yml log -e "\n\n" } ## ## read a variable from the config file and stick it in read_config_result ## 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") } ## ## call rake emails:test inside the container ## check_email() { log -e "\n==================== MAIL TEST ====================" log "For a robust test, get an address from http://www.mail-tester.com/" echo "Or just send a test message to yourself." EMAIL=$(echo $DEVELOPER_EMAILS |cut -d , -f 1) read -p "Email address for mail test? ('n' to skip) [$EMAIL]: " new_value if [ ! -z "$new_value" ] then EMAIL="$new_value" fi if [ "$new_value" != "n" ] && [ "$new_value" != "N" ] then log "Sending mail to $EMAIL. . . " log "$(sudo docker exec -w /var/www/discourse -i $app_name rake emails:test[$EMAIL])" else log "Mail test skipped." fi } get_yml_file() { app_name="" if [ -f containers/app.yml ] then app_name="app" web_file=containers/$app_name.yml log "Found $web_file" elif [ -f containers/web_only.yml ] then log "YML=web_only.yml" app_name="web_only" web_file=containers/$app_name.yml log "Found $web_file" else log "Can't find app.yml or web_only.yml." log "Giving up." exit fi } check_docker() { docker ps | tail -n +2 > /tmp/$UUID-docker.txt if grep $app_name /tmp/$UUID-docker.txt then log -e "\nDiscourse container $app_name is running" else log "==================== SERIOUS PROBLEM!!!! ====================" log "$app_name not running!" log "Attempting to rebuild" log "==================== REBUILD LOG ====================" # too hard to pass STDERR of ./launcher to log() ./launcher rebuild $app_name 2>&1 | tee -a $LOG_FILE log "==================== END REBUILD LOG ====================" docker ps | tail -n +2 > /tmp/$UUID-docker.txt if grep $app_name /tmp/$UUID-docker.txt then log -e "\nDiscourse container $app_name is now running." log ". . . waiting 30 seconds for container to crank up. . . " sleep 30 else log "Failed to rebuild $app_name." # check_ip_match checks if curl to $DISCOURSE_HOSTNAME gets to this server # It works only if ports 80 and 443 are free check_ip_match $DISCOURSE_HOSTNAME log "You should probably remove any non-standard plugins and rebuild." NO_CONTAINER='y' log "Attempting to restart existing container. . . " ./launcher start $app_name 2>&1 | tee -a $LOG_FILE docker ps | tail -n +2 > /tmp/$UUID-docker.txt if grep $app_name /tmp/$UUID-docker.txt then log "Restarted the container." NO_CONTAINER='n' else log "Failed to restart the container." fi fi fi } ## ## redact passwords and email addresses from log file ## clean_up_log_file() { for VAR in SMTP_PASSWORD LETSENCRYPT_ACCOUNT_EMAIL DEVELOPER_EMAILS DISCOURSE_DB_PASSWORD 'Sending mail to' do echo "Replacing: $VAR" sed -i -e 's/'"$VAR"'\([=: ]\)\S*/'"$VAR"'\1REDACTED /g' $LOG_FILE done } print_done() { log log "==================== DONE! ====================" DOCTOR_FILE=$(date +%s | sha256sum | base64 | head -c 20).txt if [ $app_name == 'app' ] && [ "$NO_CONTAINER" != 'y' ] then read -p "Would you like to serve a publicly available version of this file? (Y/n)" serve if [ $serve == 'Y' ] then cp $LOG_FILE shared/standalone/log/var-log/$DOCTOR_FILE sudo docker exec -w /var/www/discourse -i $app_name cp /var/log/$DOCTOR_FILE public log "The output of this program may be available at http://$DISCOURSE_HOSTNAME/$DOCTOR_FILE" log "You should inspect that file carefully before sharing the URL." else log "Publicly available log not generated." fi fi # The following is not in the web log file since it was copied above, which seems corect log log "You can examine the output of this script with " log "LESS=-Ri less $LOG_FILE" log log "BUT FIRST, make sure that you know the first three commands below!!!" log log "Commands to know when viewing the file with the above command (called 'less'): " log "q -- quit" log "/error -- search for the word 'error'" log "n -- search for the next occurrence" log "g -- go to the beginning of the file" log "f -- go forward a page" log "b -- go back a page" log "G -- go to the end of the file" } initialize_log_file() { rm -f $LOG_FILE touch $LOG_FILE log DISCOURSE DOCTOR $(date) log -e "OS: $(uname -a)\n\n" } ## ## END FUNCTION DECLARATION ## check_root cd $WORKING_DIR || exit initialize_log_file get_yml_file get_discourse_config check_docker_is_installed check_docker check_plugins check_if_hostname_resolves_here check_disk_and_memory check_email clean_up_log_file print_done