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