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