Commit | Line | Data |
---|---|---|
24b887f2 JP |
1 | #!/usr/bin/env bash |
2 | LOG_FILE="/tmp/discourse-debug.txt" | |
3 | WORKING_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" | |
4 | ||
5 | log() { | |
6 | if [ "$1" == "-e" ] | |
7 | then | |
8 | shift | |
9 | echo -e "$*" | tee -a "$LOG_FILE" | |
10 | else | |
11 | echo "$*" | tee -a "$LOG_FILE" | |
12 | fi | |
13 | } | |
14 | ||
15 | check_root() { | |
16 | if [[ $EUID -ne 0 ]]; then | |
17 | log "This script must be run as root. Please sudo or log in as root first." 1>&2 | |
18 | exit 1 | |
19 | fi | |
20 | } | |
21 | ||
22 | ## | |
23 | ## Check whether a connection to HOSTNAME ($1) on PORT ($2) is possible | |
24 | ## | |
25 | connect_to_port() { | |
26 | HOST="$1" | |
27 | PORT="$2" | |
28 | VERIFY=$(date +%s | sha256sum | base64 | head -c 20) | |
29 | echo -e "HTTP/1.1 200 OK\n\n $VERIFY" | nc -w 4 -l -p $PORT >/dev/null 2>&1 & | |
30 | if curl --proto =http -s $HOST:$PORT --connect-timeout 3 | grep $VERIFY >/dev/null 2>&1 & | |
31 | then | |
32 | return 0 | |
33 | else | |
34 | return 1 | |
35 | fi | |
36 | } | |
37 | ||
38 | check_ip_match() { | |
39 | HOST="$1" | |
40 | log | |
41 | log Checking your domain name . . . | |
42 | if connect_to_port $HOST 443 | |
43 | then | |
44 | log | |
45 | log "Connection to $HOST succeeded." | |
46 | else | |
47 | log WARNING:: This server does not appear to be accessible at $HOST:443. | |
48 | log | |
49 | if connect_to_port $HOST 80 | |
50 | then | |
51 | log A connection to port 80 succeeds, however. | |
52 | log This suggests that your DNS settings are correct, | |
53 | log but something is keeping traffic to port 443 from getting to your server. | |
54 | log Check your networking configuration to see that connections to port 443 are allowed. | |
55 | else | |
56 | log "A connection to http://$HOST (port 80) also fails." | |
57 | log | |
58 | log This suggests that $HOST resolves to the wrong IP address | |
59 | log or that traffic is not being routed to your server. | |
60 | fi | |
61 | log | |
62 | log Google: \"open ports YOUR CLOUD SERVICE\" for information for resolving this problem. | |
63 | log | |
64 | log This test might not work for all situations, | |
65 | log so if you can access Discourse at http://$HOST, this might not indicate a problem. | |
66 | sleep 3 | |
67 | fi | |
68 | } | |
69 | ||
70 | check_docker_is_installed() { | |
71 | log -e "\n==================== DOCKER INFO ====================" | |
72 | docker_path="$(which docker.io || which docker)" | |
73 | if [ -z $docker_path ]; then | |
74 | log "Docker is not installed. Have you installed Discourse at all?" | |
75 | log "Perhaps you're looking for ./discourse-setup ." | |
76 | log "There is no point in continuing." | |
77 | exit | |
78 | else | |
79 | log -e "DOCKER VERSION: $(docker --version)" | |
80 | log -e "\nDOCKER PROCESSES (docker ps -a)\n\n$(sudo docker ps -a)\n" | |
81 | fi | |
82 | } | |
83 | ||
84 | get_OS() { | |
85 | log -e "OS: $(uname -s)" | |
86 | } | |
87 | ||
88 | check_disk_and_memory() { | |
89 | log -e "\n\n==================== MEMORY INFORMATION ====================" | |
90 | os_type=$(get_OS) | |
91 | if [ "$os_type" == "Darwin" ]; then | |
92 | log -e "RAM: $( free -m | awk '/Mem:/ {print $2}' ) \n" | |
93 | else | |
94 | log -e "RAM (MB): $( free -m --si | awk ' /Mem:/ {print $2} ')\n" | |
95 | fi | |
96 | log "$(free -m)" | |
97 | ||
98 | log -e "\n==================== DISK SPACE CHECK ====================" | |
99 | log "---------- OS Disk Space ----------" | |
100 | log "$(df -h / /var/discourse /var/lib/docker /var/lib/docker/* | uniq)" | |
101 | ||
102 | if [ "$version" != "NOT FOUND" ] | |
103 | then | |
104 | log | |
105 | log "---------- Container Disk Space ----------" | |
106 | 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)" | |
107 | fi | |
108 | ||
109 | log -e "\n==================== DISK INFORMATION ====================" | |
110 | log "$( fdisk -l )" | |
111 | log -e "\n==================== END DISK INFORMATION ====================" | |
112 | ||
113 | free_disk="$(df /var | tail -n 1 | awk '{print $4}')" | |
114 | # Arguably ./launcher is doing this so discourse-doctor does not need to . . . | |
115 | if [ "$free_disk" -lt 5000 ]; then | |
116 | log "\n\n==================== DISK SPACE PROBLEM ====================" | |
117 | log "WARNING: you appear to have very low disk space." | |
118 | log "This could be the cause of problems running your site." | |
119 | log "Please free up some space, or expand your disk, before continuing." | |
120 | log | |
121 | log "Run \'apt-get autoremove && apt-get autoclean\' to clean up unused" | |
122 | log "packages and \'./launcher cleanup\' to remove stale Docker containers." | |
123 | exit 1 | |
124 | fi | |
125 | } | |
126 | ||
127 | get_discourse_version() { | |
128 | version="" | |
129 | 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 | |
130 | if ! echo $version | grep Discourse | |
131 | then | |
132 | 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 | |
133 | fi | |
134 | if [ -z "$version" ] | |
135 | then | |
136 | version="NOT FOUND" | |
137 | fi | |
138 | log "Discourse version at $VERSION_HOSTNAME: $version" | |
139 | } | |
140 | ||
141 | check_if_hostname_resolves_here() { | |
142 | log "========================================" | |
143 | VERSION_HOSTNAME=$DISCOURSE_HOSTNAME | |
144 | get_discourse_version | |
145 | DISCOURSE_VERSION="$version" | |
146 | VERSION_HOSTNAME=localhost | |
147 | get_discourse_version | |
148 | LOCALHOST_VERSION="$version" | |
149 | if [ "$DISCOURSE_VERSION" != "$LOCALHOST_VERSION" ] | |
150 | then | |
151 | log "==================== DNS PROBLEM ====================" | |
152 | log "This server reports $LOCALHOST_VERSION, but $DISCOURSE_HOSTNAME reports $DISCOURSE_VERSION." | |
153 | log "This suggests that you have a DNS problem or that an intermediate proxy is to blame." | |
154 | log "If you are using Cloudflare, or a CDN, it may be improperly configured." | |
155 | fi | |
156 | } | |
157 | ||
158 | ## | |
159 | ## get discourse configuration values from YML file | |
160 | ## | |
161 | get_discourse_config() { | |
162 | log -e "\n==================== YML SETTINGS ====================" | |
163 | read_config "DISCOURSE_HOSTNAME" | |
164 | DISCOURSE_HOSTNAME=$read_config_result | |
165 | log DISCOURSE_HOSTNAME=$DISCOURSE_HOSTNAME | |
166 | read_config "DISCOURSE_SMTP_ADDRESS" | |
167 | SMTP_ADDRESS=$read_config_result | |
168 | log SMTP_ADDRESS=$SMTP_ADDRESS | |
169 | read_config "DISCOURSE_DEVELOPER_EMAILS" | |
170 | DEVELOPER_EMAILS=$read_config_result | |
171 | log DEVELOPER_EMAILS=$DEVELOPER_EMAILS | |
172 | read_config "DISCOURSE_SMTP_PASSWORD" | |
173 | SMTP_PASSWORD=$read_config_result | |
174 | log SMTP_PASSWORD=$read_config_result | |
175 | read_config "DISCOURSE_SMTP_PORT" | |
176 | SMTP_PORT=$read_config_result | |
177 | log SMTP_PORT=$read_config_result | |
178 | read_config "DISCOURSE_SMTP_USER_NAME" | |
179 | SMTP_USER_NAME=$read_config_result | |
180 | log SMTP_USER_NAME=$read_config_result | |
181 | read_config "LETSENCRYPT_ACCOUNT_EMAIL" | |
182 | letsencrypt_account_email=$read_config_result | |
183 | log "LETSENCRYPT_ACCOUNT_EMAIL=$letsencrypt_account_email" | |
184 | } | |
185 | ||
186 | check_plugins() { | |
187 | log -e "\n\n==================== PLUGINS ====================" | |
6ffca0c2 | 188 | log -e "$(grep 'git clone' containers/$app_name.yml)" |
24b887f2 JP |
189 | grep git containers/$app_name.yml > /tmp/$PPID.grep |
190 | ||
191 | if grep -cv "github.com/discourse" /tmp/$PPID.grep > /dev/null | |
192 | then | |
193 | log -e "\nWARNING:" | |
194 | log You have what appear to be non-official plugins. | |
195 | log "If you are having trouble, you should disable them and try rebuilding again." | |
196 | else | |
197 | log -e "\nNo non-official plugins detected." | |
198 | fi | |
199 | log -e "\nSee https://github.com/discourse/discourse/blob/master/lib/plugin/metadata.rb for the official list.\n" | |
200 | } | |
201 | ||
202 | dump_yaml() { | |
203 | log -e "\n\n==================== YML DUMP ====================" | |
204 | log Dumping $app_name.yml | |
205 | log -e "\n\n" | |
206 | } | |
207 | ||
208 | ## | |
209 | ## read a variable from the config file and stick it in read_config_result | |
210 | ## | |
211 | read_config() { | |
212 | config_line=$(egrep "^ #?$1:" $web_file) | |
06aa4ddb | 213 | read_config_result=$(echo $config_line | awk -F ":" '{print $2}') |
24b887f2 JP |
214 | read_config_result=$(echo $read_config_result | sed "s/^\([\"']\)\(.*\)\1\$/\2/g") |
215 | } | |
216 | ||
217 | ## | |
218 | ## call rake emails:test inside the container | |
219 | ## | |
220 | check_email() { | |
221 | log -e "\n==================== MAIL TEST ====================" | |
222 | log "For a robust test, get an address from http://www.mail-tester.com/" | |
223 | echo "Or just send a test message to yourself." | |
224 | EMAIL=$(echo $DEVELOPER_EMAILS |cut -d , -f 1) | |
225 | read -p "Email address for mail test? ('n' to skip) [$EMAIL]: " new_value | |
226 | if [ ! -z "$new_value" ] | |
227 | then | |
228 | EMAIL="$new_value" | |
229 | fi | |
230 | if [ "$new_value" != "n" ] && [ "$new_value" != "N" ] | |
231 | then | |
232 | log "Sending mail to $EMAIL. . . " | |
233 | log "$(sudo docker exec -w /var/www/discourse -i $app_name rake emails:test[$EMAIL])" | |
234 | else | |
235 | log "Mail test skipped." | |
236 | fi | |
237 | } | |
238 | ||
239 | get_yml_file() { | |
240 | app_name="" | |
241 | if [ -f containers/app.yml ] | |
242 | then | |
243 | app_name="app" | |
244 | web_file=containers/$app_name.yml | |
245 | log "Found $web_file" | |
246 | elif [ -f containers/web_only.yml ] | |
247 | then | |
248 | log "YML=web_only.yml" | |
249 | app_name="web_only" | |
250 | web_file=containers/$app_name.yml | |
251 | log "Found $web_file" | |
252 | else | |
82b2979d | 253 | log "Can't find app.yml or web_only.yml." |
24b887f2 JP |
254 | log "Giving up." |
255 | exit | |
256 | fi | |
257 | } | |
258 | ||
259 | check_docker() { | |
260 | docker ps | tail -n +2 > /tmp/$UUID-docker.txt | |
261 | ||
262 | if grep $app_name /tmp/$UUID-docker.txt | |
263 | then | |
264 | log -e "\nDiscourse container $app_name is running" | |
265 | else | |
266 | log "==================== SERIOUS PROBLEM!!!! ====================" | |
267 | log "$app_name not running!" | |
268 | log "Attempting to rebuild" | |
269 | log "==================== REBUILD LOG ====================" | |
270 | # too hard to pass STDERR of ./launcher to log() | |
271 | ./launcher rebuild $app_name 2>&1 | tee -a $LOG_FILE | |
272 | log "==================== END REBUILD LOG ====================" | |
e9db29df | 273 | docker ps | tail -n +2 > /tmp/$UUID-docker.txt |
24b887f2 JP |
274 | if grep $app_name /tmp/$UUID-docker.txt |
275 | then | |
276 | log -e "\nDiscourse container $app_name is now running." | |
277 | log ". . . waiting 30 seconds for container to crank up. . . " | |
278 | sleep 30 | |
279 | else | |
2312f4cc | 280 | log "Failed to rebuild $app_name." |
24b887f2 JP |
281 | # check_ip_match checks if curl to $DISCOURSE_HOSTNAME gets to this server |
282 | # It works only if ports 80 and 443 are free | |
283 | check_ip_match $DISCOURSE_HOSTNAME | |
284 | log "You should probably remove any non-standard plugins and rebuild." | |
285 | NO_CONTAINER='y' | |
2312f4cc JP |
286 | log "Attempting to restart existing container. . . " |
287 | ./launcher start $app_name 2>&1 | tee -a $LOG_FILE | |
e9db29df | 288 | docker ps | tail -n +2 > /tmp/$UUID-docker.txt |
2312f4cc JP |
289 | if grep $app_name /tmp/$UUID-docker.txt |
290 | then | |
291 | log "Restarted the container." | |
292 | NO_CONTAINER='n' | |
293 | else | |
294 | log "Failed to restart the container." | |
295 | fi | |
24b887f2 JP |
296 | fi |
297 | fi | |
298 | } | |
299 | ||
300 | ## | |
301 | ## redact passwords and email addresses from log file | |
302 | ## | |
303 | clean_up_log_file() { | |
304 | for VAR | |
305 | in SMTP_PASSWORD LETSENCRYPT_ACCOUNT_EMAIL DEVELOPER_EMAILS DISCOURSE_DB_PASSWORD 'Sending mail to' | |
306 | do | |
307 | echo "Replacing: $VAR" | |
308 | sed -i -e 's/'"$VAR"'\([=: ]\)\S*/'"$VAR"'\1REDACTED /g' $LOG_FILE | |
309 | done | |
310 | } | |
311 | ||
312 | print_done() { | |
313 | log | |
314 | log "==================== DONE! ====================" | |
315 | DOCTOR_FILE=$(date +%s | sha256sum | base64 | head -c 20).txt | |
316 | ||
317 | if [ $app_name == 'app' ] && [ "$NO_CONTAINER" != 'y' ] | |
318 | then | |
b0c92bac JD |
319 | read -p "Would you like to serve a publicly available version of this file? (Y/n)" serve |
320 | if [ $serve == 'Y' ] | |
321 | then | |
322 | cp $LOG_FILE shared/standalone/log/var-log/$DOCTOR_FILE | |
323 | sudo docker exec -w /var/www/discourse -i $app_name cp /var/log/$DOCTOR_FILE public | |
324 | log "The output of this program may be available at http://$DISCOURSE_HOSTNAME/$DOCTOR_FILE" | |
325 | log "You should inspect that file carefully before sharing the URL." | |
326 | else | |
327 | log "Publicly available log not generated." | |
328 | fi | |
24b887f2 JP |
329 | fi |
330 | # The following is not in the web log file since it was copied above, which seems corect | |
331 | log | |
332 | log "You can examine the output of this script with " | |
333 | log "LESS=-Ri less $LOG_FILE" | |
334 | log | |
335 | log "BUT FIRST, make sure that you know the first three commands below!!!" | |
336 | log | |
337 | log "Commands to know when viewing the file with the above command (called 'less'): " | |
338 | log "q -- quit" | |
339 | log "/error<ENTER> -- search for the word 'error'" | |
340 | log "n -- search for the next occurrence" | |
341 | log "g -- go to the beginning of the file" | |
342 | log "f -- go forward a page" | |
343 | log "b -- go back a page" | |
344 | log "G -- go to the end of the file" | |
345 | } | |
346 | ||
347 | initialize_log_file() { | |
348 | rm -f $LOG_FILE | |
349 | touch $LOG_FILE | |
350 | log DISCOURSE DOCTOR $(date) | |
351 | log -e "OS: $(uname -a)\n\n" | |
352 | } | |
353 | ||
354 | ## | |
355 | ## END FUNCTION DECLARATION | |
356 | ## | |
357 | ||
358 | check_root | |
359 | cd $WORKING_DIR || exit | |
360 | initialize_log_file | |
361 | get_yml_file | |
362 | get_discourse_config | |
363 | check_docker_is_installed | |
364 | check_docker | |
365 | check_plugins | |
366 | check_if_hostname_resolves_here | |
367 | check_disk_and_memory | |
368 | check_email | |
369 | clean_up_log_file | |
370 | print_done |