Bump base image (#538)
[discourse_docker.git] / discourse-doctor
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 ===================="
188 log -e "$(grep git containers/$app_name.yml)"
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)
213 read_config_result=$(echo $config_line | awk --field-separator=":" '{print $2}')
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
253 log "Can't find app.yml or web_only.yml."
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 ===================="
273 docker ps| tail -n +2 > /tmp/$UUID-docker.txt
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
280 log "$app_name still not running!"
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'
286 fi
287 fi
288 }
289
290 ##
291 ## redact passwords and email addresses from log file
292 ##
293 clean_up_log_file() {
294 for VAR
295 in SMTP_PASSWORD LETSENCRYPT_ACCOUNT_EMAIL DEVELOPER_EMAILS DISCOURSE_DB_PASSWORD 'Sending mail to'
296 do
297 echo "Replacing: $VAR"
298 sed -i -e 's/'"$VAR"'\([=: ]\)\S*/'"$VAR"'\1REDACTED /g' $LOG_FILE
299 done
300 }
301
302 print_done() {
303 log
304 log "==================== DONE! ===================="
305 DOCTOR_FILE=$(date +%s | sha256sum | base64 | head -c 20).txt
306
307 if [ $app_name == 'app' ] && [ "$NO_CONTAINER" != 'y' ]
308 then
309 cp $LOG_FILE shared/standalone/log/var-log/$DOCTOR_FILE
310 sudo docker exec -w /var/www/discourse -i $app_name cp /var/log/$DOCTOR_FILE public
311 log "The output of this program may be available at http://$DISCOURSE_HOSTNAME/$DOCTOR_FILE"
312 log "You should inspect that file carefully before sharing the URL."
313 fi
314 # The following is not in the web log file since it was copied above, which seems corect
315 log
316 log "You can examine the output of this script with "
317 log "LESS=-Ri less $LOG_FILE"
318 log
319 log "BUT FIRST, make sure that you know the first three commands below!!!"
320 log
321 log "Commands to know when viewing the file with the above command (called 'less'): "
322 log "q -- quit"
323 log "/error<ENTER> -- search for the word 'error'"
324 log "n -- search for the next occurrence"
325 log "g -- go to the beginning of the file"
326 log "f -- go forward a page"
327 log "b -- go back a page"
328 log "G -- go to the end of the file"
329 }
330
331 initialize_log_file() {
332 rm -f $LOG_FILE
333 touch $LOG_FILE
334 log DISCOURSE DOCTOR $(date)
335 log -e "OS: $(uname -a)\n\n"
336 }
337
338 ##
339 ## END FUNCTION DECLARATION
340 ##
341
342 check_root
343 cd $WORKING_DIR || exit
344 initialize_log_file
345 get_yml_file
346 get_discourse_config
347 check_docker_is_installed
348 check_docker
349 check_plugins
350 check_if_hostname_resolves_here
351 check_disk_and_memory
352 check_email
353 clean_up_log_file
354 print_done