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