Merge pull request #246 from pfaffman/no-more-nano
[discourse_docker.git] / launcher
1 #!/bin/bash
2
3 usage () {
4 echo "Usage: launcher COMMAND CONFIG [--skip-prereqs] [--skip-discourse-prereqs] [--docker-args STRING]"
5 echo "Commands:"
6 echo " start: Start/initialize a container"
7 echo " stop: Stop a running container"
8 echo " restart: Restart a container"
9 echo " destroy: Stop and remove a container"
10 echo " enter: Use nsenter to enter a container"
11 echo " logs: Docker logs for container"
12 echo " setup: Create a configuration file"
13 echo " bootstrap: Bootstrap a container for the config based on a template"
14 echo " rebuild: Rebuild a container (destroy old, bootstrap, start new)"
15 echo " cleanup: Remove all containers that have stopped for > 24 hours"
16 echo
17 echo "Options:"
18 echo " --skip-prereqs Don't check launcher prerequisites"
19 echo " --skip-discourse-prereqs Don't check prerequisites specifiy to Discourse"
20 echo " --docker-args Extra arguments to pass when running docker"
21 exit 1
22 }
23
24 [ $# -lt 2 ] && {
25 usage
26 }
27
28 command=$1
29 config=$2
30 shift 2
31 user_args=""
32
33 while [ ${#} -gt 0 ]; do
34 case "${1}" in
35
36 --skip-prereqs)
37 SKIP_PREREQ="1"
38 ;;
39 --skip-discourse-prereqs)
40 SKIP_DISCOURSE_PREREQS="1"
41 ;;
42 --docker-args)
43 user_args="$2"
44 shift
45 ;;
46 *)
47 echo "Unknown options '${1}'"
48 usage
49 ;;
50 esac
51
52 shift 1
53 done
54
55 # Docker doesn't like uppercase characters, spaces or special characters, catch it now before we build everything out and then find out
56 re='[A-Z/ !@#$%^&*()+~`=]'
57 if [[ $config =~ $re ]];
58 then
59 echo
60 echo "ERROR: Config name must not contain upper case characters, spaces or special characters. Correct config name and rerun $0."
61 echo
62 exit 1
63 fi
64
65 cd "$(dirname "$0")"
66
67 docker_min_version='1.6.0'
68 docker_rec_version='1.6.0'
69 git_min_version='1.8.0'
70 git_rec_version='1.8.0'
71
72 config_file=containers/"$config".yml
73 cidbootstrap=cids/"$config"_bootstrap.cid
74 local_discourse=local_discourse
75 image=discourse/discourse:1.0.17
76 docker_path=`which docker.io || which docker`
77 git_path=`which git`
78 template_path=samples/standalone.yml
79 changelog=/tmp/changelog # used to test whether sed did anything
80
81 if [ "${SUPERVISED}" = "true" ]; then
82 restart_policy="--restart=no"
83 attach_on_start="-a"
84 attach_on_run="-a stdout -a stderr"
85 else
86 attach_on_run="-d"
87 fi
88
89 if [ -n "$DOCKER_HOST" ]; then
90 docker_ip=`sed -e 's/^tcp:\/\/\(.*\):.*$/\1/' <<< "$DOCKER_HOST"`
91 elif [ -x "$(which ip 2>/dev/null)" ]; then
92 docker_ip=`ip addr show docker0 | \
93 grep 'inet ' | \
94 awk '{ split($2,a,"/"); print a[1] }';`
95 else
96 docker_ip=`ifconfig | \
97 grep -B1 "inet addr" | \
98 awk '{ if ( $1 == "inet" ) { print $2 } else if ( $2 == "Link" ) { printf "%s:" ,$1 } }' | \
99 grep docker0 | \
100 awk -F: '{ print $3 }';`
101 fi
102
103 compare_version() {
104 declare -a ver_a
105 declare -a ver_b
106 IFS=. read -a ver_a <<< "$1"
107 IFS=. read -a ver_b <<< "$2"
108
109 while [[ -n $ver_a ]]; do
110 if (( ver_a > ver_b )); then
111 return 0
112 elif (( ver_b > ver_a )); then
113 return 1
114 else
115 unset ver_a[0]
116 ver_a=("${ver_a[@]}")
117 unset ver_b[0]
118 ver_b=("${ver_b[@]}")
119 fi
120 done
121 return 1 # They are equal
122 }
123
124
125 install_docker() {
126
127 echo "Docker is not installed, you will need to install Docker in order to run Discourse"
128 echo "Please visit https://docs.docker.com/installation/ for instructions on how to do this for your system"
129 echo
130 echo "If you are running a recent Ubuntu Server, try the following:"
131 echo "sudo apt-get install docker-engine"
132
133 exit 1
134 }
135
136 prereqs() {
137
138 if [ -z $docker_path ]; then
139 install_docker
140 fi
141
142 # 1. docker daemon running?
143 # we send stderr to /dev/null cause we don't care about warnings,
144 # it usually complains about swap which does not matter
145 test=`$docker_path info 2> /dev/null`
146 if [[ $? -ne 0 ]] ; then
147 echo "Cannot connect to the docker daemon - verify it is running and you have access"
148 exit 1
149 fi
150
151 # 2. running aufs or btrfs
152 test=`$docker_path info 2> /dev/null | grep 'Driver: '`
153 if [[ "$test" =~ [aufs|btrfs|zfs|overlay] ]] ; then : ; else
154 echo "Your Docker installation is not using a supported filesystem if we were to proceed you may have a broken install."
155 echo "aufs is the recommended filesystem you should be using (zfs/btrfs and overlay may work as well)"
156 echo "You can tell what filesystem you are using by running \"docker info\" and looking at the driver"
157 echo
158 echo "If you wish to continue anyway using your existing unsupported filesystem, "
159 echo "read the source code of launcher and figure out how to bypass this."
160 exit 1
161 fi
162
163 # 3. running recommended docker version
164 test=($($docker_path --version)) # Get docker version string
165 test=${test[2]//,/} # Get version alone and strip comma if exists
166
167 # At least minimum version
168 if compare_version "${docker_min_version}" "${test}"; then
169 echo "ERROR: Docker version ${test} not supported, please upgrade to at least ${docker_min_version}, or recommended ${docker_rec_version}"
170 exit 1
171 fi
172
173 # Recommend best version
174 if compare_version "${docker_rec_version}" "${test}"; then
175 echo "WARNING: Docker version ${test} deprecated, recommend upgrade to ${docker_rec_version} or newer."
176 fi
177
178 # 4. discourse docker image is downloaded
179 test=`$docker_path images | awk '{print $1 ":" $2 }' | grep "$image"`
180
181 if [ -z "$test" ]; then
182 echo
183 echo "WARNING: We are about to start downloading the Discourse base image"
184 echo "This process may take anywhere between a few minutes to an hour, depending on your network speed"
185 echo
186 echo "Please be patient"
187 echo
188
189 fi
190
191 # 5. running recommended git version
192 test=($($git_path --version)) # Get git version string
193 test=${test[2]//,/} # Get version alone and strip comma if exists
194
195 # At least minimum version
196 if compare_version "${git_min_version}" "${test}"; then
197 echo "ERROR: Git version ${test} not supported, please upgrade to at least ${git_min_version}, or recommended ${git_rec_version}"
198 exit 1
199 fi
200
201 # Recommend best version
202 if compare_version "${git_rec_version}" "${test}"; then
203 echo "WARNING: Git version ${test} deprecated, recommend upgrade to ${git_rec_version} or newer."
204 fi
205
206 # 6. able to attach stderr / out / tty
207 test=`$docker_path run $user_args -i --rm -a stdout -a stderr $image echo working`
208 if [[ "$test" =~ "working" ]] ; then : ; else
209 echo "Your Docker installation is not working correctly"
210 echo
211 echo "See: https://meta.discourse.org/t/docker-error-on-bootstrap/13657/18?u=sam"
212 exit 1
213 fi
214
215 }
216
217
218
219 check_resources() {
220 # Memory
221 resources="ok"
222 avail_mem="$(LANG=C free -m | grep '^Mem:' | awk '{print $2}')"
223 if [ "$avail_mem" -lt 900 ]; then
224 resources="insufficient"
225 echo "WARNING: You do not appear to have sufficient memory to run Discourse."
226 echo
227 echo "Your system may not work properly, or future upgrades of Discourse may"
228 echo "not complete successfully."
229 echo
230 echo "See https://github.com/discourse/discourse/blob/master/docs/INSTALL-cloud.md#create-new-cloud-server"
231 elif [ "$avail_mem" -lt 1800 ]; then
232 total_swap="$(LANG=C free -m | grep ^Swap: | awk '{print $2}')"
233 if [ "$total_swap" -lt 1000 ]; then
234 resources="insufficient"
235 echo "WARNING: You must have at least 1GB of swap when running with less"
236 echo "than 2GB of RAM."
237 echo
238 echo "Your system may not work properly, or future upgrades of Discourse may"
239 echo "not complete successfully."
240 echo
241 echo "See https://github.com/discourse/discourse/blob/master/docs/INSTALL-cloud.md#set-up-swap-if-needed"
242 fi
243 fi
244
245 # Disk space
246 free_disk="$(df /var | tail -n 1 | awk '{print $4}')"
247 if [ "$free_disk" -lt 5000 ]; then
248 resources="insufficient"
249 echo "WARNING: You must have at least 5GB of *free* disk space to run Discourse."
250 echo
251 echo "Insufficient disk space may result in problems running your site, and may"
252 echo "not even allow Discourse installation to complete successfully."
253 echo
254 echo "Please free up some space, or expand your disk, before continuing."
255 echo
256 echo "Run \`apt-get autoremove && apt-get autoclean\` to clean up unused packages and \`./launcher cleanup\` to remove stale Docker containers."
257 exit 1
258 fi
259
260 if [ -t 0 ] && [ "$resources" != "ok" ]; then
261 echo
262 read -p "Press ENTER to continue, or Ctrl-C to exit and give your system more resources"
263 fi
264 }
265
266 check_ports() {
267 local valid=$(netstat -tln | awk '{print $4}' | grep ":${1}\$")
268
269 if [ -n "$valid" ]; then
270 echo "Launcher has detected that port ${1} is in use."
271 echo
272 echo "If you are trying to run Discourse simultaneously with another web server like Apache or nginx, you will need to bind to a different port."
273 echo "See https://meta.discourse.org/t/17247 for help."
274 echo "To continue anyway, re-run Launcher with --skip-prereqs"
275 exit 1
276 fi
277 }
278
279 if [ -z "$SKIP_PREREQS" ] ; then
280 prereqs
281 fi
282
283 host_run() {
284 read -r -d '' env_ruby << 'RUBY'
285 require 'yaml'
286
287 input = STDIN.readlines.join
288 yaml = YAML.load(input)
289
290 if host_run = yaml['host_run']
291 params = yaml['params'] || {}
292 host_run.each do |run|
293 params.each do |k,v|
294 run = run.gsub("$#{k}", v)
295 end
296 STDOUT.write "#{run}--SEP--"
297 end
298 end
299 RUBY
300
301 host_run=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e "$env_ruby"`
302
303 while [ "$host_run" ] ; do
304 iter=${host_run%%--SEP--*}
305 echo
306 echo "Host run: $iter"
307 $iter || exit 1
308 echo
309 host_run="${host_run#*--SEP--}"
310 done
311 }
312
313
314 set_volumes() {
315 volumes=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \
316 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['volumes'].map{|v| '-v ' << v['volume']['host'] << ':' << v['volume']['guest'] << ' '}.join"`
317 }
318
319 set_links() {
320 links=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \
321 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['links'].map{|l| '--link ' << l['link']['name'] << ':' << l['link']['alias'] << ' '}.join"`
322 }
323
324 set_template_info() {
325
326 templates=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
327 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['templates']"`
328
329 arrTemplates=(${templates// / })
330 config_data=$(cat $config_file)
331
332 input="hack: true"
333
334 for template in "${arrTemplates[@]}"
335 do
336 [ ! -z $template ] && {
337 input="$input _FILE_SEPERATOR_ $(cat $template)"
338 }
339 done
340
341 # we always want our config file last so it takes priority
342 input="$input _FILE_SEPERATOR_ $config_data"
343
344 read -r -d '' env_ruby << 'RUBY'
345 require 'yaml'
346
347 input=STDIN.readlines.join
348 # default to UTF-8 for the dbs sake
349 env = {'LANG' => 'en_US.UTF-8'}
350 input.split('_FILE_SEPERATOR_').each do |yml|
351 yml.strip!
352 begin
353 env.merge!(YAML.load(yml)['env'] || {})
354 rescue Psych::SyntaxError => e
355 puts e
356 puts "*ERROR."
357 rescue => e
358 puts yml
359 p e
360 end
361 end
362 puts env.map{|k,v| "-e\n#{k}=#{v}" }.join("\n")
363 RUBY
364
365 raw=`exec echo "$input" | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e "$env_ruby"`
366
367 env=()
368 ok=1
369 while read i; do
370 if [ "$i" == "*ERROR." ]; then
371 ok=0
372 elif [ -n "$i" ]; then
373 env[${#env[@]}]=$i
374 fi
375 done <<< "$raw"
376
377 if [ "$ok" -ne 1 ]; then
378 echo "${env[@]}"
379 echo "YAML syntax error. Please check your /var/discourse/containers/*.yml config files."
380 exit 1
381 fi
382 }
383
384 if [ -z $docker_path ]; then
385 install_docker
386 fi
387
388 [ "$command" == "cleanup" ] && {
389 echo
390 echo "The following command will"
391 echo "- Delete all docker images for old containers"
392 echo "- Delete all stopped and orphan containers"
393 echo
394 read -p "Are you sure (Y/n): " -n 1 -r && echo
395 if [[ $REPLY =~ ^[Yy]$ || ! $REPLY ]]
396 then
397 space=$(df /var/lib/docker | awk '{ print $4 }' | grep -v Available)
398 echo "Starting Cleanup (bytes free $space)"
399
400 STATE_DIR=./.gc-state scripts/docker-gc
401
402 space=$(df /var/lib/docker | awk '{ print $4 }' | grep -v Available)
403 echo "Finished Cleanup (bytes free $space)"
404
405 else
406 exit 1
407 fi
408 exit 0
409 }
410
411 if [[ ! -e $config_file ]]
412 then
413 echo "Config file was not found, ensure $config_file exists"
414 echo
415 echo "Available configs ( `cd containers && ls -dm *.yml | tr -s '\n' ' ' | awk '{ gsub(/\.yml/, ""); print }'`)"
416 exit 1
417 fi
418
419 docker_version=($($docker_path --version))
420 docker_version=${test[2]//,/}
421 restart_policy=${restart_policy:---restart=always}
422
423 set_existing_container(){
424 existing=`$docker_path ps -a | awk '{ print $1, $(NF) }' | grep " $config$" | awk '{ print $1 }'`
425 }
426
427 run_stop() {
428
429 set_existing_container
430
431 if [ ! -z $existing ]
432 then
433 (
434 set -x
435 $docker_path stop -t 10 $config
436 )
437 else
438 echo "$config was not started !"
439 exit 1
440 fi
441 }
442
443 set_run_image() {
444 run_image=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
445 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['run_image']"`
446
447 if [ -z "$run_image" ]; then
448 run_image="$local_discourse/$config"
449 fi
450 }
451
452 set_boot_command() {
453 boot_command=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
454 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['boot_command']"`
455
456 if [ -z "$boot_command" ]; then
457
458 no_boot_command=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
459 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['no_boot_command']"`
460
461 if [ -z "$no_boot_command" ]; then
462 boot_command="/sbin/boot"
463 fi
464 fi
465 }
466
467 scale_ram_and_cpu() {
468
469 # grab info about total system ram and physical (NOT LOGICAL!) CPU cores
470 avail_mem="$(LANG=C free -m | grep '^Mem:' | awk '{print $2}')"
471 avail_gb=$(( $avail_mem / 950 ))
472 avail_cores=`cat /proc/cpuinfo | grep "cpu cores" | uniq | awk '{print $4}'`
473 echo "Found ${avail_gb}GB of memory and $avail_cores physical CPU cores"
474
475 # db_shared_buffers: 128MB for 1GB, 256MB for 2GB, or 256MB * GB, max 4096MB
476 if [ "$avail_gb" -eq "1" ]
477 then
478 db_shared_buffers=128
479 else
480 if [ "$avail_gb" -eq "2" ]
481 then
482 db_shared_buffers=256
483 else
484 db_shared_buffers=$(( 256 * $avail_gb ))
485 fi
486 fi
487 db_shared_buffers=$(( db_shared_buffers < 4096 ? db_shared_buffers : 4096 ))
488
489 sed -i -e "s/^ #db_shared_buffers:.*/ db_shared_buffers: \"${db_shared_buffers}MB\"/w $changelog" $config_file
490 if [ -s $changelog ]
491 then
492 echo "setting db_shared_buffers = ${db_shared_buffers}MB based on detected CPU/RAM"
493 rm $changelog
494 fi
495
496
497 # UNICORN_WORKERS: 2 * GB for 2GB or less, or 2 * CPU, max 8
498 if [ "$avail_gb" -le "2" ]
499 then
500 unicorn_workers=$(( 2 * $avail_gb ))
501 else
502 unicorn_workers=$(( 2 * $avail_cores ))
503 fi
504 unicorn_workers=$(( unicorn_workers < 8 ? unicorn_workers : 8 ))
505
506 sed -i -e "s/^ #UNICORN_WORKERS:.*/ UNICORN_WORKERS: ${unicorn_workers}/w $changelog" $config_file
507 if [ -s $changelog ]
508 then
509 echo "setting UNICORN_WORKERS = ${unicorn_workers} based on detected CPU/RAM"
510 rm $changelog
511 fi
512
513 }
514
515 set_config() {
516 if [ -f $config_file ]
517 then
518 echo $config_file exists already.
519 echo To remove it use: rm $config_file
520 exit 1
521 fi
522 cp ./samples/standalone.yml $config_file
523 if [ ! -f $config_file ]
524 then
525 echo Unable to copy $config_file. Are you root?
526 exit 1
527 fi
528
529 local hostname="discourse.example.com"
530 local developer_emails="me@example.com"
531 local smtp_address="smtp.example.com"
532 local smtp_user_name="user@example.com"
533 local smtp_password="pa\$\$word"
534 local letsencrypt_account_email="your.email@example.com"
535 local letsencrypt_status="change to enable"
536
537 local new_value=""
538 local letsencrypt_status="change to enable"
539 local config_sane="n"
540 local config_ok="n"
541 local update_ok="y"
542
543 while [[ "$config_ok" == "n" || "$config_sane" == "n" ]]
544 do
545 if [ ! -z $hostname ]
546 then
547 read -p "hostname: [$hostname]: " new_value
548 if [ ! -z $new_value ]
549 then
550 hostname=$new_value
551 else
552 echo "Unchanged."
553 fi
554 fi
555 if [ ! -z $developer_emails ]
556 then
557 read -p "developer_emails [$developer_emails]: " new_value
558 if [ ! -z $new_value ]
559 then
560 developer_emails=$new_value
561 fi
562 fi
563 if [ ! -z $smtp_address ]
564 then
565 read -p "smtp_address [$smtp_address]: " new_value
566 if [ ! -z $new_value ]
567 then
568 smtp_address=$new_value
569 fi
570 fi
571 if [ "$smtp_address" == "smtp.sparkpostmail.com" ]
572 then
573 smtp_user_name="SMTP_Injection"
574
575 fi
576 if [ "$smtp_address" == "smtp.sendgrid.net" ]
577 then
578 smtp_user_name="apikey"
579 fi
580 if [ ! -z $smtp_user_name ]
581 then
582 read -p "smtp_user_name [$smtp_user_name]: " new_value
583 if [ ! -z $new_value ]
584 then
585 smtp_user_name=$new_value
586 fi
587 fi
588 if [ ! -z $smtp_password ]
589 then
590 read -p "smtp_password [$smtp_password]: " new_value
591 if [ ! -z $new_value ]
592 then
593 smtp_password=$new_value
594 fi
595 fi
596 if [ ! -z $letsencrypt_account_email ]
597 then
598 read -p "letsencrypt_account_email ($letsencrypt_status) [$letsencrypt_account_email]: " new_value
599 if [ ! -z $new_value ]
600 then
601 letsencrypt_account_email=$new_value
602 if [ "$new_value" == "off" ]
603 then
604 letsencrypt_status="change to enable"
605 else
606 letsencrypt_status="Enter 'OFF' to disable."
607 echo "Letsencrypt enabled."
608 fi
609 else
610 echo "letsencrypt unchanged"
611 fi
612 fi
613
614 #TODO sanity check these values. For now we trust the user's input.
615 config_sane="y"
616
617 if [ "$config_sane" == "y" ]
618 then
619 echo -e "\nThat's it! Everything is set. Read carefully before continuing.\n"
620 else
621 echo "Errors found in settings"
622 fi
623
624 echo "DISCOURSE_HOSTNAME: $hostname"
625 echo "DISCOURSE_DEVELOPER_EMAILS: $developer_emails"
626 echo "DISCOURSE_SMTP_ADDRESS: $smtp_address"
627 echo "DISCOURSE_SMTP_USER_NAME: $smtp_user_name"
628 echo "DISCOURSE_SMTP_PASSWORD: $smtp_password"
629 if [ "$letsencrypt_status" == "Enter 'OFF' to disable." ]
630 then
631 echo "LETSENCRYPT_ACCOUNT_EMAIL: $letsencrypt_account_email"
632 echo "LETSENCRYPT will be enabled."
633 else
634 echo "LETSENCRYPT will not be enabled."
635 fi
636 echo
637 read -p "Enter to write these settings to $config_file, 'N' to retry, or ^C to start again: " config_ok
638 done
639
640 echo -e "\nWriting changes to $config_file:"
641 sed -i -e "s/^ DISCOURSE_HOSTNAME: 'discourse.example.com'/ DISCOURSE_HOSTNAME: $hostname/w $changelog" $config_file
642 if [ -s $changelog ]
643 then
644 cat $changelog
645 rm $changelog
646 else
647 echo DISCOURSE_HOSTNAME change failed.
648 update_ok="n"
649 fi
650
651 sed -i -e "s/^ DISCOURSE_DEVELOPER_EMAILS:.*/ DISCOURSE_DEVELOPER_EMAILS: \'$developer_emails\'/w $changelog" $config_file
652 if [ -s $changelog ]
653 then
654 cat $changelog
655 rm $changelog
656 else
657 echo DISCOURSE_DEVELOPER_EMAILS change failed.
658 update_ok="n"
659 fi
660
661 sed -i -e "s/^ DISCOURSE_SMTP_ADDRESS: smtp.example.com.*/ DISCOURSE_SMTP_ADDRESS: $smtp_address/w $changelog" $config_file
662 if [ -s $changelog ]
663 then
664 cat $changelog
665 rm $changelog
666 else
667 echo DISCOURSE_SMTP_ADDRESS change failed.
668 update_ok="n"
669 fi
670
671 sed -i -e "s/^ #DISCOURSE_SMTP_USER_NAME: user@example.com.*/ DISCOURSE_SMTP_USER_NAME: $smtp_user_name/w $changelog" $config_file
672 if [ -s $changelog ]
673 then
674 cat $changelog
675 rm $changelog
676 else
677 echo DISCOURSE_SMTP_USER_NAME change failed.
678 update_ok="n"
679 fi
680
681 sed -i -e "s/^ #DISCOURSE_SMTP_PASSWORD: pa\$\$word.*/ DISCOURSE_SMTP_PASSWORD: $smtp_password/w $changelog" $config_file
682 if [ -s $changelog ]
683 then
684 cat $changelog
685 rm $changelog
686 else
687 echo DISCOURSE_SMTP_PASSWORD change failed.
688 update_ok="n"
689 fi
690
691 if [ "$letsencrypt_status" != "change to enable" ]
692 then
693 sed -i -e "s/^ #LETSENCRYPT_ACCOUNT_EMAIL: your.email@example.com/ LETSENCRYPT_ACCOUNT_EMAIL: $letsencrypt_account_email/w $changelog" $config_file
694 if [ -s $changelog ]
695 then
696 cat $changelog
697 rm $changelog
698 else
699 echo LETSENCRYPT_ACCOUNT_EMAIL change failed.
700 update_ok="n"
701 fi
702 local src='^ #- "templates\/web.ssl.template.yml"'
703 local dst=' \- "templates\/web.ssl.template.yml"'
704 sed -i -e "s/$src/$dst/w $changelog" $config_file
705 if [ -s $changelog ]
706 then
707 echo " web.ssl.template.yml enabled"
708 else
709 update_ok="n"
710 echo " web.ssl.template.yml NOT ENABLED--was it on already?"
711 fi
712 local src='^ #- "templates\/web.letsencrypt.ssl.template.yml"'
713 local dst=' - "templates\/web.letsencrypt.ssl.template.yml"'
714
715 sed -i -e "s/$src/$dst/w $changelog" $config_file
716 if [ -s $changelog ]
717 then
718 echo " letsencrypt.ssl.template.yml enabled"
719 else
720 update_ok="n"
721 echo "letsencrypt.ssl.template.yml NOT ENABLED--was it on already?"
722 fi
723 fi # enable letsencrypt
724
725 if [ "$update_ok" == "y" ]
726 then
727 echo -e "\n$config_file updated successfully."
728 else
729 echo -e "There was an error changing the configuration.\n"
730 fi
731 }
732
733 run_start() {
734
735 existing=`$docker_path ps | awk '{ print $1, $(NF) }' | grep " $config$" | awk '{ print $1 }'`
736 echo $existing
737 if [ ! -z $existing ]
738 then
739 echo "Nothing to do, your container has already started!"
740 exit 0
741 fi
742
743 existing=`$docker_path ps -a | awk '{ print $1, $(NF) }' | grep " $config$" | awk '{ print $1 }'`
744 if [ ! -z $existing ]
745 then
746 echo "starting up existing container"
747 (
748 set -x
749 $docker_path start $config
750 )
751 exit 0
752 fi
753
754 host_run
755
756 if [ -z "$SKIP_DISCOURSE_PREREQS" ] ; then
757 ports=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \
758 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['expose'].map{|p| \"-p #{p}\"}.join(' ')"`
759
760 IFS='-p ' read -a array <<< "$ports"
761 for element in "${array[@]}"
762 do
763 IFS=':' read -a args <<< "$element"
764 if [ "${#args[@]}" == "2" ]; then
765 check_ports "${args[0]}"
766 elif [ "${#args[@]}" == "3" ]; then
767 check_ports "${args[1]}"
768 fi
769 done
770 fi
771
772 docker_args=`cat $config_file | $docker_path run $user_args --rm -i -a stdout -a stdin $image ruby -e \
773 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['docker_args']"`
774
775 set_template_info
776 set_volumes
777 set_links
778 set_run_image
779 set_boot_command
780
781 # get hostname and settings from container configuration
782 for envar in "${env[@]}"
783 do
784 if [[ $envar == DOCKER_USE_HOSTNAME* ]] || [[ $envar == DISCOURSE_HOSTNAME* ]]
785 then
786 # use as environment variable
787 eval $envar
788 fi
789 done
790
791 (
792 hostname=`hostname -s`
793 # overwrite hostname
794 if [ "$DOCKER_USE_HOSTNAME" = "true" ]
795 then
796 hostname=$DISCOURSE_HOSTNAME
797 else
798 hostname=$hostname-$config
799 fi
800
801 # we got to normalize so we only have allowed strings, this is more comprehensive but lets see how bash does first
802 # hostname=`$docker_path run $user_args --rm $image ruby -e 'print ARGV[0].gsub(/[^a-zA-Z-]/, "-")' $hostname`
803 # docker added more hostname rules
804 hostname=${hostname/_/-}
805
806 set -x
807 $docker_path run $user_args $links $attach_on_run $restart_policy "${env[@]}" -h "$hostname" \
808 -e DOCKER_HOST_IP=$docker_ip --name $config -t $ports $volumes $docker_args $run_image $boot_command
809
810 )
811 exit 0
812
813 }
814
815 valid_config_check() {
816
817 valid_config="y"
818 for x in DISCOURSE_SMTP_ADDRESS DISCOURSE_SMTP_USER_NAME DISCOURSE_SMTP_PASSWORD \
819 DISCOURSE_DEVELOPER_EMAILS DISCOURSE_HOSTNAME
820 do
821 mail_var=`grep "^ $x:" $config_file`
822 local result=$?
823 local default="example.com"
824 if (( result == 0 ))
825 then
826 if [[ $mail_var = *"$default"* ]]
827 then
828 echo "Warning: $x left at incorrect default of example.com"
829 valid_config="n"
830 fi
831 else
832 echo "Warning: $x not configured"
833 valid_config="n"
834 fi
835 done
836 if [ -t 0 ] && [ "$valid_config" != "y" ]; then
837 echo
838 read -p "Press Ctrl-C to exit and edit $config_file or ENTER to continue"
839 fi
840 }
841
842 run_bootstrap() {
843 if [ -z "$SKIP_DISCOURSE_PREREQS" ] ; then
844 # Does your system meet the minimum requirements?
845 check_resources
846
847 # is our configuration file valid?
848 valid_config_check
849
850 # make minor scaling adjustments for RAM and CPU
851 scale_ram_and_cpu
852 fi
853
854 # I got no frigging clue what this does, ask Sam Saffron. It RUNS STUFF ON THE HOST I GUESS?
855 host_run
856
857 # Is the image available?
858 # If not, pull it here so the user is aware what's happening.
859 $docker_path history $image >/dev/null 2>&1 || $docker_path pull $image
860
861 set_template_info
862
863 base_image=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
864 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['base_image']"`
865
866 update_pups=`cat $config_file | $docker_path run $user_args --rm -i -a stdin -a stdout $image ruby -e \
867 "require 'yaml'; puts YAML.load(STDIN.readlines.join)['update_pups']"`
868
869 if [[ ! X"" = X"$base_image" ]]; then
870 image=$base_image
871 fi
872
873 set_volumes
874 set_links
875
876 rm -f $cidbootstrap
877
878 run_command="cd /pups &&"
879 if [[ ! "false" = $update_pups ]]; then
880 run_command="$run_command git pull &&"
881 fi
882 run_command="$run_command /pups/bin/pups --stdin"
883
884 echo $run_command
885
886 (exec echo "$input" | $docker_path run $user_args $links "${env[@]}" -e DOCKER_HOST_IP=$docker_ip --cidfile $cidbootstrap -i -a stdin -a stdout -a stderr $volumes $image \
887 /bin/bash -c "$run_command") \
888 || ($docker_path rm `cat $cidbootstrap` && rm $cidbootstrap)
889
890 [ ! -e $cidbootstrap ] && echo "** FAILED TO BOOTSTRAP ** please scroll up and look for earlier error messages, there may be more than one" && exit 1
891
892 sleep 5
893
894 $docker_path commit `cat $cidbootstrap` $local_discourse/$config || echo 'FAILED TO COMMIT'
895 $docker_path rm `cat $cidbootstrap` && rm $cidbootstrap
896 }
897
898
899
900 case "$command" in
901 bootstrap)
902 run_bootstrap
903 echo "Successfully bootstrapped, to startup use ./launcher start $config"
904 exit 0
905 ;;
906
907 setup)
908 set_config
909 read -p "Press ENTER to continue, or Ctrl-C to exit to check $config_file"
910 run_bootstrap
911 exit 0
912 ;;
913
914 enter)
915 exec $docker_path exec -it $config /bin/bash --login
916 ;;
917
918 stop)
919 run_stop
920 exit 0
921 ;;
922
923 logs)
924
925 $docker_path logs $config
926 exit 0
927 ;;
928
929 restart)
930 run_stop
931 run_start
932 exit 0
933 ;;
934
935 start)
936 run_start
937 exit 0
938 ;;
939
940 rebuild)
941 if [ "$(git symbolic-ref --short HEAD)" == "master" ]; then
942 echo "Ensuring discourse docker is up to date"
943
944 git remote update
945
946 LOCAL=$(git rev-parse @)
947 REMOTE=$(git rev-parse @{u})
948 BASE=$(git merge-base @ @{u})
949
950 if [ $LOCAL = $REMOTE ]; then
951 echo "Discourse Docker is up-to-date"
952
953 elif [ $LOCAL = $BASE ]; then
954 echo "Updating Discourse Docker"
955 git pull || (echo 'failed to update' && exit 1)
956 exec /bin/bash $0 $@
957
958 elif [ $REMOTE = $BASE ]; then
959 echo "Your version of Discourse Docker is ahead of origin"
960
961 else
962 echo "Discourse Docker has diverged source, this is only expected in Dev mode"
963 fi
964
965 fi
966
967 set_existing_container
968
969 if [ ! -z $existing ]
970 then
971 echo "Stopping old container"
972 (
973 set -x
974 $docker_path stop -t 10 $config
975 )
976 fi
977
978 run_bootstrap
979
980 if [ ! -z $existing ]
981 then
982 echo "Removing old container"
983 (
984 set -x
985 $docker_path rm $config
986 )
987 fi
988
989 run_start
990 exit 0
991 ;;
992
993
994 destroy)
995 (set -x; $docker_path stop -t 10 $config && $docker_path rm $config) || (echo "$config was not found" && exit 0)
996 exit 0
997 ;;
998 esac
999
1000 usage