-
Notifications
You must be signed in to change notification settings - Fork 743
/
discourse-doctor
executable file
·371 lines (343 loc) · 11.7 KB
/
discourse-doctor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
#!/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