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