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