371 lines
12 KiB
Bash
Executable file
371 lines
12 KiB
Bash
Executable file
#!/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/main/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=$(grep -E "^ #?$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
|
|
case "${serve:-Y}" in
|
|
y*|Y*)
|
|
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."
|
|
;;
|
|
*)
|
|
log "Publicly available log not generated."
|
|
;;
|
|
esac
|
|
fi
|
|
# The following is not in the web log file since it was copied above, which seems correct
|
|
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<ENTER> -- 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
|