b5f89f218785c0ce4b8ce57253b06d6e49a64a72
[discourse_docker.git] / discourse-setup
1 #!/usr/bin/env bash
2
3 ##
4 ## Make sure only root can run our script
5 ##
6 check_root() {
7 if [[ $EUID -ne 0 ]]; then
8 echo "This script must be run as root. Please sudo or log in as root first." 1>&2
9 exit 1
10 fi
11 }
12
13
14 ##
15 ## Does the current IP match the domain name?
16 ##
17 check_IP_match () {
18 echo
19 echo Checking your domain name . . .
20 sleep 1
21 local IFACE=none
22 local IFCONFIG=`which ifconfig`
23 /sbin/route |grep default > /tmp/route$PPID
24
25 if grep default /tmp/route$PPID > /dev/null
26 then
27 local IFACE=`cut -c 73-100 /tmp/route$PPID |head -1`
28 else
29 echo WARNING: Cannot check your IP number.
30 fi
31 local IP=`$IFCONFIG $IFACE|grep "inet addr:" |cut -d":" -f 2|cut -d" " -f1|head -1`
32 local RESOLVED_IP=`dig +short $1`
33
34 IP_LOOKS_GOOD=0
35 if [[ ! -z $RESOLVED_IP ]]
36 then
37 if [ $IP == $RESOLVED_IP ]
38 then
39 echo $1 resolves to $IP. Looks good!
40 echo
41 local IP_LOOKS_GOOD=1
42 fi
43 fi
44 if [ $IP_LOOKS_GOOD == 0 ]
45 then
46 echo "-----------------------------------------"
47 echo $1 does not resolve to $IP.
48 echo IT IS ALMOST CERTAINLY A BAD IDEA TO TURN ON LET\'S ENCRYPT!!
49 echo Unless you know why this check failed, DO NOT USE Let\'s Encrypt.
50 echo
51 echo You should answer \"n\" at the next prompt and disable Let\'s Encrypt.
52 echo You have been warned.
53 echo "-----------------------------------------"
54 fi
55 sleep 1
56 }
57
58 ##
59 ## Do we have docker?
60 ##
61 check_and_install_docker () {
62 docker_path=`which docker.io || which docker`
63 if [ -z $docker_path ]; then
64 read -p "Docker not installed. Enter to install from https://get.docker.com/ or Ctrl+C to exit"
65 curl https://get.docker.com/ | sh
66 fi
67 docker_path=`which docker.io || which docker`
68 if [ -z $docker_path ]; then
69 echo Docker install failed. Quitting.
70 exit
71 fi
72 }
73
74 ##
75 ## What are we running on
76 ##
77 check_OS() {
78 echo `uname -s`
79 }
80
81 ##
82 ## OS X available memory
83 ##
84 check_osx_memory() {
85 echo `top -l 1 | awk '/PhysMem:/ {print $2}' | sed s/G//`
86 }
87
88 ##
89 ## Linux available memory
90 ##
91 check_linux_memory() {
92 echo `free -g --si | awk ' /Mem:/ {print $2} '`
93 }
94
95 ##
96 ## Do we have enough memory and disk space for Discourse?
97 ##
98 check_disk_and_memory() {
99
100 os_type=$(check_OS)
101 avail_mem=0
102 if [ $os_type == "Darwin" ]; then
103 avail_mem=$(check_osx_memory)
104 else
105 avail_mem=$(check_linux_memory)
106 fi
107
108 if [ "$avail_mem" -lt 1 ]; then
109 echo "WARNING: Discourse requires 1GB RAM to run. This system does not appear"
110 echo "to have sufficient memory."
111 echo
112 echo "Your site may not work properly, or future upgrades of Discourse may not"
113 echo "complete successfully."
114 exit 1
115 fi
116
117 if [ "$avail_mem" -le 2 ]; then
118 total_swap=`free -g --si | awk ' /Swap:/ {print $2} '`
119 if [ "$total_swap" -lt 2 ]; then
120 echo "WARNING: Discourse requires at least 2GB of swap when running with 2GB of RAM"
121 echo "or less. This system does not appear to have sufficient swap space."
122 echo
123 echo "Without sufficient swap space, your site may not work properly, and future"
124 echo "upgrades of Discourse may not complete successfully."
125 echo
126 read -p "ENTER to create a 2GB swapfile now, or Ctrl+C to exit"
127
128 ##
129 ## derived from https://meta.discourse.org/t/13880
130 ##
131 install -o root -g root -m 0600 /dev/null /swapfile
132 dd if=/dev/zero of=/swapfile bs=1k count=2048k
133 mkswap /swapfile
134 swapon /swapfile
135 echo "/swapfile swap swap auto 0 0" | tee -a /etc/fstab
136 sysctl -w vm.swappiness=10
137 echo vm.swappiness = 10 | tee -a /etc/sysctl.conf
138
139 total_swap=`free -g --si | awk ' /Swap:/ {print $2} '`
140 if [ "$total_swap" -lt 2 ]; then
141 echo "Failed to create swap, sorry!"
142 exit 1
143 fi
144
145 fi
146 fi
147
148
149 free_disk="$(df /var | tail -n 1 | awk '{print $4}')"
150 if [ "$free_disk" -lt 5000 ]; then
151 echo "WARNING: Discourse requires at least 5GB free disk space. This system"
152 echo "does not appear to have sufficient disk space."
153 echo
154 echo "Insufficient disk space may result in problems running your site, and"
155 echo "may not even allow Discourse installation to complete successfully."
156 echo
157 echo "Please free up some space, or expand your disk, before continuing."
158 echo
159 echo "Run \`apt-get autoremove && apt-get autoclean\` to clean up unused"
160 echo "packages and \`./launcher cleanup\` to remove stale Docker containers."
161 exit 1
162 fi
163
164 }
165
166
167 ##
168 ## If we have lots of RAM or lots of CPUs, bump up the defaults to scale better
169 ##
170 scale_ram_and_cpu() {
171
172 local changelog=/tmp/changelog.$PPID
173 # grab info about total system ram and physical (NOT LOGICAL!) CPU cores
174 avail_gb=0
175 avail_cores=0
176 os_type=$(check_OS)
177 if [ $os_type == "Darwin" ]; then
178 avail_gb=$(check_osx_memory)
179 avail_cores=`sysctl hw.ncpu | awk '/hw.ncpu:/ {print $2}'`
180 else
181 avail_gb=$(check_linux_memory)
182 avail_cores=$((`awk '/cpu cores/ {print $4;exit}' /proc/cpuinfo`*`sort /proc/cpuinfo | uniq | grep -c "physical id"`))
183 fi
184 echo "Found ${avail_gb}GB of memory and $avail_cores physical CPU cores"
185
186 # db_shared_buffers: 128MB for 1GB, 256MB for 2GB, or 256MB * GB, max 4096MB
187 if [ "$avail_gb" -eq "1" ]
188 then
189 db_shared_buffers=128
190 else
191 if [ "$avail_gb" -eq "2" ]
192 then
193 db_shared_buffers=256
194 else
195 db_shared_buffers=$(( 256 * $avail_gb ))
196 fi
197 fi
198 db_shared_buffers=$(( db_shared_buffers < 4096 ? db_shared_buffers : 4096 ))
199
200 sed -i -e "s/^ #\?db_shared_buffers:.*/ db_shared_buffers: \"${db_shared_buffers}MB\"/w $changelog" $config_file
201 if [ -s $changelog ]
202 then
203 echo "setting db_shared_buffers = ${db_shared_buffers}MB"
204 rm $changelog
205 fi
206
207 # UNICORN_WORKERS: 2 * GB for 2GB or less, or 2 * CPU, max 8
208 if [ "$avail_gb" -le "2" ]
209 then
210 unicorn_workers=$(( 2 * $avail_gb ))
211 else
212 unicorn_workers=$(( 2 * $avail_cores ))
213 fi
214 unicorn_workers=$(( unicorn_workers < 8 ? unicorn_workers : 8 ))
215
216 sed -i -e "s/^ #\?UNICORN_WORKERS:.*/ UNICORN_WORKERS: ${unicorn_workers}/w $changelog" $config_file
217 if [ -s $changelog ]
218 then
219 echo "setting UNICORN_WORKERS = ${unicorn_workers}"
220 rm $changelog
221 fi
222
223 }
224
225
226 ##
227 ## standard http / https ports must not be occupied
228 ##
229 check_ports() {
230 check_port "80"
231 check_port "443"
232 echo "Ports 80 and 443 are free for use"
233 }
234
235
236 ##
237 ## check a port to see if it is already in use
238 ##
239 check_port() {
240
241 local valid=$(netstat -tln | awk '{print $4}' | grep ":${1}\$")
242
243 if [ -n "$valid" ]; then
244 echo "Port ${1} appears to already be in use."
245 echo
246 echo "If you are trying to run Discourse simultaneously with another web"
247 echo "server like Apache or nginx, you will need to bind to a different port"
248 echo
249 echo "See https://meta.discourse.org/t/17247"
250 echo
251 echo "If you are reconfiguring an already-configured Discourse, use "
252 echo
253 echo "./launcher stop app"
254 echo
255 echo "to stop Discourse before you reconfigure it and try again."
256 exit 1
257 fi
258 }
259
260 ##
261 ## read a variable from the config file
262 ##
263 read_config() {
264
265 config_line=`egrep "^ #?$1:" $config_file`
266 read_config_result=`echo $config_line | awk '{print $2}'`
267 read_config_result=`echo $read_config_result | sed "s/^\([\"']\)\(.*\)\1\$/\2/g"`
268 }
269
270
271
272 ##
273 ## prompt user for typical Discourse config file values
274 ##
275 ask_user_for_config() {
276
277 # NOTE: Defaults now come from standalone.yml
278
279 local changelog=/tmp/changelog.$PPID
280 read_config "DISCOURSE_SMTP_ADDRESS"
281 local smtp_address=$read_config_result
282 # NOTE: if there are spaces between emails, this breaks, but a human should be paying attention
283 read_config "DISCOURSE_DEVELOPER_EMAILS"
284 local developer_emails=$read_config_result
285 read_config "DISCOURSE_SMTP_PASSWORD"
286 local smtp_password=$read_config_result
287 read_config "DISCOURSE_SMTP_PORT"
288 local smtp_port=$read_config_result
289 read_config "DISCOURSE_SMTP_USER_NAME"
290 local smtp_user_name=$read_config_result
291 if [ "$smtp_password" = "pa$$word" ]
292 then
293 smtp_password = ""
294 fi
295 read_config "LETSENCRYPT_ACCOUNT_EMAIL"
296 local letsencrypt_account_email=$read_config_result
297 if [ $letsencrypt_account_email = "me@example.com" ]
298 then
299 local letsencrypt_status="ENTER to skip"
300 else
301 local letsencrypt_status="Enter 'OFF' to disable."
302 fi
303
304 read_config "DISCOURSE_HOSTNAME"
305 hostname=$read_config_result
306
307 local new_value=""
308 local config_ok="n"
309 local update_ok="y"
310
311 echo ""
312
313 while [[ "$config_ok" == "n" ]]
314 do
315 if [ ! -z $hostname ]
316 then
317 read -p "Hostname for your Discourse? [$hostname]: " new_value
318 if [ ! -z $new_value ]
319 then
320 hostname=$new_value
321 fi
322 fi
323
324 if [ ! -z $developer_emails ]
325 then
326 read -p "Email address for admin account(s)? [$developer_emails]: " new_value
327 if [ ! -z $new_value ]
328 then
329 developer_emails=$new_value
330 fi
331 fi
332
333 if [ ! -z $smtp_address ]
334 then
335 read -p "SMTP server address? [$smtp_address]: " new_value
336 if [ ! -z $new_value ]
337 then
338 smtp_address=$new_value
339 fi
340 fi
341
342 if [ ! -z $smtp_port ]
343 then
344 read -p "SMTP port? [$smtp_port]: " new_value
345 if [ ! -z $new_value ]
346 then
347 smtp_port=$new_value
348 fi
349 fi
350
351 ##
352 ## automatically set correct user name based on common mail providers
353 ##
354 if [ "$smtp_address" == "smtp.sparkpostmail.com" ]
355 then
356 smtp_user_name="SMTP_Injection"
357 fi
358 if [ "$smtp_address" == "smtp.sendgrid.net" ]
359 then
360 smtp_user_name="apikey"
361 fi
362 if [ "$smtp_address" == "smtp.mailgun.org" ]
363 then
364 smtp_user_name="postmaster@$hostname"
365 fi
366
367 if [ ! -z $smtp_user_name ]
368 then
369 read -p "SMTP user name? [$smtp_user_name]: " new_value
370 if [ ! -z $new_value ]
371 then
372 smtp_user_name=$new_value
373 fi
374 fi
375
376 read -p "SMTP password? [$smtp_password]: " new_value
377 if [ ! -z $new_value ]
378 then
379 smtp_password=$new_value
380 fi
381
382 if [ ! -z $letsencrypt_account_email ]
383 then
384 read -p "Let's Encrypt account email? ($letsencrypt_status) [$letsencrypt_account_email]: " new_value
385 if [ ! -z $new_value ]
386 then
387 letsencrypt_account_email=$new_value
388 if [ "${new_value,,}" = "off" ]
389 then
390 letsencrypt_status="ENTER to skip"
391 else
392 letsencrypt_status="Enter 'OFF' to disable."
393 fi
394 fi
395 fi
396
397 if [ "$letsencrypt_status" == "Enter 'OFF' to disable." ]
398 then
399 check_IP_match $hostname
400 fi
401
402 echo -e "\nDoes this look right?\n"
403 echo "Hostname : $hostname"
404 echo "Email : $developer_emails"
405 echo "SMTP address : $smtp_address"
406 echo "SMTP port : $smtp_port"
407 echo "SMTP username : $smtp_user_name"
408 echo "SMTP password : $smtp_password"
409
410 if [ "$letsencrypt_status" == "Enter 'OFF' to disable." ]
411 then
412 echo "Let's Encrypt : $letsencrypt_account_email"
413 fi
414
415
416 echo ""
417 read -p "ENTER to continue, 'n' to try again, Ctrl+C to exit: " config_ok
418 done
419
420 sed -i -e "s/^ DISCOURSE_HOSTNAME:.*/ DISCOURSE_HOSTNAME: $hostname/w $changelog" $config_file
421 if [ -s $changelog ]
422 then
423 rm $changelog
424 else
425 echo "DISCOURSE_HOSTNAME change failed."
426 update_ok="n"
427 fi
428
429 sed -i -e "s/^ DISCOURSE_DEVELOPER_EMAILS:.*/ DISCOURSE_DEVELOPER_EMAILS: \'$developer_emails\'/w $changelog" $config_file
430 if [ -s $changelog ]
431 then
432 rm $changelog
433 else
434 echo "DISCOURSE_DEVELOPER_EMAILS change failed."
435 update_ok="n"
436 fi
437
438 sed -i -e "s/^ DISCOURSE_SMTP_ADDRESS:.*/ DISCOURSE_SMTP_ADDRESS: $smtp_address/w $changelog" $config_file
439 if [ -s $changelog ]
440 then
441 rm $changelog
442 else
443 echo "DISCOURSE_SMTP_ADDRESS change failed."
444 update_ok="n"
445 fi
446
447 sed -i -e "s/^ #\?DISCOURSE_SMTP_PORT:.*/ DISCOURSE_SMTP_PORT: $smtp_port/w $changelog" $config_file
448 if [ -s $changelog ]
449 then
450 rm $changelog
451 else
452 echo "DISCOURSE_SMTP_PORT change failed."
453 update_ok="n"
454 fi
455
456 sed -i -e "s/^ #\?DISCOURSE_SMTP_USER_NAME:.*/ DISCOURSE_SMTP_USER_NAME: $smtp_user_name/w $changelog" $config_file
457 if [ -s $changelog ]
458 then
459 rm $changelog
460 else
461 echo "DISCOURSE_SMTP_USER_NAME change failed."
462 update_ok="n"
463 fi
464
465 sed -i -e "s/^ #\?DISCOURSE_SMTP_PASSWORD:.*/ DISCOURSE_SMTP_PASSWORD: \"${smtp_password/\//\\/}\"/w $changelog" $config_file
466 if [ -s $changelog ]
467 then
468 rm $changelog
469 else
470 echo "DISCOURSE_SMTP_PASSWORD change failed."
471 update_ok="n"
472 fi
473
474 if [ "$letsencrypt_status" = "ENTER to skip" ]
475 then
476 local src='^ #\?- "templates\/web.ssl.template.yml"'
477 local dst=' #\- "templates\/web.ssl.template.yml"'
478 sed -i -e "s/$src/$dst/w $changelog" $config_file
479 if [ ! -s $changelog ]
480 then
481 update_ok="n"
482 echo "web.ssl.template.yml NOT DISABLED--Are you using a non-standard template?"
483 fi
484 local src='^ #\?- "templates\/web.letsencrypt.ssl.template.yml"'
485 local dst=' #- "templates\/web.letsencrypt.ssl.template.yml"'
486
487 sed -i -e "s/$src/$dst/w $changelog" $config_file
488 if [ ! -s $changelog ]
489 then
490 update_ok="n"
491 echo "web.ssl.template.yml NOT DISABLED--Are you using a non-standard template?"
492 fi
493 else # enable let's encrypt
494 echo "Let's Encrypt will be enabled for $letsencrypt_account_email"
495 sed -i -e "s/^ #\?LETSENCRYPT_ACCOUNT_EMAIL:.*/ LETSENCRYPT_ACCOUNT_EMAIL: $letsencrypt_account_email/w $changelog" $config_file
496 if [ -s $changelog ]
497 then
498 rm $changelog
499 else
500 echo "LETSENCRYPT_ACCOUNT_EMAIL change failed."
501 update_ok="n"
502 fi
503 local src='^ #\?- "templates\/web.ssl.template.yml"'
504 local dst=' \- "templates\/web.ssl.template.yml"'
505 sed -i -e "s/$src/$dst/w $changelog" $config_file
506 if [ -s $changelog ]
507 then
508 echo "web.ssl.template.yml enabled"
509 else
510 update_ok="n"
511 echo "web.ssl.template.yml NOT ENABLED--was it on already?"
512 fi
513 local src='^ #\?- "templates\/web.letsencrypt.ssl.template.yml"'
514 local dst=' - "templates\/web.letsencrypt.ssl.template.yml"'
515
516 sed -i -e "s/$src/$dst/w $changelog" $config_file
517 if [ -s $changelog ]
518 then
519 echo "letsencrypt.ssl.template.yml enabled"
520 else
521 update_ok="n"
522 echo "letsencrypt.ssl.template.yml NOT ENABLED -- was it on already?"
523 fi
524 fi
525
526 if [ "$update_ok" == "y" ]
527 then
528 echo -e "\nConfiguration file at $config_file updated successfully!\n"
529 else
530 echo -e "\nUnfortunately, there was an error changing $config_file\n"
531 exit 1
532 fi
533 }
534
535 ##
536 ## is our config file valid? Does it have the required fields set?
537 ##
538 validate_config() {
539
540 valid_config="y"
541
542 for x in DISCOURSE_SMTP_ADDRESS DISCOURSE_SMTP_USER_NAME DISCOURSE_SMTP_PASSWORD \
543 DISCOURSE_DEVELOPER_EMAILS DISCOURSE_HOSTNAME
544 do
545 config_line=`grep "^ $x:" $config_file`
546 local result=$?
547 local default="example.com"
548
549 if (( result == 0 ))
550 then
551 if [[ $config_line = *"$default"* ]]
552 then
553 echo "$x left at incorrect default of example.com"
554 valid_config="n"
555 fi
556 config_val=`echo $config_line | awk '{print $2}'`
557 if [ -z $config_val ]
558 then
559 echo "$x was left blank"
560 valid_config="n"
561 fi
562 else
563 echo "$x not present"
564 valid_config="n"
565 fi
566 done
567
568 if [ "$valid_config" != "y" ]; then
569 echo -e "\nSorry, these $config_file settings aren't valid -- can't continue!"
570 echo "If you have unusual requirements, edit $config_file and then: "
571 echo "./launcher bootstrap $app_name"
572 exit 1
573 fi
574 }
575
576
577 ##
578 ## template file names
579 ##
580 app_name=app
581 template_path=samples/standalone.yml
582 config_file=containers/$app_name.yml
583 changelog=/tmp/changelog
584
585 ##
586 ## Check requirements before creating a copy of a config file we won't edit
587 ##
588 check_root
589 check_and_install_docker
590 check_disk_and_memory
591 check_ports
592
593 ##
594 ## make a copy of the simple standalone config file
595 ##
596 if [ -a $config_file ]
597 then
598 echo "The configuration file $config_file already exists!"
599 echo
600 echo ". . . reconfiguring . . ."
601 echo
602 else
603 cp $template_path $config_file
604 fi
605
606 scale_ram_and_cpu
607 ask_user_for_config
608 validate_config
609
610 ##
611 ## if we reach this point without exiting, OK to proceed
612 ## rebuild won't fail if there's nothing to rebuild and does the restart
613 ##
614 sleep 5 # Just a chance to ^C in case they were too fast on the draw
615 time ./launcher rebuild $app_name