Merge pull request #297 from simongareste/master
[discourse_docker.git] / discourse-setup
1 #!/bin/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 ## Do we have docker?
16 ##
17 check_and_install_docker () {
18 docker_path=`which docker.io || which docker`
19 if [ -z $docker_path ]; then
20 read -p "Docker not installed. Enter to install from https://get.docker.com/ or Ctrl+C to exit"
21 curl https://get.docker.com/ | sh
22 fi
23 docker_path=`which docker.io || which docker`
24 if [ -z $docker_path ]; then
25 echo Docker install failed. Quitting.
26 exit
27 fi
28 }
29
30 ##
31 ## What are we running on
32 ##
33 check_OS() {
34 echo `uname -s`
35 }
36
37 ##
38 ## OS X available memory
39 ##
40 check_osx_memory() {
41 echo `top -l 1 | awk '/PhysMem:/ {print $2}' | sed s/G//`
42 }
43
44 ##
45 ## Linux available memory
46 ##
47 check_linux_memory() {
48 echo `free -g --si | awk ' /Mem:/ {print $2} '`
49 }
50
51 ##
52 ## Do we have enough memory and disk space for Discourse?
53 ##
54 check_disk_and_memory() {
55
56 os_type=$(check_OS)
57 avail_mem=0
58 if [ $os_type == "Darwin" ]; then
59 avail_mem=$(check_osx_memory)
60 else
61 avail_mem=$(check_linux_memory)
62 fi
63
64 if [ "$avail_mem" -lt 1 ]; then
65 echo "WARNING: Discourse requires 1GB RAM to run. This system does not appear"
66 echo "to have sufficient memory."
67 echo
68 echo "Your site may not work properly, or future upgrades of Discourse may not"
69 echo "complete successfully."
70 exit 1
71 fi
72
73 if [ "$avail_mem" -le 2 ]; then
74 total_swap=`free -g --si | awk ' /Swap:/ {print $2} '`
75 if [ "$total_swap" -lt 2 ]; then
76 echo "WARNING: Discourse requires at least 2GB of swap when running with 2GB of RAM"
77 echo "or less. This system does not appear to have sufficient swap space."
78 echo
79 echo "Without sufficient swap space, your site may not work properly, and future"
80 echo "upgrades of Discourse may not complete successfully."
81 echo
82 read -p "ENTER to create a 2GB swapfile now, or Ctrl+C to exit"
83
84 ##
85 ## derived from https://meta.discourse.org/t/13880
86 ##
87 install -o root -g root -m 0600 /dev/null /swapfile
88 dd if=/dev/zero of=/swapfile bs=1k count=2048k
89 mkswap /swapfile
90 swapon /swapfile
91 echo "/swapfile swap swap auto 0 0" | tee -a /etc/fstab
92 sysctl -w vm.swappiness=10
93 echo vm.swappiness = 10 | tee -a /etc/sysctl.conf
94
95 total_swap=`free -g --si | awk ' /Swap:/ {print $2} '`
96 if [ "$total_swap" -lt 2 ]; then
97 echo "Failed to create swap, sorry!"
98 exit 1
99 fi
100
101 fi
102 fi
103
104
105 free_disk="$(df /var | tail -n 1 | awk '{print $4}')"
106 if [ "$free_disk" -lt 5000 ]; then
107 echo "WARNING: Discourse requires at least 5GB free disk space. This system"
108 echo "does not appear to have sufficient disk space."
109 echo
110 echo "Insufficient disk space may result in problems running your site, and"
111 echo "may not even allow Discourse installation to complete successfully."
112 echo
113 echo "Please free up some space, or expand your disk, before continuing."
114 echo
115 echo "Run \`apt-get autoremove && apt-get autoclean\` to clean up unused"
116 echo "packages and \`./launcher cleanup\` to remove stale Docker containers."
117 exit 1
118 fi
119
120 }
121
122
123 ##
124 ## If we have lots of RAM or lots of CPUs, bump up the defaults to scale better
125 ##
126 scale_ram_and_cpu() {
127
128 local changelog=/tmp/changelog.$PPID
129 # grab info about total system ram and physical (NOT LOGICAL!) CPU cores
130 avail_gb=0
131 avail_cores=0
132 os_type=$(check_OS)
133 if [ $os_type == "Darwin" ]; then
134 avail_gb=$(check_osx_memory)
135 avail_cores=`sysctl hw.ncpu | awk '/hw.ncpu:/ {print $2}'`
136 else
137 avail_gb=$(check_linux_memory)
138 avail_cores=`cat /proc/cpuinfo | grep "cpu cores" | uniq | awk '{print $4}'`
139 fi
140 echo "Found ${avail_gb}GB of memory and $avail_cores physical CPU cores"
141
142 # db_shared_buffers: 128MB for 1GB, 256MB for 2GB, or 256MB * GB, max 4096MB
143 if [ "$avail_gb" -eq "1" ]
144 then
145 db_shared_buffers=128
146 else
147 if [ "$avail_gb" -eq "2" ]
148 then
149 db_shared_buffers=256
150 else
151 db_shared_buffers=$(( 256 * $avail_gb ))
152 fi
153 fi
154 db_shared_buffers=$(( db_shared_buffers < 4096 ? db_shared_buffers : 4096 ))
155
156 sed -i -e "s/^ #db_shared_buffers:.*/ db_shared_buffers: \"${db_shared_buffers}MB\"/w $changelog" $config_file
157 if [ -s $changelog ]
158 then
159 echo "setting db_shared_buffers = ${db_shared_buffers}MB"
160 rm $changelog
161 fi
162
163 # UNICORN_WORKERS: 2 * GB for 2GB or less, or 2 * CPU, max 8
164 if [ "$avail_gb" -le "2" ]
165 then
166 unicorn_workers=$(( 2 * $avail_gb ))
167 else
168 unicorn_workers=$(( 2 * $avail_cores ))
169 fi
170 unicorn_workers=$(( unicorn_workers < 8 ? unicorn_workers : 8 ))
171
172 sed -i -e "s/^ #UNICORN_WORKERS:.*/ UNICORN_WORKERS: ${unicorn_workers}/w $changelog" $config_file
173 if [ -s $changelog ]
174 then
175 echo "setting UNICORN_WORKERS = ${unicorn_workers}"
176 rm $changelog
177 fi
178
179 }
180
181
182 ##
183 ## standard http / https ports must not be occupied
184 ##
185 check_ports() {
186 check_port "80"
187 check_port "443"
188 echo "Ports 80 and 443 are free for use"
189 }
190
191
192 ##
193 ## check a port to see if it is already in use
194 ##
195 check_port() {
196
197 local valid=$(netstat -tln | awk '{print $4}' | grep ":${1}\$")
198
199 if [ -n "$valid" ]; then
200 echo "Port ${1} appears to already be in use."
201 echo
202 echo "If you are trying to run Discourse simultaneously with another web"
203 echo "server like Apache or nginx, you will need to bind to a different port"
204 echo
205 echo "See https://meta.discourse.org/t/17247"
206 exit 1
207 fi
208 }
209
210 ##
211 ## prompt user for typical Discourse config file values
212 ##
213 ask_user_for_config() {
214
215 local changelog=/tmp/changelog.$PPID
216 local hostname="discourse.example.com"
217 local developer_emails="me@example.com,you@example.com"
218 local smtp_address="smtp.example.com"
219 local smtp_port="587"
220 local smtp_user_name="postmaster@discourse.example.com"
221 local smtp_password=""
222 local letsencrypt_account_email="me@example.com"
223 local letsencrypt_status="ENTER to skip"
224
225 local new_value=""
226 local config_ok="n"
227 local update_ok="y"
228
229 echo ""
230
231 while [[ "$config_ok" == "n" ]]
232 do
233 if [ ! -z $hostname ]
234 then
235 read -p "Hostname for your Discourse? [$hostname]: " new_value
236 if [ ! -z $new_value ]
237 then
238 hostname=$new_value
239 fi
240 fi
241
242 if [ ! -z $developer_emails ]
243 then
244 read -p "Email address for admin account? [$developer_emails]: " new_value
245 if [ ! -z $new_value ]
246 then
247 developer_emails=$new_value
248 fi
249 fi
250
251 if [ ! -z $smtp_address ]
252 then
253 read -p "SMTP server address? [$smtp_address]: " new_value
254 if [ ! -z $new_value ]
255 then
256 smtp_address=$new_value
257 fi
258 fi
259
260 if [ ! -z $smtp_port ]
261 then
262 read -p "SMTP port? [$smtp_port]: " new_value
263 if [ ! -z $new_value ]
264 then
265 smtp_port=$new_value
266 fi
267 fi
268
269 ##
270 ## automatically set correct user name based on common mail providers
271 ##
272 if [ "$smtp_address" == "smtp.sparkpostmail.com" ]
273 then
274 smtp_user_name="SMTP_Injection"
275 fi
276 if [ "$smtp_address" == "smtp.sendgrid.net" ]
277 then
278 smtp_user_name="apikey"
279 fi
280 if [ "$smtp_address" == "smtp.mailgun.org" ]
281 then
282 smtp_user_name="postmaster@$hostname"
283 fi
284
285 if [ ! -z $smtp_user_name ]
286 then
287 read -p "SMTP user name? [$smtp_user_name]: " new_value
288 if [ ! -z $new_value ]
289 then
290 smtp_user_name=$new_value
291 fi
292 fi
293
294 read -p "SMTP password? [$smtp_password]: " new_value
295 if [ ! -z $new_value ]
296 then
297 smtp_password=$new_value
298 fi
299
300 if [ ! -z $letsencrypt_account_email ]
301 then
302 read -p "Let's Encrypt account email? ($letsencrypt_status) [$letsencrypt_account_email]: " new_value
303 if [ ! -z $new_value ]
304 then
305 letsencrypt_account_email=$new_value
306 if [ "$new_value" == "off" ]
307 then
308 letsencrypt_status="ENTER to skip"
309 else
310 letsencrypt_status="Enter 'OFF' to disable."
311 fi
312 fi
313 fi
314
315 echo -e "\nDoes this look right?\n"
316 echo "Hostname : $hostname"
317 echo "Email : $developer_emails"
318 echo "SMTP address : $smtp_address"
319 echo "SMTP port : $smtp_port"
320 echo "SMTP username : $smtp_user_name"
321 echo "SMTP password : $smtp_password"
322
323 if [ "$letsencrypt_status" == "Enter 'OFF' to disable." ]
324 then
325 echo "Let's Encrypt : $letsencrypt_account_email"
326 fi
327
328 echo ""
329 read -p "ENTER to continue, 'n' to try again, Ctrl+C to exit: " config_ok
330 done
331
332 sed -i -e "s/^ DISCOURSE_HOSTNAME:.*/ DISCOURSE_HOSTNAME: $hostname/w $changelog" $config_file
333 if [ -s $changelog ]
334 then
335 rm $changelog
336 else
337 echo "DISCOURSE_HOSTNAME change failed."
338 update_ok="n"
339 fi
340
341 sed -i -e "s/^ DISCOURSE_DEVELOPER_EMAILS:.*/ DISCOURSE_DEVELOPER_EMAILS: \'$developer_emails\'/w $changelog" $config_file
342 if [ -s $changelog ]
343 then
344 rm $changelog
345 else
346 echo "DISCOURSE_DEVELOPER_EMAILS change failed."
347 update_ok="n"
348 fi
349
350 sed -i -e "s/^ DISCOURSE_SMTP_ADDRESS:.*/ DISCOURSE_SMTP_ADDRESS: $smtp_address/w $changelog" $config_file
351 if [ -s $changelog ]
352 then
353 rm $changelog
354 else
355 echo "DISCOURSE_SMTP_ADDRESS change failed."
356 update_ok="n"
357 fi
358
359 sed -i -e "s/^ #DISCOURSE_SMTP_PORT:.*/ DISCOURSE_SMTP_PORT: $smtp_port/w $changelog" $config_file
360 if [ -s $changelog ]
361 then
362 rm $changelog
363 else
364 echo "DISCOURSE_SMTP_PORT change failed."
365 update_ok="n"
366 fi
367
368 sed -i -e "s/^ #DISCOURSE_SMTP_USER_NAME:.*/ DISCOURSE_SMTP_USER_NAME: $smtp_user_name/w $changelog" $config_file
369 if [ -s $changelog ]
370 then
371 rm $changelog
372 else
373 echo "DISCOURSE_SMTP_USER_NAME change failed."
374 update_ok="n"
375 fi
376
377 sed -i -e "s/^ #DISCOURSE_SMTP_PASSWORD:.*/ DISCOURSE_SMTP_PASSWORD: \"${smtp_password/\//\\/}\"/w $changelog" $config_file
378 if [ -s $changelog ]
379 then
380 rm $changelog
381 else
382 echo "DISCOURSE_SMTP_PASSWORD change failed."
383 update_ok="n"
384 fi
385
386 if [ "$letsencrypt_status" != "ENTER to skip" ]
387 then
388 sed -i -e "s/^ #LETSENCRYPT_ACCOUNT_EMAIL:.*/ LETSENCRYPT_ACCOUNT_EMAIL: $letsencrypt_account_email/w $changelog" $config_file
389 if [ -s $changelog ]
390 then
391 rm $changelog
392 else
393 echo "LETSENCRYPT_ACCOUNT_EMAIL change failed."
394 update_ok="n"
395 fi
396 local src='^ #- "templates\/web.ssl.template.yml"'
397 local dst=' \- "templates\/web.ssl.template.yml"'
398 sed -i -e "s/$src/$dst/w $changelog" $config_file
399 if [ -s $changelog ]
400 then
401 echo "web.ssl.template.yml enabled"
402 else
403 update_ok="n"
404 echo "web.ssl.template.yml NOT ENABLED--was it on already?"
405 fi
406 local src='^ #- "templates\/web.letsencrypt.ssl.template.yml"'
407 local dst=' - "templates\/web.letsencrypt.ssl.template.yml"'
408
409 sed -i -e "s/$src/$dst/w $changelog" $config_file
410 if [ -s $changelog ]
411 then
412 echo "letsencrypt.ssl.template.yml enabled"
413 else
414 update_ok="n"
415 echo "letsencrypt.ssl.template.yml NOT ENABLED -- was it on already?"
416 fi
417 fi
418
419 if [ "$update_ok" == "y" ]
420 then
421 echo -e "\nConfiguration file at $config_file updated successfully!\n"
422 else
423 echo -e "\nUnfortunately, there was an error changing $config_file\n"
424 exit 1
425 fi
426 }
427
428 ##
429 ## is our config file valid? Does it have the required fields set?
430 ##
431 validate_config() {
432
433 valid_config="y"
434
435 for x in DISCOURSE_SMTP_ADDRESS DISCOURSE_SMTP_USER_NAME DISCOURSE_SMTP_PASSWORD \
436 DISCOURSE_DEVELOPER_EMAILS DISCOURSE_HOSTNAME
437 do
438 config_line=`grep "^ $x:" $config_file`
439 local result=$?
440 local default="example.com"
441
442 if (( result == 0 ))
443 then
444 if [[ $config_line = *"$default"* ]]
445 then
446 echo "$x left at incorrect default of example.com"
447 valid_config="n"
448 fi
449 config_val=`echo $config_line | awk '{print $2}'`
450 if [ -z $config_val ]
451 then
452 echo "$x was left blank"
453 valid_config="n"
454 fi
455 else
456 echo "$x not present"
457 valid_config="n"
458 fi
459 done
460
461 if [ "$valid_config" != "y" ]; then
462 echo -e "\nSorry, these $config_file settings aren't valid -- can't continue!"
463 echo "If you have unusual requirements, edit $config_file and then: "
464 echo "./launcher bootstrap $app_name"
465 exit 1
466 fi
467 }
468
469
470 ##
471 ## template file names
472 ##
473 app_name=app
474 template_path=samples/standalone.yml
475 config_file=containers/$app_name.yml
476 changelog=/tmp/changelog
477
478 ##
479 ## Check requirements before creating a copy of a config file we won't edit
480 ##
481 check_root
482 check_and_install_docker
483 check_disk_and_memory
484 check_ports
485
486 ##
487 ## make a copy of the simple standalone config file
488 ##
489 if [ -a $config_file ]
490 then
491 echo "The configuration file $config_file already exists!"
492 echo ""
493 echo "If you want to delete your old configuration file and start over:"
494 echo "rm $config_file"
495 exit 1
496 else
497 cp $template_path $config_file
498 fi
499
500 scale_ram_and_cpu
501 ask_user_for_config
502 validate_config
503
504 ##
505 ## if we reach this point without exiting, OK to proceed
506 ##
507 ./launcher bootstrap $app_name && ./launcher start $app_name